Tuesday, September 8, 2026
26 changes · saas-19.4
Resolved issues and error corrections
The live map now shows technicians as their locations are found instead of waiting for every address lookup to finish. This reduces waiting time when OpenStreetMap is used and helps dispatchers see field staff on the map sooner.
Original PR description
Opening the live map took a long time whenever technicians were sharing their live location and OpenStreetMap was used to find their address, since only one address lookup can be done per second, and every technician had to be fully processed before anything was shown on the map. Technicians are now displayed on the map as soon as their address is found, one by one, instead of waiting for all of them at once, similar to the way customer pins are already handled. task-6524180
Vendor bills can now correctly create assets that do not need depreciation. This removes an unnecessary restriction, helping accounting teams record those 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
Users can now select Unsplash images in product and other relational image fields without hitting a save error. The change ensures external Unsplash images are properly converted into Odoo attachments before being linked to the target record.
Original PR description
**Description of the issue/feature this PR addresses:** When selecting Unsplash images from a relational field (which uses `CustomMediaDialog`), the images would fail to process or link correctly to…
**Description of the issue/feature this PR addresses:** When selecting Unsplash images from a relational field (which uses `CustomMediaDialog`), the images would fail to process or link correctly to the target record. In the web editor, Unsplash image selections are handled by patching the base `MediaDialog`. When a user selects an Unsplash image, that patch intercepts the save action and uses the `unsplash` service to notify the backend. The server then fetches the external Unsplash URL and converts it into a native `ir.attachment` record before the frontend completes the save. Because `CustomMediaDialog` is a distinct component used for relational fields, it bypassed the existing `MediaDialog` patch entirely and lacked this specialized fetch-and-convert logic. This commit introduces a parallel patch specifically for `CustomMediaDialog`. It intercepts `imageSave`, routes any Unsplash records through the `unsplash` service, and replaces the raw Unsplash records with the newly generated Odoo attachments before executing the underlying save. **Steps to reproduce:** - POS > Products > Products > choose any product > click the ‘edit’ button in the image > search something, e.g. ‘burger’ > add Unsplash Access Key and Application ID when prompted > select one of the resulting Unsplash images **Current behavior before PR:** - Error when saving an unsplash image when editing product images **Desired behavior after PR is merged:** - No error when saving an unsplash image when editing product images opw-6445843 Forward-Port-Of: odoo/odoo#283694
Employee unavailable time is now calculated consistently across Time Off and Attendance, including periods outside a contract and flexible schedules. This ensures calendars correctly grey out unavailable days and gives managers a more reliable view of employee availability.
Original PR description
purpose: 1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days,…
purpose:
1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days, flexible and fully flexibe employees. 2- In time off calendar view, if the employee does not have a contract at all, the current working schedule will appear in the calendar and it will not be greyed out. This is inconsistent with the behavior of the attendance application.
Fix:
1:
- implemented `_get_employee_unavailable_intervals` in employee model to be used in both time off and attendance.
- more optimized than the old implementation in time off as it calls `_work_intervals_batch` once per calendar instead of calling it for each contract in `_unavailable_intervals_batch`
- greys out "out of contract" periods
- for flexible and fully flexible employees, the whole period is considered available except leave periods
- for duration based calendars, morning and afternoon map to 12 hours of availability and full day maps to 24 hours of availability
- made `_get_calendar_periods` use version date start instead of contract date start and corrected a bug in tz conversion 2:
- made `_get_unusual_days` return True for all the days outside of contracts for the employee instead of not returning anything for them or getting values from the working schedule of the employee (means that they will be greyed out in the callendar view) and added a test for it
task-id: 5473055
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286854
Forward-Port-Of: odoo/odoo#258604Inventory users without Accounting permissions can now view and create Indian E-Waybills without encountering access errors. This helps warehouse teams complete shipping documentation smoothly without needing extra accounting rights.
Original PR description
Before this commit Inventory users without Accounting permissions could get an Access Error when viewing or creating an E-Waybill because they could not access the required document types. After this commit Inventory users can now view and create E-Waybills without an Access Error. task-6515093 Forward-Port-Of: odoo/odoo#285023
The Field Service Gantt schedule now opens without waiting for buffer time calculations to finish first. This helps users see their planning view sooner while the remaining timing details update in the background.
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
Employee availability is now shown consistently across Attendance and Time Off planning views. Days outside an employee's contract are correctly greyed out, while flexible work schedules and leave periods are handled more predictably, reducing confusion for managers reviewing calendars.
Original PR description
purpose: 1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days,…
purpose:
1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days, flexible and fully flexibe employees. 2- In time off calendar view, if the employee does not have a contract at all, the current working schedule will appear in the calendar and it will not be greyed out. This is inconsistent with the behavior of the attendance application.
Fix:
1:
- implemented `_get_employee_unavailable_intervals` in employee model to be used in both time off and attendance.
- more optimized than the old implementation in time off as it calls `_work_intervals_batch` once per calendar instead of calling it for each contract in `_unavailable_intervals_batch`
- greys out "out of contract" periods
- for flexible and fully flexible employees, the whole period is considered available except leave periods
- for duration based calendars, morning and afternoon map to 12 hours of availability and full day maps to 24 hours of availability
- made `_get_calendar_periods` use version date start instead of contract date start and corrected a bug in tz conversion 2:
- made `_get_unusual_days` return True for all the days outside of contracts for the employee instead of not returning anything for them or getting values from the working schedule of the employee (means that they will be greyed out in the callendar view) and added a test for it
task-id: 5473055
Forward-Port-Of: odoo/enterprise#130587
Forward-Port-Of: odoo/enterprise#113498This fix changes how the mail module records active database transactions, using a compact list instead of a large mostly empty map. It reduces memory usage significantly in cases with long-running transactions, helping avoid memory errors and improving reliability.
Original PR description
The store version snapshot encoded in progress transactions (xip) as a bitmap spanning the whole [xmin, xmax) range, so its size grows with how far apart those bounds are rather than with how many transactions are actually in progress. A single long-lived transaction can push that range into the hundreds of thousands, leading to memory errors, most of it being zeroes. Sending it as a list of strings is much smaller (95-98% smaller tested on odoo). 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 fix ensures that when a copied or restored database is neutralized, scheduled background jobs cannot start before the neutralization is complete. This reduces the risk of unwanted automated actions, such as emails or integrations, running from a database that is meant to be safely inactive.
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
Export invoices in Argentina now use the correct recipient identification code in their QR data. This prevents ARCA from showing the wrong ID type and allows these invoices to be verified as valid legal documents on the ARCA verification page.
Original PR description
Before this change we were sending id type code 0 and this generate two problems * ARCA verification page it was wrongly taking "CI Policia Federal" as the identification type of the receptor * We were not able to validate the expo invoice, we get always an error With this change the expo invoice can be checked as a real legal document in the ARCA page https://servicioscf.afip.gob.ar/publico/comprobantes/cae.aspx Forward-Port-Of: odoo/enterprise#126704
Preparation and pickup receipts in Point of Sale now use the shop/company timezone instead of the account that happens to print them. This prevents customer-facing tickets from showing incorrect UTC times, reducing confusion around order pickup or preparation schedules.
Original PR description
Preparation tickets are rendered server-side for self orders. format_datetime and format_time only fall back on env.user.tz, and the render runs under whichever user triggered it: the public user for an online payment confirmed on /payment/status/poll, OdooBot for the payment cron, the self ordering default user for an OBOX print. None of them is guaranteed to have a timezone, so the ticket could be printed in UTC: an 18:15 pickup showed as 16:15 Take the timezone from res.company.tz instead. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285370
This fix checks for an existing local proxy user before contacting Odoo's online service to create a new one. It prevents rare race conditions that could leave a company database with outdated credentials and cause later electronic invoicing proxy calls to fail.
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
This fixes an issue where dropshipped kit products could show a zero cost on sales orders when using FIFO or average costing. Sales margins now reflect the purchase and component costs more accurately, helping businesses avoid understated costs and misleading profitability figures.
Original PR description
Currently, when a user dropships a kit product with FIFO/AVCO category costing, the computed cost of the kit on the Sales Order becomes 0. ## Steps to replicate: - Install Sales, Purchase, and…
Currently, when a user dropships a kit product with FIFO/AVCO category costing, the computed cost of the kit on the Sales Order becomes 0. ## Steps to replicate: - Install Sales, Purchase, and Manufacturing modules. - Enable Margins and Dropshipping in settings. - Go to Product Categories > Goods and set costing method to FIFO - Create two products MOBO and CPU: - Cost: $300 - Category: Goods - Add a vendor in Purchase section with unit price same as cost - Enable Dropshipping route - Create a kit product Computer Kit with the same configuration as above (except cost) and add MOBO and CPU as components on its BoM. - Open the Computer Kit and click Compute Price from BoM. - Create and confirm a Sales Order with the Computer Kit. - Confirm the related Purchase Order and validate the dropship picking. - Return to the Sales Order > make the `Cost` field visible on SO lines . ## Observed Behavior: The product cost appears as 0 on the Sales Order, even though a price is set on the related Purchase Order. ## Root cause: When the dropshipping picking is confirmed, the method `_compute_purchase_price` is triggered to compute the cost on the Sales Order line. It calls `_get_price_unit_delivery` at [1], which then calls `_get_price_unit_dropshipped` at [2] since the products are dropshipped. Because dropshipping moves do not carry stock values, it calls `_get_value` at [3] to determine an appropriate value. This method uses `_get_value_data` at [4], which retrieves the value from the quotation via `_get_value_from_quotation` at [5]. Here, a cost ratio is applied at [6] to distribute the cost based on the BoM cost share (i.e., the percentage split of cost across kit components). Since no cost share is defined on the BoM, the ratio is 0, causing the final computed cost ratio to also be 0 at [7] and the cost value being returned as zero as shown in at [6]. **Why not in lower versions?** This issue did not occur in versions 18.4 and earlier due to the presence of the stock valuation layer and the defined logic for kit products to calculate cost, as shown in [8]. [1]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/sale_stock_margin/models/sale_order_line.py#L18-L21 [2]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L672 [3]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L681-L684 [4]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L336 [5]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L392-L397 [6]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/purchase_stock/models/stock_move.py#L225-L241 [7]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/purchase_mrp/models/stock_move.py#L12-L25 [8]: https://github.com/odoo/odoo/blob/7f1cd04259202bcafc94965d6420df360f9152c1/addons/mrp_account/models/product.py#L65-L89 ## Solution: It should not be assumed that users will always define a cost share on the Bill of Materials. In many cases, they may expect the kit price to be derived directly from the costs of its component products. To support this, we can override `_get_price_unit_dropshipped` to properly handle kit products, ensuring the cost is computed based on the component product costs instead. opw-6113398 Forward-Port-Of: odoo/odoo#282616 Forward-Port-Of: odoo/odoo#261705
This fixes incorrect Spanish TicketBAI reporting when a POS order uses a gift card during a refund-related transaction. Gift card lines are now signed consistently with the overall order, preventing mismatches between product totals and invoice totals in official XML reporting.
Original PR description
The issue fixed in commit[1] is again reproducible after commit [2] the pos order line for gift card is now not considered as a refund Step to reproduce: - Install pos_loyalty and…
The issue fixed in commit[1] is again reproducible after commit [2] the pos order line for gift card is now not considered as a refund Step to reproduce: - Install pos_loyalty and l10n_es_edi_tbai_pos with demo data - Create a gift card (add a tax to the discount product, any 0%) - start pos, add a product and use the gift card - fulfill the order - go to backend and open that order - In the TicketBAI XML, the values for the giftcard product will be positive, causing an inconsistency between the product line total and the invoice total [1] https://github.com/odoo/odoo/commit/0bbc5ebdcc7e014d87130b2ff9cee98e3aa7479a [2] https://github.com/odoo/odoo/commit/b17c9713e7a3305c240298fa54fcf1bc87a9bb8c FIX - we used to determine `sign` based on each order line's `is_refund` property - this property is quite sensitive as it depends on factors like line's qty, price, is reward or not. - so its better to depend on order's refund property for sign reversal https://github.com/odoo/odoo/blob/4fef2c5b69fac10594fac81b149d542ee4621b13/addons/point_of_sale/models/pos_order.py#L1768 opw-6226003 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286448 Forward-Port-Of: odoo/odoo#278042
Odoo now recognizes five new response codes introduced by Chile's tax authority (SII). This prevents affected supplier electronic documents from getting stuck during processing after the regulatory update.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
Fixes incorrect totals on French association balance sheets so active and passive amounts are calculated accurately. This prevents missing or double-counted accounting entries, giving organizations more reliable financial reports.
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
Colombian point-of-sale receipts now include the required DIAN information when generated in Odoo. This helps businesses provide compliant receipts to customers and avoid missing tax validation details.
Original PR description
Receipts are now generated in POS using the `generateReceiptData` function. This commit extends this function and adds all the data necessary to properly show the receipt in Colombian POS. opw-6414901 Forward-Port-Of: odoo/enterprise#130005 Forward-Port-Of: odoo/enterprise#112881
This fix ensures Saudi Arabia tax tag updates are applied during Odoo 19 upgrades. It prevents outdated VAT tax grids and invoice tags, reducing the need for manual corrections 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
Fixes an inventory issue where relocating stock in a package that was already reserved for delivery could fail with an access error. Warehouse users can now move reserved packaged stock between locations without being unexpectedly blocked, improving reliability in package-based inventory workflows.
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
This fix prevents access errors when selling shared products through Point of Sale with invoicing in multi-company setups. It ensures Odoo only uses vendor information relevant to the active company, so sales can proceed without being blocked by another company's restricted supplier data.
Original PR description
**Steps to reproduce:** - Install PoS and Purchase - Make 2 companies, A and B - On product "Office Lamp" that is accessed by both companies, put Partner A in the vendor tab for company B - Company A…
**Steps to reproduce:**
- Install PoS and Purchase
- Make 2 companies, A and B
- On product "Office Lamp" that is accessed by both companies, put Partner A in the vendor tab for company B
- Company A should have a Partner A and Partner B in this tab
- In the stock, set a replenishment for company A, with Partner A on Office Lamp
- Go to company B and open the PoS
- Try to buy Office Lamp while requesting an invoice
- An access error appears
**Why the fix:**
When requesting an invoice in the PoS, we try to create the stock picking. Doing so will trigger the replenishment rules linked to the product to be recomputed.
Those are executed when we **flush_all()**, processing Company A's replenishments as sudo(), meaning all of Company A's **seller_id** are fetched and cached. This means the product's **seller_ids** now contains Company A's **seller_id**, even though we are currently in Company B.
While trying to get the product's code, we iterate over **product.seller_ids**, but we do not have access to every record in that product.
https://github.com/odoo/odoo/blob/b8e5291d103d9f43bd8db6d2dfe708076a57ea37/addons/product/models/product_product.py#L337-L343
As we don't have access to those, we get an access error when we stumble upon it.
To avoid those errors, we now filter the sellers to only have the ones compatible with our current Company in the given product we are currently buying.
Another solution would be to do **product.invalidate_recordset(['seller_ids'])** before looping over it, but feels more like a band-aid than the current fix IMO.
We could also write **self.lines.product_id.mapped('code')** in **_create_order_picking(self)** to have the solution be in PoS directly, but the error might arise from somewhere else at some point, and this just hides the issue by adding the code to the cache so that we don't have to fetch it again later.
opw-6308182
Forward-Port-Of: odoo/odoo#286656
Forward-Port-Of: odoo/odoo#275354Point of Sale now correctly includes product variant extra charges when applying a pricelist that is based on another pricelist. This prevents discounted or chained pricelists from showing prices that are too low, helping cashiers charge customers accurately.
Original PR description
## Steps to reproduce: - Create a product, with never variant, the variant has an extra price of 100 - Make Pricelist 1, just leave it as default - Make Pricelist 2, make it a discount, based on Pricelist 1, for all products - Go to the PoS, click on the created product - Change the pricelist to Pricelist 2 -> the price does not take the extra price into account ## Why the fix: When we have a pricelist based on another pricelist, we recursively calculate the price on the base pricelist. Before this commit, in the recursive call, we gave 0 as the extra price. We now give the extra price in the recursive function call. opw-6500086 Forward-Port-Of: odoo/odoo#286608 Forward-Port-Of: odoo/odoo#285371
This fix ensures restaurant table orders edited on one point of sale device remain marked for synchronization even after another device checks the same table. It prevents newly added order lines from being lost, helping staff keep shared table orders accurate across devices.
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
Early payment discount entries now preserve the analytic allocation from the original invoice lines for all discount calculation methods. This helps ensure accounting reports and cost tracking remain accurate when invoices are paid early with mixed or excluded discount settings.
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
Non-admin accounting users can now view and select the email template when sending manual payment reminders. This prevents an access error that blocked invoice reminder workflows for regular accounting staff.
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#129966Public holidays created from the form now require the payroll work entry type, preventing incomplete records from being saved. This avoids payslip creation failures for employees in months that include those holidays and keeps payroll processing more reliable.
Original PR description
**Steps to reproduce:** 1. Install Payroll and Time Off modules on v19.2. 2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type…
**Steps to reproduce:**
1. Install Payroll and Time Off modules on v19.2.
2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type and save.
3. Open Payroll and try to create a payslip for any employee in the same month as the public holiday you will face below traceback.
**Issue:**
The `work_entry_type_id` is required for payroll calculations. If it is null the `_round_days` calculation evaluates an empty recordset, producing a [ValueError](https://github.com/odoo/enterprise/blob/39095789c2b6a7e558d11f479a249871b3b800b6/hr_payroll/models/hr_payslip.py#L1021
).
While in this [PR](https://github.com/odoo/odoo/pull/254666/changes) made this field was made required in the
list view, it was missed in the form view. This allows users to save a holiday without a work entry type, crashing payslip generation later.
**Solution:**
Make the `work_entry_type_id` field required in the form view as well to prevent the creation of inconsistent public holiday records.
**Traceback:**
```.py
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll/models/hr_payslip.py",
line 1021, in _round_days
day_rounded = float_round(days, precision_rounding=precision_rounding,
rounding_method=work_entry_type.round_days_type)
File "/home/odoo/src/odoo/saas-19.2/odoo/tools/float_utils.py", line 152, in
float_round
raise ValueError(msg)
ValueError: unknown rounding method: False
```
opw-6477587
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285597
Forward-Port-Of: odoo/odoo#283762Guest shoppers who enter a valid EU VAT number during checkout now have that number verified immediately. This ensures eligible intra-community orders receive the correct 0% VAT treatment instead of being charged domestic VAT by mistake.
Original PR description
# Description **Steps to Reproduce** 1. Install Belgium Localization, then go to **Settings → Accounting/Invoicing**. 2. Enable **Verify VAT Numbers** (`vat_check_vies`). 3. Go to **Accounting →…
# Description **Steps to Reproduce** 1. Install Belgium Localization, then go to **Settings → Accounting/Invoicing**. 2. Enable **Verify VAT Numbers** (`vat_check_vies`). 3. Go to **Accounting → Configuration → Fiscal Positions** and confirm or create an **Intra-Community** fiscal position with: - Detect Automatically (`auto_apply`): enabled - VAT Required (`vat_required`): enabled - Country Group: EU, no specific country configured 4. Open an incognito/private browser window and make sure the session is unauthenticated. 5. Go to the website's `/shop` page and add any product to the cart. 6. Proceed to checkout until reaching the Address step (`/shop/address`). 7. Enter a delivery address in an EU country different from the company's country (e.g. company in Belgium, delivery address in the Netherlands). 8. In the VAT Number field, enter a real, valid, VIES-registered VAT number corresponding to the delivery country (e.g. a valid NL VAT number for a Netherlands address). 9. Click **Save Address / Continue** and proceed to the Payment step (`/shop/payment`). 10. Check the tax applied to the delivery line and the resulting order total. **Issue** The delivery product and the overall order are taxed at the standard/domestic VAT rate instead of the expected 0% intra-community rate — even though the customer provided a valid, VIES-registered EU VAT number matching the delivery country. **Root Cause** In `base_vat`, `res.partner.create()` unconditionally removes `vies_valid` from the ORM's pending computation queue via `env.remove_to_compute()`, relying on a subsequent `write()` to trigger the actual VIES check. This holds for the standard backend flow, where creation is followed by a `write()` — but the website guest checkout flow differs: - `website_sale` creates the guest partner through `_create_new_address()`. - The partner is created via a single `create()` call, with no follow-up `write()`. - `_compute_vies_valid()` is therefore never triggered. - `vies_valid` remains permanently unset (`NULL`), despite a VAT number being provided. Downstream, `account.fiscal.position._get_vat_required_valid()` reads this unset value as falsy, so the Intra-Community fiscal position's `vat_required` condition fails and is rejected in favor of another applicable position (e.g. EU B2C or Domestic). **Solution** After partner creation, explicitly trigger `_compute_vies_valid()` when the partner has a VAT number and the operation is not part of a file import (`import_file` context) — performing the VIES check immediately instead of relying on a `write()` that guest checkout never issues. **Result** Guest customers providing a valid EU VAT number now get `vies_valid` computed immediately at creation. Fiscal position detection correctly identifies the Intra-Community position, and the expected 0% VAT treatment is applied to the delivery and order. OPW: 6522992 Forward-Port-Of: odoo/odoo#286201