Thursday, September 17, 2026
15 changes · saas-19.1
Resolved issues and error corrections
Argentinian electronic invoices where a full advance payment cancels the final invoice amount will no longer be rejected by ARCA because of zero VAT lines. The fix prevents sending empty VAT details when taxable amounts net to zero, supporting standard 100% down-payment billing flows.
Original PR description
## Description of the issue When an Argentinian invoice contains advance-payment deduction lines that exactly cancel the product lines (net taxable base = 0 for a given VAT aliquot), the invoice is…
## Description of the issue
When an Argentinian invoice contains advance-payment deduction lines that exactly cancel the product lines (net taxable base = 0 for a given VAT aliquot), the invoice is rejected by the ARCA (AFIP) WSFE web service with:
> **Error 10018**: "Si ImpIva es igual a 0 el objeto Iva y AlicIva son obligatorios. Id iva = 3 (iva 0)"
This is the standard "100% down payment" flow: the customer is invoiced an advance for the full amount, and the final invoice deducts that advance, resulting in a $0 invoice that must still be validated against ARCA.
This is a forward-port to 19.0 of #270846 (same fix, targeted at 18.0, closed unmerged). The bug is still present in 19.0: `_get_vat()` in `addons/l10n_ar/models/account_move.py` evaluates its filter on the raw unrounded aggregated floats.
## Steps to reproduce
1. On a company with the Argentinian localization (`l10n_ar_edi`) configured for electronic invoicing (WSFE), create a sale order with one or more product lines taxed at IVA 21% (e.g. total $121,000).
2. Create a **down payment invoice for 100%** of the order and validate it against ARCA (this one succeeds).
3. Create the final invoice from the sale order: it contains the product lines (positive) and the down-payment deduction line (negative), both at IVA 21%. Total to pay: **$0.00**.
4. Confirm the invoice and send it to ARCA.
5. **Current behavior (bug):** ARCA rejects the request with error 10018. Inspecting the generated WSFE request shows `ImpNeto=0.0`, `ImpIVA=0.0`, `ImpTotal=0.0` and an `Iva` block containing an all-zero aliquot, e.g. `{'AlicIva': [{'Id': '5', 'BaseImp': 0.0, 'Importe': 0.0}]}` — instead of `Iva: null`.
6. **Expected behavior (after fix):** no zero-amount aliquot is sent (`Iva` is `null`) and ARCA approves the $0 invoice.
## Root cause
In `_get_vat()`, the positive product lines and the negative down-payment deduction line share the same VAT aliquot, so the aggregation by `vat_afip_code` nets the group to zero. However, floating-point accumulation in the aggregated tax details leaves a tiny residual (~1e-12) in `base_amount_currency` / `tax_amount_currency`. The filter condition checks the **raw unrounded** values:
```python
if grouping_key['vat_afip_code'] not in (False, '0', '1', '2') and (values['base_amount_currency'] or values['tax_amount_currency']):
```
The ~1e-12 residual is truthy in Python, so the aliquot entry is kept — even though both `BaseImp` and `Importe` are rounded to `0.00` two lines below when building the entry. The WSFE request therefore carries a non-null `Iva` block with all-zero amounts, which ARCA rejects with error 10018.
## Fix
Round `BaseImp` and `Importe` to 2 decimals **before** evaluating the filter condition, so an aliquot whose amounts cancel out is excluded from the `Iva` array (the same rounded values are then reused when building the entry, keeping the sent amounts unchanged for every other case):
```python
base_imp = float_round(amount_sign * values['base_amount_currency'], precision_digits=2)
importe = float_round(amount_sign * values['tax_amount_currency'], precision_digits=2)
if grouping_key['vat_afip_code'] not in (False, '0', '1', '2') and (base_imp or importe):
```
Behavior is unchanged for every invoice whose aliquots round to a non-zero base or tax amount.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286005This fix prevents gift cards and eWallets from being used with on-site payment flows in cases where they are not supported. It helps avoid checkout issues and payment combinations that could create operational or customer service problems.
Original PR description
We don't want to support gift cards and eWallets in certain conditions opw-6483282 Forward-Port-Of: odoo/odoo#287928 Forward-Port-Of: odoo/odoo#286946
Fixed a problem that could prevent Arabic-English GCC invoice PDFs from being generated when invoices included section or note lines. This improves reliability for users printing or previewing compliant invoices in GCC localizations.
Original PR description
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not…
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not iterable ``` This happens because `account.move.line` records of type `line_section` or `line_note` have `name = False`. The template evaluates `arabic_name not in line.name` (and the same for `english_name`), which fails because Python cannot apply the `in` operator on a boolean value. ## Fix Add a `line.name and` guard before each `not in` check: ```xml <!-- Before --> <span t-if="arabic_name not in line.name" .../> <span t-if="(english_name != arabic_name) and (english_name not in line.name)" .../> <!-- After --> <span t-if="line.name and arabic_name not in line.name" .../> <span t-if="line.name and (english_name != arabic_name) and (english_name not in line.name)" .../> ``` ## Steps to reproduce 1. Install `l10n_gcc_invoice` on an Odoo 16.0 instance. 2. Create a customer invoice and add a **Section** line. 3. Print/preview the invoice PDF. 4. Observe `Internal Server Error` / `TypeError: argument of type 'bool' is not iterable`. Forward-Port-Of: odoo/odoo#278887 Forward-Port-Of: odoo/odoo#267147
The web search panel now handles records linked only to archived or restricted items without crashing. This keeps affected views, such as Social Marketing posts, usable even when some related values are hidden from the user.
Original PR description
Opening a view whose search panel has a `select="multi"` field of type many2many crashes when a record is only linked to comodel records the user cannot see: File "/addons/web/models/models.py", line…
Opening a view whose search panel has a `select="multi"` field of type many2many crashes when a record is only linked to comodel records the user cannot see: File "/addons/web/models/models.py", line 1496, in _search_panel_domain_image id_, display_name = group_id_name(group[field_name]) TypeError: cannot unpack non-iterable bool object Steps to reproduce: - On a db with Social Marketing installed, social accounts are automatically created per website - Open Social Marketing > Posts - Create a new Post with an social account - Open Settings > Social Accounts > the chosen social account - Archive it - Get back to the Post created above => crash `_search_panel_domain_image` restricts the domain with `(field_name, '!=', False)`, which is evaluated on the comodel with sudo and active_test=False, while the group by of the same query joins the comodel through `_search`, which applies record rules and active_test. A record whose values are all archived or hidden by a record rule therefore passes the condition but ends up in a group with no value, which the image loop unpacks as a tuple. This commit skips those groups: their values have no place in the range anyway. Seen on runbot, where `runbot.build.error.trigger_ids` points at `runbot.trigger` records that are archived or restricted by the project group rule. Forward-Port-Of: odoo/odoo#288503
Mail plugin users can now stay signed in longer instead of needing to log in every day. Administrators can adjust how long these access tokens last, with a default duration of seven days, while keeping the tokens limited to Outlook-related access.
Original PR description
Purpose ======= Allow customizing the expiration time of tokens, so users don't need to login everyday in the plugin. This is customized with a system parameter, with a default of 7 days. Those tokens are limited to endpoints `auth="outlook"`. Task-6466253 Forward-Port-Of: odoo/odoo#287767
Correcting a date on an OCR-scanned vendor bill now works reliably even when the date field already contains a value. This prevents the selection boxes from disappearing before the chosen date is applied, reducing manual correction frustration during bill processing.
Original PR description
**Steps to reproduce:** 1. Upload a vendor bill with OCR digitization (pdf in ticket attachments) 2. Make sure the Bill Date field already has a value 3. Click into the Bill Date field so the OCR…
**Steps to reproduce:** 1. Upload a vendor bill with OCR digitization (pdf in ticket attachments) 2. Make sure the Bill Date field already has a value 3. Click into the Bill Date field so the OCR boxes appear on the attachment preview 4. Click on a different date box in the attachment to correct the value **Issue:** The field is not updated with the clicked box's value. Instead, the field simply loses focus and the OCR boxes disappear, as if the user had clicked outside the field. This only happens when the date field already has a value; it works fine when the field is empty. **Cause:** - When a date field has a value, the date widget renders it as a button and only swaps in the real `<input>` once focused [1] - Removing the button triggers `onBlurFieldWidget`, and when the datepicker is focused, it triggers `onFocusFieldWidget` once again: https://github.com/odoo/odoo/blob/89650a5f44b5835028dba57a9ff6fa30515bc5ec/addons/web/static/src/views/fields/datetime/datetime_field.js#L204-L214 - The datetime picker's popover uses `useClickAway`, which reacts to `pointerdown` on `window` to detect clicks outside itself and close the popover. - Clicking a box in the attachment preview is therefore caught by this listener before anything else: it closes the popover, removing the currently focused DOM node and firing a `focusout`. - `ExtractMixinFormRenderer` reacted to that `focusout` by resetting the active field and destroying the box overlay. Since the box's own value-selection ran on `click`, the last event in the `pointerdown → mousedown → mouseup → click` sequence, the reset had already been done, so the click was lost. **Fix:** - Apply the box's selection on `pointerdown` instead of `click`, so it runs synchronously in the same event dispatch that triggers the popover's close logic, guaranteeing it executes before any asynchronous re-render can remove the field's state. - Remove the legacy `pointerdown` listener in `ExtractMixinFormRenderer.`. Because our box now listens to `pointerdown`, it was intercepting the event and breaking selection for images [1] - https://github.com/odoo/odoo/pull/218387 opw-6511803 Forward-Port-Of: odoo/enterprise#130192
This fixes an error that could appear when a user clicked a shared Sign document link while still editing a website page. The website editor now handles pages without editable backend records more safely, avoiding an unexpected traceback and keeping the editing experience stable.
Original PR description
# How to reproduce - Go to Sign > Templates - Upload a PDF & click on Share - Copy the link - Go to a page with a EditInBackend systray item (e.g. a Product or Event page) - In the editor, add the…
# How to reproduce - Go to Sign > Templates - Upload a PDF & click on Share - Copy the link - Go to a page with a EditInBackend systray item (e.g. a Product or Event page) - In the editor, add the copied link to a button or some text - Save - While still in editor mode, click the link # The issue A traceback is shown # Cause The traceback is caused by the call to this function : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/services/website_service.js#L336 In our case, `this.currentWebsite.metadata.mainObject` is undefined, so trying to access `.model` throws. `mainObject` is undefined because the sign link opens an XML file, which does not have any metadata associated to it : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/services/website_service.js#L150-L155 The call to `getUserModelName` is done by the `EditInBackendSystrayItem` component which subscribes to the "CONTENT-UPDATED" event : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/client_actions/website_preview/edit_in_backend.js#L33 However, this method should not be called by the bus because the sign page does not have this systray item. The issue is, the display of the component is managed by the `WebsiteSystrayItem`, based on the `hasEditableRecordInBackend` getter : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/client_actions/website_preview/website_systray_item.js#L10 https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/client_actions/website_preview/website_systray_item.xml#L9 And the re-render of that component that would remove the `EditInBackendSystrayItem` component is triggered by a call to `renderAndAdapt`, which is also subscribed to "CONTENT-UPDATED" : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/components/navbar/navbar.js#L48 So the destroy of the `EditInBackendSystrayItem`, which should remove the listener that calls `getUserModelName` is trigerred AFTER the call to that function is already done opw-6443794 Forward-Port-Of: odoo/odoo#281763
EU OSS sales are now reported in French e-invoicing flows as non-French VAT taxable amounts, while keeping the correct destination VAT on invoices and accounting records. This prevents valid OSS transactions from being rejected by French reporting checks that only accept French VAT rates.
Original PR description
OSS sales are taxed in the customer's Member State but are not subject to French VAT. They must therefore be reported under TNT1, while the Flow 10 Schematron only accepts French VAT rates. Identify taxes generated for the EU OSS scheme through their OSS tag. Keep the destination VAT on the invoice and in accounting, but report the taxable base under TNT1 with a zero tax rate and amount. Continue rejecting unsupported rates for regular taxes and invalid OSS rates. no task id Forward-Port-Of: odoo/odoo#288013
New Singapore companies will now be created with the accounting setting needed to properly record purchase price differences. This helps ensure vendor bills reflect cost differences when products are bought at a price different from their standard product price.
Original PR description
Singaporean companies didn't have the anglo saxon accounting enabled. Price difference account was then not hit when buying a product with a different price than the one in the product page. Steps to reproduce: ------------------- * Create a product P * Set the costing method to "Standard Price" and the inventory valuation to "Perpetual (automated)" * Make sure a price difference account is set in the product category * Create a RFQ for P and set a different price than the one in the product page * Confirm the RFQ and receive the product * Create a vendor bill for the RFQ and validate it > Observation: If you check the lines in the bill there is no price difference account hit. Why the fix: ------------ We set anglo saxon accounting to True so that all new companies have the correct setup. opw-6525817 Forward-Port-Of: odoo/odoo#288261
Custom product attribute values entered in Point of Sale orders are now carried through inter-company purchase and sales flows. This ensures related delivery slips keep the correct product descriptions, reducing confusion and manual corrections for multi-company operations.
Original PR description
*= sale_purchase_inter_company_rules Step to reproduce: - install `sale_purchase_stock_inter_company_rules` and pos with demo - from setting : - enable Multi-Step Routes - enable all options for…
*= sale_purchase_inter_company_rules
Step to reproduce:
- install `sale_purchase_stock_inter_company_rules` and pos with demo
- from setting :
- enable Multi-Step Routes
- enable all options for Inter-Company Transactions (for both companies)
- go to routes, un-archive MTO route
- create a product "A" with routes "MTO" and "buy"
- make the product available for pos (add into `Misc` category)
- create a attribute with custom attribute value and link it to "A"
- in vendor list, add "My Company (Chicago)" as vendor
- switch to Chicago company, for same product add some other vendor
- switch back to "My Company (San Francisco)"
- for pos, in setting enable "Allow Ship Later"
- open Furniture pos, create a order with Product "A"
- go to payment page and select "Ship Later" and pay
- a RFQ is created for this pos order, open and confirm it
- switch to "My Company (Chicago)"
- open sale order (remove "my quotation" filter)
- notice a quotation created from company 1, confirm it.
- open linked delivery slip
Observation:
- the delivery slip will not have description (custom value for attribute for A
is lost)
Cause:
- when preparing vals for SO for inter-company transaction from
`_prepare_sale_order_line_data` we never considered order from pos
- hence in this case, order lines never had custom attributed linked to them
- the description is computed from the attributes from SO
- SO never had them to begin with
Fix
- we introduced a method `_get_pcavs_from_pos_order` which fetch such attributes from pos order too
- to accomplish this we introduce a new module As there is no dependency with purchase and pos
opw-6213076
Forward-Port-Of: odoo/enterprise#131626
Forward-Port-Of: odoo/enterprise#119233Inter-company sales that automatically create validated purchase orders no longer mark the related receipt lines as already picked. This lets warehouse teams process the transfer normally in the barcode app and avoids confusion or skipped handling steps.
Original PR description
Issue ----- When confirming a SO, the corresponding PO picking should not have its' MLs set as `picked` to allow treating the transfer in the barcode app. Steps to reproduce ----- - Create 2 companies A & B - Settings > Inter-Company Transactions - Create Purchase Orders - Set the created PO to be "Validated" by default - Create a SO from company A to B - Validate the OUT picking in company A - Open the PO in company B - Go to its' picking and open it in barcode > The line is already picked ----- Ticket: opw-6481828 Forward-Port-Of: odoo/enterprise#131391
Fixes a website theme update issue where deleted theme elements could leave dependent website copies behind and block the update. This helps ensure theme changes apply cleanly without requiring manual cleanup.
Original PR description
When a record disappears from a theme's data files, updating that theme deletes its per-website copies. The context built for `copy_ids` in `_process_end_unlink_record` used the literal `'MODULE_UNINSTALL_FLAG'` instead of the constant, and passed it as a positional dict, which replaces the whole context rather than extending it. `ir.ui.view.unlink` therefore did not cascade to the inheriting views and the `inherit_id` foreign key refused the deletion, aborting the theme update. Reported by: https://github.com/odoo/odoo/issues/286171 Forward-Port-Of: odoo/odoo#288548
Fixed an issue where confirming an upsell or renewal quote could fail if the original cancelled subscription no longer had a recurring plan. This helps sales teams complete renewal and upsell flows without unexpected errors after subscription changes.
Original PR description
## Steps to Reproduce: - Install the Subscriptions module with demo data. - Create a quotation containing a subscription product. - Set a recurring plan and confirm the quotation. - Create an upsell…
## Steps to Reproduce: - Install the Subscriptions module with demo data. - Create a quotation containing a subscription product. - Set a recurring plan and confirm the quotation. - Create an upsell quotation or a renewal quotation. - Cancel the original subscription. - Remove the recurring plan from the cancelled subscription. - Open either the upsell or renewal quotation and confirm it. ## Error: `TypeError - '>=' not supported between instances of 'datetime.date' and 'bool'` ## Cause: Since Commit https://github.com/odoo/enterprise/commit/315be581a4212b46191e0c4f82b02a2c3fde51dc#diff-07cf1dda5423f99452a763a54fb6e7fdbb861e19b1a9dca00cab093a832490a9, `plan_id` is no longer required when a subscription is in the cancelled state. During the confirmation of an upsell or renewal quotation, the parent subscription's next invoice date is used for several date validations. However, if the parent subscription is cancelled and its plan is removed, the `next_invoice_date` is computed as False. Comparing a date object with a boolean value leads to an error. ## Fix: This commit adds an extra check before using the next invoice date. sentry-7661150764 Forward-Port-Of: odoo/enterprise#128093
Customers buying through a Spanish website can now complete checkout without entering a VAT number when the site is not configured for business-to-business sales. This prevents unnecessary address form blockers and keeps consumer checkout fields relevant.
Original PR description
Steps: - Install l10n_es and website_sale module. - set up Spain as website country. - Go to checkout page. - Fill address without vat. Issue: - It won't allow save address and process with checkout because vat is required even if we make `b2b_fields` non required or hide b2b_fields from address page they are still visible and don't allow to save address. Casue: - In PR https://github.com/odoo/odoo/pull/184734 they made vat required for Spain website considering EDI but it does not make sense to always ask for vat even website is not b2b and in other PR they made b2b_fields always visible which make b2c website will always display those fields which is wrong. Fix: - Remove hook to make vat required and always display b2b_fields. opw-6446240 opw-6520397 Forward-Port-Of: odoo/odoo#286450
Employee-paid expenses that have been posted now remain included in the Waiting Reimbursement dashboard tile and filter until the employee is actually paid. This prevents reimbursable expenses from disappearing prematurely, giving staff and finance teams a more reliable view of outstanding reimbursements.
Original PR description
#### Description of the issue/feature this PR addresses: The "Waiting Reimbursement" tile stops counting an employee-paid expense once its journal entry is posted, instead of once the employee is reimbursed. #### Current behavior before PR: _compute_state sets the state to 'posted' as soon as a move is posted and unpaid, so an own_account expense never stays 'approved', yet get_expense_dashboard still totals the tile on 'approved' only. Regression from the removal of hr.expense.sheet in 704a5a1, which carried the 'posted' state until then. The "Waiting Reimbursement" search filter has the same condition. #### Desired behavior after PR is merged: The tile and the filter also match 'posted', so the expense stays counted until the payment is registered. opw-6410953 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281882