Tuesday, September 8, 2026
69 changes · master
Resolved issues and error corrections
Peruvian electronic invoices now include cash rounding in the final amount due sent to SUNAT. This prevents mismatches between the displayed rounding and the payable total in generated XML documents, reducing invoice validation or compliance issues.
Original PR description
Steps to reproduce ------------------ 1. On a peruvian company, set a cash rounding method on an invoice 2. Post the invoice and generate the SUNAT UBL 2.1 XML -> the rounding is in…
Steps to reproduce ------------------ 1. On a peruvian company, set a cash rounding method on an invoice 2. Post the invoice and generate the SUNAT UBL 2.1 XML -> the rounding is in `PayableRoundingAmount` but `PayableAmount` still has the amount before rounding! Why it's happening ------------------ The generic code computes `PayableAmount` from `amount_residual`, and Peru overrides it to be the total tax included of the XML minus the prepaid amounts, because the residual can not be used there. Then odoo/odoo@b847552872ad changed the meaning of the totals. The cash rounding line is not part of the base lines anymore, its amount is kept aside in `cash_rounding_base_amount_currency` and the totals do not contain it anymore. The generic code stays correct because `amount_residual` already has the rounding inside but the Peru total using `tax_inclusive_amount_currency` is now the amount before rounding and this is what ends up in the `PayableAmount`! The fix ------- Add the cash rounding amount when computing the `PayableAmount`. opw-6509677 Forward-Port-Of: odoo/enterprise#130732 Forward-Port-Of: odoo/enterprise#129983
Entering a VAT or GST number in the partner name field now correctly triggers partner autocomplete again. This helps users find and select existing partners faster, reducing manual entry and duplicate records.
Original PR description
PURPOSE - Typing a VAT/GST number in the partner name field failed to trigger the autocomplete. - This was due to a [refactoring](https://github.com/odoo/odoo/pull/272884/changes#diff-0a5505b3e2038534267e3af27e91e6466d9326d8954d38c4bc44d69fa333989eR68) that restricted VAT validation strictly to the `vat` field. SPECIFICATION - We are automatically switching the search logic to VAT if a valid VAT/GST number is detected inside the name field. task-6544712
This fixes a timing issue in the restaurant point-of-sale order tracking test by waiting until order validation and syncing are complete. It helps prevent false test failures where the system checked order details before the latest changes reached the backend.
Original PR description
The order tracking tour only waited for the feedback screen to be shown after validating the payment. Since order validation is performed asynchronously while the feedback screen is displayed, the tour could finish before the updated order was synced to the backend. This caused the Python test to still see the original quantity and `is_edited` set to false. To fix we wait for the feedback screen continue button to be enabled, which ensures order validation and synchronization have completed before the tour ends. [error-940386](https://runbot.odoo.com/odoo/error/940386) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282726
Customers who open an order through a secure access link can now use the Return button without signing in. This fixes a broken self-service return flow and reduces friction for portal or guest customers returning delivered items.
Original PR description
Accessing an order on the website without connecting (only with the access token) doesn't allow the user to return the order (nothing happens when click on the "Return" button) Steps to reproduce: 1.…
Accessing an order on the website without connecting (only with the access token) doesn't allow the user to return the order (nothing happens when click on the "Return" button) Steps to reproduce: 1. Install Sales and Inventory 2. Go to Settings > Inventory > Operations and enable "Allow Spontaneous Returns" 3. Go to Sales and create a new quotation for customer "Acme Corporation" with product "Acoustic Bloc Screens" and confirm it 4. Go to the related delivery, set the quantity to 1 and validate it 5. Go back to the sale order and click the "Preview" smart button 6. Copy the url (/my/orders/<id>?access_token=...) and open it in an incognito window 7. Click the return button on the order page 8. Nothing happens Issue: The route to `my_order_return_data` requires the user to be logged in Solution: Make the routes `my_order_return_data` and `order_return_label` public For 19.4: access `return.reason` records with sudo (should be removed in master) For master: add read right on `return.reason` for all users opw-6487767 Forward-Port-Of: odoo/odoo#285290
This fix removes leftover setup data from an earlier change that could conflict with current repair subcontracting records. It helps keep the manufacturing repair configuration consistent and avoids unexpected conflicts during use or upgrades.
Original PR description
An old refactor left some data around that are in conflict with other records for the same model. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282103 Forward-Port-Of: odoo/odoo#281276
Manufacturing order notes in the shop floor view now stack vertically instead of appearing as separate columns. Long notes are contained in a small scrollable area, keeping the layout consistent and easier for operators to read.
Original PR description
Before : As multiple notes on MO are stored as a conjunction of div, they are shown as multiple columns in the shop floor view due to the flex layout. After : Wrap the notes in a scrollable container capped at 3 lines so the line height stays consistent regardless of note length. Changes: As we want to keep the edit icon aligned as it is, we keep the flex layout and wrap the notes in a separate div with a vertical flex container. We furthermore limit the height of the note to 3 lines with an overflow auto so that the line height stays consistent regardless of note length. [opw-6544909](https://www.odoo.com/odoo/project/966/tasks/6544909)
This fix prevents errors when company car benefit values are calculated outside Belgian company contexts or when expected payroll parameters are missing. It improves reliability for multi-company setups and avoids interruptions for users managing vehicle models across different countries.
Original PR description
. Handle multi-company and non-Belgian contexts in fleet.vehicle.model's . Add raise_if_not_found=False when retrieving min_car_atn to prevent UserError/TypeError when parameter records are missing or when processing non-BE models. . Add test to verify that computing BIK for non-Belgian companies executes safely without exceptions. task-6544760
Customers can now open signed report PDFs from the portal history without the preview crashing. This ensures Field Service reports remain accessible after signing while preserving the existing backend signing workflow.
Original PR description
Steps to reproduce: - Open a Field Service shift that has a customer (in progress or done). - Click Sign Report, sign, and confirm. - In History, click the report PDF. Before this commit: the preview crashed instead of opening. After this commit: the PDF opens on the portal. In the backend, the Sign button in the viewer still opens the template editor. The viewer Sign button needs the backend action manager, which does not exist on the portal. Requiring it blocked the preview itself. task-6535111
The Field Service planning Gantt view now opens without waiting for buffer time calculations to finish. This reduces potential delays for users, while the missing timing details are filled in automatically as soon as they are ready.
Original PR description
This commit fixes a potential performance issue in the Gantt view of Field Service when computing the buffer times. Prior to this commit, the view waited for the buffer times to be computed before rendering. With this commit, we trigger the buffer time computation in the background (i.e., fire and forget), while letting the view to render. As buffer times are computed, the view will be notified. no-task Forward-Port-Of: odoo/enterprise#130782
This fixes automated Point of Sale stock test scenarios so they can find the intended test customer even when many demo customers are present. It helps keep test runs stable and prevents false failures during quality checks.
Original PR description
When running tests with demo data, the partner list is populated with many records, causing 'Partner Test 1' to fall outside the initial 100 loaded partners in the PoS session cache. Because clickCustomer defaulted to pressEnter=false, searching in the UI filtered the in-memory cache and displayed 'No customers found, press Enter to load more.', but never dispatched the Enter key to fetch the partner from the backend, timing out the tour step. Pass pressEnter=true in pos_stock customer selection tours so that the Enter key is dispatched and the partner is fetched from the server via RPC. runbot-error: 242001 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286513
Fixes the French association balance sheet so active and passive totals include the right lines and avoid double-counting entries. This helps associations relying on Odoo financial reports see accurate balance sheet totals for compliance and decision-making.
Original PR description
## [FIX] l10n_fr_reports: fix missing lines in association balance sheet ### Issue: The Active part of the Balance Sheet for associations shows an incorrect `TOTAL ACTIVE` — some accounts are not…
## [FIX] l10n_fr_reports: fix missing lines in association balance sheet ### Issue: The Active part of the Balance Sheet for associations shows an incorrect `TOTAL ACTIVE` — some accounts are not propagated to their parent aggregations ### Cause: `ACTIF_IMMOBILISE` was missing `BIEN_PAR_DONATION` in its formula `ACTIF_CIRCULANT` was missing both `DISPONIBILITES` and `INSTRU_FINAN` in its formula ### Steps to reproduce: - Install `l10n_fr_reports` and `accountant` - Create a new FR asso company and switch to it - In Accounting Settings, select: France - Associations accounting plan - Create a Journal Entry: Account: 240000, Debit: 100 (BIEN_PAR_DONATION) Account: 512001, Debit: 100 (DISPONIBILITES) Account: 520000, Debit: 100 (INSTRU_FINAN) Account: 509000, Credit: 300 - Open the Balance Sheet and select Balance Sheet for associations Before the fix, `TOTAL ACTIVE` is 0 instead of 300 -------------- ## [FIX] l10n_fr_reports: add force_date_scope to asso cross_report ### Issue: The Passive part of the Balance Sheet for associations shows an incorrect `TOTAL PASSIVE` — the same move line is counted twice, once in `Retained earnings` and once in `Profit or loss for the year` ### Cause: In 19.1, `cross_report` aggregations were refactored: https://github.com/odoo/enterprise/commit/e699a14a1922d6b8a5bc6b88ad9c02a411e4b073 By default, a `cross_report` no longer forces its `date_scope` to the terms it calls — `force_date_scope` must now be explicitly passed in the subformula The association balance sheet was added in 19.1 without this parameter, so `RESULT_LEXERCICE` (`from_fiscalyear`) and `REPORT_NOUVEAU` (`to_beginning_of_fiscalyear`) both used the current report's `date_scope` instead of their own This caused both expressions to match the same entries and double the `TOTAL PASSIVE` ### Steps to reproduce: - Install `l10n_fr_reports` and `accountant` - Create a new FR asso company and switch to it - In Accounting Settings, select: France - Associations accounting plan - Create a Journal Entry: Account: 512001, Debit: 100 Account: 701100, Credit: 100 - Open the Balance Sheet and select Balance Sheet for associations Before the fix, `TOTAL PASSIVE` is 200 instead of 100 opw-6520639 Forward-Port-Of: odoo/enterprise#130147
Users with permission to edit a spreadsheet can still rename it after refreshing the page. This fixes a problem where refreshed spreadsheet links were incorrectly treated as read-only, reducing confusion and preserving normal editing workflows.
Original PR description
Spreadsheet backend URLs now contain an access token for regular users. After a refresh, the router restores this token and the navbar treated its presence as read-only, even when the user had write access. An access token identifies the spreadsheet URL and collaborative session; it no longer indicates that the current user is read-only. Actual permissions are provided by `has_write_access`, while archived spreadsheets are handled by `spreadsheetMode`. Use the spreadsheet mode as the single source of truth for both spreadsheet content and filename edition. Task: 6496749
This fix changes the registration flow so Odoo checks for an existing proxy user before contacting the external IAP service. It prevents rare concurrent registrations from leaving a database with outdated credentials that block later electronic document proxy calls.
Original PR description
In the current flow, _register_proxy_user first calls IAP create_user, then inserts the returned credentials in account_edi_proxy_client.user. At the same time, IAP create_user_2 may replace an existing user with a new one with different credentials before local persistence settles. So Tx A calls IAP and gets credentials for remote user U1. Tx B calls IAP and gets credentials for remote user U2 and unlinks U1. Tx A persists U1 locally. Tx B fails local insert due to a unique constraint. Client DB keeps U1 credentials, but IAP now expects U2. Subsequent proxy calls from the client fail. The DB is left with stale, unusable credentials and cannot recover without re-registration. IAP: https://github.com/odoo/iap-apps/pull/1816 task-6520501 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285648
The accounting module now uses today's date when a required reporting date is not provided. This prevents an unexpected error from interrupting currency consolidation calculations and keeps accounting workflows running smoothly.
Original PR description
`date_to` is accessed directly from `self.env.context` in `_compute_sql_consolidation_rate`. When the key is missing from the context, this raises an error and breaks the flow. Use today's date as the default value when `date_to` is not provided in the context, preventing the traceback and allowing the computation to continue normally. Forward-Port-Of: odoo/odoo#286965
Changing a shift will no longer automatically restore a sales order line that a user intentionally removed. The sales link is now updated only when the related project or task changes, helping keep staffing and billing information aligned with user intent.
Original PR description
Before this commit, when a change is made in a shift, the SOL initially removed by the user can be set by the one set on the task/project linked to that shift. The reason is because each time the compute of SOL field is triggered, the compute will set the SOL of the task or the project linked. The problem is we only want that behavior when the user changes the project or the task. This commit removes the logic the compute to set the SOL of the project/task, to do that logic inside an onchange method instead. Task-6354078 Forward-Port-Of: odoo/enterprise#130739 Forward-Port-Of: odoo/enterprise#125814
This fix makes Odoo's mail tracking tests use a consistent language setting instead of depending on each developer or server machine's regional settings. It prevents false test failures in accounting, CRM, HR recruitment, manufacturing, mailing, and related modules when systems are configured in non-US English locales.
Original PR description
Since the removal of tracking values, they are only accessible formatted and localized embedded into mail messages. This means when we try to check them in tests, we need to format the "reference"…
Since the removal of tracking values, they are only accessible formatted and localized embedded into mail messages. This means when we try to check them in tests, we need to format the "reference" values using the same locale. During testing the locale is *generally* `en_US` because that's the default for odoo / test users, but `TransactionCase.env` does not have a lang configured at all, so when tracking we call something like `babel_locale_parse(self.env.lang)` = `babel_locale_parse(None)`. `None` is obviously not a valid locale, so `babel_locale_parser` ~~raises an error~~ falls back, and the first fallback is `Locale.default()`, which checks the language / regional envvars (`LANGUAGE`, `LC_LANG`, `LC_ALL`, `LANG`) and returns the first valid one, otherwise `babel_locale_parse` then defaults to `en_US`. The middle case is the problem here, if a machine is configured with a locale other than `en_US` then that's what's used to format the tracking values against the stored message, so if someone's locale is `zh_TW` they get `2020年1月1日`, which for some reason does not match a english-US format, making pretty much every test checking tracking values fail. But hey for once it's not a babel version issue.
This update ensures the partner autocomplete identifier field is added through the correct module instead of being embedded in the core contact views. It also restores save and cancel prompts when users manually edit multiple identifiers, preventing changes from being missed.
Original PR description
This commit fixes 2 things : 1. the `additional_identifiers_list_partner_autocomplete` widget was hardcoded directly into the core `base` module's views for res.partner and res.company. 2. Manually editing the multi-ids field wouldn't show the save/cancel buttons. Fix af3f8f6bc267eb431c0be108f30c061f0b0e1c01 no task
This fix makes automated checks use a consistent language when verifying change history values. It helps prevent false test failures caused by translations, improving release reliability without changing customer-facing functionality.
Spreadsheet editing screens now consistently use a topbar color that blends with the main navigation, not just in the Documents area. This creates a more consistent and polished experience for users working with spreadsheets across the system.
Original PR description
The recent fix in #130001 to blend the topbar color with the navbar only worked for the documents action. This revision extends the behaviour for every spreadsheet editing action. task-6529910
The VoIP flow editor now keeps its visual layout consistent when used in right-to-left languages. This prevents misplaced connection points, resize controls, and overlapping text, making call flow editing more reliable for affected users.
Original PR description
The flow editor stores and computes its geometry using physical coordinates: inputs are placed on the left, outputs on the right, and nodes are resized from the bottom-right corner. RTLCSS mirrored some of the corresponding styles in RTL languages. This made ports and their labels inconsistent with SVG connections, changed the resize handle position and cursor, and altered the canvas transform origin. Prevent RTLCSS from flipping these geometry-dependent declarations so the editor behaves consistently in LTR and RTL. The record description in a terminal node's body was also missing min-width: 0 on its flex item, so text-truncate never actually constrained it: in RTL the text starts flush against the physically right-anchored output port labels, overlapping them.
Products added to an active Point of Sale session after it starts now receive the correct pricelist discounts or prices based on their category. This prevents unexpected full-price charges when staff add newly created products from categories covered by pricing rules.
Original PR description
Steps to reproduce: - Create a pricelist with a rule applied on a product category and make it available in the PoS - Open a PoS session - From the backend, create a product in a new category covered…
Steps to reproduce: - Create a pricelist with a rule applied on a product category and make it available in the PoS - Open a PoS session - From the backend, create a product in a new category covered by such a rule - Back in the PoS, find that product through Search > Search more and add it to the order Issue: The product is priced at its sale price, the pricelist rule set on its category is ignored. Cause: A product that is not part of the initial payload is loaded on the fly by load_product_from_pos, which sends back the rules returned by get_pos_ui_product_pricelist_item_by_product. That domain only matches the rules set on the template or on the variant, never the ones set on a product category, and the payload carries no product.category record either. The client therefore has neither the rule nor the category: parentCategories walks categ_id, which resolves to nothing, so getCategoryRulesIds returns no rule and getPrice falls back to the sale price. This stayed unnoticed because product.category is fully loaded when the session starts, along with every category rule, so only the categories created after the session was opened are missing. Fix: Send the categories of the loaded products, since a rule set on a parent category applies to its children - along with the products, and match the rules set on those categories in get_pos_ui_product_pricelist_item_by_product. The initial loading domain of product.pricelist.item no longer filters the category rules on the loaded categories: such a rule has to be loaded whatever the products sent to the client are, since a product of that category may be loaded later on. opw-6477745 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285721 Forward-Port-Of: odoo/odoo#284660
This fix stops Point of Sale invoice settlements in Argentina from incorrectly creating a separate zero-value invoice. It prevents settlement validation errors, allowing businesses to record customer payments reliably without consuming unnecessary electronic invoice numbers.
Original PR description
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices",…
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices", select that invoice, pay the balance by bank transfer and validate Issue: Validation fails with "There should be a single tax from the "VAT" tax group per line, but this is not the case for line ..." and the settlement cannot be recorded at all. The invoice being settled is correct; the rejected line belongs to a second invoice the PoS creates for the settlement itself. Cause: `setToInvoice` already refuses to invoice a settlement, but its condition, `is_settling_account and no line`, only describes a deposit: the deposit line is added at validation. When settling a due or an invoice, `is_settling_account` stays false and the order does carry lines - the settle lines - so the guard never fires. l10n_ar_pos then sets `to_invoice` on mount, as a sale must generate an electronic document in AR, and the settle line, untaxed on purpose since it pays an existing document rather than selling anything, reaches `_check_argentinean_invoice_taxes`. Fix: Refuse the flag as well when every line is a settle line. Such an order generates an empty document - all its lines, the receivable one included, have a zero balance - so it records nothing and only consumes a document number, which in AR means an AFIP number for a zero-amount invoice. An order that also sells something keeps its invoice, since the sale still has to be reported. Reconciliation is unaffected: it happens in `_reconcile_account_move_lines` at session close, and is already covered for both invoiced and non-invoiced settlement orders. opw-6464675 Forward-Port-Of: odoo/enterprise#129902 Forward-Port-Of: odoo/enterprise#128975
Odoo now ensures that a private chat between the same two people can only exist once, even when requests happen at the same time. This avoids duplicate conversations, preserves chat history when a contact is deleted, and keeps chat names clearer for users.
Original PR description
A chat between 2 persons should be unique, but it can currently be created multiple times because we don't have a proper constraint. A lock could be added in the main create route, but it wouldn't…
A chat between 2 persons should be unique, but it can currently be created multiple times because we don't have a proper constraint. A lock could be added in the main create route, but it wouldn't catch all flows, and it would also slow down the route and potentially create unnecessary retries. The best way to ensure uniqueness is to add a proper SQL constraint. As the combination of members depends on a x2many relational field, a new field is added to track this combination on the channel table. This is a similar pattern as combination_indices on product.product. The new field is computed during create and not with a compute: - compute are computed after creating the record in database, which makes it impossible to have SQL constraints to ensure the field is properly set, as it would initially not be set - chat with a partner that later gets deleted should be kept for history, in this case the member is deleted but the channel remains, re-computing the member_indices could lead to several channels with only the current user in it, which the constraint does not allow. This also significantly simplifies the search of existing chat channels. task-4664353 https://github.com/odoo/upgrade/pull/9120
This fix prevents electronic invoice imports from failing when an invoice line has a 100% discount together with tax included in the price. Odoo now recalculates the tax from the original price in this edge case, improving reliability for accounting document imports.
Original PR description
Due to the following commit: 01efd8cfcce3269ca6b88d549a670b08a90298cb, a division by zero error is raised when a 100% discount is used with a price-included tax. When the discount is 100%, it is impossible to retrieve the original tax amount before discount using a simple multiplication as the current raw_tax_amount_currency is zero. In that case, we need to recompute taxes using the original unit price before discount. opw-6242701 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283877 Forward-Port-Of: odoo/odoo#282430
Restored or duplicated databases using the neutralize option are now neutralized before the system makes them active for scheduled background work. This prevents automated actions from briefly running on a copied database before it has been safely disabled, reducing the risk of unintended emails, integrations, or jobs.
Original PR description
Before this commit: If you restore a backup or duplicate a database via /web/database/manager with "Neutralize" enabled, the cron workers had a small window between the creation of the Registry and the neutralization of the database where they could try to execute crons. Since neutralize_database does not require a full registry to run (it only does raw SQL operations), we can run it before the Registry creation (which has the effect, among other things, of making the cron workers aware of the new database). opw-6517819 Forward-Port-Of: odoo/odoo#286870 Forward-Port-Of: odoo/odoo#286532
Badge counters in the Discuss app now display correctly when they contain two or more digits. This prevents numbers from being cut off in tabs and messaging menu items, making unread or important counts easier to read.
Original PR description
This PR fixes the position of badge counter in tab and messaging menu item when they have more than 1 digits. Before / After <img width="401" height="466" alt="Screenshot 2026-09-07 at 23 41 58" src="https://github.com/user-attachments/assets/30d3a826-0898-4b5a-81a1-1ed816ed166b" /> <img width="403" height="466" alt="Screenshot 2026-09-07 at 23 34 36" src="https://github.com/user-attachments/assets/45687a6e-924d-413b-bd15-1b16bf5b012c" />
This fix prevents errors when sales reports or tracking references encounter outdated internal model records. It helps keep sales and marketing tracking features stable even when system metadata is temporarily out of sync.
Original PR description
Following 6dedae804748, in case `ir.model` models are out-of-sync with the available models in the registry, trying to compute the target selection model will result in a crash (`KeyError`). This commit ensure the target model is available in the registry to avoid that crash. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284541 Forward-Port-Of: odoo/odoo#284161
The Bangladesh reports module no longer includes an outdated tax return setup that depended on a removed report. This prevents installation failures while keeping the corporate tax report working through existing account tags.
Original PR description
This commit removes the tax return type built on the Bangladesh tax report, which no longer exists. Before: `bd_tax_return_type` resolved `l10n_bd.tr_form` through a `ref`. That report did not match the NBR reporting format and is dropped from the community module, so the reference would fail to resolve and take the whole module down with it at install. Now: the return type and the data file holding it are gone. The corporate tax report is untouched; it reads through the `account_tags_tax_c_exempt`, `c_cog` and `c_exp` account tags rather than through `tr_form`, and those tags are kept on the accounts in the community module. task-6219535
AI image generation and editing now receives the product or website image context that was previously being dropped when opening the media dialog. This prevents the AI assistant from asking for information it should already know and helps it generate results based on the correct product image.
Original PR description
When opening the media dialog from a product image (avatar) edit button, the `ImageFieldWithMediaDialog` (patched by the `ai`) attempts to pass `record`, `originalRecordModel`, and `originalRecordId` as props However, since these props are not declared in `mediaDialogProps`, so prop validation filters them out of `this.props`. Thus they are `undefined` in the MediaDialog. Thus they are not passed to the AI chat launcher. Thus ai has to ask user about product name. This commit registers these props on the `mediaDialogProps` schema in the `ai module patch, preventing Owl from stripping them. task-6365588 **Community counterpart: https://github.com/odoo/odoo/pull/273676** Forward-Port-Of: odoo/enterprise#123050
When an outgoing email fails, Odoo now shows the configured SMTP server name instead of a blank 'None' value. This makes delivery problems clearer for administrators and support teams, helping them identify which mail server needs attention.
Original PR description
Steps to reproduce: 1. Run a local SMTP server that responds with a server error. The server file is provided in the task [refuse_smtp.py](https://github.com/user-attachments/files/27166855/refuse_smtp.py) 2. Create an outgoing server for this SMTP server 3. Send an email Issue: The delivery failure reason displays Mail delivery failed via SMTP server 'None' instead of the configured server name. Cause: ir.mail_server.send_email() builds the failure message from the smtp_server argument, but in the common path the mail is sent via mail_server_id. In that case, the actual SMTP server is resolved in connect(), while smtp_server remains unset, so the error message shows None. Solution: Store the resolved server label on the SMTP connection when opening it, and reuse that value when formatting send failures. opw-6139168 Forward-Port-Of: odoo/odoo#280750 Forward-Port-Of: odoo/odoo#261776
This fix ensures that when a payment is unreconciled, the related cash basis tax reversal is dated in the same reporting period as the original tax entry. This prevents tax reports from showing mismatched amounts across different months, keeping period totals accurate.
Original PR description
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax…
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax report nets to zero for that period. Steps to reproduce: - Enable cash basis and create a cash basis tax (exigibility on payment) - Post an invoice dated in the past with that tax - Reconcile a bank statement line to the invoice - Resequence the cash basis entry so the month is dropped from the name (CABA/08/2026/0001 -> CABA/2026/0001) - Unreconcile the statement line Issue: The reversal CABA entry created on the last unreconcile is dated today instead of the origin entry's month. In the tax report the original tax amount stays in the statement's month while the reversal amount appears in the current month, so the two no longer cancel out. Analysis: While under a monthly journal sequence a past date returns the last day of that month, under a yearly sequence a past date within the current year returns the latter between the move date and today, moving the reversal out of the origin period. opw-6301553 Forward-Port-Of: odoo/odoo#284840 Forward-Port-Of: odoo/odoo#281250
Rental order lines created from the rental schedule now keep the normal product name, such as "Bike", instead of adding stock quantity details meant only for schedule rows. This prevents confusing descriptions on rental orders while still showing availability information where it is useful in the schedule view.
Original PR description
Versions -------- 19.0 and later Steps ----- - Install `sale_stock_renting`. - Go to Rental > Orders > Rental Schedule. - Create a new rental order line from a cell of the gantt view, on a row…
Versions -------- 19.0 and later Steps ----- - Install `sale_stock_renting`. - Go to Rental > Orders > Rental Schedule. - Create a new rental order line from a cell of the gantt view, on a row grouped by a storable rentable product. Issue ----- The first line of the description of the created line is named "Bike (3 items)" instead of "Bike". Cause ----- The `display_name` override adding that quantity is keyed on the `in_rental_schedule` context key. That key is set on the `action_rental_order_schedule` action itself, so it is part of the search context and is propagated to every record, dialog and dropdown opened from the schedule, while it is only meant to flag that we are in the schedule (default values conversion, hidden onchange buttons, group expansion, ...). Solution -------- Introduce a dedicated `display_renting_stock_quantity` context key and depend on it instead when fetching data to build the gantt rows, leaving the records opened from the schedule with their regular display name. Forward-Port-Of: odoo/enterprise#130651 Forward-Port-Of: odoo/enterprise#130322
The emoji picker in Discuss now stays stable when users select emojis during a search and then clear the search field. This prevents an unexpected crash, making chat interactions smoother and more reliable.
Original PR description
Steps to reproduce: - open Discuss, open any chat, open the emoji picker (no 'Frequently used' emojis) - search a term and select emojis without closing the picker (shift+click on desktop, plain…
Steps to reproduce:
- open Discuss, open any chat, open the emoji picker (no 'Frequently used'
emojis)
- search a term and select emojis without closing the picker (shift+click on
desktop, plain click on mobile)
- clear the search with backspace
=> traceback: 'Cannot read properties of null (reading
`getBoundingClientRect`)' in adaptNavbar().
This happens because when we clear the search input it calls
`highlightActiveCategory()`, which sets `categoryId` to the topmost category of
the grid, which is now the 'Frequently used' category (sortId 0), added to the
picker since the emojis we just picked updated the recent state. To update the
navbar, `currentNavbarPanel` then looks for the panel holding it in
`emojiNavbarRepr`, but that representation is only built in `adaptNavbar()`,
which runs on mount and from the `ResizeObserver` only, so it was built without
the 'Frequently used' category and no panel contains it. It returns undefined,
the navbar renders empty, its size change wakes the `ResizeObserver`, and
`adaptNavbar()` crashes on querySelector('.o-Emoji').getBoundingClientRect()`.
This commit solves the issue by rendering the `recentEmojis` from a snapshot
taken when the picker is opened, so they are not added to the picker while the
while `emojiNavbarRepr` does not contain their category id.
partial backported PR: https://github.com/odoo/odoo/pull/281104
Task-[6204249](https://www.odoo.com/odoo/project/1519/tasks/6204249)
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#287126
Forward-Port-Of: odoo/odoo#284372Vendor bills can now correctly create assets that do not require depreciation. This removes an unnecessary restriction, helping accounting teams record these assets without manual workarounds.
Original PR description
This commit fixes the ability to create no depreciation assets from vendor bills. Previously, a condition on the depreciation account and expense account restricted the asset creation. backport of https://github.com/odoo/enterprise/pull/123070 task-6283929 opw-6540498 Forward-Port-Of: odoo/enterprise#130653
Accounting dashboard KPI calculations now use the right date periods when converting values across currencies. This prevents errors when viewing companies with different currencies and helps ensure profitability and cashflow figures use appropriate exchange rates.
Original PR description
Multicurrency KPI computation requires date bounds to determine the applicable consolidation rates, without them selecting companies with different currencies raises a keyerror on date_to. Use the fiscal year dates for profitability KPIs and the rolling 12-month period for the cashflow one. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Invoices linked to Malaysian MyInvois documents now continue to show their final status when a submission is cancelled or invalid. This avoids confusion where invoices appeared as if they had never been sent, while still allowing users to resend when appropriate.
Original PR description
`l10n_my_edi_state` on the invoice was left empty whenever the related document wasn't in an "active" state (`valid`/`rejected`/`in_progress`), even once it reached a terminal outcome like…
`l10n_my_edi_state` on the invoice was left empty whenever the related document wasn't in an "active" state (`valid`/`rejected`/`in_progress`), even once it reached a terminal outcome like `cancelled` or `invalid`. As a result, the form and list view showed no MyInvois status at all for those invoices, making them look like they were never sent to MyInvois - even though the underlying `myinvois.document` clearly was. Instead of deriving `l10n_my_edi_state` purely from the active document, fall back to a cancelled/invalid document when no active one exists, so the invoice always reflects its real MyInvois status. Since the state can now be `invalid`/`cancelled` (truthy) instead of always empty, every place that used to treat a falsy `l10n_my_edi_state` as license to send/resend is updated to also allow `invalid`/`cancelled`, since those are equally free to resend: `_compute_show_reset_to_draft_button`, `action_l10n_my_edi_send_invoice`, and the "Send To MyInvois" button/field visibility in the view. `l10n_my_invoice_need_edi` also used `l10n_my_edi_state` in its computation, purely to exclude `rejected` invoices (which still have an active document blocking a new send). That state check is now handled directly by each caller instead: the field is renamed to `l10n_my_edi_is_applicable` and reduced to the structural question of whether MyInvois applies to the invoice at all (posted, MY, proxy user configured), while `_compute_highlight_send_button` checks `l10n_my_edi_state != 'rejected'` explicitly. task-6442567
This fixes an issue where demo data could not be installed when the password policy module was active. Businesses can now set up demo or test databases more reliably without being blocked by default demo user credentials.
Original PR description
If auth_password_policy, demo data cannot be installed due to demo:demo user
Checkout step labels are now preserved when website checkout steps are copied during website setup. This prevents customer-facing checkout steps from showing an unwanted “(copy)” suffix, keeping the buying flow clean and professional.
Original PR description
Following PR: https://github.com/odoo/odoo/pull/272177, char fields "name" now append ["(copy)" by default](https://github.com/odoo/odoo/blob/b84ffce402d3fd12e3dd9521b248b15336763a76/odoo/orm/fields_textual.py#L503) on copy. As checkout steps names are generated from generic records at website creation, we want to keep their original name. opw-6538023
Warehouse moves created by push rules now use the most precise source location available, including sublocations when relevant. This helps stock transfers and picking records better reflect where items actually moved from, reducing location mismatches in inventory operations.
Original PR description
This PR fixes the moves (and their picking) `location_id` to be more accurate after applying push rules on the moves. Before this PR: The new move introduced from push rules (and its new picking)…
This PR fixes the moves (and their picking) `location_id` to be more accurate after applying push rules on the moves. Before this PR: The new move introduced from push rules (and its new picking) would have the `location_id = old_move.location_dest_id` which could be a parent of the location_dest_id of the old move_lines which is more accurate. For example, if a move has a `location_dest_id = 'stock'` and the user changes the move_lines of that move so that `move_lines.location_dest_id = 'stock/sublocation'`. When push rules are applied on the move_lines, `new_move.location_id = old_move.location_dest_id = 'stock'`. But, it should be `new_move.location_id = 'stock/sublocation'`. After this PR: The new move now (and its picking) will have the `location_id` set as the `rule.location_src_id` or `old_move.location_dest_id` just in case the `old_move.location_dest_id` is a sublocation of `rule.location_src_id` which gives a more accurate source location for the move and the picking. Task-6226781
Pressing Escape in a date or time picker now discards any typed changes and restores the previous value instead of saving the edit. This makes the behavior consistent with other dropdown fields and helps prevent accidental date changes; Ctrl+Enter now behaves like Enter by saving and closing.
Original PR description
Steps to reproduce: - Click in the input of a datetime picker (with an existing value) - Manually edit the text in the input - Press Escape Current behavior: - The manually typed value is parsed and…
Steps to reproduce: - Click in the input of a datetime picker (with an existing value) - Manually edit the text in the input - Press Escape Current behavior: - The manually typed value is parsed and validated, exactly as if Enter had been pressed instead. Expected behavior: - Escape should discard the edit and restore the field's previous value, closing the picker without applying any change. This is how other dropdown-based fields (e.g. many2one) behave. Solution: - On Escape, reset the input(s) to the current (unedited) value instead of parsing the typed text. If the popover is open, let its own "closeOnEscape" hotkey close it (avoids a double-close race with our own close call); otherwise close/apply directly, since there is no such hotkey to rely on. Also, Ctrl+Enter had a dedicated behavior: it validated the typed value and re-opened/kept the picker open, refreshing its content to reflect the newly entered date. This is removed, so Ctrl+Enter now behaves like a plain Enter (validates and closes). task-6537497 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes an inventory issue where moving stock stored in a package that was already reserved for delivery could fail with an access error. Warehouse users can now relocate these packaged quantities without being blocked, improving day-to-day stock management reliability.
Original PR description
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track…
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track localization in settings - Create a new storable product. - Using an inventory adjustment, add some quantity of that product in Stock, in a new package X. - Create a sales order for that product and confirm it, so the quantity in Stock gets reserved. - Go to Inventory > Reporting > Locations. - Select the quant and try to relocate it, e.g. to WH/Input. -> Raises an AccessError: "Failed to write field stock.package.picking_ids" **Cause** Relocating a quant creates a move: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1531 which creates a new move line without a `picking_id`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1276-L1287 Since both move lines (the new one and the one linked to the SO delivery) share the same `result_package_id`, in `_compute_picking_ids`, both move lines are grouped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L174-L176 Thus, two "pickings" end up associated with the package: the SO's, and `None`. While setting those pickings on the package, it tries to access them: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L182 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1497-L1503 And since `self` isn't just `None`, this check won't be skipped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4152 This eventually raises an AccessError since `None` gets filtered out by `filtered_domain`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4154-L4156 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1504-L1505 opw-6427070 Forward-Port-Of: odoo/odoo#283937 Forward-Port-Of: odoo/odoo#282209
The website builder's automated checks were adjusted to handle a new way Chrome reports background image sizing. This helps keep quality checks reliable across browser versions without changing what users see or do.
Original PR description
In Chrome 152, single-value `background-size` properties may be serialized or expanded to include implicit dimensions (e.g., appending `auto` like `100px auto`), causing strict exact-string test assertions to fail. This commit updates `website` builder test expectations to use regex prefix matching or substring inclusion so tests remain reliable across different Chrome versions. runbot-946570 Forward-Port-Of: odoo/odoo#286094 Forward-Port-Of: odoo/odoo#285591
This fixes an issue where changing a subcontracting manufacturing order from a later delivery could incorrectly update quantities on earlier deliveries. Each new subcontracting order is now linked only to its own delivery, helping keep purchase and delivery records accurate.
Original PR description
**STEP TO REPRODUCE** 1. Create a purchase order for a subcontracted product. 2. validate the picking. 3. Return to the PO, and increase the purchased qty and save, this should create a new picking. 4. On the new picking, click on the smart button to see the subcontracting MO details. 5. Change the product quantity on the MO and save. 6. Return to the first picking, and notice the delivered quantity was changed, this should not be the case. **CAUSE** When creating a new MO, its `move_finished_ids` is linked to the moves of all previous pickings when we create the MO. It should only be linked to the new picking move. opw-6320704 Forward-Port-Of: odoo/odoo#284605 Forward-Port-Of: odoo/odoo#271556
This fix updates a stock test so it searches for the exact product name instead of a broader term. This prevents unrelated products from being selected during automated checks, improving reliability without changing everyday user workflows.
Original PR description
When searching for the product created in the test we were only searching for "Serial" but another product with this word in the internal reference was showing up alone. To fix this we now look for the exact product name to avoid finding another product. The other matching record was introduced in this commit : https://github.com/odoo/enterprise/commit/6d4f4ec471d0d20c4deae5ad5d4fd4f803c20933 runbot-242820 Forward-Port-Of: odoo/odoo#284499
Map location lookups now finish saving even if a user leaves the map view before the lookup completes. This avoids losing coordinates and reduces repeat requests to external, rate-limited location services.
Original PR description
Prevents discarding geocoded coordinates when navigating away from the map view before asynchronous requests complete. Previously, caching calls used a component-scoped ORM service that threw an uncaught "Component is destroyed" error upon unmount. Decoupling the caching write from the component's lifecycle preserves fetched coordinates and avoids duplicate rate-limited API requests. task-6535656
Map popovers now correctly follow whether editing is enabled or disabled for a map view. This prevents users from being blocked from editing when the view is configured to allow it, improving consistency with other Odoo views.
Original PR description
MapPopover.readonly reads model.metaData.canEdit, but it was never set: canEdit was missing from modelParams, so it was always undefined and the popover was permanently readonly regardless of the edit attribute on the arch. Read canEdit from archInfo.activeActions.edit when building modelParams, mirroring gantt_arch_parser and calendar_arch_parser. Task-6537184
Restaurant orders edited on one device will no longer be skipped during synchronization after another device refreshes the same table. This helps ensure added order lines are saved and shared correctly across point-of-sale devices, reducing the risk of lost sales or service errors.
Original PR description
Steps to reproduce: - Device A opens table 10 and adds a product - Device B opens table 10, then goes back to the floor plan - Device A goes back to the floor plan => The lines added on A are lost, they are never synced to the database, nor to the other device When B triggers a synchronisation, A reads the open orders from the server. The local lines of A are kept, but `setup`, which is also called when a record is updated, resets the dirty flag of the order. Going back to the floor plan calls `syncAllOrders`, which filters the order out because it is not dirty anymore, so the lines are never sent. The dirty flag is now kept when the record is updated with server data, since that data does not contain the changes made locally and must be synced in the next synchronization call task-id: 6486258 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286279 Forward-Port-Of: odoo/odoo#284701
The timer now excludes timesheet entries that are not linked to a project when loading systray timer data. This prevents irrelevant or incomplete entries from appearing in the timer, making time tracking clearer for users.
Original PR description
exclude AAL without a project when loading systray timer data task: 6538364 Forward-Port-Of: odoo/enterprise#130515
Early payment discount entries now preserve the original invoice line cost allocation for all discount calculation methods. This prevents accounting details from being lost when invoices with mixed or excluded discount handling are paid early.
Original PR description
_get_invoice_counterpart_amls_for_early_payment_discount_per_payment_term_line only split the early payment discount amount per invoice line (and thus preserved each line's own analytic distribution)…
_get_invoice_counterpart_amls_for_early_payment_discount_per_payment_term_line only split the early payment discount amount per invoice line (and thus preserved each line's own analytic distribution) when the payment term's early_pay_discount_computation was "included". For "mixed" and "excluded", it instead built a single counterpart line, discarding the analytic distribution of the invoice lines. Compute the per-invoice-line base amounts (and the corresponding price_unit for "mixed") for all three computations, keeping only the tax calculation restricted to "included". This way "mixed" and "excluded" discount entries now keep the analytic distribution of the invoice line they come from, while behavior for "included" is unchanged. Steps: - Create 3 payment terms with EPD, each one with a different `early_pay_discount_computation` setting - For each payment terms, create one invoice with two lines, only one having an analytic distribution - Confirm and pay the 3 invoices (early payment, discount applied) Issue: For 'mixed' and 'excluded', the discount line is the sum of the discount from the 2 invoice lines, and the analytic distribution is lost. opw-6380278 Forward-Port-Of: odoo/odoo#286816 Forward-Port-Of: odoo/odoo#282541
VoIP text-to-speech sounds now have to be generated before they can be saved, ensuring the saved text and voice match the actual audio users will hear. This prevents incomplete or outdated audio settings from being saved and makes call flow setup more reliable.
Original PR description
Creating a text-to-speech sound did not require its audio to be generated. It was therefore possible to save a sound whose text and voice did not match any audio file. Moreover, the Generate object button saved the form before generating the audio. This required specific reload logic in call flow dialogs and could persist incomplete values prematurely. Generate the audio as a preview and update the form's draft with the result instead of saving the record through an object action. Store the text and voice used for the generation, and prevent saving when they no longer match the current values or when no generated audio is present. Also discard an asynchronous generation result if the text or voice was changed while the request was running, and remove the now-unnecessary call flow reload logic.
Accounting users who are not administrators can now view and select email templates when sending manual payment reminders. This prevents an access error in the reminder flow, helping teams send customer follow-ups without needing admin permissions.
Original PR description
**Steps to reproduce:**
1. Log in as demo user
2. Go to Accounting > Customers > Invoices
3. Select at least one invoice and click on "Send Reminder"
4. Attempt to change or view the available choices in the "Email Template" field.
**Issue:**
An AccessError is raised:
`You are not allowed to access 'Model' (ir.model) records.`
**Cause:**
The view domain on `template_id` was set to `[('model_id.model', '=', 'res.partner')]`. Traversing `model_id.model` forces the ORM to evaluate security permissions on the `ir.model` relation, which fails for non-admin users.
opw-6452738
Forward-Port-Of: odoo/enterprise#129966When selecting a private city on an employee address, the state and ZIP code now appear immediately instead of only after saving. This makes address entry clearer and reduces the chance of incomplete or confusing employee address information.
Original PR description
Picking a city didn't fill in state and zip until you saved. Added an onchange so it happens live, same as we already do for state/country. Task 6482304
Fixed an issue where card views could stop refreshing correctly after the first reload. This helps users see up-to-date information consistently without needing extra manual refreshes or workarounds.
Original PR description
`this.key` is the signal function, so `this.key + 1` concatenates its source and `set` stores that same string on every reload: the first reload changes the key and re-creates the Record, the next ones are no-ops. Introduced in odoo/odoo#260098 Forward-Port-Of: odoo/odoo#286988
This fixes the messaging menu so it explicitly opens the Chats tab instead of relying on a fallback behavior. It makes the user experience more reliable and reduces the chance of future tab-order changes causing the menu to open incorrectly.
Original PR description
Before this commit, the systray state is inserted with `activeTab: MENU_TABS.CHATS`, a name no module defines, so the value is undefined and the state links no tab at all. This happens because each module names its own tabs, and the discuss one declares `MENU_TABS.CHAT`. The eager compute of `activeTab` hides the mistake: with no tab to keep, it falls back to the first visible tab, which is Chats whenever it is shown, as its sequence is the lowest. This commit inserts `MENU_TABS.CHAT`, so the state says which tab it opens on rather than depending on the sequences around it.
This fixes Saudi localization issues around additional customer identifiers used for electronic invoicing. Businesses can now better record required buyer IDs, including for non-Saudi contacts, helping invoices meet Saudi compliance requirements.
Original PR description
This commit fixes the issues with multi id implementation of `l10n_sa_edi`. Before this PR: - The additional identifiers for `l10n_sa` were declared in `account`. - The `slice` function in js had wrong arguments, `slice(10)`. - `l10n_sa_invoice_type` was not getting updated when the company status (`is_company`) of the partner was updated. - `SA_OTH` additional_identifiers was not visible to non-saudi contacts. After this PR: - The additonal identifiers are now moved into `l10n_sa`. - The `slice` function has been removed. - Added an onchange to populate Saudi Arabia TIN. - Add test cases for l10n_sa additional identifiers - Add test cases for l10n_sa_edi additional identifiers - Added `copy=False` for `l10n_sa_invoice_type` and added `is_company` in dependencies - `SA_OTH` is now visible to non saudi contacts. task-6413445 task-6319117 Forward-Port-Of: odoo/odoo#270776
Fixed a typo in the Italian electronic invoicing withholding tax reason text. This improves the accuracy of tax-related wording shown or reported by the Italian localization without changing business processes.
Original PR description
Correction of a typo in italian withholding tax reason. opw-6514615 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284992
This fix ensures Saudi Arabia tax tag updates are applied during Odoo 19 upgrades. It prevents VAT tax grids and invoices from keeping outdated tax tags, reducing the need for manual correction after migration.
Original PR description
**Issue:** During migration to Odoo 19, the SA tax tag migration logic is present in: "migrations/2.1/pre-migrate.py" was not executed during the migration of databases because this: * SA compact tax…
**Issue:** During migration to Odoo 19, the SA tax tag migration logic is present in: "migrations/2.1/pre-migrate.py" was not executed during the migration of databases because this: * SA compact tax tags were not renamed * VAT tax grids still use old tags * Localization reload was partially updating taxes * manual intervention was required Added logs/debugging in: * 2.1/pre-migrate.py * 2.1/end-migrate.py * 2.2/end-migrate.py - Verified in both local and customer databases that only the 2.2 migration path was executed during upgrade, while the 2.1 migration scripts were skipped because the Odoo 19 manifest upgrade path already targeted the 2.2 migration version. - 19:https://github.com/odoo/odoo/blob/67510fd36f7af31f83ef110602f9922ed5a43024/addons/l10n_sa/__manifest__.py#L6 **Solution:** - Bumped the l10n_sa module version to 2.3 to apply these changes to databases that are already in production. - Moved `migrations/2.1/pre-migrate.py` to `migrations/2.3/pre-migrate.py` to ensure the SA tax tag migration logic is executed during the `2.3` upgrade flow. - Moved `migrations/2.2/end-migrate.py` to `migrations/2.3/end-migrate.py` to align with the `2.3` version bump and refresh the SA tax mappings correctly during migration. * SA tax tags migrate correctly * VAT tax grids update automatically * invoices use new tags correctly * No manual localization reload required OPW - [6117408](https://www.odoo.com/odoo/project/70/tasks/6117408), [6224788](https://www.odoo.com/odoo/project/70/tasks/6224788) Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#264571
This fixes an issue where editing only the analytic details of a bank reconciliation line could be blocked by accounting lock date rules. Users can now make these analytic updates without being incorrectly stopped, while lock date protections remain in place for other accounting changes.
Original PR description
In the case of the bank rec widget, when modifying a line, we actually unlink it and create a new one. In that case, modifying the analytic would trigger the lock date error. But user should be allowed to modify the analytic all the time. In the edit of the bank rec widget, we add a context key that will be added if analytic distribution is the only key modified. task-6397993 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277260
New database installation tests now skip older AI App modules and their connector modules. This keeps fresh installations aligned with the newer AI Agentic app while preserving the legacy modules for existing upgraded databases.
Original PR description
Exclude AI App and its bridge modules from fresh-install builds. They are kept for upgraded databases only; new databases use AI Agentic. task-id-6497307 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixed an issue that could leave Ecuadorian electronic invoicing withholding totals blank in the tax totals widget. This helps users see the expected withholding amounts reliably and avoids errors when reopening or refreshing the document view.
Original PR description
TaxTotalsComponent now declares its totals as a computed, so formatData runs inside that computed's own getter. TaxTotalsComponentForWithhold still overrides formatData to assign this.totals directly, which overwrites the computed from within it: the getter returns undefined, so the table's t-if hides it and the widget renders empty, and every later read raises "this.totals is not a function" because this.totals is now a plain object. From: odoo/odoo#270245. Forward-Port-Of: odoo/enterprise#130691
Bank reconciliation edits that only change analytic information will no longer be blocked by lock date checks. This lets users update analytic allocations when needed without disrupting accounting controls for other changes.
Original PR description
In the case of the bank rec widget, when modifying a line, we actually unlink it and create a new one. In that case, modifying the analytic would trigger the lock date error. But user should be allowed to modify the analytic all the time. In the edit of the bank rec widget, we add a context key that will be added if analytic distribution is the only key modified. task-6397993 Forward-Port-Of: odoo/enterprise#124852
This change rolls back a recent database connection handling update because it caused connection usage to spike on gevent servers. Reverting it helps stabilize server resource usage and reduces the risk of performance issues for users relying on real-time features.
Original PR description
This reverts commit 11e76ba8042648596dc0a7c731a78dd786e36884 and PR #285423 The connection usage on the gevent server is spiking. Forward-Port-Of: odoo/odoo#287008
Time off types that are not available for time off no longer show a misleading allocation balance such as (0/0). This keeps payroll and time off screens cleaner and avoids confusion for users reviewing leave-related entries.
Original PR description
**Issue:** When a user deselects the `time_off_selectable` field on a Time Off Type, the `requires_allocation` field is hidden from the view but retains its default background value of `True`. Because the system still evaluates it as true, the `display_name` appends a misleading allocation ratio of `(0/0)` next to the time off type's name. **Solution:** Updated the `_compute_display_name` method to account for the `time_off_selectable` state. The allocation ratio `(0/0)` is now only calculated and appended to the display name **if the time off type is both selectable for time off *and* requires allocation.** Before: <img width="469" height="413" alt="image" src="https://github.com/user-attachments/assets/469fc063-23d9-46a2-9367-8f3bf4ef7312" /> After: <img width="418" height="392" alt="image" src="https://github.com/user-attachments/assets/030451b2-b9cf-4954-a598-01d9846c738e" /> task-6487681
Creating a new CRM stage no longer shows an unnecessary warning about recalculating opportunities. The warning is now reserved for editing existing stages, reducing confusion for CRM users while keeping the helpful alert where it matters.
Original PR description
Changing whether a CRM stage is won may trigger the recomputation of its opportunities. An onchange warning was added to inform users about this potentially expensive operation. However, the warning was also displayed when creating a stage because the onchange was triggered while initializing the form. Fix: Only display the warning when editing an existing stage, as it's useless to show this warning when creating a new stage. Task-6424174 Forward-Port-Of: odoo/odoo#284679
This fixes an error that could stop Website SEO content from being updated with AI when the AI agent used certain document sources. Businesses can now use those documents reliably in AI-assisted SEO updates without the process crashing.
Original PR description
### Problem When an AI agent includes a document source whose underlying `ir.attachment` has `res_field` set (e.g., an invoice PDF generated from a Sale Order), triggering **Update With AI** from…
### Problem
When an AI agent includes a document source whose underlying `ir.attachment`
has `res_field` set (e.g., an invoice PDF generated from a Sale Order),
triggering **Update With AI** from **Website → Site → Optimize SEO**
raises a `KeyError` in `_build_rag_context`.
### Steps to Reproduce
1. Open the **AI** app.
2. Configure the **Odoo Agent**.
3. Add a source → **Add From Documents**.
4. Select a document whose underlying `ir.attachment` has `res_field` set
(e.g., an invoice PDF generated from a Sale Order).
5. Go to **Website → Site → Optimize SEO**.
6. Click **Update With AI**.
7. Observe the following error:
```
KeyError: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```
[Video](https://drive.google.com/file/d/1AAg0AvC3TRQzMLhI9hWFuSarOTHzQE-b/view?usp=sharing)
### Root Cause
In `ai/models/ai_agent.py`, `_build_rag_context()` retrieves the
`ai.agent.source` records corresponding to the embeddings by searching on the
attachment checksum:
```python
agent_sources = self.env["ai.agent.source"].search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
```
This domain traverses `attachment_id.checksum`, which internally calls
`ir.attachment._search()`.
As part of the standard attachment search behavior,
`ir.attachment._search()` automatically injects a
`('res_field', '=', False)` filter unless:
- `skip_res_field_check=True` is set in the context,
- the domain explicitly references `id` or `res_field`, or
- `bypass_access` is enabled.
```python
domain = Domain(domain)
if (
not self.env.context.get("skip_res_field_check")
and not any(d.field_expr in ("id", "res_field") for d in domain.iter_conditions())
and not bypass_access
):
disable_binary_fields_attachments = True
domain &= Domain("res_field", "=", False)
```
[Reference](https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/ir_attachment.py#L630)
Because of this implicit filter, attachments with `res_field` set are excluded
from the search. Consequently, `agent_sources` does not contain all the sources
corresponding to the retrieved embeddings.
Later, `_build_rag_context()` builds a checksum-to-source mapping:
```python
source_map = {
source.attachment_id.checksum: source
for source in agent_sources
}
for embedding in similar_embeddings:
checksum = embedding.attachment_id.checksum
agent_source = source_map[checksum]
```
Since `source_map` is built from the incomplete `agent_sources` recordset, it is
missing entries for attachments filtered by `ir.attachment._search()`.
However, `similar_embeddings` still contains embeddings for those attachments.
As a result, the lookup:
```python
agent_source = source_map[checksum]
```
raises a `KeyError`.
### Solution
Bypass the implicit `res_field` filter when searching `ai.agent.source`:
```python
agent_sources = (
self.env["ai.agent.source"]
.with_context(skip_res_field_check=True)
.search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
)
```
This ensures that all `ai.agent.source` records matching the requested
attachment checksums are returned, including those referencing attachments with
`res_field` set. As a result, `source_map` contains all expected entries and
`_build_rag_context()` no longer raises a `KeyError`.
opw-6379816
Forward-Port-Of: odoo/enterprise#129999
Forward-Port-Of: odoo/enterprise#125780This fixes a website shop error that could occur when all variants of a product were deleted. The shop now falls back to the main product information for unit pricing, so customers can continue browsing without encountering an error.
Original PR description
Currently, an error occurs when user deletes all the variants of a product and opens the website raises an error. Steps to replicate: - Install `website_sale`, open settings and turn on `Product…
Currently, an error occurs when user deletes all the variants of a product and opens the website raises an error. Steps to replicate: - Install `website_sale`, open settings and turn on `Product Reference Price` and `Product Variants`. - Create a new product `test` , give an attribute and 2 values. - Click on the `Variants` smart button, select all and delete. - Publish this produce in website and open shop on website. Error: ``` ValueError: Expected singleton: product.product() ``` Cause: - Issue occurs after a recent addition of a feature that shows product's unit price on website (check [PR]). - As the user deleted all the variants of the product `test`, when trying to get the unit price of the product's variants [1] causes this error to occur. Solution: - If the product doesnt have any variants we should get the unit price from the product template itself. [PR]: https://github.com/odoo/odoo/pull/254309 [1]: https://github.com/odoo/odoo/blob/22ede9cd601f54ebf248a7ddace433746f066e68/addons/website_sale/models/product_template.py#L686 sentry-7635385579 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282945
This fix ensures mail-related background data is cleaned up whenever an Odoo app is closed, including apps like Point of Sale and self-ordering that embed mail features. It prevents stale callbacks from accumulating over time, reducing memory use in affected test scenarios by around 38%.
Original PR description
ChatHub's cleanup (the shared `subscribeToStorage` listener) only ran when something explicitly called `store._runDisposeFns()`. Only mail's own test helper did that; any other app embedding…
ChatHub's cleanup (the shared `subscribeToStorage` listener) only ran when something explicitly called `store._runDisposeFns()`. Only mail's own test helper did that; any other app embedding `mail.store` (point_of_sale, pos_self_order, ...) never disposed it, so `subscribeToStorage` kept one stale callback per undisposed ChatHub, unbounded. Hook disposal into the services registry's "CLEANUP" event instead, which Owl already fires on every app teardown regardless of embedding (same pattern as `profiling_service.js`). Converting `storeService` to an Owl plugin using its own `onWillDestroy` (see TODO) would be the cleaner long-term fix. Measured via hoot's [MEMINFO] on the self_order_service suite that exposed this leak, same db/commit, only the fix toggled: | suite | before (MB) | after (MB) | % | |---------------------------------------------------------|------------:|-----------:|-------:| | pos_self_order/services/self_order_service (30 tests) | 253.8 | 155.6 | -38.7% | | pos_online_payment_self_order/self_order_service | 253.1 | 155.0 | -38.8% | | pos_self_order_event/self_order_service | 253.2 | 155.1 | -38.7% | | pos_self_order_iot/self_order_service | 260.1 | 162.0 | -37.7% | | tests done (final) | 251.9 | 153.8 | -38.9% | --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Since [1], we have 2 calls to `_bootstrap_homepage` - one in the `post_init_hook`, and one in `website.create`, causing the menu hierarchy to be duplicated. This doesn't apply to the default website (from base) as there was no `create` method override at the point of creation (website is not installed yet) and the homepage/menu is only created once, by the `post_init_hook`. Steps to reproduce: 1. Install website with demo data. 2. Menus for website 2 are duplicated. Solution: Si
Original PR description
Since [1], we have 2 calls to `_bootstrap_homepage` - one in the `post_init_hook`, and one in `website.create`, causing the menu hierarchy to be duplicated. This doesn't apply to the default website (from base) as there was no `create` method override at the point of creation (website is not installed yet) and the homepage/menu is only created once, by the `post_init_hook`. Steps to reproduce: 1. Install website with demo data. 2. Menus for website 2 are duplicated. Solution: Simply check if the website doesn't have a menu before duplicating it. [1]: https://github.com/odoo/odoo/commit/ae81b6f6074632d1a609387e53f97a61ee6c95f4 task-6030210 Forward-Port-Of: odoo/odoo#286713
The settings screen now hides both the Project field and its label when billing is turned off or Project Planning is enabled. This removes a confusing leftover label and makes the Billable configuration clearer for users.
Original PR description
*: planning_field_service_sale_timesheet,project_timesheet_forecast_field_service_sale ## Before this commit Only the project field was hidden when "Billing" is not enable, or if "Project Planning" is enabled, leaving the "Project" label visible in any case. ## After this commit The label and field are hidden if "Billing" is not enabled, or if "Project Planning" is enabled. task-6478896 Forward-Port-Of: odoo/enterprise#129852