Monday, August 24, 2026
13 changes · 18.0
Enhancements to existing features
Financial reports that count accounting entries now run much faster on standard queries by avoiding unnecessary duplicate counting. This improves responsiveness for large companies while keeping report results unchanged.
Original PR description
On standard AML-backed domain-engine queries, `id` is unique. `COUNT(DISTINCT id)` is therefore redundant and prevents PostgreSQL from using a partial hash aggregation. Analytic-groupby and cash-basis modes retain `DISTINCT`, as their temporary AML relations can contain duplicate AML IDs. Benchmark on the Balance Sheet query, 5 runs per size: | Matching AMLs | Before | After | |---:|---:|---:| | 2.3M | 3.19 s | 2.06 s | | 5.8M | 6.51 s | 2.91 s | | 11.5M | 9.78 s | 3.64 s | | 17.2M | 15.63 s | 4.67 s | | 23.0M | 16.01 s | 4.68 s | At 23M AMLs, execution time drops from 16.01 s to 4.68 s (3.42x). Result sets are identical before and after. opw-6459372
Resolved issues and error corrections
Odoo now recognizes newer search, social, and AI crawler tools so they are not repeatedly redirected away from default-language website pages. This helps pages be inspected and indexed correctly while keeping normal visitor language behavior unchanged.
Original PR description
Modern crawlers now send an `Accept-Language` header (for example, `en-US,en;q=0.9`), whereas historically they did not. When that language differs from the website's default language,…
Modern crawlers now send an `Accept-Language` header (for example, `en-US,en;q=0.9`), whereas historically they did not. When that language differs from the website's default language, `ir.http._match()` issues a 303 redirect from `/page` to `/<lang>/page`. Since crawlers do not retain cookies, unrecognized agents are redirected on every request and never reach the default-language page. Customers reported that Google Search Console URL Inspection live tests only receive a redirect and that pages remain unindexed. Googlebot itself is not affected because it already matches the existing `bot` token. `_match()` already skips language redirects for recognized bots by serving the default-language page directly. Extend the `bots` user-agent list with modern crawler identifiers, each verified against vendor documentation: * `google-inspectiontool`: Search Console URL Inspection / Rich Results Test * `googleother`: Google generic crawler (`GoogleOther`, `GoogleOther-Image`, `GoogleOther-Video`) * `meta-external`: `meta-externalagent`, `meta-externalfetcher`, and `meta-externalads`, successors to the already-listed `facebookexternalhit` * `meta-webindexer`: Meta AI search indexer * `chatgpt-user`: OpenAI user-request fetcher (currently matched only through the `bot` substring in its info URL, which is fragile) * `claude-user`: Anthropic user-request fetcher * `perplexity-user`: Perplexity user-request fetcher The redirect behavior remains unchanged for human visitors. Localized pages continue to be crawlable through their own URLs (for example, `/fr/page`) via `hreflang` alternates. As a side effect, `link_tracker` and `mass_mailing_sms` no longer count clicks from these crawlers, and website visitor tracking skips them. task-6213245
Invoices for timesheet-based services now use the actual timesheet period selected during invoice creation instead of defaulting to the invoice and due dates. This improves accuracy in Factur-X/CII XML exports and helps customers receive billing documents that reflect the real service period.
Original PR description
### Issue before this commit: Invoices generated from timesheet-based service products show the wrong billing period in the Factur-X/CII XML BillingSpecifiedPeriod — the invoice date (and due date)…
### Issue before this commit: Invoices generated from timesheet-based service products show the wrong billing period in the Factur-X/CII XML BillingSpecifiedPeriod — the invoice date (and due date) instead of the actual timesheet period s elected by the user. ### Steps to reproduce the issue: 1. Download Sales, Timesheets and l10n_de 2. Go to products, find a service product (like flooring service) and set: 1. Create on order as 'project' 2. Invoicing policy as 'Based on Timesheets' 3. Go to sales and create a new quotation with that product, confirm it, and record some hours for July through the smart button 4. Click 'create invoice' and select from 1 July till 30 July 5. Send it and check factur-x.xml 6. The tag BillingSpecifiedPeriod is wrong and report the date of the invoice instead of the selected dates ### Cause of the issue: deferred_start_date/deferred_end_date are never populated when an invoice line is generated from a timesheet-based sale order line. ### Reason to introduce the fix: The wizard already captures the intended timesheet period (timesheet_start_date/timesheet_end_date in the context) but never forwards it to the invoice line, so the correct data is available but unused. Populating deferred_start_date/deferred_end_date from that context, and giving line-level dates priority over document-level fallback dates, ensures the exported XML accurately reflects the billed period. opw-6391359 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Customers using point of sale loyalty rewards will now see next-order coupons on receipts even after the page is refreshed or data is reloaded. This keeps receipts accurate when staff reprint past orders and helps avoid missed customer rewards.
Original PR description
Step to reproduce: = - Complete order with Next order coupon - Open Order and print receipt (This time coupon is visible) - Refresh page Or Reload Data - Same order > Print receipt Issue: = - Next time on-wards next order coupon will not shown on the receipt. Reason: = - Loyalty program data is stored in the uistate which destroyed on refresh page OR reload data. Fix: = - Fetched data from the server when uistate doent have any loyalty program data for that order while receipt printing only. task-5890998 related pr: https://github.com/odoo/enterprise/pull/128730
This fix prevents Shop Floor users from accidentally applying the same added component quantity to every matching manufacturing move when a product appears on multiple BoM lines. Those multi-line products are now read-only in the catalog, reducing incorrect material demand and production adjustments.
Original PR description
**Issue** In an MO, if a product is spread across multiple moves, adding consumption from the Shop Floor app, adds the entered quantity to each of those moves instead of once in total. **Steps to…
**Issue** In an MO, if a product is spread across multiple moves, adding consumption from the Shop Floor app, adds the entered quantity to each of those moves instead of once in total. **Steps to reproduce** - Configure a BoM with multiple component lines that have the same product. - Start a MO with the BoM, then go to the Shop Floor app and choose the "Add Components" option. - Select the product with multiple lines in the catalog and add any amount. - Go back to the MO. -> The amount added in Shop Floor is applied to each line individually, instead of increasing the total demand by that amount. **Cause** By default, editing the consumption from the Shop Floor catalog is not read-only: https://github.com/odoo/odoo/blob/fc7bd92cc52f366a0d8730bca066b757aefe870c/addons/stock/models/stock_move.py#L2615 When the quantity is updated, the controller calls `_update_order_line_info`, which fetches every stock move linked to that product: https://github.com/odoo/odoo/blob/fc7bd92cc52f366a0d8730bca066b757aefe870c/addons/product/controllers/catalog.py#L32-L46 https://github.com/odoo/odoo/blob/fc7bd92cc52f366a0d8730bca066b757aefe870c/addons/mrp/models/mrp_production.py#L3028 The new quantity is then applied to all of them at once: https://github.com/odoo/odoo/blob/fc7bd92cc52f366a0d8730bca066b757aefe870c/addons/mrp/models/mrp_production.py#L3031 https://github.com/odoo/odoo/blob/fc7bd92cc52f366a0d8730bca066b757aefe870c/addons/mrp/models/mrp_production.py#L3046-L3047 **Solution** Backport the fix made in 19.0, which makes those lines read-only: https://github.com/odoo/odoo/commit/c7a894dfbc025eb27528a383f33e9aebbf4bd54e opw-6479210
The project profitability report now correctly displays cost of goods sold for delivered products when Anglo-Saxon accounting and analytic accounting are enabled. This ensures project managers see the true product cost impact instead of having costs hidden by offsetting accounting entries.
Original PR description
**Problem:** Since this PR https://github.com/odoo/odoo/pull/261798, both cogs lines have an analytic account, which causes the cogs to not appear on the project profitability report because cogs…
**Problem:** Since this PR https://github.com/odoo/odoo/pull/261798, both cogs lines have an analytic account, which causes the cogs to not appear on the project profitability report because cogs lines balance each other **Steps to reproduce:** - enable 'anglo saxon accounting' and 'analytic accounting' settings - create a storable product with automated std category, a cost of 10 and on hand quantity - create a service product and set the 'create on order' field to 'project' - confirm a SO for 1 unit of the product and 1 unit of the service - validate the delivery and create and confirm invoice - from the sale order, click on the project smart button - from the project click on the dashboard smart button **Current behavior:** the cogs section don't appear in the profitability report **Expected behavior:** it should appear with a line with a value of -10 **Cause of the issue:** since this PR https://github.com/odoo/odoo/pull/261798, both cogs line are linked to the analytic account. That's the expected behaviour but in the case of the project profitability reports, it prevents the user the see the cost of the product in the cogs section. That's because, inside the _get_revenues_items_from_invoices() method, bot cogs_line are added to the cogs_line list. https://github.com/odoo/odoo/blob/141cb292dc5e456161119e19f7a91665feaa0198/addons/sale_project/models/project_project.py#L699-L700 So when computing the amount_to_invoice for the costs ml_type, the balance of the lines will zero out each other and amount_to_invoice will be 0. https://github.com/odoo/odoo/blob/141cb292dc5e456161119e19f7a91665feaa0198/addons/sale_project/models/project_project.py#L703-L716 As a consequence, the cost of goods sold section won't be created https://github.com/odoo/odoo/blob/141cb292dc5e456161119e19f7a91665feaa0198/addons/sale_project/models/project_project.py#L718-L719 **fix:** only the line with an account of internal type 'expense' reflects the actual cost of the product sold in the context of the project. So when computing the profitability report that's the only line we should consider **test:** test_report_invoice_items_anglo_saxon_automatic_valuation checks that the cogs section is well displayed in the project profitability report. In the PR (mentionned above) which sets the analytic account on the stock cogs line, lines were added in the test to manually remove the analytic account on the stock cogs line to make the test pass. With the fix of this PR we can remove those additional lines in the test and it will check our use case well again. opw-6412409
Phone and mobile numbers are now formatted whenever partner records are created or updated, not only when edited through a form. This keeps contact data consistent across website checkout, imports, API updates, and other entry points while leaving invalid numbers unchanged.
Original PR description
Phone and mobile numbers were only formatted via @api.onchange, which fires solely in the interactive form view. Numbers set through other write paths (website checkout, data import, RPC/API, other controllers) were stored unformatted. Core was also inconsistent: website_crm's form controller reformats numbers before saving while website_sale checkout and plain ORM writes did not. This fixes the inconsistency and now every write path behaves consistently. Invalid/unparseable numbers are left untouched. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Self-billing invoices received through Peppol can again be imported into the dedicated self-billing sales journal. The change also avoids an error screen for databases where the self-billing module is not installed, improving reliability for affected users.
Original PR description
This PR https://github.com/odoo/odoo/pull/277239 has been merged before being rebased on https://github.com/odoo/odoo/pull/259935/changes/568e3e1d4f100e22bd5e724a6afa6d0437f56830 This commit restores the possibility of importing a self-billing invoice into a dedicated self-billing sale journal, and prevents a traceback from being shown in case the user's DB has no `account_peppol_selfbilling` module installed (which is auto-installed with `account_peppol` by default, but we got a feedback of a user having this issue). task-no feedback : https://www.odoo.com/odoo/project/49/tasks/6481452 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283581
Export invoices in the Argentine electronic invoicing flow now send the correct recipient identification information for QR validation. This lets businesses verify these invoices as legal documents on the ARCA/AFIP validation page and avoids incorrect identification labels.
Original PR description
Before this change we were sending id type code 0 and this generate two problems * ARCA verification page it was wrongly taking "CI Policia Federal" as the identification type of the receptor * We were not able to validate the expo invoice, we get always an error With this change the expo invoice can be checked as a real legal document in the ARCA page https://servicioscf.afip.gob.ar/publico/comprobantes/cae.aspx
Vendor bills uploaded for OCR from a project now keep the project's cost allocation when the OCR result is applied later. This prevents bills from appearing disconnected from the project and avoids the need for users to manually reload AI data.
Original PR description
Issue: Vendor bills uploaded for OCR from a project's Vendor Bills view do not receive the project's analytic distribution. As a result, the bill is not linked to the project until the user clicks…
Issue: Vendor bills uploaded for OCR from a project's Vendor Bills view do not receive the project's analytic distribution. As a result, the bill is not linked to the project until the user clicks Reload AI Data. Steps to reproduce: * Create a billable project with an analytic account. * Open the project's Tasks and select Vendor Bills. * Upload a vendor bill for OCR. * Wait for the extraction result. Cause: The project is provided through the vendor bill action context, but OCR results are applied asynchronously from a webhook or cron. The invoice is browsed again without that context, and `_save_form()` creates the extracted lines with only their labels: https://github.com/odoo/enterprise/blob/039012c15aa97f51e29502265b2146356bd4dc0f/account_invoice_extract/models/account_invoice.py#L862-L874 The project analytic computation can therefore no longer determine which distribution should be assigned to the extracted lines. Solution: We need to preserve the effective analytic distribution when the document is uploaded, while its originating context is still available, and use it as a default when the extracted lines are later created. Storing the computed distribution instead of the project context keeps OCR independent of Project, supports other analytic default sources, and leaves invoices without an upload time distribution unchanged. opw-6412993
This fixes an error that could block reconciliation when users work with a parent company and branch company at the same time. The accounting reconciliation process now uses the relevant journal item company for currency conversion, helping users complete matching entries without interruption.
Original PR description
When having multiple companies selected at the same time, _get_conversion_rate returns: File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in get raise ValueError("Expected singleton: %s" %…
When having multiple companies selected at the same time, _get_conversion_rate returns:
File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in get
raise ValueError("Expected singleton: %s" % record)
1 - Create a new company with currency EUR.
2 - Create a branch company underneath the main company.
3 - In Accounting, install fiscal localization, e.g. Belgian Companies on the company configuration settings.
4 - Select an account like 600000 Raw Materials, and enable Allow Reconciliation on this account. The exact account isn't important, only that we can make credits / debits to it to be reconciled.
5 - With only the top level company selected, make a debit of 100 USD, e.g. Vendor Bill, set in currency USD to the account 600000.
6 - Now with only the branch level company selected, make a credit of 100EUR, e.g. Customers Invoices, set in currency EUR to the same account with an amount equal to the credit in step 5. (if 1USD == 1EUR, 1-1), so that there is no residual amount, i.e. credit == debit.
7 - Now select both the top level company and the sub branch company in the company context.
8 - In Journal Items, reconcile the unreconciled journal items for the Account 600000.
With this commit we select the first company of the aml instead of every companies on the amls.
opw-6290703This fix prevents subscription billing from crashing when an automatic payment fails and the related payment record is rolled back. It also avoids incorrectly detaching invoices after successful payments, helping recurring invoicing continue more reliably.
Original PR description
Step to reproduce: - create a faulty token that won't work and link it to a subscription - launch the recurring invoice cron - the following traceback occurs ``` last_tx_sudo = (self.transaction_ids…
Step to reproduce:
- create a faulty token that won't work and link it to a subscription
- launch the recurring invoice cron
- the following traceback occurs
```
last_tx_sudo = (self.transaction_ids - existing_transactions).sudo()
```
When the payment fails, the system rollback and we store the last_tx_sudo value in a dedicated variable. After rollback, the record does not exists anymore. Therefore, accessing the value fails.
```
File "/home/odoo/src/enterprise/saas-18.2/sale_subscription/models/sale_order.py", line 1703, in _handle_automatic_invoices
if not last_tx_sudo or last_tx_sudo.renewal_state in ['pending', 'authorized']:
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1439, in __get__
self.compute_value(record)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1603, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/models.py", line 4575, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 69, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-18.2/sale_subscription/models/payment_transaction.py", line 25, in _compute_renewal_state
if tx.state in ['draft', 'pending']:
^^^^^^^^
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1406, in __get__
raise MissingError("\n".join([
odoo.exceptions.MissingError: Record does not exist or has been deleted.
```
Moreover, since https://github.com/odoo/enterprise/pull/45236/files#diff-c36fd7952cc2bef40716419a668de41963d49e1aa4177d9319d503fc260da588R1678-R1682
```
if not last_tx_sudo or not last_tx_sudo.renewal_state not in ['pending', 'authorized']:
```
has become
```
if not last_tx_sudo or last_tx_sudo.renewal_state in ['pending', 'authorized']:
```
But it feels strange to unlink the invoice when the payment succeed.
This PR fixes it.This update corrects several issues in SAF-T/FAIA reports, including invalid negative tax amounts, overly long software version values, incorrect foreign-currency tax amounts, and invoice customer/supplier data conflicts. These fixes help Luxembourg and Romania accounting reports pass audit and schema checks more reliably.
Original PR description
This is one of many errors fixing the FAIA report, which is the SAF-T report for Luxembourg. See PR #113316 for a list of similar PRs. ### Error 1: Negative tax amounts Auditors from Luxemborg…
This is one of many errors fixing the FAIA report, which is the SAF-T report for Luxembourg. See PR #113316 for a list of similar PRs. ### Error 1: Negative tax amounts Auditors from Luxemborg provided one Odoo user with analysis files of their FAIA xml report. The following discrepancy was present in more than 300 lines: `[TaxInformation/TaxAmount/Amount] # is negative. Only postive values are admitted. The sign is automatically determined by the corresponding CreditAmount (-) Or DebitAmount (+) on the same Line.` This discrepancy was caused by two different scenarios. The first was a negative `unit_price` line, such as a Discount product. The second was a tax with negative and positive repartition lines, such as a tax with xml ID `lu_2015_tax_AP-EC-17`. Luxembourg officials confirmed the following behavior: 1. The TaxInformation/TaxAmount/Amount element must be positive. 2. The TaxInformationTotals/TaxAmount/Amount element may be negative. 3. There may only be one TaxInformationTotals element per TaxCode in an Invoice element. This commit ensures that these conditions are met for the FAIA report. I'm not sure if the TaxInformation changes should also be applied to the base `account_saft saft_report.xml` file. ### Error 2: SoftwareVersion The SoftwareVersion element is limited to 18 characters. The relevant error from a customer's analysis file is below. Error: Value exceeds maxLength of "18". ### Error 3: CurrencyAmount The `account_saft` method `GeneralLedgerCustomHandler._saft_fill_report_tax_details_values()` does not report the amount of tax in foreign currency, instead replacing this value with the amount in company currency. No errors prompted this change; it just seems wrong on its face. ### Error 4: PR #113720 ensured that the TaxType element is always TVA. This means that the TaxType should no longer should be ignored in our example documents. ### Error 5: Schema validation failure The elements Inovice/CustomerInfo and Invoice/SupplierInfo are defined with the element `<xs:choice>` in the XSD file linked below. Only one can be present at any time, not both. https://pfi.public.lu/dam-assets/backup/FAIA/FAIA/XSD_Files.zip. note: currently the link is broken. PR #100749 allowed many parts of SAF-T code to display both customer and supplier data, including these elements. This commit ensures that the elements are mutually exclusive. opw-6344914 [Link](https://www.odoo.com/odoo/project.task/6344914) Forward-Port-Of: odoo/enterprise#126121