Thursday, October 9, 2025
19 changes · saas-18.3
Enhancements to existing features
The Peppol configuration screen now remains available even if the external service list cannot be loaded. This lets users continue important actions such as unregistering instead of being blocked by a temporary service error.
Original PR description
Handle `api/peppol/2/get_services` errors gracefully, without blocking critical section of peppol functionnal flow (deletion). If the API endpoint for services returns an error (which should not be affecting any users), the whole peppol config wizard is no longer accessible. The users will therefore not be able to unregister. Note that with this change, if we get an API error, all services will be marked as disabled (which is fair, and better than displaying an API request error) no-task Forward-Port-Of: odoo/odoo#228800
Resolved issues and error corrections
Fixes an issue where users could see an error message after successfully reconciling the last available batch payment in the bank reconciliation widget. The reconciliation itself worked, but the confusing traceback interrupted the workflow; this change keeps the screen stable when no batch payments remain.
Original PR description
…batch In the bank reconciliation widget, after reconciling the last batch payment, a traceback will be shown to the user. Steps to reproduce: - Create and confirm a customer payment - Create a batch…
…batch
In the bank reconciliation widget, after reconciling the last batch payment, a traceback will be shown to the user.
Steps to reproduce:
- Create and confirm a customer payment
- Create a batch payment including the above payment (ensure this is the only batch payment available for reconciliation)
- Open Bank reconciliation widget
- Create a Bank transaction to reconcile with the batch payment
- Add the batch payment and click "Validate"
Issue: Reconciliation succeeds, but a traceback will be shown:
```
OwlError: The following error occurred in onWillUnmount: "Cannot set properties of undefined (setting 'exportState')"
Error
at wrapError (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11557:23) (/web/static/lib/owl/owl.js:2685)
at onWillUnmount (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11626:34) (/web/static/lib/owl/owl.js:2754)
at BankRecBatchPaymentsRenderer.setup (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:217012:9) (/account_accountant_batch_payment/static/src/components/bank_reconciliation/batch_payments_list_view.js:11)
at new ComponentNode (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11305:28) (/web/static/lib/owl/owl.js:2433)
at http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:14948:28 (/web/static/lib/owl/owl.js:6076)
at BankRecEmbeddedListController.slot7 (eval at compile (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:14679:20), <anonymous>:128:32) (/web/static/lib/owl/owl.js:5807)
at callSlot (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11922:37) (/web/static/lib/owl/owl.js:3050)
at Layout.template (eval at compile (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:14679:20), <anonymous>:39:10) (/web/static/lib/owl/owl.js:5807)
at Fiber._render (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:10655:38) (/web/static/lib/owl/owl.js:1783)
at Fiber.render (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:10647:18) (/web/static/lib/owl/owl.js:1775)
Caused by: TypeError: Cannot set properties of undefined (setting 'exportState')
at BankRecBatchPaymentsRenderer.saveSearchState (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:217039:32) (/account_accountant_batch_payment/static/src/components/bank_reconciliation/batch_payments_list_view.js:38)
at BankRecBatchPaymentsRenderer.<anonymous> (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11571:26) (/web/static/lib/owl/owl.js:2699)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11400:24) (/web/static/lib/owl/owl.js:2528)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11404:23) (/web/static/lib/owl/owl.js:2532)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11404:23) (/web/static/lib/owl/owl.js:2532)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11404:23) (/web/static/lib/owl/owl.js:2532)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11404:23) (/web/static/lib/owl/owl.js:2532)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11404:23) (/web/static/lib/owl/owl.js:2532)
at ComponentNode._destroy (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11404:23) (/web/static/lib/owl/owl.js:2532)
at ComponentNode.beforeRemove (http://127.0.0.1:8982/web/assets/debug/web.assets_web.js:11534:18) (/web/static/lib/owl/owl.js:2662)
```
This occurs because we want to hide the Batch Payments tab from the reconciliation widget when no more batches are available for reconciliation but the `saveSearchState` still expects the batch payments to be defined in the view.
opw-4830466
Forward-Port-Of: odoo/enterprise#96598Stripe payments now handle currencies like the Ugandan shilling that use different decimal rules in Stripe than in standard currency settings. This prevents valid payments from being rejected or sent with the wrong amount, improving checkout reliability for affected currencies.
Original PR description
Steps: - Activate the 'UGX' currency. - Make a sale order with amount 100 with 'UGX' currency - Try to pay that order (100 USh) using card Issue: - stripe throws the following error - > 'The Checkout Session's total amount must convert to at least 50 cents. 1.00 USh converts to approximately €0.00.' - Hence 100 USh sent was identified as 1 USh by stripe. This confirms issue with decimals and currency mapping. Cause: - 'UGX' is zero-decimal currency but stripe identify it as two-decimal. Fix: - Update mapping for such special currency cases for stripe that don't follow general rules opw-5075707 Forward-Port-Of: odoo/odoo#227688
Tables pasted from tools like Google Docs now keep the expected formatting and structure in Odoo's HTML editor. This prevents invisible or incomplete tables and makes pasted content easier to edit reliably.
Original PR description
### Purpose of this PR: - Ensure that pasted table elements get the standard classes: `table, table-bordered, and o_table.` - When content is pasted from other source (e.g., Google Docs inside iframe), attribute nodes coming from another JavaScript context do not match the `Attr` prototype of the current context. Use `item.nodeType === Node.ATTRIBUTE_NODE` instead of `instanceof Attr` to detect attribute nodes. - Insert a base container into empty `<td>` elements when pasting tables from external sources. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#230425 Forward-Port-Of: odoo/odoo#230208
The bank reconciliation widget now keeps existing invoice matches when users choose a write-off account that has a default tax. This prevents matched bills from disappearing during reconciliation, reducing rework and avoiding confusion for accounting users.
Original PR description
In the Bank reconciliation widget, users can click a button to set the account to write off the remaining balance. However, if the chosen account has a default tax set, the widget will lose any existing matches with invoices. Steps to reproduce: - Have an account with a default tax - Create a bill with a total - Create a bank statement for a greater amount - Open the bank reconciliation widget - In the created statement, first add the bill, then click 'Set Account,' and choose the account with tax Issue: Bill matching will be lost. This occurs because we remove and recreate the matching line, but we don't keep the line to be reconciled. opw-5002624
Fixes a checkout issue where valid promotions or coupons could be removed because the storefront and payment confirmation used different time zones or timing. This helps prevent paid orders from getting stuck as unconfirmed and ensures expired coupon discounts are removed before payment is finalized.
Original PR description
Versions -------- - 17.0+ Steps ----- 1. Have a database with GeoIP enabled; 2. have a promotion program that gives a discount on a specific product; 3. use a VPN to browse /shop from California…
Versions -------- - 17.0+ Steps ----- 1. Have a database with GeoIP enabled; 2. have a promotion program that gives a discount on a specific product; 3. use a VPN to browse /shop from California before 7:00 UTC; 4. add the product to your cart; 5. go to checkout; 6. pay for the order. Issue ----- Order cannot be confirmed due to incomplete payment. Cause ----- The `_frontend_pre_dispatch` method of `website` adds a timezone value to the context based on the request's `geoip`. This context value then gets used to find applicable loyalty programs via `Date.context_today`, and applies them to the order. Then after payment was initiated, the order gets validated again using server time (UTC), which now considers the applied program expired, and removes the reward before confirming the order. Consequently, with the discount removed, the paid amount no longer matches the order total, thus the order remains unconfirmed. Solution -------- If the order has a confirmed transaction, use its `create_date` to verify loyalty expiration dates. For time zone, instead of `Date.context_today`, using whatever `tz` value is in the context, use a helper function which retrieves the current day in the company's timezone. For website orders, if defined, use the eCommerce salesperson's time zone instead. Also fix an issue where expired coupon lines weren't getting removed from the order. opw-4765873 opw-4781346 opw-4939268 Forward-Port-Of: odoo/odoo#229749 Forward-Port-Of: odoo/odoo#222428
Product searches in Point of Sale now show all relevant matches instead of stopping at an exact match. This helps cashiers find similar or related products more reliably, reducing missed items during checkout.
Original PR description
The POS search only returned products with an exact match (when existing), ignoring other relevant products that partially matched the search string. Steps to reproduce: 1. Create a product "TEST" - Create a variant with attributes value including "TEST" and "OTHER". 2. Create a second product "TEST 2". 3. Open the POS. 4. Search for "TEST". 5. Only "TEST" is shown; "TEST 2" is missing, even with "Search more". To align with the behavior introduced in v18, I’ve removed the exact match condition, as it no longer appears necessary due to the absence of fuzzy search. I’ve also adjusted the logic to perform the search on `product.product` instead of `product.template`. opw-4958141 Forward-Port-Of: odoo/odoo#221520
This fix prevents an error when saving a manufacturing order after changing the duration of a work order that has not yet been scheduled. It ensures only scheduled work orders are used to calculate production start and finish dates, improving reliability for manufacturing teams using work orders.
Original PR description
**Steps to reproduce:** 1. In Settings, enable "Work Orders". 2. Create a product with a BOM that has 2 operations: op1 and op2. 3. Create and confirm an MO for 1 unit. 4. Start op1 and change the…
**Steps to reproduce:** 1. In Settings, enable "Work Orders". 2. Create a product with a BOM that has 2 operations: op1 and op2. 3. Create and confirm an MO for 1 unit. 4. Start op1 and change the Real Duration of op2. 5. Try to save. **Issue:** - Traceback : `'<' not supported between instances of 'datetime.datetime' and 'bool'` **Cause of the issue:** Starting the first operation launches a call of the `button_start` method creating a `resource.calendar.leaves` to set on the `leave_id` of this first operation: https://github.com/odoo/odoo/blob/97a70e71c32ea6183f87fd5eb558b32bcbd2d231/addons/mrp/models/mrp_workorder.py#L630-L641 Then, setting the duration of the second operation from the form view of the MO and saving triggers a call of the write of the MO containing the `[Command.update(op_2.id, new_duration)]` as vals.This, in turn, calls `_plan_workorders`: https://github.com/odoo/odoo/blob/97a70e71c32ea6183f87fd5eb558b32bcbd2d231/addons/mrp/models/mrp_production.py#L939-L942 while the first operation has a set `leave_id` but the second do not However, the `min` operator will be applied to both the set and the unset values, comparing a `boolean` with a `datetime`: https://github.com/odoo/odoo/blob/97a70e71c32ea6183f87fd5eb558b32bcbd2d231/addons/mrp/models/mrp_production.py#L1581-L1588 **Solution:** Only workorders with a `leave_id` (i.e., those planned in work-center schedule) should be considered when computing MO `date_start` and `date_finished`. Workorders without a `leave_id` are not yet scheduled and therefore should not influence MO start and end dates. As both `date_start` and `date_finished` of a workorder are related to the `leave_id` record. As per `mrp_workorder._compute_dates`, these dates reflect the work-center scheduling (`leave_id.date_from` and `leave_id.date_to`). https://github.com/odoo/odoo/blob/6a075fa3c090920499ccbd5fe673819da7e3f95e/addons/mrp/models/mrp_workorder.py#L250-L260 Therefore, when computing MO dates, it logically follows that only workorders with an assigned `leave_id` should be used. This avoids mixing unscheduled operations (`leave_id = False`) with scheduled ones, preventing invalid comparisons and ensuring accurate production timing. **opw-5068080** Forward-Port-Of: odoo/odoo#226436
This fixes an issue where French POS order integrity hashes could be created before all order details were finalized. The change helps prevent valid orders from being incorrectly flagged as altered during compliance checks.
Original PR description
Description of the issue/feature this PR addresses:
Starting from version 18.0, inalterability hashes in the FR localization are sometimes calculated with incomplete data, leading to incorrect `l10n_fr_hash`. Related orders are then wrongly flagged as altered when running the POS Inalterability Check.
Current behavior before PR:
When you post a POS order and a related draft order exists, Odoo updates the existing order with the new vals. Because `{'state': 'paid'}` is amongst the new vals, it triggers the generation of the `l10n_fr_hash` before the order is fully processed. For instance, the hash will be generated before a new payment line is added for change with `_process_payment_lines()`.
Desired behavior after PR is merged:
The hash should be generated at the end of the order processing, with the final write in `action_pos_order_paid()`.
Forward-Port-Of: odoo/odoo#226032Fixes an issue where opening a shared employee profile link could display an error when the profile contained private information. Users now receive a clearer message and can be redirected to the public employee list, reducing confusion and improving reliability.
Original PR description
Sharing a link of an employee profile containing private info generated a traceback. Permissions had to be applied to the private field. I've also put a more explicit error message that allows the user to get redirected to the public employee list. I couldn't find a way to get the employee id from the url before the generic permission warning comes in. Thus I had to resort to redirecting to the general public employees list. Other tracebacks may happen each time a private field without the corresponding groups is put in the xml. Thus I added a test to prevent us from doing that again. Forward-Port-Of: odoo/odoo#229611 Forward-Port-Of: odoo/odoo#228623
Installing the POS Settle Due module now applies its required products to every point-of-sale configuration, including those with active sessions. This prevents checkout issues when staff need to settle dues, take deposits, or settle invoices in POS.
Original PR description
Before this commit, when the module pos_settle_due was installed, the special products (settle due, deposit, settle invoice) were only set on the POS configurations that did not have any open session. This could lead to issues when trying to use these products in a POS session of a configuration that did not have them set. Now, the special products are set on all POS configurations when installing the module. Community PR: https://github.com/odoo/odoo/pull/229074
This fix prevents essential point of sale products, such as discounts, tips, and settlement items, from being deleted or archived. It helps avoid checkout and configuration issues caused by missing required products.
Original PR description
Before this commit, it was possible to delete or archive some products even if they were special for the pos (discount, tips, settle, etc.). This commit adds a mechanism to prevent this and reduce the risk of errors linked to missing products in the pos. Enterprise PR: https://github.com/odoo/enterprise/pull/95789 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Releasing an unused restaurant table no longer leaves behind a pointless draft order. Orders already sent to the kitchen are still cancelled properly to keep kitchen records accurate, while unused orders are removed entirely.
Original PR description
Steps to reproduce: ------------------------- - Install POS restaurant. - Open any table to order. - Release the table. Issue: ------- - A draft order is created without purpose or use. Cause: --------- - On releasing the table we were not deleting the order, we were just cancelling the order even if it's not useful. Fix: ----- - We have called a proper function to manage the conditions like - If the order is sent to kitchen it will cancel the kitchen ticket to avoid inaccuracy kitchen side and already recorded on server so it will be cancelled and if the order was not sent to kitchen than there is no need of the order so will be removed totally. - We have corrected condition to send order in kitchen as `last_order_preparation_change` will always have some keys with blank values but we need to send data based on the lines changed in lopc. task: 4774814
This fix ensures a Spanish 0% VAT tax for services outside the EU is reported as not subject to VAT instead of as an export in Modelo 303. It also corrects the refund sign for the related service tax, helping Spanish VAT returns reflect these transactions more accurately.
Original PR description
The s_iva_e tax (IVA 0% Extracomunitaria (Servicios)) is configured as no_sujeto_loc, but is reported as "Exportacion" in modelo 303. There might have been the idea that we need a tax for services that are just a complement to some goods, but this tax is not used that way in practice. So, it is better to treat it as a duplicate of the s_iva_ns tax (Not Subject To VAT (services)) where we also see that the refund sign was wrong. opw-5079297 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#230383 Forward-Port-Of: odoo/odoo#229752
Fixes Italian fiscal printer receipts so they print automatically even when the POS is configured to skip the receipt screen. The update also ensures the correct completed sale is printed, avoiding missing or incorrect fiscal receipts for businesses using Italian POS compliance features.
Original PR description
This PR fixes two related bugs happening when printing a receipt with the Italian Fiscal printer First, before this PR, if the option to skip the receipt screen was ticked, then the receipt would…
This PR fixes two related bugs happening when printing a receipt with the Italian Fiscal printer First, before this PR, if the option to skip the receipt screen was ticked, then the receipt would never print. This was caused by the printing logic being implemented on the receipt screen instead of on the pos itself. steps to reproduce: 1. install l10n_it_pos 2. configure one pos 3. configure the IT printer 4. select to skip the receipt screen (print automatically) 5. open the pos 6. make a sale => no ticket printed and the chrome console shows a printer error With this new verison the printing logic was moved to the pos so that printing of fiscal receipts with the italian fiscal printer works, even when receipt screen is skipped. This put to light another potential bug related to how the `order` variable was treated. Before this PR, the printReceipt logic in the module would not pass the order to be printed. This can become a problem upon context changes, where `pos.get_order()` does not return the completed order, but a newly created one. This can for example happen when skipping the receipt screen with the option to "print automatically" (iface_print_auto). After this PR, we keep order as an argument, so we always print the last completed order and not a newly created one. opw-4882480 Forward-Port-Of: odoo/enterprise#95985 Forward-Port-Of: odoo/enterprise#91412
This update prevents Chrome on iOS from automatically changing certain text on Odoo pages in a way that could disrupt the interface. It helps keep screens stable and usable for affected mobile users, especially on Chrome iOS versions where the browser behavior reappeared.
Original PR description
Chrome iOS wraps some text nodes (like measures, email...) with a `<chrome_annotation>` tag, which breaks OWL rendering. This commit works around it by adding the (undocumented) `<meta name="chrome"…
Chrome iOS wraps some text nodes (like measures, email...) with a `<chrome_annotation>` tag, which breaks OWL rendering. This commit works around it by adding the (undocumented) `<meta name="chrome" content="nointentdetection">` tag to disable this Chrome behavior. The tag has to be set before the onDOMContentLoaded event to be taken into account. Note: Looks like this behavior was present in Chrome iOS 127 and disabled afterward (because it already had issues) but it appeared again in version 140-141. References: - https://issues.chromium.org/issues/353650041 - https://issues.chromium.org/issues/388718411 - https://stackoverflow.com/questions/78207646/how-do-i-disable-chrome-annotation-tags - https://stackoverflow.com/questions/78575970/prevent-auto-detection-of-phone-numbers-in-chrome-mobile - https://stackoverflow.com/questions/78725191/stop-chrome-ios-auto-detecting-numbers-followed-by-letter-m-as-metre-units-an - https://github.com/solidjs/solid/issues/2235 opw-4969197 Forward-Port-Of: odoo/odoo#230081
Adyen payment requests now include extra checkout details required by some payment methods, such as Klarna. This helps prevent affected transactions from failing when country information and order line items are needed to complete payment.
Original PR description
Some payment methods eg. Klarna require 'country code' and 'line items' in order to process the transaction. opw-5077617 Forward-Port-Of: odoo/odoo#230292
The manufacturing Bill of Materials overview now avoids showing a planning error when the maximum producible quantity cannot fit into the long-term work center schedule. Instead, it falls back to the requested quantity, allowing users to open the overview and continue replenishment planning normally.
Original PR description
### Steps to reproduce: 1. Install mrp + purchase 2. Create a new product (A) 1. Add the Buy route on the product 2. Add a vendor line on the Purchase tab 3. Set the quantity on hands to 2000 3.…
### Steps to reproduce: 1. Install mrp + purchase 2. Create a new product (A) 1. Add the Buy route on the product 2. Add a vendor line on the Purchase tab 3. Set the quantity on hands to 2000 3. Create a second product (B) with manufacturing route 4. Create a BoM for this product (B) 1. Add the product (A) as the component with 1 quantity 2. Create a new operation with a duration of 600:00 5. On the product B's page, click Replenish 1. Put 10 quantities to replenish 2. Select the manufacturing route and confirm 6. Go to the BoM and open the BoM overview 7. 'Impossible to plan. Please check the workcenter availabilities.' https://github.com/user-attachments/assets/58697fd9-4e3e-4df6-98e1-5de7e8759715 ### Before this commit: When opening the BoM overview, if the producible quantity for this BoM exceed the quantity we can plan in the 700 following days, an error is displayed. ### After this commit: If the quantity producible cannot be planned, we retry automatically with the requested quantity. opw-5031724 Forward-Port-Of: odoo/odoo#229745 Forward-Port-Of: odoo/odoo#227433
Emails sent from Odoo could fail when they included an attached email file containing accented or other non-English characters. This fix makes those attachments handled correctly, improving reliability for users sharing email conversations through the system.
Original PR description
The previous fix for `message/rfc822` attachments forced binary encoding (`cte='binary'`) to comply with RFC 2046. However it also introduced a new issue: emails containing `.eml` attachments with…
The previous fix for `message/rfc822` attachments forced binary encoding (`cte='binary'`) to comply with RFC 2046. However it also introduced a new issue: emails containing `.eml` attachments with non-ASCII characters could not be serialized ### Steps to reproduce 1. Send an email via the chatter with a `.eml` file attached containing non-ASCII characters (e.g., "é") in its body. The sending of that email will fail with a `UnicodeEncodeError` error ### Cause Commit 6197233ef1611ddd974cfdb06ae2568e4af369de attempted to fix an issue where `.eml` (`message/rfc822`) attachments were not RFC-compliant. It did this by forcing the `Content-Transfer-Encoding` to `binary` for the raw byte content of the attachment. While this worked for simple ASCII attachments, it failed for attachments containing non-ASCII characters. When Python's `email` library later tried to serialize the entire message, it treated the attachment's content as an opaque binary blob. It did not understand the character encoding within that blob, leading to a `UnicodeEncodeError` during the final serialization process. ### Fix Instead of attaching the raw bytes, we now: * Parse `.eml` contents using `email.parser.BytesParser`, producing a proper `Message` object. * Attach the parsed message directly, letting the email library handle correct encoding and transfer settings automatically. opw-4655868 Forward-Port-Of: odoo/odoo#230384 Forward-Port-Of: odoo/odoo#223790