Friday, August 28, 2026
5 changes · saas-18.3
Resolved issues and error corrections
PDF quotes using Quote Builder with dynamic fields no longer fail during generation in affected Python/pypdf environments. The fix preserves existing PDF form settings while adding the missing form resource structure needed to avoid the crash.
Original PR description
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1-…
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1- Using a python 3.13 env, install requirements.txt. (You could instead uninstall pypdf2 and install pypdf==5.4.0) 2- Enable Quote Builder. 3- Create a SO and in quote builder tab, select a document. This document should have dynamic fields. e.g. you could use`Office Furnitures Header` document. 4- Print -> PDF Quote. Cause: --- In previous fix, we fixed the traceback when no dynamic field is set. However, if you have a dynamic field, then inside `PdfWriter._update_field_annotation()`, the font is get from `DR` dict inside acroform: https://github.com/py-pdf/pypdf/blob/f20954f2241640feb484800e191373f8fbdfa44b/pypdf/_writer.py#L917-L929 Even if we are not setting a font, we need to have an empty `DR` dict inside acroform, in order to avoid calling `get` on a none object, which is leading to the traceback. Also in previous fix we were losing `NeedAppearances` inside `AcroForm` by creating new dict which wasn't right. opw-6392001 Forward-Port-Of: odoo/odoo#278881
This fix ensures Uruguayan electronic invoicing uses the exact exchange rate saved on the original invoice, rather than recalculating it later. This prevents mismatches on credit notes when currency rates are updated after an invoice is posted.
Original PR description
18.0 introduced an `invoice_currency_rate` field, storing the currency rate used for each invoice. Currently `_l10n_uy_edi_get_used_rate` uses the `currency.convert()` method using the date and company of the invoice. This is vulnerable to inaccuracy if the currency rates are changed after the invoice posting. For example, if an invoiceis posted and the currency rates are updated, the invoice will use the old rate, while `_l10n_uy_edi_get_used_rate` will return the new one. In the case of the customer in the related ticket, this caused their credit note reference currency rate to mismatch with the one on the invoice. This PR changes the `currency.convert()` call to fetching and inverting the `invoice_currency_rate` field. This ensures the returned value will match the one used for the invoice. opw-6456867 Forward-Port-Of: odoo/enterprise#128190
Checkout now detects carts that become zero-priced due to country-based pricing rules when zero-priced product sales are blocked. Instead of hanging during payment, customers are sent back to the cart with a clear warning, reducing failed checkout experiences and support issues.
Original PR description
When 'Prevent Sale of Zero Priced Product' is enabled and a country-group pricelist prices a product at 0 once the customer's country becomes known during checkout, the cart total becomes 0. The 'free order' branch then validated the order at the payment step, which could hang and time out for a cart that is not actually sellable. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284959
This fixes an issue where timesheets could lose their connection to an invoice after an invoice was reversed and recreated through a credit note. Businesses can now keep accurate links between billed timesheets and replacement invoices, improving billing traceability and reducing manual cleanup.
Original PR description
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior…
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior before PR: When reversing an invoice tied to timesheets and creating a replacement via a credit note, the timesheets linked to the original invoice have their timesheet_invoice_id cleared. Because the modify_moves function builds the replacement invoice directly via copy_data()/create(), it bypasses the normal sale order invoicing flow. As a result, the unbilled timesheets are left permanently unlinked from the newly created invoice, leaving the new invoice with no reference to the timesheets linked to the original sale order. ### Desired behavior after PR is merged: When a replacement invoice is created, each timesheet is properly relinked to the corresponding line on the new invoice. This linkage matches on the sale order line (so_line) rather than line position, ensuring accuracy since line order and count are not guaranteed to be preserved between the original and modified invoices. opw-6449995 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283104
This fix prevents subscription invoicing from crashing when an automatic payment fails because of an invalid or faulty payment token. It helps recurring billing jobs continue handling failures cleanly instead of stopping with an error, reducing manual intervention for subscription operations.
Original PR description
Step to reproduce: - create a faulty token that won't work and link it to a subscription - launch the recurring invoice cron - the following traceback occurs ``` last_tx_sudo = (self.transaction_ids…
Step to reproduce:
- create a faulty token that won't work and link it to a subscription
- launch the recurring invoice cron
- the following traceback occurs
```
last_tx_sudo = (self.transaction_ids - existing_transactions).sudo()
```
When the payment fails, the system rollback and we store the last_tx_sudo value in a dedicated variable. After rollback, the record does not exists anymore. Therefore, accessing the value fails.
```
File "/home/odoo/src/enterprise/saas-18.2/sale_subscription/models/sale_order.py", line 1703, in _handle_automatic_invoices
if not last_tx_sudo or last_tx_sudo.renewal_state in ['pending', 'authorized']:
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1439, in __get__
self.compute_value(record)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1603, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/models.py", line 4575, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 69, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-18.2/sale_subscription/models/payment_transaction.py", line 25, in _compute_renewal_state
if tx.state in ['draft', 'pending']:
^^^^^^^^
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1406, in __get__
raise MissingError("\n".join([
odoo.exceptions.MissingError: Record does not exist or has been deleted.
```
Moreover, since https://github.com/odoo/enterprise/pull/45236/files#diff-c36fd7952cc2bef40716419a668de41963d49e1aa4177d9319d503fc260da588R1678-R1682
```
if not last_tx_sudo or not last_tx_sudo.renewal_state not in ['pending', 'authorized']:
```
has become
```
if not last_tx_sudo or last_tx_sudo.renewal_state in ['pending', 'authorized']:
```
But it feels strange to unlink the invoice when the payment succeed.
This PR fixes it.
Forward-Port-Of: odoo/enterprise#83913