Wednesday, September 9, 2026
9 changes · 18.0
Resolved issues and error corrections
This fixes a checkout issue where valid free reward products from loyalty promotions could block customers from completing their order. Customers can now continue checkout when a zero-priced item is a legitimate loyalty reward, while other zero-price product safeguards remain in place.
Original PR description
Steps to reproduce: - In a fresh 18.0 database (not runbot), install `website_sale_loyalty` - Website > Configuration > enable "Prevent Sale of Zero Priced Product" - Set up a "Buy X Get Y" loyalty…
Steps to reproduce: - In a fresh 18.0 database (not runbot), install `website_sale_loyalty` - Website > Configuration > enable "Prevent Sale of Zero Priced Product" - Set up a "Buy X Get Y" loyalty program to reward a free product (the 3 Large Cabinet one from demo data will work) - Go to website and add one of the product to cart - Go the the shop page and increase the quantity one at a time Issue: The first additional product over the threshold works fine, but adding one more after that will disable the checkout button and give error message "Warning! Some products in your cart are not available for purchase in your country. Please remove them or contact us." Cause: When the reward line is first added, it has the default `price_unit` of the product and 100% discount. On subsequent changes, `price_unit` is reset to `0.00` and gets caught by this check introduced in #284959. The unit_price of the reward order line is changed to 0 in `_reset_loyalty()@sale_loyalty/models/sale_order_line.py` and is needed for other logic to successfully complete. Fix: Another exception is added to `_get_zero_priced_lines()` so order lines from reward products are exempt from the check, using `is_reward_line` opw-6528237 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents the Point of Sale customer list from crashing after a cashier removes a coupon reward from an order. Staff can continue selecting customers normally, while valid loyalty cards and coupons remain available.
Original PR description
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line -…
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line - Click the customer button Issue: The customer list does not open. The PoS crashes with "TypeError: Cannot read properties of undefined (reading 'id')" raised while rendering PartnerLine. Cause: `partnerId2CouponIds` maps a partner to the ids of its `loyalty.card` records. It is filled at boot and on every `loyalty.card` create, but nothing ever removes an id from it: the models only trigger a create event. Removing the reward line of a code activated coupon deletes that card from the local models (`_setValue` in the order summary), so its id stays in the map while the record is gone. `getLoyaltyCards` pushed `models["loyalty.card"].get(id)` unconditionally, hence an `undefined` entry in the list the PartnerLine template iterates over with `t-key="_loyaltyCard.id"`. Fix: Skip the ids whose record no longer exists. The partner keeps its remaining cards, and the path where no card was deleted is unchanged. The stale id is not pruned from the map: it is reached through the reactive store proxy, and mutating it there would notify subscribers in the middle of a render. opw-6517564 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Self-service kiosk orders are now marked as already sent to the kitchen when the kiosk prints the kitchen ticket. This prevents staff from accidentally sending the same order again from the main Point of Sale, reducing duplicate food preparation and kitchen confusion.
Original PR description
Steps to reproduce: - Restaurant PoS with a kitchen printer, self-ordering in kiosk mode - Place an order on the kiosk: the kiosk prints the kitchen ticket - Open the same order in the PoS and press "Order" Issue: The PoS prints every line of the order again as a new one, so the kitchen prepares the order twice. Cause: The kiosk prints its own kitchen tickets (printKioskChanges) but never records that in last_order_preparation_change, the state the PoS diffs against to find the lines that still have to be sent. The order thus reaches the PoS with all its lines unsent. Fix: Update last_order_preparation_change before the kiosk submits the order to the server, as 19.0 already does since 555df96ac87a. Only in kiosk mode: in 18.0 a mobile order is still sent to the kitchen by the cashier from the PoS, so it has to stay unsent. opw-6520435 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes a Point of Sale issue where selling two physical gift cards of the same value could combine them into one line with a duplicate code. Each gift card, e-wallet, or discount line that must stay separate now remains separate, preventing checkout failures and unsynced orders.
Original PR description
Steps to reproduce: - Create a gift card program (several programs sharing the same gift card product show the same issue) - In the PoS, sell a physical gift card: click the gift card product and set…
Steps to reproduce:
- Create a gift card program (several programs sharing the same gift card product show the same issue)
- In the PoS, sell a physical gift card: click the gift card product and set a code through "Sell physical gift card?"
- Click the gift card product again to sell a second physical card of the same value
- Validate the order
Issue:
The second unit is merged into the already coded orderline (one line, qty 2, one code), so the "Sell physical gift card?" link is no longer displayed and the second code cannot be entered. Validating the order then fails with "The operation cannot be completed: A coupon/loyalty card must have a unique code." and the order stays unsynced: the qty 2 line is split into two point entries both carrying the same gift_code, so two loyalty.card records are created with the same code.
Cause:
_setupGiftCardOptions() (and setupEWalletOptions()) pass merge=false so that a gift card line is never merged, and until 17.0 add_product() honored it ("options.merge !== false"). The 18.0 store refactoring dropped it: addLineToOrder() decides merging from a local variable that is only set to false when a price_unit is given in vals, and never reads opts.merge. The option became dead code, in point_of_sale's addLineToOrder as well as for the pos_discount caller.
Fix:
Honor opts.merge === false in addLineToOrder(). Callers that do not pass the option are unaffected, so the default merging behaviour is byte-for-byte the same; only the callers explicitly forbidding a merge (gift card, ewallet and discount lines) get their pre-18.0 behaviour back.
opw-6466324
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis fixes a problem where the HTML editor could keep outdated content after detecting a stale document. The editor now uses the latest saved version from the record, helping users return to the correct server-held content instead of being stuck with stale text.
Original PR description
Before this commit, an editor recovering from a stale document could stop on this error and keep the stale content:
```
Error: Concurency detected while recovering from a stale document. The
last history id of the server is different from the history id received
by the html_field_write event.
at CollaborationOdooPlugin.resetFromServerAndResyncWithPeers
```
This happens because the recovery compares the history id of the record with the one the html_field_write event carried. A write keeps only the last step id in the field, so an event handled after a later write names an id the record no longer holds. As a result, the recovery stops there and the document stays stale.
This commit fixes the issue by taking the history id read from the record as the new server reference, so the editor converges on the document the server holds.
https://runbot.odoo.com/odoo/error/944595Right-to-left Arabic text entered in multiline signature fields now aligns correctly in downloaded PDF documents. This improves document readability and presentation for users signing or generating Arabic-language documents.
Original PR description
Issue: ---------------------------------------- Arabic text in a rigth-to-left text area is not aligned in the downloaded PDF documents. Steps to reproduce: ---------------------------------------- -…
Issue: ---------------------------------------- Arabic text in a rigth-to-left text area is not aligned in the downloaded PDF documents. Steps to reproduce: ---------------------------------------- - Create a template with a rigth-to-left textarea., Make it fillable by the user. - Click "Sign Now" - Enter multiline arabic text - Validate and download PDF - The lines aren't aligned in the PDF Cause: ---------------------------------------- We compute the empty space before the line with `stringWidth()` on the line but this method doesn't do text-shaping on arabic text. We then do the text-shaping using `reshape_text()` and draw the line. the text shpaing "merged" some caracters making `stringWidth()` return a higher number than the actual drawned line. Solution: ---------------------------------------- Call `reshape_text()` before `stringWidth()`. Before: <img width="937" height="247" alt="image" src="https://github.com/user-attachments/assets/8a43a677-fd29-4d8e-a1cd-419b358667b9" /> After: <img width="896" height="213" alt="image" src="https://github.com/user-attachments/assets/cf8d0ebe-e363-4e07-9eb5-e4963e5ca137" /> opw-6438643x
This fixes a Mexican electronic invoicing issue where payment documents could show a slightly different exchange rate than the related invoice, even when both used the same official rate date. Payments now keep the stored official rate when the difference is only a rounding tolerance, reducing CFDI mismatches and potential validation issues.
Original PR description
Issue: Currency rate for payment is recomputed up to 6 digit. However, it may differ from the official one stored in db up to 4 digits. Which creates differencies between invoice and payment rate…
Issue: Currency rate for payment is recomputed up to 6 digit. However, it may differ from the official one stored in db up to 4 digits. Which creates differencies between invoice and payment rate made at the same date. Steps to reproduce: - In MX company, - Enable USD, - Set currency rate for today to 1 USD = 17.4455 MXN - Create a PPD invoice (due date > 40 days) in USD - Add a line with - qty: 1, - unit_price: 3.488 - tax: 16% (default tax) - Send it to CFDI - Create payment - On the invoice Form click on "Update Payment" Current behavior: - In the CFDI sheet, Payment and Invoice XML files will have different currency rates Expected behavior: - In the CFDI sheet, Payment and Invoice XML files will have the same currency rate Cause: PACs require having the payment `amount` to be equal to `currency_amount * currency_rate`. For huge amout it may happen that using the 6 digits rounding of currency rate to compute the amount won't fall exactly on the two digit precision for the amount and payment would be refused. Therefore, for all payment, we recompute a 6 digits precision `currency_rate` from `amount` and `currency_amount` then using it to compute the final amount. However, Banxico (Mexican central Bank) publish rates with a 4 digit precision. Recomputing the currency rate up to 6 digits may slightly change it from the 4 digit precision official currency rate. opw-6411530
This fix makes the product identifiers sent to Google Analytics match the identifiers used in Google Merchant Center feeds. This helps Google Ads correctly connect Shopping ad clicks with purchases, improving attribution and diagnostic accuracy.
Original PR description
**Issue:** When google analytics (GA) and google merchant center (GMC) are setup, Google Ads diagonistic reports that item IDs cannot be matched to Merchant Center. **Why this happens:** `order_lines_2_google_api` sets `product.barcode or product.id` for `item_id`, while `product.feed._prepare_gmc_items` defaults to `product.default_code or product.id` for the feed's `id` field. Google Ads/Analytics attribution relies on GA4's `item_id` matching GMC's `id` for the same product to connect Shopping ad clicks to purchase events. **References:** https://support.google.com/merchants/answer/6324405?sjid=15224664355638221483-NC https://support.google.com/google-ads/answer/14943675?hl=en Backport of d0bf183b053eb0133e6f7e5e04481ca07bff6bb3 opw-6443326 Forward-Port-Of: odoo/odoo#287152
Saudi e-invoices using ZATCA validation can no longer be confirmed when rounding creates negative untaxed or tax amounts. This prevents invalid invoices from being submitted and rejected by the ZATCA/Fatoora portal.
Original PR description
**Steps to reproduce:** - Install the l10n_sa_edi module, set the company to Saudi Arabia, and onboard the sales journal. - Set the 15% sales tax to Included in Price. - Create a customer invoice for…
**Steps to reproduce:** - Install the l10n_sa_edi module, set the company to Saudi Arabia, and onboard the sales journal. - Set the 15% sales tax to Included in Price. - Create a customer invoice for an individual with three lines: 19.00, 12.00, and -31.00, all using the 15% tax. - The invoice totals are -0.01 untaxed, 0.01 tax, and 0.00 total. - Generate the ZATCA UBL. **Observed behavior:** - ZATCA rejects the submission with BR-S-08, BR-CO-13, and BR-CO-15. **Cause:** - When the tax is Included in Price, rounding can result in a negative untaxed amount (-0.01) while the tax amount remains positive (0.01). Negative invoice amounts are not accepted by the ZATCA/Fatoora portal, resulting in an invalid UBL and rejection. **Fix:** - Add a validation before confirming the invoice to prevent invoices with a negative untaxed or tax amount from being confirmed when ZATCA is enabled. This prevents invalid invoices from being submitted to ZATCA and avoids the resulting UBL validation errors. Reference: [ZATCA Guidance on Negative Values in Unit Price](https://zatca1.discourse.group/t/negative-values-in-unit-price-and-line/5376negative-values-in-unit-price-and-line) opw-6433617