Skip to content

Conversation

@adasatorres
Copy link
Contributor

This implementation adds an option in the template to create the invoice and automatically create the payment, using the last payment token used by the customer.

@adasatorres adasatorres force-pushed the 16.0-imp-subscription_oca branch from 9e7db3c to 62928eb Compare October 30, 2025 15:28
@chrisandrewmann
Copy link

chrisandrewmann commented Oct 31, 2025

@adasatorres I did a quick test on runbot.

  1. Using "Azure Interior, Brandon Freeman", create saved payment token using "Demo" payment provider
  2. Create Subscription Template (monthly), setting the Invoicing Mode to "Invoice & Recurring Payment"
  3. Create new Subscription and add a product
  4. Manually run the scheduled action "Subscriptions Management"
  5. Error thrown below
Traceback (most recent call last):
  File "/opt/odoo/odoo/api.py", line 1013, in get
    cache_value = field_cache[record._ids[0]]
KeyError: 45

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/odoo/odoo/fields.py", line 1161, in __get__
    value = env.cache.get(record, self)
  File "/opt/odoo/odoo/api.py", line 1020, in get
    raise CacheMiss(record, field)
odoo.exceptions.CacheMiss: 'account.move.line(45,).name'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/odoo/odoo/http.py", line 1848, in _serve_db
    return service_model.retrying(self._serve_ir_http, self.env)
  File "/opt/odoo/odoo/service/model.py", line 152, in retrying
    result = func()
  File "/opt/odoo/odoo/http.py", line 1876, in _serve_ir_http
    response = self.dispatcher.dispatch(rule.endpoint, args)
  File "/opt/odoo/odoo/http.py", line 2080, in dispatch
    result = self.request.registry['ir.http']._dispatch(endpoint)
  File "/opt/odoo/odoo/addons/base/models/ir_http.py", line 154, in _dispatch
    result = endpoint(**request.params)
  File "/opt/odoo/odoo/http.py", line 763, in route_wrapper
    result = endpoint(self, *args, **params_ok)
  File "/opt/odoo/addons/web/controllers/dataset.py", line 47, in call_button
    action = self._call_kw(model, method, args, kwargs)
  File "/opt/odoo/addons/web/controllers/dataset.py", line 34, in _call_kw
    return call_kw(Model, method, args, kwargs)
  File "/opt/odoo/odoo/api.py", line 484, in call_kw
    result = _call_kw_multi(method, model, args, kwargs)
  File "/opt/odoo/odoo/api.py", line 469, in _call_kw_multi
    result = method(recs, *args, **kwargs)
  File "/opt/odoo/odoo/addons/base/models/ir_cron.py", line 104, in method_direct_trigger
    self.env.flush_all()
  File "/opt/odoo/odoo/api.py", line 761, in flush_all
    self._recompute_all()
  File "/opt/odoo/odoo/api.py", line 757, in _recompute_all
    self[field.model_name]._recompute_field(field)
  File "/opt/odoo/odoo/models.py", line 6335, in _recompute_field
    field.recompute(records)
  File "/opt/odoo/odoo/fields.py", line 1382, in recompute
    apply_except_missing(self.compute_value, recs)
  File "/opt/odoo/odoo/fields.py", line 1355, in apply_except_missing
    func(records)
  File "/opt/odoo/odoo/fields.py", line 1404, in compute_value
    records._compute_field_value(self)
  File "/opt/odoo/odoo/models.py", line 4276, in _compute_field_value
    fields.determine(field.compute, self)
  File "/opt/odoo/odoo/fields.py", line 98, in determine
    return needle(*args)
  File "/opt/odoo/addons/account/models/account_move_line.py", line 483, in _compute_name
    if not line.name or line._origin.name == line._origin.move_id.payment_reference:
  File "/opt/odoo/odoo/fields.py", line 1187, in __get__
    recs._fetch_field(self)
  File "/opt/odoo/odoo/models.py", line 3245, in _fetch_field
    self._read(fnames)
  File "/opt/odoo/odoo/models.py", line 3322, in _read
    cr.execute(query_str, params + [sub_ids])
  File "/opt/odoo/odoo/sql_db.py", line 324, in execute
    res = self._obj.execute(query, params)
psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block


The above server error caused the following client error:
RPC_ERROR: Odoo Server Error
    RPC_ERROR
        at makeErrorFromResponse (http://oca-contract-16-0-pr1331-62928ebfb7e6.runboat.odoo-community.org/web/assets/429-ecc5975/web.assets_backend.min.js:1003:163)
        at XMLHttpRequest.<anonymous> (http://oca-contract-16-0-pr1331-62928ebfb7e6.runboat.odoo-community.org/web/assets/429-ecc5975/web.assets_backend.min.js:1011:13)

@adasatorres
Copy link
Contributor Author

Hi @chrisandrewmann, I followed the steps you mentioned to try to reproduce the error, but it doesn’t generate any error for me.

I’m attaching some screenshots:

image image image image

@chrisandrewmann
Copy link

Hi @chrisandrewmann, I followed the steps you mentioned to try to reproduce the error, but it doesn’t generate any error for me.

I’m attaching some screenshots:

@adasatorres Sorry, not sure then.
Did you use the "base" runbot build? I think I used this one and installed only the subscription_oca module afterwards.
Otherwise not sure.

@adasatorres
Copy link
Contributor Author

@adasatorres Sorry, not sure then. Did you use the "base" runbot build? I think I used this one and installed only the subscription_oca module afterwards. Otherwise not sure.

@chrisandrewmann I’ve been reviewing the base runboat instance, and it seems that the error you were getting was caused by an issue when generating the invoice. I created a new subscription using the same template, and I also installed the demo provider, which wasn’t configured. Once everything was set up, I triggered the scheduled action button, and it generated all the invoices for the subscriptions.

@chrisandrewmann
Copy link

@adasatorres thanks, that's good to know! Did you get around to comparing with my own V18 version of this feature?
Interested if there is anything you would change.

@adasatorres
Copy link
Contributor Author

Hi @chrisandrewmann, I think the PR in its current state is the most streamlined option available. Personally, I would leave it as it is. I’d like to know your opinion and whether you would change anything, etc.

@chrisandrewmann
Copy link

Hi @chrisandrewmann, I think the PR in its current state is the most streamlined option available. Personally, I would leave it as it is. I’d like to know your opinion and whether you would change anything, etc.

Hi @adasatorres

My queries would be:

  1. How easy will it be to migrate to V17-V19?
  2. Does it support retrying failed payments, what happens when it fails?

Suggestions:

  1. I think my proposed way of handling payment tokens might be worth considering?
    From the code I sent you, I added a field to the subscription model for payment_token_id as well as the form.
    The benefit is that it allows you to modify the token if it should be different, for example if the customer has multiple saved payment tokens.
    payment_token_id = fields.Many2one(
        comodel_name="payment.token",
        string="Payment Token",
        domain="[('partner_id', '=', partner_id)]",
    )
  1. The logic I used to select payment token is different to yours. The benefit I see is that it allows you to set a preference for the payment method on the res.partner record, ensuring that only tokens for that method are available.
    For example the customer may want to pay via Stripe not Paypal but have multiple tokens saved.
    def _get_payment_token(self):
        self.ensure_one()
        active_token = False
        payment_method = self.partner_id.property_inbound_payment_method_line_id
        if payment_method:
            active_token = (
                self.env["payment.token"]
                .sudo()
                .search(
                    [
                        ("provider_id", "=", payment_method.payment_provider_id.id),
                        ("partner_id", "=", self.partner_id.id),
                    ],
                    limit=1,
                    order="write_date desc",
                )
            )
            return active_token

Copy link

@rrebollo rrebollo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review complete. LGTM!

Please consider @chrisandrewmann's suggestions — they make sense and would improve the implementation.

@chrisandrewmann
Copy link

chrisandrewmann commented Dec 9, 2025

@rrebollo @adasatorres

Done some more testing and another point i'd raise is that when confirming invoices in your automated workflow from Subscriptions, the email "Invoice: Sending" is sent to the customer BEFORE payment_state is set to "Paid". This causes it to read as requiring payment (Please remit payment at your earliest convenience).
Rather it should confirm payment, reconcile and THEN send the email to the customer notifying them it is already paid. Otherwise risks the customer paying twice.

image

@adasatorres
Copy link
Contributor Author

Hi @chrisandrewmann, I’m implementing the changes you mentioned, but the field property_inbound_payment_method_line_id does not exist in versions 16 and 17.

@chrisandrewmann
Copy link

Hi @chrisandrewmann, I’m implementing the changes you mentioned, but the field property_inbound_payment_method_line_id does not exist in versions 16 and 17.

Ok in that case maybe best to use your existing logic as a fallback instead, only if the payment_token_id is not manually set on subscription?

@adasatorres adasatorres force-pushed the 16.0-imp-subscription_oca branch 5 times, most recently from ed63f08 to 3e1800d Compare December 9, 2025 14:09
@adasatorres
Copy link
Contributor Author

Hi, @chrisandrewmann, could you review it again? I’ve implemented the two functionalities you mentioned, ignoring the issue with the res.partner field, which we can implement in Odoo 18+

@chrisandrewmann
Copy link

chrisandrewmann commented Dec 9, 2025

Hi, @chrisandrewmann, could you review it again? I’ve implemented the two functionalities you mentioned, ignoring the issue with the res.partner field, which we can implement in Odoo 18+

Thanks @adasatorres, i've just done a quick test and generally looks good. A few issues noted.

Test 1 steps

  1. Activate Demo payment provider
  2. Create an invoice manually against "Mitchell Admin", pay using portal and "Save Token"
  3. Create a new Subscription Template ("Monthly Auto")
  4. Create new Subscription
  5. Add product
  6. Move subscription to "In Progress" state
  7. Run the scheduler task
    Issue: Error occurred as payment token was missing. It looks like your code should be setting a default token but isn't?
    Invoice is still generated but payment fails.

Test 2 steps

  1. Same steps as before, this time manually setting the Payment Token field on the subscription
  2. Run the scheduler
  3. Invoice generated and payment successful
    Issue: There is no email sent to the customer after invoice payment is taken, was this intentional? Note I manually sent the email via button instead.

@adasatorres adasatorres force-pushed the 16.0-imp-subscription_oca branch from 3e1800d to e4962ff Compare December 10, 2025 11:34
@adasatorres
Copy link
Contributor Author

Good morning @chrisandrewmann , the issues you mentioned should already be resolved. Right now, if the partner doesn’t have a payment token to use, the invoice is sent for them to pay manually. If they do have a payment token, the invoice is sent but it already appears as paid.

@chrisandrewmann
Copy link

chrisandrewmann commented Dec 10, 2025

Good morning @chrisandrewmann , the issues you mentioned should already be resolved. Right now, if the partner doesn’t have a payment token to use, the invoice is sent for them to pay manually. If they do have a payment token, the invoice is sent but it already appears as paid.

Hi @adasatorres thanks a lot for this. Just tested and looks great to me.
I'll approve it now.

Something I think we should add for future though, is that currently the recurring payment only works for invoices.
It would be great if it can be supported for "Sale Order & Invoice" scenarios, which would be necessary if selling recurring physical goods as an SO is required to generate deliveries.

Copy link

@chrisandrewmann chrisandrewmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@OCA-git-bot
Copy link
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

@chrisandrewmann
Copy link

chrisandrewmann commented Dec 17, 2025

@carlos-domatix @carolina-domatix @tarteo would appreciate your reviews on this and also V17 + V18 here please:
#1356
#1357

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants