Wednesday, September 9, 2026
9 changes · saas-19.1
Enhancements to existing features
Bank statement lines can now automatically reconcile installment-based accounting entries when the system is confident they belong together. This reduces manual reconciliation work and applies payments to installments in the expected order.
Original PR description
Moves with installments should be auto reconciled if we're sure the move is linked to the statement line. If we are sure that the installments are linked to the statement line then the installment with the lowest id should be reconciled first. task-6285410 Forward-Port-Of: odoo/enterprise#119891
Resolved issues and error corrections
The accounting duplicate check now covers receipts as well as bills and invoices. This helps prevent duplicate supplier or customer documents from being missed when their type is changed.
Original PR description
Right now a duplicate is detected if it's a bill, but is not when you switch it to a receipt. This fix makes sure that both bills and purchase receipts duplicates are detected and are checked against each other. The same change is done for invoices and outgoing receipts. task-6115836 Forward-Port-Of: odoo/odoo#279455
Closing an AI live chat conversation no longer triggers an unwanted chat window to appear afterward. This improves the website visitor experience by making the close action behave as expected and avoiding confusing follow-up messages.
Original PR description
Closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. -…
Closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. - Click on edit and choose `Contact & Forms`. - Add the AI Livechat website snippet. - From the snippet options, add an AI Agent and choose a livechat team. Make sure that Mitchell Admin is configured as an operator for that livechat team (livechat channel). - Click on save. - Open an incognito tab. Log in as Marc Demo. - Go to Website. - Type a message inside the `ASK AI` text area and press enter. - Wait until you receive a response and then click close. - A chat window will popup with the messages of the conversation with the AI along with a message saying `Visitor has left the channel`. This happens because `close` button will call `closeConversation` => `livechatService.leave()` => `visitor_leave_session` => `_close_livechat_session` that posts a message that the visitor has left the channel. This commit solves the issue by setting the user who left the channel as the author of the `visitor left chat` message instead of OdooBot. Forward-Port-Of: odoo/odoo#258836
Company-paid expenses created from a project now apply the project allocation only to the actual expense line, not to balancing lines. This prevents project expense reporting from incorrectly cancelling itself out and gives businesses more accurate project cost figures.
Original PR description
### Current behavior: Creating a company-paid expense from the Project overview posts a journal entry with the project analytic on both the expense and outstanding lines, so the analytic balance nets to zero ### Expected behavior: Analytic distribution should only be on the P&L (expense) line ### Steps to reproduce: 1. Open a project overview and create a company-paid expense 2. Submit, approve, and post it 3. Open the journal entry: analytic is on debit and credit lines ### Cause of the issue: `project_id` stays in the context after `clean_context` during `_create_company_paid_moves`. With `sale_project`, AML analytic compute then applies the project distribution to outstanding/tax lines as well ### Fix: Removed `project_id` from the context when creating company-paid moves opw-6368848 Forward-Port-Of: odoo/odoo#280256
Fixed an issue where submitting a tax report opened from a return could update or submit a different return for the same period. This is especially important for Dutch VAT corrections, ensuring the correction return is handled instead of the original VAT return.
Original PR description
Opening a tax report from a return and submitting it could act on a different return than the one on screen. _get_return_from_report_options searches by company, period and report with limit=1, but…
Opening a tax report from a return and submitting it could act on a different return than the one on screen. _get_return_from_report_options searches by company, period and report with limit=1, but that combination is not unique: l10n_nl declares two return types on l10n_nl.tax_report, nl_tax_return_type and nl_tax_correction_return_type, so a VAT return and its correction both match. Which one is returned is then decided by _order (is_completed, date_deadline, name, id). For a Dutch VAT correction it resolves to the original VAT return of the same quarter, so send_xbrl submits and flags that record instead of the correction. The options already carry the return type they were built for, in the return_periodicity filter, so restrict the search to it when it is set. l10n_nl_reports kept a return_id option for the same reason when computing the already declared amount of a suppletie; it can use _get_return_from_report_options now. opw-6421300 Forward-Port-Of: odoo/enterprise#127073
Fixed a crash that could stop Factur-X/CII vendor bill imports when a matched reverse-charge tax had no eligible positive tax lines. This prevents users from seeing a vague import failure and helps affected bills import reliably.
Original PR description
Steps to reproduce: Import a Factur-X / CII bill in a company where the matched purchase tax is the negative-only sibling of a reverse-charge pair (e.g. intra-EU services in the RRIF Croatian chart).…
Steps to reproduce:
Import a Factur-X / CII bill in a company where the matched purchase tax is the negative-only sibling of a reverse-charge pair (e.g. intra-EU services in the RRIF Croatian chart). The import fails with:
Error importing attachment 'factur-x.xml' (...):
This specific error occurred during the import: list index out of range
Observation:
_distribute_delta_amount_smoothly spreads leftover rounding cents across a tax's positive-factor lines. For the negative only sibling, that list (target_factors) is empty, but delta_amount is still non-zero. The method goes straight to factors[0] and raises IndexError. account_edi_ubl_cii catches the traceback and shows only str(e), which is why the user sees the bare "list index out of range" with no file or line.
The fix:
Return early with an empty list when target_factors is empty, same as the existing early-return for a zero delta_amount. Mirrored in the JS helper.
opw-6462024
Forward-Port-Of: odoo/odoo#286163Dropshipped purchases are now excluded from average cost calculations, preventing them from changing inventory values when the goods never enter stock. This avoids incorrect negative balances in stock valuation accounts after dropshipping and later normal sales.
Original PR description
stock_*: stock_account, stock_dropshipping, stock_landed_costs **Problem:** dropship moves impact the average cost of products which can lead to negative balance in stock valuation account **Steps to…
stock_*: stock_account, stock_dropshipping, stock_landed_costs **Problem:** dropship moves impact the average cost of products which can lead to negative balance in stock valuation account **Steps to reproduce:** On a new db with no demo data and stock_dropshipping, sale_management and accountant module installed (bug also reproducible in runbot with same steps, but it's easier to see the negative impact on accounting on a new db) : 1) enable dropshipping 2) create a storable product with average perpetual category 3) in the purchase tab set a vendor with a price of 10 4) in the inventory tab select the dropship route 5) create PO for 1 unit @ 5, validate receipt and confirm bill 6) confirm a SO for 1 unit of the product 7) confirm linked PO and validate dropship move 8) confirm invoice and vendor bill -> see how the standard price is now 7.5 9) remove dropship route from the inventory tab of the product 10) confirm a SO for 1 unit of the product 11) validate delivery and confirm invoice 12) open 'inventory valuation' view **Current behavior:** the initial balance of stock valuation is -2.5 **Expected behavior:** it should be 0 (there shouldn't be a negative initial balance if all invoices and bills are confirmed) **Cause of the issue:** The issue happens after step 8) The problem is that the dropship has an impact on the average price of the product but not on the accounting. Before the dropship we have 1 unit in stock @ 5 and the stock valuation account has a balance of 5 (from the bill), so all is good. The dropship then changes the standard price to 7.5. That's because currently, in _run_average_batch() the dropship move first impacts average cost like an incoming move with a value of 10 (at this point we have 1 move @ 10 and 1 @ 5 so average cost is 7.5) https://github.com/odoo/odoo/blob/bcaa8b6b00fa1f16513d680c5187fd424bc7cf8c/addons/stock_account/models/product.py#L493-L500 https://github.com/odoo/odoo/blob/bcaa8b6b00fa1f16513d680c5187fd424bc7cf8c/addons/stock_account/models/product.py#L505-L510 and then it impacts the value as a regular outgoing move (meaning it leaves the inventory at the average cost of 7.5) and does not impact the average cost (which is the basic behaviour of outgoing moves) https://github.com/odoo/odoo/blob/bcaa8b6b00fa1f16513d680c5187fd424bc7cf8c/addons/stock_account/models/product.py#L515-L517 https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/addons/stock_account/models/product.py#L522 Therefore after step 8), the standard price is 7.5 and we have a unit in stock so, in the inventory valuation view the ending stock is 7.5$. But the dropship did not impact the stock valuation account so the initial balance is still 5$ and we have lines with credits and debits of 2.5$ in the the stock variation section. After steps 9 to 12, both the initial balance and ending stock decrease by 7.5 (which is expected), leading to a negative initial balance in stock valuation. **fix:** We don't take into account the stock move from dropships in the avco computation **tests:** The fix requires modifications in a few tests: - test_dropship_bill_standard_price_update checks that the bill of a dropship move impacts the standard price, so we delete this test - test_lot_normal_3, the asserts on the total_value still make sense but not those on standard_price - test_dropship_kit_bom_updates_component_standard_price test_average_cost_dropship_in_negative_quantity, test_out_move_validate_as_stock_user: standard price should not be impacted by dropship Task 6515358 Forward-Port-Of: odoo/odoo#285576
Journal item labels created by reconciliation models now use the company language instead of varying by the user or automated process applying them. This prevents inconsistent or incorrect labels on accounting entries, especially after duplicating and translating reconciliation models.
Original PR description
### Problem `label` on `account.reconcile.model.line` is a **translatable** field, but its value is written onto the journal item created when the model is applied (`account.move.line.name`). That…
### Problem
`label` on `account.reconcile.model.line` is a **translatable** field, but its value is
written onto the journal item created when the model is applied (`account.move.line.name`).
That means the label is read in the language of whoever applies the model:
- a user working in another language writes the translated value;
- the auto-reconciliation cron writes the **source** value, since it runs as OdooBot.
So the very same reconcile model ends up writing two different labels on the journal items,
depending on who applied it.
### How it shows up
It becomes visible when a reconcile model is created by **duplicating** an existing one and
the label is then edited while working in a non-source language. The translation holds the
new text, while the source value silently keeps the label of the original model — and the
source value is exactly the one the cron writes. The result is a set of journal items where
some carry the intended label and some carry the label of an unrelated model.
### Fix
The journal item belongs to the company, so the label is read in the **company** language
via a small `_get_aml_label()` helper, instead of the language of the current environment.
It falls back to the current behaviour when the company has no language set.
`_prepare_aml_vals()` is the only place in 18.0 that reads `self.label` for the journal item.
### Test
Adds `TestBankRecWidget.test_auto_reconcile_model_label_uses_company_lang`: a reconcile model
whose line label is translated in the company language, applied by the auto-reconciliation
cron running in the source language, and asserts the journal item carries the company-language
label.
```
odoo -d <db> -u account_accountant --test-enable --stop-after-init \
--test-tags /account_accountant:TestBankRecWidget.test_auto_reconcile_model_label_uses_company_lang
```
Without the fix the test fails with `[{'name': 'Frais bancaires'}] != [{'name': 'Bank fees'}]`;
with the fix it passes. The full `account_accountant` suite was also run on a clean 18.0
database: 203 tests, 0 failed, 0 errors.
Forward-Port-Of: odoo/enterprise#130719
Forward-Port-Of: odoo/enterprise#128433SEPA direct debit XML files now include a bank-specific identifier only for countries where it is required by Nordea. This prevents Italian banks from rejecting payment files due to an unexpected extra field, helping businesses process direct debit batches more reliably.
Original PR description
### Issue: An unexpected `<SchmeNm><Cd>CUST</Cd></SchmeNm>` node was added inside `<InitgPty><Id><OrgId><Othr>` for all countries Some Italian banks reject SDD files containing this node ### Cause:…
### Issue: An unexpected `<SchmeNm><Cd>CUST</Cd></SchmeNm>` node was added inside `<InitgPty><Id><OrgId><Othr>` for all countries Some Italian banks reject SDD files containing this node ### Cause: https://github.com/odoo/enterprise/commit/3c3c64b511d07fc1ba33c30363972f5b0f7283d6 added `<SchmeNm><Cd>CUST</Cd></SchmeNm>` unconditionally for all countries The original fix was intended for Nordea (Sweden) only, which requires this node explicitly The assumption that other countries would accept it was incorrect ### Steps to reproduce: - Install `account_sepa_direct_debit` and `l10n_it` - Switch to the IT company - In Settings, set SEPA Direct Debit Creditor Identifier to `BE30ZZZ300D000000042` - Create and confirm a Payment (Method: SEPA Direct Debit, any customer and amount) - Create and validate a Batch Payment with that payment - Open the generated PAIN008 XML Before the fix, `<SchmeNm><Cd>CUST</Cd></SchmeNm>` is present opw-6530996 Forward-Port-Of: odoo/enterprise#130304