Thursday, September 17, 2026
28 changes · saas-19.4
Enhancements to existing features
Partner lists and searches now include reminder level and overdue filtering for account follow-ups. This helps teams quickly identify customers needing collection attention and prioritize follow-up work more efficiently.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/enterprise#130440 task-6501327 Forward-Port-Of: odoo/odoo#286688
Customer follow-up lists now show reminder levels and offer filters for overdue items and reminder level. This helps finance teams quickly find customers needing attention and prioritize collection actions more easily.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/odoo#286688 task-6501327 Forward-Port-Of: odoo/enterprise#130440
Product variant names are now generated more efficiently by avoiding unnecessary checks across all attribute values. This can significantly reduce loading time in Point of Sale for catalogs with many product variants, with the reported case improving from about 200 seconds to 70 seconds.
Original PR description
Issue --> `product.product's` display_name appends the variant's combination name, which `_get_combination_name` builds by dropping the values that come from single value lines. The check behind that, `_is_from_single_value_line`, only needs to know whether the line holds exactly one active value, but it filtered the line's entire set of values through `_only_active()` to find out. That check runs once per attribute value of every variant being named, so its cost follows the number of values on the template rather than the number of lines. Solution --> Stop at the second active value instead: finding two is enough to know the line is not single valued. The archived path (only_active=False) only measures the line's length and is unchanged, as is the returned name in both cases. Benchmark -> Reading display_name for the 23989 variants on the related database loads in the Point of Sale from approximately 200s to 70s. opw-6530735 Forward-Port-Of: odoo/odoo#288183
Resolved issues and error corrections
This fix prevents certain page interactions from running before the related screen element is fully ready. It reduces the risk of unexpected errors across documentation, project sharing, automation, website editing, and general web interface areas.
Original PR description
The commit [1] introduced some issues. The dom events could trigger while the component is not mounted yet. To fix this, this commit introduces a new hook `useExternalRef` which sets a signal to a given element when the current component is mounted and removes it when the component will unmount. The mix `useExternalRef` + `useListener` brings back the removed `useExternalListener` behaviour. [1]: https://github.com/odoo-dev/odoo/commit/918be39e1ad0bd83012cf02840ff412abe9b3548
This fixes an issue where cancelling an Adyen card payment from Point of Sale could leave the payment terminal still waiting for payment. The cancellation now targets the correct transaction, reducing confusion for cashiers and customers during checkout.
Original PR description
Steps to reproduce: - Configure a POS with an Adyen payment terminal - Open the POS, add a product and go to the payment screen - Select the Adyen payment method and click Send - Wait at least 5 seconds - Cancel the payment from the POS - => The terminal keep waiting for the payment (it's not canceled) `_adyenCancel` read `most_recent_service_id` but this value is overwritten by the `_adyenCheckPaymentStatus` polling after 5s, so we try to cancel the wrong payment. We now read the ServiceID from the payment line instead of using `most_recent_service_id`. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288874
Users with limited sales access can now validate backorders for multi-step deliveries without being blocked by sales order access restrictions. This keeps warehouse processing moving when a delivery is linked to a sales order owned by another user.
Original PR description
Multi-step deliveries backorders may create a new picking when processed by a user who only has access to their own sales orders. But, `_key_assign_picking` reads from the related sales order that they don’t have access to. Previously, these moves were confirmed with superuser rights, so this read did not trigger the sales order record rule. Since 19.2, the `sudo()` was removed. The fix would be to add this back in so the access rights error would not be raised. Steps to reproduce on Runbot: 1. Log in as User A (Admin) Turn on Multi-Step Routes Set the warehouse to use 2-step delivery (Pick then Deliver) 2. Create a Sales Order for a product with demand more than stock available and confirm it. 3. Log in as User B with (Demo): Sales: User: Own Documents Only Inventory: User 4. Open the picking transfer generated from User A’s Sales Order. 5. Click Validate and choose Create Backorder. Related: opw-6509032 Forward-Port-Of: odoo/odoo#285778
This fix prevents certain screen events from running before their related page elements are fully ready. It reduces the risk of unexpected errors in Documents, Knowledge, Manufacturing work orders, Sign, and Studio promotion dialogs, improving reliability without changing user workflows.
Original PR description
The commit [1] introduced some issues. The dom events could trigger while the component is not mounted yet. To fix this, this commit introduces a new hook `useExternalRef` which sets a signal to a given element when the current component is mounted and removes it when the component will unmount. The mix `useExternalRef` + `useListener` brings back the removed `useExternalListener` behaviour. [1]: https://github.com/odoo-dev/enterprise/commit/aa92285ee2bb0ae4e528c77b3f2a40d7412a8b5d
Correcting a bill date from the document preview now updates the field even when a date was already present. This prevents users from losing their selection when the date picker closes, making OCR-assisted vendor bill correction more reliable.
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#131818 Forward-Port-Of: odoo/enterprise#130192
Fixed an issue where fully prepaid Argentinian invoices could be rejected by ARCA because the system sent VAT details with zero amounts. Odoo now omits those zero VAT entries, allowing valid $0 final invoices to pass electronic invoicing checks.
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#286005The search panel now avoids crashing when a view includes records linked only to archived or inaccessible items. This keeps users from being blocked when opening affected views, such as Social Marketing posts, and safely ignores values they cannot access anyway.
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
Updating an employee's working schedule no longer changes the dates already selected on existing time off requests. The request duration is still recalculated based on the new schedule, helping payroll and HR records stay accurate without unexpected date shifts.
Original PR description
## Description Changing an employee version's working schedule recomputes the internal dates of subsequent time off requests. The `hr.leave` write override then mirrors these recomputed UTC values…
## Description Changing an employee version's working schedule recomputes the internal dates of subsequent time off requests. The `hr.leave` write override then mirrors these recomputed UTC values back to the request dates. For timezones ahead of UTC, midnight on a non-working day can therefore shift the requested date to the previous day. This fix marks the schedule-triggered re-computation as an internal fast update, ensuring that the dates selected on the time off request remain the source of truth while still allowing the leave duration to be recomputed according to the new working schedule. Regression coverage is added to ensure that changing an employee's working schedule: * preserves the dates selected on existing time off requests; * correctly recomputes the leave duration according to the new schedule. ## Steps to reproduce 1. Create an employee with timezone Europe/Brussels and a Monday–Friday working schedule. Before creating any leave, set the employment record’s effective date and contract start date to 3 March 2025. 2. Create a full-day time off request from 23 to 26 February 2026. Its duration is initially four working days. 3. Change only the employee’s working schedule to a duration-based Tuesday–Friday schedule, with 7.6 hours per working day and Monday off. Leave the effective date and contract dates unchanged, then save. **Before the fix:** the request’s start date incorrectly shifts to 22 February. **After the fix:** the dates remain 23–26 February, while the duration correctly decreases to three working days. opw-6456107 Forward-Port-Of: odoo/odoo#281385
GCC Arabic-English invoice PDFs no longer fail when an invoice includes section or note lines. This prevents an internal server error during PDF preview or printing, helping users reliably issue invoices that use these formatting lines.
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
Auto-completing a vendor bill no longer replaces a manually selected recipient bank account when the bill currency has not actually changed. This prevents accidental payment details changes for vendors with multiple bank accounts and helps preserve user intent during bill entry.
Original PR description
### Issue: When a bank account is manually set on a bill for a partner with multiple bank accounts, using the Auto-Complete feature could reset the manual selection even without a currency change ###…
### Issue: When a bank account is manually set on a bill for a partner with multiple bank accounts, using the Auto-Complete feature could reset the manual selection even without a currency change ### Cause: When `invoice_vendor_bill_id` is set, an onchange assigns `currency_id` unconditionally, even when it is the same value This triggers `_compute_partner_bank_id`, which always recomputes the best matching bank account from scratch without considering the currently set value If the currency did not change, this recompute is unnecessary and silently overrides the manual selection ### Steps to reproduce: - Install `account` - Create a Vendor with 2 bank accounts (keep default values to have equal priority on all accounts) - Create and post a Bill for this vendor with at least one line - Create a new Bill for the same vendor - Set the Recipient Bank to the second account in the list - In Auto-Complete, select the first Bill Before the fix, the Recipient Bank is reset to the first account opw-6210414 Forward-Port-Of: odoo/odoo#288386 Forward-Port-Of: odoo/odoo#283479
This fix prevents users from being sent back to the login screen when they sign in while a live connection is active, such as during live chat. It ensures the correct signed-in session remains in the browser, improving login reliability.
Original PR description
Before this commit, signing in from a page holding a websocket connection could land the user back on the login form. This happens because the websocket handshake marks the session dirty to get it written on disk, and _save_session sends a "session_id" cookie back for every dirty session. A handshake answered between the login response and the request that follows it therefore puts the pre-login session back in the browser. Note that the failure was seen on master, where a livechat tour signs in with the bus connected, but every version since 17.0 answers the handshake the same way. This commit saves the session from the handshake itself, so that the response carries no session cookie. https://runbot.odoo.com/odoo/error/947173 Forward-Port-Of: odoo/odoo#288424 Forward-Port-Of: odoo/odoo#287960
Swiss payroll users can now manually enter an hourly salary factor on a draft ELM payslip without it being erased on the first save. This prevents payroll corrections from being lost and helps ensure hourly-paid employees are paid using the intended rate.
Original PR description
**Steps to reproduce:** 1. Create a draft Swiss ELM payslip for an hourly-paid employee with no automatic hourly work-entry/input 2. In the Wages tab, manually update the `Factor` (`rate`) field of the `Hourly Salary` line 3. Save the payslip **Issue:** The manually entered `Factor` is reset to `0.00` **Cause:** - `l10n_ch_swiss_wage_ids` is a stored computed field without explicit `readonly=False`, causing manual modifications to the line values to be discarded during field recomputation on save. opw-6536243 Forward-Port-Of: odoo/enterprise#131636 Forward-Port-Of: odoo/enterprise#130824
Fixed an issue where rental returns could fail after processing pickups or partial returns through inventory transfers. The system now keeps the serial number history needed by the return wizard, helping staff complete subsequent returns without manual workarounds.
Original PR description
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return…
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return wizard. **Steps to reproduce** - Activate "Rental Transfers" in the settings - Create a rental product P, tracked by serial number - Create two serial numbers for P - Create and confirm a rental order for 2 units of P - Validate the pickup transfer - Partially validate the return transfer without creating a backorder - Open the rental order and click on "Return" -> The return wizard opens without any available serial number and validation fails with a serial number-related error. **Cause** When clicking on "Return", if there is no pending pickup/return transfer: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/models/sale_order.py#L62-L68 the rental return wizard is opened directly: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/models/sale_order.py#L316 No serial number is prefilled in the wizard because `returned_lot_ids` is empty: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L122-L124 This is because `returnable_lot_ids` is empty as well. `returnable_lot_ids` is computed while generating the wizard lines: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L38 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L47-L48 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L99-L106 and `returnable_lots` is empty because both `pickedup_lots` and `returned_lots` are. Those fields are currently only populated through the rental wizard flow: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L42-L43 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L160-L161 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L166-L167 Since this flow uses stock pickings instead of the rental wizard, those fields are never updated, preventing the wizard from determining any returnable serial number. opw-6150305 Forward-Port-Of: odoo/enterprise#127590 Forward-Port-Of: odoo/enterprise#119257
Fixed an issue where invoice forms using document extraction could crash when users clicked into certain code-style fields. The update ensures the system only reacts to valid field elements, improving form reliability without changing user workflows.
Original PR description
Follow-up of odoo/odoo#287085, as suggested by @aab-odoo: on a stable branch the fix belongs here rather than in the ace templates. Fixes odoo/odoo#286871. opw-6543997 ### The problem The `focusin`…
Follow-up of odoo/odoo#287085, as suggested by @aab-odoo: on a stable branch
the fix belongs here rather than in the ace templates.
Fixes odoo/odoo#286871.
opw-6543997
### The problem
The `focusin` listener of the extract mixin walks up from the focused node with
`closest(".o_field_widget,.o_field_cell")` and assumes whatever it finds
identifies a field. Not every `.o_field_widget` node does: a widget template
may render its own `.o_field_widget` inside the one the `Field` wrapper already
provides, and that inner div has no `name`. `web.AceField` (`widget="code"`)
does exactly that:
```html
<div name="my_field" class="o_field_widget o_field_code ...">
<div class="o_field_widget oe_form_field o_ace_view_editor oe_ace_open">
<div class="ace_editor">... <textarea class="ace_text-input">
```
The listener stops on the inner div, `getFullFieldName` finds no name and
builds `"<parent_field>.null"`. Since the name now contains a dot, `getBoxType`
takes the x2many branch:
```js
modelFieldType = this.props.record.data[parentField]?._config.fields[fieldName]?.type;
```
On a code field `record.data[parentField]` is a plain string. It is truthy, so
the optional chaining does not short-circuit, but it has no `_config` →
`undefined.fields` → `TypeError: Cannot read properties of undefined (reading
'fields')`, and the whole form breaks.
`account_invoice_extract` installs the renderer for `account_move_form` with
`force: true`, so this runs on every invoice form.
### Steps to reproduce
1. Install `account_invoice_extract`.
2. Put a `widget="code"` field on the `account.move` form view (with
`l10n_ar_edi` installed there are already two on the ARCA tab).
3. Give the field a value — with an empty value the error does not happen,
`record.data[parentField]` is `false` and the optional chaining cuts.
4. Enable developer mode and click inside the code editor.
Reproduced on a plain 19.0 runbot build.
### The fix
Restrict the selector to `.o_field_widget[name]`, so nodes that cannot identify
a field are skipped. The `focusout` listener a few lines below also matches
`.o_field_widget`, but it only calls `onBlurFieldWidget()` and never resolves a
name, so it is left as is.
The duplicated class in the ace templates looks like the actual root cause and
is handled separately in odoo/odoo#287085, retargeted to `master`.
Forward-Port-Of: odoo/enterprise#130952Project task rescheduling now treats each dependent task independently, so one task that cannot fit in the planning window no longer blocks other tasks assigned to the same person. This helps keep schedules accurate by placing later tasks in the next available slot instead of pushing them unnecessarily far into the future.
Original PR description
Steps to reproduce: 1. create three tasks A,B & C with the same assignee 2. make tasks B and C depend on A and start on the `date_deadline` of task A 3. set the duration of task B (processed before C) to be longer than the 53-week search window 4. reschedule task A to end after task C starts Problem: A candidate that couldn't be scheduled was putting its assignee in conflict, subtracting the inspected availability while failing from the shared pool. Consequently, every later candidate sharing that assignee got force-placed into the same forward reschedule fallback that lands near the tail of the 53-week search window instead of the next available slot. Solution: Each candidate should be evaluated on its own ability to fit, and the time it occupies should count against the shared pool. opw-6380163 --- Forward-Port-Of: odoo/enterprise#131351 Forward-Port-Of: odoo/enterprise#129326
Accounting users in Chilean companies can now upload XML files to create customer invoices without needing administrator rights. This ensures the uploaded tax document is properly processed, reducing errors and manual follow-up for finance teams.
Original PR description
Scenario: - be not an admin but have access to accounting - switch to chilean company - go to customer invoices - click on Upload and upload an XML file Result: an invoice is created, but the XML is not treated because of this permission error: 19.0/l10n_cl_edi/…/account_move.py", line 1078, in _l10n_cl_import_dte invoice.l10n_cl_dte_file = file_data['attachment'] odoo.exceptions.AccessError: You do not have enough rights to access the field "l10n_cl_dte_file" on Journal Entry (account.move). Please contact your system administrator Cause: l10n_cl_dte_file field is only accessible to administrator. opw-6509550 Forward-Port-Of: odoo/enterprise#131575 Forward-Port-Of: odoo/enterprise#130416
Colombian point-of-sale receipts no longer fail when a company has several DIAN obligation types configured. This ensures sales can sync successfully and receipts can be printed after DIAN acceptance.
Original PR description
Steps to reproduce: - Colombian company with DIAN electronic invoicing enabled in PoS - On the company contact, set more than one obligation type (e.g. O-13, O-15 and O-23) - Open a PoS session, sell…
Steps to reproduce: - Colombian company with DIAN electronic invoicing enabled in PoS - On the company contact, set more than one obligation type (e.g. O-13, O-15 and O-23) - Open a PoS session, sell a product and pay Issue: The order fails to sync with "Expected singleton: l10n_co_edi.type_code(x, y, z)" as soon as the DIAN document is accepted, and the receipt cannot be printed. On 18.0 to saas-19.1 the same crash happens when the receipt data is generated for printing. Cause: `_compute_l10n_co_edi_pos_receipt_data` fills `obligation_type_description` by reading `description` directly on `l10n_co_edi_obligation_type_ids`, which is a many2many. The read only works when the company carries exactly one obligation type, while a Colombian company commonly has several (they are all sent to the DIAN in `TaxLevelCode`). Fix: Join the descriptions of all obligation types, the same way the DIAN invoice PDF report already does. opw-6576523 Forward-Port-Of: odoo/enterprise#131747
Fixes a crash that prevented users from generating the Peruvian “Inventory and Balance” General Ledger export. The report now keeps the required SUNAT file format while using a safer export method, so businesses can complete statutory reporting reliably.
Original PR description
### Description of the issue/feature this PR addresses: This PR fixes a server crash in the Peruvian localization (l10n_pe_reports_lib) that occurs when generating the "Inventory and Balance" General…
### Description of the issue/feature this PR addresses: This PR fixes a server crash in the Peruvian localization (l10n_pe_reports_lib) that occurs when generating the "Inventory and Balance" General Ledger report. The crash is triggered by strict validation rules within Python's csv module, which rejects the custom line terminator used to fulfill the SUNAT PLE formatting requirements. ### Current behavior before PR: When a user attempts to generate and export the "Inventory and Balance" report, the server crashes with a ValueError: bad delimiter or lineterminator value. This happens because the csv.DictWriter is initialized with lineterminator='|\n' to ensure every row ends with a pipe. Python's underlying csv implementation rejects this, as it expects standard line endings (\r, \n, or \r\n) and throws an error if the delimiter character (|) is included in the terminator string. ### Desired behavior after PR is merged: The "Inventory and Balance" report generates successfully without server errors. The code now uses the standard lineterminator='\n' to satisfy Python's validation rules. To maintain the mandatory trailing pipe (|) at the end of each row required by SUNAT, a dummy empty column (['']) is appended to the field names with restval=''. This prompts the writer to naturally append the final pipe as a column delimiter before the newline, resulting in the exact |\n output format required, safely and reliably. opw-6509674 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#129636
This fix prevents sale orders from failing when a user saves immediately after reordering order lines. It temporarily pauses saving while the reorder action finishes, making quotation and order editing more reliable.
Original PR description
When we save a sale order just after reordering its sale order lines, there's a chance that an error is thrown Steps to reproduce: 1. Install Sales 2. Go to Sales and open any quotation that has…
When we save a sale order just after reordering its sale order lines, there's a chance that an error is thrown Steps to reproduce: 1. Install Sales 2. Go to Sales and open any quotation that has multiple sale order lines 3. Reorder a sale order line and immediately save (there's more chance to reproduce if you have a lot of sol and if you use the shortcut `ALT + S`) 4. An error is thrown Issue: It is possible that the save takes place during the execution of super.sortDrop. This reassigns the id of all the records in this.props.list.records Therefore, calling `_handleQuantityAdjustment` with the recordMap computed before the save uses an id that has disappeard from this.props.list.records so we cannot find it at https://github.com/odoo/odoo/blob/4973903252865a6a7a2da235bc3a01675dbbff4d/addons/sale_management/static/src/fields/sale_order_line_field/sale_order_line_field.js#L263 which eventually throws an error Solution: Suspend any save mechanism when sortDrop starts and resume it when sortDrop has finished opw-6483206 Forward-Port-Of: odoo/odoo#288370 Forward-Port-Of: odoo/odoo#286787
New Singapore company setups now enable the appropriate accounting method so purchase price differences are recorded correctly. This helps ensure vendor bills reflect price variances when purchase prices differ from standard product prices.
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
Inter-company sales that automatically create purchase orders now leave the related receipt lines unpicked. This ensures warehouse teams can process the transfer normally in the barcode app instead of finding items already marked as handled.
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
This fix ensures that when shoppers choose an available delivery date during checkout, that date remains on the order instead of being replaced by the earliest possible date. This improves order accuracy and helps businesses honor delivery expectations promised to customers.
Original PR description
Steps to reproduce: =================== 1. Enable Estimated Delivery on a delivery method, lead 1 day, range 90. 2. On the website, add a product to the cart and go to checkout. 3. Pick a date far in the range, then confirm the order. => Promised Delivery is the order date + 1 day, not the date picked. Root cause: =========== `_set_delivery_method` always writes the earliest offered date on `commitment_date`, and `_recompute_cart` calls it again on the way to payment, so the choice is overwritten just before the order is placed. - [1] added the date selector with that unconditional write. Fix: ==== Only fall back to the earliest date when there is none yet, or when the one set is no longer offered, as the checkout already requires. [1]: https://github.com/odoo/odoo/commit/7e51e356728d24ca0aeb14c5ff94a0eac66e188f opw-6538537 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287236
EU OSS sales are now reported correctly in French e-invoicing flows by excluding destination-country VAT from the French VAT-specific report. This keeps invoices and accounting accurate while preventing reporting errors caused by non-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
Spanish online shoppers can now complete checkout without entering a VAT number when the website is not set up for business-to-business sales. This prevents unnecessary address form blocks and avoids showing business-only fields to regular consumers.
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
Fixes an issue where users opening Studio to edit the sales order line list could hit an error when existing line items were present. Sales teams can now customize columns and add fields to order lines without interruption.
Original PR description
Before this change: When opening Studio on a Sales Order with existing line items, clicking "Edit List View" on the order lines component triggered a JavaScript error. This prevented users from customizing columns or adding custom fields to the sale order line list view. To reproduce: 1. Open the Sales app and create a new Sales Order with at least one product line. 2. Toggle Studio on. 3. Select the Sale Order Lines list component and click "Edit List View". After this change: Clicking "Edit List View" on sale order lines in Studio works as intended without throwing errors, allowing standard view customizations. opw-6545970