Wednesday, September 23, 2026
18 changes · saas-19.1
Resolved issues and error corrections
This fixes an issue where Point of Sale could fail to open product options after cached data became outdated. Products using newly added options on existing attributes now load correctly without requiring cache clearing or session resets.
Original PR description
Steps to reproduce: - Open a PoS session so the browser caches the data (IndexedDB) - Add an attribute line on a product available in PoS, using an attribute that already existed and was not modified…
Steps to reproduce: - Open a PoS session so the browser caches the data (IndexedDB) - Add an attribute line on a product available in PoS, using an attribute that already existed and was not modified since - Reopen the PoS and click the product Issue: TypeError: undefined is not an object (evaluating 'values[0].is_custom') in openConfigurator. The attribute line is loaded but none of its values are; the variants have no attribute values on the frontend. Closing the session or reloading the browser does not help since the cache is kept. Cause: On an incremental load (pos_last_server_date in context), every model only returns the records written after the last sync. The domain of product.template.attribute.value restricts attribute_id to the ids in data['product.attribute'], which in that case only holds the attributes modified since the last sync. Values created on an older attribute are therefore excluded, and the frontend drops the ids it cannot resolve. Fix: Filter attribute_id with the domain of product.attribute itself instead of the ids of the records returned in the current load. A full load returns the same records as before. opw-6568808 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288065
This fixes an issue in Point of Sale where scanned products sold by weight no longer opened the weighing prompt unless the barcode already included a weight. Cashiers will again be prompted to weigh these items, helping avoid incorrect quantities and prices at checkout.
Original PR description
Since 3604790e1fee, `needToConfigure()` returns a real `false` for a product without attribute lines instead of `undefined`. The barcode handlers of the product screen pass its result as the `configure` argument of `addLineToCurrentOrder`, whose default value is `true`. The scale popup was gated on that argument, so it only opened on scan because the `undefined` fell back to the default. It now stays closed for every scanned weighed product. Weigh the product whenever it was added interactively or scanned with a barcode that does not already carry a weight. opw-6561067 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287781
Fixed an issue where importing or creating several product variants at once could remove the product image from both the main product and its variants. This helps ensure product catalogs keep their images during bulk uploads, reducing manual cleanup for users.
Original PR description
Steps to reproduce: - Import a product CSV with one row per attribute value ("Product Values" column) and an image URL on every row, or create several variants of the same template in one `create()`…
Steps to reproduce:
- Import a product CSV with one row per attribute value ("Product Values" column) and an image URL on every row, or create several variants of the same template in one `create()` call with an image.
- Open the product: neither the template nor the variants have an image. Products without attribute values imported in the same file are fine.
The import creates all variants of a template in a single batch. The inverse of `image_1920` is applied record by record: the first variant finds no image on the template and writes its image there. Writing `image_1920` on the template invalidates the image cache of every product.product, including the sibling variants being created, whose `image_1920` is protected during the inverse and therefore reads back as empty. The next variant then takes the "clear the image from the template" branch and writes `False` on the template, undoing the first write. Nothing is stored.
Read the value to inverse for every record before writing anything, so the invalidation triggered by the template write cannot change what the following records write.
opw-6545651
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#287419The Odoo Gmail plugin now uses the correct system setting lookup method for version 19.1 and later. This prevents an error that could stop users from authenticating with Odoo through Gmail, improving reliability for affected users.
Original PR description
In version 19.1+, the ir.config_parameter model no longer has the function get_param. Instead you use get_int, get_str, etc. This was causing a traceback in auth_access_token when a user tries to authenticate with Odoo via the Odoo gmail plugin.
Odoo now prevents time off requests from being saved or moved forward when the selected time off type requires a supporting document and none is attached. This helps HR teams keep leave records complete and ensures employees follow the required documentation process.
Original PR description
Problem: When a time off type is configured to require a supporting document, the system should prevent users from submitting a request without one. However, users could bypass this requirement by…
Problem: When a time off type is configured to require a supporting document, the system should prevent users from submitting a request without one. However, users could bypass this requirement by creating and saving a request without uploading a file. Because the validation was not strictly enforced during creation or subsequent write operations, requests could enter or remain in active states without the mandatory documentation. Solution: This commit ensures the system enforces mandatory attachments during the modification of time off requests that require a supporting document. The system now evaluates state transitions to block undocumented submissions. Steps to reproduce(runbot v19): 1. Go to Time Off > Configuration > Time Off Types, create a new leave type and enable the "Allow To Attach Supporting Document" setting. 2. Create a new time off request for this type, leave the attachment empty, and save. 3. Notice that the system allows the invalid request to be saved and persist in the database without a document. opw-6413621 Forward-Port-Of: odoo/odoo#278991
Final invoices now correctly show remaining down payment amounts when a related credit note exists. French electronic invoicing via PDP also uses the correct document classifications and references, helping businesses stay compliant and avoid invoice discrepancies.
Original PR description
## Bug n°1 (sale) Downpayment lines don't appear on the final invoice, when there is a credit note linked to the downpayment, even if the resulting downpayment is not zero. **STEP TO REPRODUCE** 1.…
## Bug n°1 (sale) Downpayment lines don't appear on the final invoice, when there is a credit note linked to the downpayment, even if the resulting downpayment is not zero. **STEP TO REPRODUCE** 1. Create a SO. 2. Create a downpayment (let's say 100$). 3. On this downpayment create a credit note, and set the downpayment line on the credit note to 50$. 4. Return to the SO, and create the final invoice. 5. On the final invoice, there is no mention of the downpayment and credit note. Expected behavior: The final invoice contains a downpayment line with a unit price of 100$ - 50$ = 50$. **CAUSE** We invoice the downpayment line 1 time for the downpayment, and 1 time for the credit note. This make qty_to_invoice computed on the downpayment line equal to 0, so it's skipped in the final invoice. **FIX** For downpayment line, we ignore the qty_to_invoice, and check if the price_unit of the downpayment line is not 0. If so, we should invoice the downpayment line. For final invoice, the invoiced qty should be -1, and 1 for downpayments and downpayment credit notes. ## Bug n°2 (l10n_fr_pdp) Odoo doesn't correcly handle downpayments when sending invoices using pdp. 1. Because of bug n°1, since downpayment lines don't appear on the final invoice when there is a credit note on the downpayment, the downpayment lines also don't appear in the generated xml. 2. For downpayment, the InvoiceTypeCode should be 386. For credit note of downpayment, the CreditNoteTypeCode should be 503. For the final invoice (invoice done after downpayments), the ProfileID should be B4. 3. On the final invoice, their should be BillingReference for each downpayments and credit note of those downpayments. With a corresponding DocumentTypeCode (386 downpayments, 503 credit notes). On import, there is 2 cases to handles for the final invoice. 1st case: the downpayments lines are present in the final invoice. (already handled by default) 2nd case: the downpayments line are not present in the final invoice. The downpayment amount is in the prepaidAmount node, and all amounts on the xml correspond to the invoice without any downpayments. In the 2nd case, we need to find the downpayments, and copy their lines to the final invoice. **STEP TO REPRODUCE** 1. Create a sale order. 2. Create a downpayment. 3. Create the final invoice and send it via pdp. 5. Inspect the xml and notice the requirements stated previously are not met. opw-6420924 Forward-Port-Of: odoo/odoo#287354 Forward-Port-Of: odoo/odoo#281717
Fixes two Slovak accounting asset templates that were linked to the wrong balance sheet accounts. This prevents vendor bills from creating assets under the wrong category, including incorrectly treating other tangible assets as land.
Original PR description
In `account.asset-sk.csv` the asset-account column has slipped by one row for the last two entries, so each of those two models pairs an asset account with the accumulated depreciation account of a…
In `account.asset-sk.csv` the asset-account column has slipped by one row for the last two entries, so each of those two models pairs an asset account with the accumulated depreciation account of a different asset class.
As shipped (Slovak account names glossed in English):
sk_asset_basic_herd_and_draft_animals
asset 029000 Ostatný dlhodobý hmotný majetok "Other tangible fixed assets"
depreciation 086000 Oprávky k základnému stádu "Accumulated depreciation, basic herd"
sk_asset_other_tangible_fixes_assets
asset 031000 Pozemky "LAND"
depreciation 089000 Oprávky k ostatnému DHM "Accumulated depreciation, other tangible"
Every other row in the file pairs an account with the accumulated depreciation account that names it: 012000 with 072000, 021000 Stavby ("Buildings") with 081000 Oprávky k stavbám ("Accumulated depreciation, buildings"), and so on. Only these two break the pattern, and both break it the same way.
`account.account-sk.csv`, in the same directory, disagrees with them and is right: it links 026000 Základné stádo a ťažné zvieratá ("Basic herd and draft animals") to the herd model and 029000 to the other-tangible one, and gives 031000 Pozemky no asset model at all, land not being depreciable under Slovak law or any other.
The consequence is not cosmetic.
`account.account.asset_model_ids` creates an asset when a vendor bill line hits the account, and the asset takes its accounts from the model. So on a Slovak database a bill booked to 029000 "Other tangible fixed assets" produces an asset whose value sits on 031000 "Land" and depreciates over six years, while the account that should have carried it never does.
Corrected to the accounts that both the accumulated depreciation side and `account.account-sk.csv` point at:
sk_asset_basic_herd_and_draft_animals asset 026000 Základné stádo a ťažné zvieratá "Basic herd and draft animals"
sk_asset_other_tangible_fixes_assets asset 029000 Ostatný dlhodobý hmotný majetok "Other tangible fixed assets"
Verified by loading the SK chart on a clean database: all ten asset models now pair with their own accumulated depreciation account, and none points at 031000.
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#289542Shipment cancellations now send each delivery carrier only the shipment records that belong to it. This prevents failed or incorrect carrier cancellations when multiple deliveries with different carriers are cancelled together, reducing operational errors.
Original PR description
When cancelling shipments for multiple pickings with different delivery carriers, passing the entire recordset (self) instead of the individual picking to the carrier's cancel_shipment method causes each carrier to receive tracking references that do not belong to it. This results in API errors or silent wrong cancellations at the carrier level, and each shipment being cancelled N times instead of once, where N is the total number of pickings being processed. Forward-Port-Of: odoo/odoo#289809
This fix stops a Point of Sale register opening from being silently treated as complete when the server connection fails unexpectedly. Staff are informed that the register cannot be opened while offline, helping ensure sessions, opening cash, and later orders are recorded correctly.
Original PR description
Steps to reproduce: - Open a PoS so that the Opening Control popup is displayed. - Cut the connection to the server without the browser going "offline" (e.g. the router stays up but the internet link…
Steps to reproduce: - Open a PoS so that the Opening Control popup is displayed. - Cut the connection to the server without the browser going "offline" (e.g. the router stays up but the internet link is down). - Click on "Open Register". - Restore the connection, make some orders, close the session. Issue: The session is closed with orders but was never opened on the server: it has no opening date, keeps its temporary name "<config>/00000", and the opening cash was never recorded. `set_opening_control` was called with the `queue` flag. On a ConnectionLostError the call was silently pushed to the in-memory unsync queue, and the popup marked the session as opened locally and closed itself. That queue is only replayed on the browser `online` event, which is never fired when the device stayed connected to its local network, and it is lost when the tab is closed or reloaded. Meanwhile the server accepts orders on a session in `opening_control`, and nothing checks it at closing. Opening the register is not an operation that can be deferred: the session must be opened before any order is made. The call is no longer queued. When the connection is lost, the user is told that the register cannot be opened offline, and the popup stays open so the opening can be retried once the connection is back. opw-6580262 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288899
Live Chat now checks URL matching rules before they are saved, preventing invalid patterns from breaking chat initialization on the website. This helps website visitors continue to access pages normally and gives administrators earlier feedback when configuring Live Chat rules.
Original PR description
Currently, an error occurs when a user opens the website and Live Chat is initialized with an invalid URL regex. Steps to Reproduce: - Install `website_livechat` without demo data. - Open `Live Chat`…
Currently, an error occurs when a user opens the website and Live Chat is initialized with an invalid URL regex. Steps to Reproduce: - Install `website_livechat` without demo data. - Open `Live Chat` and, in the `YourWebsite.com` channel, click the `three-dot` menu and select `Configure Channel`. - Go to the `Rules` tab. - Add a rule, select `Welcome Bot` in `Chatbot`, and set an invalid regex such as `*` in `URL Regex` and save. - Click `Join Channel`. - Open the `website`. `re.PatternError: nothing to repeat at position 0 when serializing dict item 'result' ` - when `URL Regex is '['` `re.error: unterminated character set at position 0 when serializing dict item 'result'` When a user opens the website, Live Chat is initialized by checking operator availability and finding the matching country/URL rule [1]. The rule matching checks whether `regex_url` matches the current page URL. It uses `re.search()` [2], which compiles the regex pattern before searching. If `regex_url` contains an invalid pattern such as `*`, `re.search()` raises `nothing to repeat at position 0` because `*` must follow a preceding regex element. Similarly, `[` starts a character set (`[...]`) but has no closing `]`, causing `unterminated character set at position 0`. This commit adds a constraint to validate `regex_url` and reject invalid regex patterns when creating or updating Live Chat rules. [1]- https://github.com/odoo/odoo/blob/f962f1eccecfdbc95375f795174c8f687459b521/addons/im_livechat/controllers/main.py#L87 [2]- https://github.com/odoo/odoo/blob/f962f1eccecfdbc95375f795174c8f687459b521/addons/im_livechat/models/im_livechat_channel.py#L387 sentry-7679767396 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283545
Fixed an issue where partially processing subcontracted purchase receipts in the Barcode app could block validation with an error. Businesses can now split backorders for subcontracted products without disrupting the linked manufacturing order, keeping receiving workflows reliable.
Original PR description
eproduction steps: - MRP subcontracting + purchase - Subcontracted product with BOM - Create PO with a qty > 1 for this product - Go into barcode - Scan > 0 and < all of the expected quantity - Back…
eproduction steps: - MRP subcontracting + purchase - Subcontracted product with BOM - Create PO with a qty > 1 for this product - Go into barcode - Scan > 0 and < all of the expected quantity - Back out of barcode - Click validate -> Error **OR** Go back into barcode, attempt to validate -> Error Added the StockMove and StockPicking classes to stock_barcode_mrp_subcontracting to extend functions `_subcontracted_produce`, `_clean_merged`, and `split_uncompleted_moves` to use a new context flag, `keep_subcontract_production`. When this flag is set, the move Barcode creates from the split keeps the MO of the move it was split from rather than splitting the MO in two, gives that link up before it is merged away so its cancellation does not cancel the MO, and the MO quantity is resynced with the merged move afterwards. Error thrown here: https://github.com/odoo/odoo/blob/f84eeb3ed1421e0cc07c906ff6444734dc01d35f/addons/mrp_subcontracting/models/stock_move.py#L248 opw-6428928 Forward-Port-Of: odoo/enterprise#131459
Updates the Vietnam Profit and Loss report to match Circular 99/2025/TT-BTC effective FY2026, including a new required investment property gain/loss line and revised line numbering. It also prevents double counting in key revenue and cost lines and keeps period comparisons available without showing an unsupported growth percentage column.
Original PR description
Circular 99/2025/TT-BTC (effective FY2026) replaces Circular 200/2014/TT-BTC. The Profit & Loss statement (Mẫu B02-DN) gains a new mandatory line, "Lãi/lỗ của hoạt động bán, thanh lý bất động sản đầu…
Circular 99/2025/TT-BTC (effective FY2026) replaces Circular 200/2014/TT-BTC. The Profit & Loss statement (Mẫu B02-DN) gains a new mandatory line, "Lãi/lỗ của hoạt động bán, thanh lý bất động sản đầu tư" (mã số 21), computed from the new 5117/6327 sub-accounts (see the paired l10n_vn commit). Financial income, financial expenses and the interest memo line shift to mã số 22/23/24 accordingly, the "Net profit from operating activities" formula is updated to include the new line, and all following line numbers/labels are renumbered to match the printed form. The interest memo line itself is renamed from "Chi phí lãi vay" to "Chi phí đi vay" (Borrowing costs), per the gazette text. Also, per revised guidance: - Revenue and cost of goods sold now exclude the new investment property sub-accounts (5117/6327), so those amounts aren't double-counted between the main lines and the new gain/loss line. - Other income is now computed from the "income_other" account type instead of a hardcoded account code, matching how the chart of accounts already classifies it. Both the Balance Sheet and Profit and Loss VN reports also declare an extra "Code" string column (for the printed-form mã số) alongside "Balance". Core's growth-comparison heuristic only turns on the auto growth-% column when there are exactly 2 total columns; with Code+Balance that becomes 4 once a comparison period is added, so the % never renders even though the underlying side-by-side comparison values are fine. account.report exposes filter_growth_comparison as a toggle independent from filter_period_comparison for exactly this case (see Trial Balance and the Deferred reports), so both reports set it to False: period comparison stays available, only the auto growth-% column is suppressed. Task-6518304 Forward-Port-Of: odoo/enterprise#131085
Resetting an expense report to draft now also clears any Studio approval already granted for posting journal entries. This prevents old approvals from being reused and ensures the required approval is requested again before reposting.
Original PR description
Currently, when resetting to draft an expense sheet, approval steps added with studio are not reset, silently granting the change Steps to reproduce: - In Studio, add an approval rule on the "Post Journal Entries" button on expense sheet - Create an expense report, submit it and approve it - Click "Post Journal Entries" and approve approve it - Open the journal entry and reset it to draft - Back to the expense sheet, reset it to draft too Issue: The "Post Journal Entries" button is still approved, showing the previous approver with the same approval date. Analysis: Approval entries are dropped on state change by a base automation that Studio builds when the rule is created. However it is only done for sale order, account move and purchase order. opw-6530689 Forward-Port-Of: odoo/enterprise#130692
Sales to foreign customers are now handled correctly when the goods remain in Brazil. This ensures the transaction is treated as a domestic sale for Brazilian tax purposes while still identifying the customer as foreign, helping avoid incorrect export tax classification.
Original PR description
**PROBLEM** If you try to sale to a foreign customer, but the good never leave the country, the sale should be treated like a domestic sale, and the customer should be reported as a foreigner. (idEstrangeiro / UF "EX" / cMun 9999999). **STEP TO REPRODUCE** 1. On a local db, set up avatax (you can ask me in my DM for how to do it). 2. Create an invoice for a foreign customer. 3. Set the delivery address to your company address or any address that is brazilian. 4. Compute the taxes with avatax. 5. Notice the CFOP on the lines is 7XXX, and not 5XXX. This means the sale is treated like an export and not a as a domestic sale. expected behavior: In the avatax request we send, idDest should equal 1, and the client should be reported as a foreigner, so avatax assign the right CFOP. opw-6519774
Australian payroll now defaults casual employees to the regular casual tax treatment instead of daily casual. This helps ensure student loan withholding can be applied correctly when relevant.
Original PR description
Issue: Odoo doesn't provide tax treatments for Daily casual employee. However, it the employement basis is set as casual, it incorrectly defaults to RDXXXX (which is Daily Casual). This prevents the employee from having student loan withhold. This commit defaults it back to regular casual based on the tax free threshold status. Most common case. Daily casual case to be handled in Master. task - 6387496 Forward-Port-Of: odoo/enterprise#130752
This fixes Peruvian electronic delivery guides for itinerant issuer transfers so they no longer include an address code that SUNAT rejects. Businesses using this transfer reason can generate compliant delivery guides and avoid failed submissions.
Original PR description
### Issue before this commit: When issuing a Delivery Guide for an "Itinerant issuer transfer CP", SUNAT rejects the document with error 3416 because the establishment code of the arrival point is…
### Issue before this commit: When issuing a Delivery Guide for an "Itinerant issuer transfer CP", SUNAT rejects the document with error 3416 because the establishment code of the arrival point is being reported. ### Steps to reproduce the issue: 1. Download Stock, l10n_pe_edi_stock and l10n_pe_reports_stock 2. Create one contact setting a valid RUC 3. Go to settings and insert Credentials for Sunat Delivery Guide API 4. Create a new warehouse 5. Go to deliveries and create a new one with: 1. Delivery Address: the contact you created 2. Transport Type: Public Transport 3. Reason for transfer: Itinerant issuer transfer CP 4. Operator (in the PE EDI tab): any 6. Validate the delivery 7. Generate the delivery guide 8. There was an error communicating with the SUNAT service. Details: 400 Client Error: Bad Request for url: https://api-seguridad.sunat.gob.pe/v1/clientessol/test/oauth2/token/ 9. If you download the generated XML, you will see that the node <cbc:AddressTypeCode> inside <cac:DeliveryAddress> is being generated with a default value of "0" but it should not be there ### Cause of the issue: The UBL template automatically forces the <cbc:AddressTypeCode> node with a default value of "0" for any delivery address associated with a RUC (identification type 6). However, SUNAT's validation rules strictly forbid this node when the transfer reason is 18. ### Reason to introduce the fix: Update the XML template to conditionally omit the <cbc:AddressTypeCode> tag inside <cac:DeliveryAddress> whenever the l10n_pe_edi_reason_for_transfer is '18'. This ensures compliance with SUNAT's validation rules and allows the successful generation of the delivery guide. opw-6493067 Forward-Port-Of: odoo/enterprise#131431
Spanish amounts written in words now use the grammatically correct form "un" in phrases such as "un mil" on invoices and other documents. This improves the accuracy and professionalism of printed amounts for Spanish-language reports, especially in localized accounting contexts.
Original PR description
#### Description of the issue/feature this PR addresses: Spanish amounts in words render "uno" where the grammar requires the apocopated "un" — on invoice PDFs, CFDI amounts, and anywhere num2words…
#### Description of the issue/feature this PR addresses: Spanish amounts in words render "uno" where the grammar requires the apocopated "un" — on invoice PDFs, CFDI amounts, and anywhere num2words is called with a Spanish language. #### Current behavior before PR: "DOS MILLONES TRESCIENTOS UNO MIL CUATROCIENTOS TREINTA Y NUEVE PESOS 88/100 M.N." num2words applies the apocope only in to_currency(), never in to_cardinal(), and Odoo renders the plain cardinal then appends the currency label itself. Present in both versions pinned in requirements.txt (0.5.10, 0.5.13). #### Desired behavior after PR is merged: "DOS MILLONES TRESCIENTOS UN MIL CUATROCIENTOS TREINTA Y NUEVE PESOS 88/100 M.N." The apocope is applied in to_cardinal() through the num2words monkey patches, for es, es_CO and es_VE. Other languages are untouched. Note: the cardinal is now always apocopated, so a standalone count reads "un" rather than "uno". opw-6375677 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287359 Forward-Port-Of: odoo/odoo#277919
Instagram posts now have more time to complete when Instagram needs to download images from Odoo servers. This reduces failed posts caused by timeouts and makes social media publishing more dependable for affected customers.
Original PR description
Bug === On some database on the saas, timeout issue happen for Instagram. Because Instagram downloads the image on our server, it needs more timeout than other social media. Task-6547753 Forward-Port-Of: odoo/enterprise#131456 Forward-Port-Of: odoo/enterprise#131069