Daily updates from Odoo
Monday, July 6, 2026
164 changes
10 changes
Resolved issues and error corrections
This update fixes an issue where dialogs opened from Kanban quick creates would unexpectedly close when switching tabs. Now, dialogs remain open and functional, allowing users to seamlessly navigate between views and complete their tasks. This enhancement ensures a smoother user experience when creating records from Kanban.
Original PR description
similar fix: https://github.com/odoo/odoo/pull/181220 - On a kanban view, click "New" to open a quick create record; - On a many2one field, type a value and click "Create and edit..."; - A dialog…
similar fix: https://github.com/odoo/odoo/pull/181220 - On a kanban view, click "New" to open a quick create record; - On a many2one field, type a value and click "Create and edit..."; - A dialog opens to create the related record; - From that dialog, open another many2one field the same way, so a second dialog opens on top of the first one; - Change tab in the browser. Before this commit, the quick create's `beforeVisibilityChange` handler unconditionally validated and closed itself as soon as the tab became hidden, with no regard for what was happening around it. Since the "Create and edit" dialogs are owned by the field widgets living inside the quick create (`useOwnedDialogs`), closing the quick create also close those dialogs, with no action from the user. This reuses the `formInDialog` counter already relied on by `FormController` for the same kind of issue: the quick create now listens to the same `FORM-CONTROLLER:FORM-IN-DIALOG` bus events, and only validates/closes itself on visibility change once every dialog opened from it has been closed. opw-6357255 Forward-Port-Of: odoo/odoo#274054
This update resolves an issue where importing vendor bills from KSeF would fail if custom taxes were used. Now, the system automatically detects and applies the correct tax based on the KSeF tax code, even with non-standard tax configurations. This ensures smoother and more accurate bill imports for users with diverse tax requirements.
Original PR description
…oder signature When importing a vendor bill from KSeF, the system strictly relied on official Odoo tax XML IDs (e.g., `vz_kraj_23`). If a user had custom taxes (e.g., from a third-party localization), the import would crash with a UserError indicating the tax was not found. Allow manually uploading a FA(3) XML file to vendor bills, and it is detected automatically by the system This commit fixes these issues by: 1. Implementing a smart fallback tax search. If the official XML ID is not found, it dynamically searches for a matching purchase tax based on the KSeF tax code (e.g., '23' -> 23% purchase tax, 'zw' -> 0%). 2. Adding an adapter method that matches the expected EDI decoder signature, processes the file data, and writes the parsed values to the draft invoice. task-6067168 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256818
The Viva.com POS tour was occasionally failing due to a delay in processing payment confirmations. This update ensures the tour correctly waits for payment responses before sending mock webhook data, preventing interruptions and improving the overall tour experience. This resolves a frustrating issue for users of the Viva.com POS system.
Original PR description
The Viva.com POS tour was failing intermittentely due to the mocked webhook response not waiting for the payment/refund request to finish. This would cause the tour to hang as it missed the webhook confirmation. We fix the issue by changing the `waitingCard` status to only be set after the payment request returns, and wait for this status before sending the fake webhook response. runbot-243758 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273393 Forward-Port-Of: odoo/odoo#273111
This update fixes a bug where holiday accruals were incorrectly applied when carryover allowances were activated at the beginning of the year. The fix ensures accruals only occur at the standard period boundaries (start/end of month or level transitions), preventing unexpected accruals and improving the accuracy of holiday balances. This change impacts how holiday allowances are calculated.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#245201
This update corrects a discrepancy in the manufacturing order forecast report. Previously, the forecast incorrectly showed incoming quantities for finished products destined for a different warehouse. The fix ensures that the forecast accurately reflects the actual movement of materials, resolving inconsistencies between the forecast header and detail lines.
Original PR description
Version: ---------- - 18.0+ Steps to reproduce: ---------------------- 1. Install `mrp`, create two warehouses A and B. 2. Create a storable product with Track Inventory True. 3. Create a…
Version: ---------- - 18.0+ Steps to reproduce: ---------------------- 1. Install `mrp`, create two warehouses A and B. 2. Create a storable product with Track Inventory True. 3. Create a Manufacturing Order for 10 qty with(Miscellaneous tab): - Components location = Warehouse A (raw materials) - Finished product Location = Warehouse B 4. Confirm the MO. 5. Open the Forecast report for the product. Issue: ------- - Warehouse B forecast shows the MO under the replenishment detail lines (correctly, via `location_dest_id`) but the header displays "0 Incoming", "0 Outgoing", "0 Forecasted". - Warehouse A forecast incorrectly shows "10 Incoming" in the header, even though no finished product is going there. Cause: ------- - When we create MO for finished Product move is created if there no `location_final_id` then it set mo.warehouse_id.lot_stock_id` as the `location_final_id`. https://github.com/odoo/odoo/blob/f958a323fd652af9251215b1b5a2fadc3bccba42/addons/mrp/models/stock_move.py#L466-L467 which is introduce in this [commit](https://github.com/odoo-dev/odoo/commit/95ce0ed97a160e3465c313ed6b9bef938d61586b) - The problem is that `mo.warehouse_id` is a related field computed from `mo.location_src_id.warehouse_id` https://github.com/odoo/odoo/blob/f958a323fd652af9251215b1b5a2fadc3bccba42/addons/mrp/models/mrp_production.py#L110 - this warehouse that supplies the **raw materials** (Warehouse A). When the user sets `location_dest_id` to Warehouse B's stock, `mo.warehouse_id` is still Warehouse A, so `location_final_id` is stamped with Warehouse A's stock location. - `product.incoming_qty` (used by the forecast header) evaluates non-done moves using `location_final_id` first (if set), falling back to `location_dest_id` only when `location_final_id` is False: https://github.com/odoo/odoo/blob/f958a323fd652af9251215b1b5a2fadc3bccba42/addons/stock/models/product.py#L331-L335 - Because `location_final_id` is set (to WH-A) and non-False, the second clause (which would pick up `location_dest_id` = WH-B) is never evaluated. The result: the move is counted as incoming in Warehouse A and ignored in Warehouse B. - The forecast detail *lines* use only `location_dest_id` to classify moves, so they correctly show the MO as incoming for Warehouse B — producing the inconsistency the user observes. https://github.com/odoo/odoo/blob/f958a323fd652af9251215b1b5a2fadc3bccba42/addons/stock/report/stock_forecasted.py#L42-L46 Fix: ---- - Replace `mo.warehouse_id.lot_stock_id.id` with `mo.location_dest_id.id`: - `location_final_id` is meant to track where the product ultimately ends up when the immediate destination is intermediate. The correct "final" location for a finished-product move is exactly what the user chose as `location_dest_id` on the MO — not the stock location of the warehouse that happens to supply the raw materials. - For the standard single-warehouse case, `mo.location_dest_id` equals `mo.warehouse_id.lot_stock_id`, so the behaviour is unchanged. For cross-warehouse MOs (destination = WH-B), `location_final_id` is now stamped with WH-B's stock, making `product.incoming_qty` and the forecast header consistent with the detail lines. ---- opw-6294479 Forward-Port-Of: odoo/odoo#270089
This update fixes an issue where splitting a restaurant order didn't correctly apply the original order's fiscal position and pricelist to the new order. Now, when splitting, the new order inherits the correct tax settings and pricing rules, ensuring accurate financial reporting and order fulfillment. This improves the reliability of order splitting functionality.
Original PR description
When splitting an order, the new order was created without the original's fiscal position and pricelist, so its lines fell back to the default taxes Steps to reproduce: 1. Create a fiscal position with some tax mapping 2. Create a pricelist with some price rules 3. Add the fiscal position and pricelist to the delivery preset 4. Create a restaurant order as delivery 5. Split the order 6. Pay both of them 7. First order will have the default taxes and prices list instead of preset's ones Part of: https://github.com/odoo/odoo/pull/268862 -opw-6246434 Forward-Port-Of: odoo/odoo#273452 Forward-Port-Of: odoo/odoo#272837
This update fixes an issue where payment redirection wasn't correctly updating after processing. Previously, the landing route was set earlier, causing inconsistencies. Now, the updated landing route from the transaction is passed through, ensuring correct redirection to the cart payment page.
Original PR description
In commit 2cb589169fb77f98900997b9266ad309dcf602f9, a feature was introduced to redirect to cart payment when transaction was canceled or if an error occurred. Since commit 4588e939e3619949473f26223ada82c642c4bede, processing was triggered after we reach the payment_status page. At this point the landing route was already set in 'el.dataset.landingRoute' so any changes done to the landing_route by the processing won't be reflected in this js handling of redirection. As a solution, we return the updated landing route of the transaction in the post_processing api call. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes a potential issue where users could still register payments against invoices that were marked as blocked. Now, blocked invoices are correctly displayed in lists and payments cannot be registered through standard flows. This ensures accurate financial reporting and prevents incorrect payment processing.
Original PR description
When an invoice is blocked for payment, the form view hides the Pay button, but users could still register a payment from list/payment-item flows. Prevent payment registration for blocked invoices in both the invoice action path and the payment register wizard path. Also make blocked invoices display as Blocked in invoice lists instead of Posted or Sent. task-6310234 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270830
This update fixes an issue where changing the start date of a work order incorrectly calculated its duration. The fix ensures that the duration remains accurate when only the start date is adjusted, aligning with previous behavior and preventing incorrect end date calculations. This improves the reliability of work order planning.
Original PR description
**Problem:** On a planned work order, changing only the start date (e.g. in the planning gantt edit dialog) corrupts the expected duration instead of just shifting the end date. The duration drifts…
**Problem:** On a planned work order, changing only the start date (e.g. in the planning gantt edit dialog) corrupts the expected duration instead of just shifting the end date. The duration drifts to a wrong value, and in some cases (e.g. dependent work orders) collapses to 0. **Steps to reproduce:** 1. Plan a work order on a workcenter (start, end, expected duration). 2. Open it and change only the start date to a time that is not on a working-hours boundary. 3. The end date updates, but the expected duration is now wrong. **Expected behavior:** Changing the start date replans the work order: the duration is kept and the end date is recomputed from it. This is how 19.0 behaves and how dragging the pill in the gantt already behaves. **Cause of the issue:** Changing date_start triggers _onchange_date_start, which recomputes date_finished from start + duration via plan_hours. That cascades into _onchange_date_finished, which recomputes duration_expected from the dates via get_work_duration_data. Since the resource calendar refactor in 19.2, plan_hours and get_work_duration_data are no longer exact inverses around the work order's own planned slot, so the round trip drifts the duration. **Fix:** Only recompute the duration when the end date was edited on its own. When date_finished already matches the planned end for the current duration, it was merely derived from the start change, so the duration is kept. This keeps the duration authoritative when moving the work order while still recomputing it on a genuine end-date resize. opw-6231569 Forward-Port-Of: odoo/odoo#271508
This update fixes an issue where breaking a combo in the Point of Sale system didn't properly update the preparation display. The fix ensures that the preparation display is now notified when a combo is reorganized, preventing unnecessary kitchen tickets and streamlining the order preparation process. This improves efficiency and reduces potential errors.
Original PR description
Issue: Breaking a combo back into individual lines was not notifying the preparation display. Fix: breakCombo now go through sendOrderInPreparation (with byPassPrint) the preparation display is updated and no ticket is printed. To avoid triggering a sound and a kitchen ticket for a reorganization the kitchen already knows about, thread a `silent` context flag through sendOrderInPreparation down to _send_load_orders_message. 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
13 changes
Resolved issues and error corrections
This update resolves a critical issue preventing the generation and sending of French PDP reports. Previously, including raw data without proper decoding caused a system crash during the JSONification process. This fix ensures reports can now be successfully generated and sent, improving the functionality of the French PDP module.
Original PR description
Before this commit, it was impossible to send a `l10n.fr.pdp.reports.flow` as we included a raw file in the payload without decoding it. This causes the JSONification to crash. 1. Install l10n_fr_pdp 2. Create a record of `l10n.fr.pdp.reports.flow` 3. Try to send it 4. See crash opw-6330820 **19.3+**
This update fixes an issue where dialogs opened from Kanban quick creates would unexpectedly close when switching tabs. Now, dialogs remain open and functional, ensuring a smoother user experience when navigating between Kanban views. This prevents disruptions and allows users to complete related record creations seamlessly.
Original PR description
similar fix: https://github.com/odoo/odoo/pull/181220 - On a kanban view, click "New" to open a quick create record; - On a many2one field, type a value and click "Create and edit..."; - A dialog…
similar fix: https://github.com/odoo/odoo/pull/181220 - On a kanban view, click "New" to open a quick create record; - On a many2one field, type a value and click "Create and edit..."; - A dialog opens to create the related record; - From that dialog, open another many2one field the same way, so a second dialog opens on top of the first one; - Change tab in the browser. Before this commit, the quick create's `beforeVisibilityChange` handler unconditionally validated and closed itself as soon as the tab became hidden, with no regard for what was happening around it. Since the "Create and edit" dialogs are owned by the field widgets living inside the quick create (`useOwnedDialogs`), closing the quick create also close those dialogs, with no action from the user. This reuses the `formInDialog` counter already relied on by `FormController` for the same kind of issue: the quick create now listens to the same `FORM-CONTROLLER:FORM-IN-DIALOG` bus events, and only validates/closes itself on visibility change once every dialog opened from it has been closed. opw-6357255 Forward-Port-Of: odoo/odoo#274054
This update resolves an issue where importing vendor bills from KSeF would fail if custom taxes were used. Now, the system automatically detects and processes FA(3) XML files, dynamically matching KSeF tax codes to the correct purchase tax rates. This ensures smoother and more accurate import of KSeF bills, regardless of the user's tax configuration.
Original PR description
…oder signature When importing a vendor bill from KSeF, the system strictly relied on official Odoo tax XML IDs (e.g., `vz_kraj_23`). If a user had custom taxes (e.g., from a third-party localization), the import would crash with a UserError indicating the tax was not found. Allow manually uploading a FA(3) XML file to vendor bills, and it is detected automatically by the system This commit fixes these issues by: 1. Implementing a smart fallback tax search. If the official XML ID is not found, it dynamically searches for a matching purchase tax based on the KSeF tax code (e.g., '23' -> 23% purchase tax, 'zw' -> 0%). 2. Adding an adapter method that matches the expected EDI decoder signature, processes the file data, and writes the parsed values to the draft invoice. task-6067168 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256818
This update addresses a rare but critical issue where the Glory cash machine occasionally sent incorrect data to Odoo. The fix ensures that all data received from the machine is correctly parsed, preventing errors and maintaining reliable transaction processing. This improves the stability and accuracy of sales data.
Original PR description
Rarely, the Glory machine can send a websocket message containing 2 root XML elements, which causes the `parseXML` function to fail. This commit fixes the issue wrapping the message in a root element, and then returning the children. opw-6292925 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272040
A recent update caused a problem where Point of Sale sessions couldn't be opened. This fix addresses an incorrect field mapping, specifically related to product categories, that was pulling data from the wrong module. The change ensures the POS system correctly accesses the necessary information for session functionality.
Original PR description
after commit [1] we are not able to open pos session, if we have installed only point_of_sale [1] https://github.com/odoo/odoo/commit/f20b2d22dbec2e6a5e682539489ab94d9c96fb21 Traceback ```py File…
after commit [1] we are not able to open pos session, if we have installed only
point_of_sale
[1] https://github.com/odoo/odoo/commit/f20b2d22dbec2e6a5e682539489ab94d9c96fb21
Traceback
```py
File "odoo/saas-19.3/addons/point_of_sale/models/pos_session.py", line 161, in load_data
response[model] = self.env[model]._load_pos_data_search_read(response, self.config_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/codebase/odoo/saas-19.3/addons/point_of_sale/models/pos_load_mixin.py", line 24, in _load_pos_data_search_read
return self._load_pos_data_read(records, config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/codebase/odoo/saas-19.3/addons/point_of_sale/models/pos_load_mixin.py", line 55, in _load_pos_data_read
records = records._filtered_access("read").read(fields, load=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/codebase/odoo/saas-19.3/odoo/orm/models.py", line 2760, in read
self._origin.fetch(fields)
File "odoo/codebase/odoo/saas-19.3/odoo/orm/models.py", line 3062, in fetch
fields_to_fetch = self._determine_fields_to_fetch(field_names, ignore_when_in_cache=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/codebase/odoo/saas-19.3/odoo/orm/models.py", line 3141, in _determine_fields_to_fetch
raise ValueError(f"Invalid field {field_name!r} on {self._name!r}") from e
ValueError: Invalid field 'removal_strategy_id' on 'product.category'
```
cause:
- field `removal_strategy_id` comes from stock and from saas-19.3 point_of_sale
is not dependent on stock.
FIx:
- correct module should be `pos_stock`
opw-6364126
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis update fixes an unexpected accrual calculation that occurred when carryover balances were applied. Previously, an extra accrual happened on the carryover date, leading to confusing accrual amounts. This change ensures accruals only occur at the standard periods (start/end of month or level transitions), improving the accuracy of holiday balances.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#272349 Forward-Port-Of: odoo/odoo#245201
This update fixes an unexpected accrual of holiday days that occurred when carryover allowances were applied at the beginning of the year. The change ensures accruals only happen at the standard period boundaries (start/end of month or level transitions), resolving a confusing and inaccurate calculation. This improves the reliability of holiday balance tracking.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#245201
This update streamlines the website forum editor by removing unnecessary toolbar features like headings and font options, and fixing a technical issue that prevented certain features from working correctly. The changes ensure a more consistent and reliable editing experience for forum content.
Original PR description
Description of the feature this PR addresses: - Remove unwanted toolbar features (heading, font_family, powerbuttons, undo/redo buttons) - Update toolbar styles in website_forum to keep them consistent - Fix table menu traceback by passing missing `localOverlayContainers` in `website_forum_wysiwyg` config task-6123698 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273635 Forward-Port-Of: odoo/odoo#263326
This update ensures that taxes are correctly saved when editing POS orders in the backend, specifically during return and exchange scenarios. Previously, the system silently dropped tax information during the save process due to the `tax_ids` field being read-only. The fix adds a setting to force the save, guaranteeing tax data is preserved.
Original PR description
The `tax_ids` field on `pos.order.line` is defined with `readonly=True`. When editing a POS order from the backend (e.g. during a return/exchange flow), the `_onchange_product_id` method correctly…
The `tax_ids` field on `pos.order.line` is defined with `readonly=True`. When editing a POS order from the backend (e.g. during a return/exchange flow), the `_onchange_product_id` method correctly sets `tax_ids` from the product, and the computed `tax_ids_after_fiscal_position` displays the mapped taxes in the UI. However, because `tax_ids` is readonly, the web client does not include it in the save payload. As a result, the taxes are silently dropped on save and `tax_ids_after_fiscal_position` recomputes to empty. Steps to reproduce: 1. Create and pay a POS order with a product that has taxes 2. Go to the backend (Point of Sale > Orders) and open that order 3. Initiate a return for the order 4. In the return order, add a new product (exchange scenario) 5. Observe that taxes are correctly shown on the new line 6. Click Save 7. The taxes disappear from the order line The fix adds `force_save="1"` to the `tax_ids` field in both the list and form views of `pos.order.line`, consistent with how `price_subtotal` and `price_subtotal_incl` are already handled in the same views. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261672 Forward-Port-Of: odoo/odoo#253680
This update fixes a potential issue where users could still register payments against invoices that were marked as blocked for payment. The changes now prevent payment registration through all interfaces and display blocked invoices clearly as 'Blocked' in invoice lists, ensuring accurate financial reporting and preventing incorrect payment processing.
Original PR description
When an invoice is blocked for payment, the form view hides the Pay button, but users could still register a payment from list/payment-item flows. Prevent payment registration for blocked invoices in both the invoice action path and the payment register wizard path. Also make blocked invoices display as Blocked in invoice lists instead of Posted or Sent. task-6310234 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270830
This update fixes an issue where changing the start date of a work order would incorrectly calculate the expected duration. The fix ensures that the duration remains accurate when only the start date is adjusted, aligning with previous behavior and preventing disruptions to scheduling.
Original PR description
**Problem:** On a planned work order, changing only the start date (e.g. in the planning gantt edit dialog) corrupts the expected duration instead of just shifting the end date. The duration drifts…
**Problem:** On a planned work order, changing only the start date (e.g. in the planning gantt edit dialog) corrupts the expected duration instead of just shifting the end date. The duration drifts to a wrong value, and in some cases (e.g. dependent work orders) collapses to 0. **Steps to reproduce:** 1. Plan a work order on a workcenter (start, end, expected duration). 2. Open it and change only the start date to a time that is not on a working-hours boundary. 3. The end date updates, but the expected duration is now wrong. **Expected behavior:** Changing the start date replans the work order: the duration is kept and the end date is recomputed from it. This is how 19.0 behaves and how dragging the pill in the gantt already behaves. **Cause of the issue:** Changing date_start triggers _onchange_date_start, which recomputes date_finished from start + duration via plan_hours. That cascades into _onchange_date_finished, which recomputes duration_expected from the dates via get_work_duration_data. Since the resource calendar refactor in 19.2, plan_hours and get_work_duration_data are no longer exact inverses around the work order's own planned slot, so the round trip drifts the duration. **Fix:** Only recompute the duration when the end date was edited on its own. When date_finished already matches the planned end for the current duration, it was merely derived from the start change, so the duration is kept. This keeps the duration authoritative when moving the work order while still recomputing it on a genuine end-date resize. opw-6231569 Forward-Port-Of: odoo/odoo#271508
This change addresses a temporary issue where the standard price of products in purchase orders was being incorrectly calculated due to timing conflicts during price updates. The fix ensures the correct standard price is applied, preventing inaccurate costing. This improves the reliability of purchase order pricing.
Original PR description
The below test sometimes fail for an incorrect reason and leads to a false positive:…
The below test sometimes fail for an incorrect reason and leads to a false
positive:
https://github.com/odoo/odoo/blob/6dbeac3a42f46b42c638c05aea8285452c944c3f/addons/stock_dropshipping/tests/test_purchase_order.py#L21
Here is another way to reproduce the issue with a higher probability of
false positive (and it is actually easier to read and understand what the
test is doing and what's wrong). It needs to edit the following test:
https://github.com/odoo/odoo/blob/ec58c5e12987401659ea0d75d3be2905ad1d807d/addons/purchase_stock/tests/test_create_picking.py#L953
With the below diff:
```diff
--- a/addons/purchase_stock/tests/test_create_picking.py
+++ b/addons/purchase_stock/tests/test_create_picking.py
@@ -965,6 +965,7 @@ class TestCreatePicking(ProductVariantsCommon):
'price': 500.0,
'discount': 10,
})]
+ self.product_id_1.standard_price = 1.0
po = self.env['purchase.order'].create(self.po_vals) # create a PO for 5 units
po.button_confirm()
with Form(po) as po_form:
```
It will lead to:
```
Traceback (most recent call last):
File ".../test_create_picking.py", line 976, in test_average_cost_updated_after_po_with_discount
self.assertEqual(self.product_id_1.standard_price, 450.0)
AssertionError: 1.0 != 450.0
```
Here are the explanations: when receiving an AVCO product, at some point, we
recompute its standard price. To do so, among several operations, we take
the last manual update, and we ignore all previous SM:
https://github.com/odoo/odoo/blob/2dbd88657395da965125c8f085da93e04c9c8f0a/addons/stock_account/models/product.py#L463-L465
This is an issue when things are done too quickly. See the pattern:
```py
self.product_a.standard_price = 5.0 # -> define valuation_from_date
po.confirm() # with another cost
receipt.button_validate() # -> define move.date
```
In case of a fast execution, both dates will be equal. We therefore ignore
the SM and rely on the manual update to define the standard price, which is
not expected. This explains the above `AssertionError`.
Fixing the codebase is quite tricky since the opposite use case could also
happen, aka first processing a receipt and only then modifiying the standard
price.
Tests side, a more important solution should probably be implemented to ease
their redaction and avoid this basic pattern. Yet, a WIP task is changing
the valo for Odoo 20, so the whole logic may change. Second, the current
issue is impacting a lot of builds, so we need to move forward. For both
reason, the commit only "fixes" the current test.
runbot-939955
Forward-Port-Of: odoo/odoo#273078This update resolves an issue where the 'Remaining Extra Hours' value in the employee attendance recap was incorrectly displaying as 00:00. The previous calculation method resulted in a strictly positive value, even when extra hours were owed. This change ensures the 'Remaining Extra Hours' accurately reflects the owed time off, aligning with other overtime calculations.
Original PR description
## Issue In the attendance view of an employee, a recap of the current extra hours is displayed, showing four values: 1. Total Extra Hours Worked 2. Total Compensable Extra Hours 3. Time Off Taken…
## Issue
In the attendance view of an employee, a recap of the current extra hours is displayed, showing four values:
1. Total Extra Hours Worked
2. Total Compensable Extra Hours
3. Time Off Taken from Extra Hours
4. Remaining Extra Hours
Each of the above values can be negative but the last one, which can seem odd as it appears to be calculated from the other values.
<img width="299" height="168" alt="6293174-before" src="https://github.com/user-attachments/assets/4377e2fd-f5f2-41a4-a82b-6f2598378499" />
## Steps to reproduce
1. Install *HR Attendance Holidays* (`hr_holidays_attendance`)
2. In Settings, toggle *Absence Management* and *Display Extra Hours*
3. For an employee E:
- In the Payroll tab, set the Working Hours to the *Standard 40 hours/week* schedule
- In the Settings tab, set the Overtime Ruleset to the *Default Ruleset*, and toggle the *Give back as time off* action for the *Employee Schedule Rule* rule
4. Create an attendance for employee E:
- Any day where they are expected to work 8 hours
- From 10am to 5pm (6 hours with lunch)
5. In the Employees app, go to employee E and click the *Monthly Hours* smart button
6. __In the *Balance* recap above the list of attendances, the *Remaining Extra Hours* row shows 00:00, which seems wrong compared to the other fields above (*Total Extra Hours Worked* and *Total Compensable Extra Hours*) which appear negative.__
## Cause
The `unspent_overtime` (*Remaining Extra Hours* in the balance recap) is computed by adding positive values, making it strictly positive.
https://github.com/odoo/odoo/blob/30c9e8c5b1e34b94c8aab8681e2c051a3b70f013/addons/hr_holidays_attendance/models/hr_employee.py#L62-L65
This was added by https://github.com/odoo/odoo/commit/2144bcfba1ac53c82fc7f2870a72bb13abee97e4, with no justification on why this value needs to be positive.
## Impact on "Time Off taken from Extra Hours"
Before this change, after following the above steps, a value of `-2:00` would be displayed in the *Time Off Taken from Extra Hours* row. This is no longer the case after this fix, since the `'unspent_compensable_overtime'` (*Remaining Extra Hours*) value is used to compute that row:
https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/hr_holidays_attendance/static/src/views/extra_hours_list_view.js#L39-L42
Instead, a value of `00:00` is shown. Before, we were substracting 0 hour of `unspent_compensable_overtime` to the -2 hours of `compensable_overtime`, now we are subectracting -2 hours of `unspent_compensable_overtime` to the same -2 hours of `compensable_overtime`. This is a side effect that was ignored, as it seems to make at least as much sense as showing `-2:00`.
<img width="321" height="179" alt="6293174-after" src="https://github.com/user-attachments/assets/907d5fb8-0c36-4b2e-bca7-1cec41baf22b" />
opw-6293174
Forward-Port-Of: odoo/odoo#273634
Forward-Port-Of: odoo/odoo#27127210 changes
Resolved issues and error corrections
This update resolves an issue where importing vendor bills from KSeF would fail if custom taxes were used. Now, the system automatically detects and processes FA(3) XML files, intelligently matching tax codes to ensure accurate bill imports. This improves the reliability of KSeF integration for businesses using customized tax configurations.
Original PR description
…oder signature When importing a vendor bill from KSeF, the system strictly relied on official Odoo tax XML IDs (e.g., `vz_kraj_23`). If a user had custom taxes (e.g., from a third-party localization), the import would crash with a UserError indicating the tax was not found. Allow manually uploading a FA(3) XML file to vendor bills, and it is detected automatically by the system This commit fixes these issues by: 1. Implementing a smart fallback tax search. If the official XML ID is not found, it dynamically searches for a matching purchase tax based on the KSeF tax code (e.g., '23' -> 23% purchase tax, 'zw' -> 0%). 2. Adding an adapter method that matches the expected EDI decoder signature, processes the file data, and writes the parsed values to the draft invoice. task-6067168 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256818
This update ensures that restaurant users can now always access course management features, even when Course Allocation is enabled. A helpful message has also been added to guide users through the course creation process. This improves the functionality for restaurants using the Odoo Point of Sale system.
Original PR description
Before this commit: ======================= The Courses menu was always hidden because it was restricted to `base.group_no_one`, making course management inaccessible even when Course Allocation was enabled in a restaurant PoS. After this commit ====================== The Courses menu now always visible. A help message is also added to the Courses action to guide users when creating courses. Task-6317914 Forward-Port-Of: odoo/odoo#271308
This update corrects a bug where an unexpected accrual of holiday days occurred when carryover balances were applied at the beginning of a new year. The fix ensures accruals only happen at the standard period start, end, or level transition times, improving the accuracy of holiday balance calculations. This resolves a confusing and incorrect accrual behavior.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#272349 Forward-Port-Of: odoo/odoo#245201
This update fixes an unexpected accrual of holiday days that occurred when carryover balances were applied at the beginning of the year. The fix ensures accruals only happen at the standard period boundaries (start/end of month or level transitions), resolving confusion and improving the accuracy of holiday balance calculations. This change impacts how holiday allowances are tracked and utilized.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#245201
This fix ensures that mass mailing emails correctly direct recipients to the unsubscribe page for their specific company website. Previously, multi-company setups caused unsubscribe links to redirect to the login page. The update now uses the recipient's company website URL for all unsubscribe links, resolving this issue and improving email deliverability.
Original PR description
In a multi-company setup with a website per company, the unsubscribe link in mass mailing emails could send recipients to the login page instead of the unsubscribe confirmation page. ### Steps to…
In a multi-company setup with a website per company, the unsubscribe link in mass mailing emails could send recipients to the login page instead of the unsubscribe confirmation page.
### Steps to reproduce
1. Enable multi-company and create a second company `Company B`.
2. Create two websites with different domains, one per company:
- `Website A` on the main company, domain `http://website-a.test`
- `Website B` on `Company B`, domain `http://website-b.test`
3. Set the system parameter `web.base.url` to `http://website-a.test`. System parameters are global, so this value applies to the whole database regardless of the company you switch to.
4. Create a contact and set its `Company` field to `Company B`.
5. In Email Marketing, create a mailing with recipient model `Contact`, target the contact above, pick any template with an unsubscribe link, and send it.
6. Open the email in an incognito window and click the unsubscribe link: you land on the login page instead of the unsubscribe page.
### Cause
Mass mailing builds the unsubscribe link in two steps.
First, each email body is rendered for its recipient. While rendering, relative URLs like `/unsubscribe_from_list` are turned into absolute URLs by prepending a base URL. That base URL comes from the recipient record itself: `recipient.get_base_url()`. The `website` module overrides this so that, when the record has a company, it returns that company's website domain. For a contact in `Company B`, the body ends up with `http://website-b.test/unsubscribe_from_list`.
Second, right before sending, `mail_mail._prepare_outgoing_list` replaces that placeholder URL with a per-recipient signed URL pointing to `/confirm_unsubscribe`. It does this by plain string replacement: it looks for `{base_url}/unsubscribe_from_list` in the body and swaps it. The `base_url` used here came from `self.mailing_id.get_base_url()`. A mailing has no company, so its base URL falls back to the global `web.base.url`, which in our setup is `http://website-a.test`.
The two base URLs no longer match. The body contains the website B URL, but the replacement code searches for the website A URL. The search fails, the placeholder stays in the email, and the recipient clicks a link to `/unsubscribe_from_list`. That route only redirects to `/mailing/my`, which requires being logged in, so the user lands on the login page.
### Fix
Compute the base URL from the recipient record (the same record used when rendering the body) instead of the mailing. The two URLs then agree and the replacement works. Fall back to the mailing's base URL if there is no recipient model on the mail.
opw-4914203
Forward-Port-Of: odoo/odoo#273888
Forward-Port-Of: odoo/odoo#264055This change addresses a temporary issue where the standard price of products wasn't being correctly updated after creating purchase orders with discounts. The fix ensures the standard price reflects the last manual valuation, resolving a false positive in a test. This improves the accuracy of product costing.
Original PR description
The below test sometimes fail for an incorrect reason and leads to a false positive:…
The below test sometimes fail for an incorrect reason and leads to a false
positive:
https://github.com/odoo/odoo/blob/6dbeac3a42f46b42c638c05aea8285452c944c3f/addons/stock_dropshipping/tests/test_purchase_order.py#L21
Here is another way to reproduce the issue with a higher probability of
false positive (and it is actually easier to read and understand what the
test is doing and what's wrong). It needs to edit the following test:
https://github.com/odoo/odoo/blob/ec58c5e12987401659ea0d75d3be2905ad1d807d/addons/purchase_stock/tests/test_create_picking.py#L953
With the below diff:
```diff
--- a/addons/purchase_stock/tests/test_create_picking.py
+++ b/addons/purchase_stock/tests/test_create_picking.py
@@ -965,6 +965,7 @@ class TestCreatePicking(ProductVariantsCommon):
'price': 500.0,
'discount': 10,
})]
+ self.product_id_1.standard_price = 1.0
po = self.env['purchase.order'].create(self.po_vals) # create a PO for 5 units
po.button_confirm()
with Form(po) as po_form:
```
It will lead to:
```
Traceback (most recent call last):
File ".../test_create_picking.py", line 976, in test_average_cost_updated_after_po_with_discount
self.assertEqual(self.product_id_1.standard_price, 450.0)
AssertionError: 1.0 != 450.0
```
Here are the explanations: when receiving an AVCO product, at some point, we
recompute its standard price. To do so, among several operations, we take
the last manual update, and we ignore all previous SM:
https://github.com/odoo/odoo/blob/2dbd88657395da965125c8f085da93e04c9c8f0a/addons/stock_account/models/product.py#L463-L465
This is an issue when things are done too quickly. See the pattern:
```py
self.product_a.standard_price = 5.0 # -> define valuation_from_date
po.confirm() # with another cost
receipt.button_validate() # -> define move.date
```
In case of a fast execution, both dates will be equal. We therefore ignore
the SM and rely on the manual update to define the standard price, which is
not expected. This explains the above `AssertionError`.
Fixing the codebase is quite tricky since the opposite use case could also
happen, aka first processing a receipt and only then modifiying the standard
price.
Tests side, a more important solution should probably be implemented to ease
their redaction and avoid this basic pattern. Yet, a WIP task is changing
the valo for Odoo 20, so the whole logic may change. Second, the current
issue is impacting a lot of builds, so we need to move forward. For both
reason, the commit only "fixes" the current test.
runbot-939955
Forward-Port-Of: odoo/odoo#273078This update fixes an issue where project templates weren't correctly associated with sale orders, preventing proper billing and limiting template choices. It now ensures project templates are linked to the sale order's company, improving accuracy and usability. This ensures users can select the appropriate project template based on the sale order.
Original PR description
Fix 1 : project, sale_timesheet: remove default_allow_billable context in Create a Project --------------------- **Issue:** When a project is created from the sale app, it is not billable by default,…
Fix 1 : project, sale_timesheet: remove default_allow_billable context in Create a Project --------------------- **Issue:** When a project is created from the sale app, it is not billable by default, and the sale order / sale order line are not set. **Fix:** Remove default_allow_billable = False in the Create a Project **Note:** default_allow_billable = True already exists in action_view_project_ids, but that default context is replaced when opening the project directly from the view. This happens because default_allow_billable = False is set in the Create a Project action. Fix 2: sale_project: show only relevant project templates per company ---------------- **Steps:** - Install sale_project - Create two companies (A, B) - Create three project templates: - Template A (company A) - Template B (company B) - Template C (no company → visible to all) - Create a sale order for company A with a service product - Confirm the sale order - Create a project and try to select a template **Issue:** All project templates were visible even if the sale order had a company set. **Fix:** Added a filter (domain) on the project template field so only templates for the sale order’s company or templates with no company are shown. Users cannot select templates from other companies. task-5074893 Forward-Port-Of: odoo/odoo#260496 Forward-Port-Of: odoo/odoo#229309
This update fixes an issue where invoices weren't accurately reflecting timesheet hours after a partial refund was issued on a sales order. The change ensures that previously invoiced hours are properly deducted when generating new invoices, preventing over-invoicing. This improves the accuracy of billing and reporting.
Original PR description
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h…
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h on timesheets - Invoice the SO - Create a credit note for 11 hours => only 9 hours are invoiced - Log 5h more on timesheets - Back to the SO > create invoice again > All the 25hrs are to invoiced, although 9 of them were invoiced before ### Cause of Issue: When generating the new invoice, `_recompute_qty_to_invoice` calls `_get_delivered_quantity_by_analytic` which retrieves the analytic values for the SO line. The values retrieved are later used to determine the delivered quantity, which is later assigned to be `line.qty_to_invoice` without taking into account the already invoiced hours. https://github.com/odoo/odoo/blob/7a6518e39d34575a3977e7c4a0053a45223e203c/addons/sale_timesheet/models/sale_order_line.py#L176-L186 ### Fix: Ensures that hours that have already been completely invoiced are deducted from the quantity to invoice. opw-6253650 Forward-Port-Of: odoo/odoo#273156 Forward-Port-Of: odoo/odoo#268025
This update resolves an issue where invoices for Italian VAT (IT) were incorrectly calculating taxes. Now, invoices can include both the 0% Digital Operations Indicator (DOI) tax and other applicable taxes, ensuring accurate plafond calculations and tax deductions. This improves the functionality for IT VAT compliance.
Original PR description
- Create a declaration of intent in the customer's contact - Issue an invoice that includes both the 0% E (DoI tax) and any other tax - You will see how the plafond is not updated and the amount of this invoice is not deducted from it The method _compute_l10n_it_edi_doi_amount specifically exclude from the doi amount lines with the doi tax and another tax. However it should be possible to use both on a single line. We can use the amount subtotal because the doi is always 0%. opw-6253475 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267669
This update fixes a potential issue where users could still register payments against invoices that were marked as blocked for payment. The changes now prevent payment registration through all interfaces and display blocked invoices clearly as 'Blocked' in invoice lists, ensuring accurate financial reporting and preventing errors.
Original PR description
When an invoice is blocked for payment, the form view hides the Pay button, but users could still register a payment from list/payment-item flows. Prevent payment registration for blocked invoices in both the invoice action path and the payment register wizard path. Also make blocked invoices display as Blocked in invoice lists instead of Posted or Sent. task-6310234 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270830
12 changes
Resolved issues and error corrections
This update fixes an unexpected accrual of holiday days that occurred when carryover balances were applied at the beginning of the year. The fix ensures accruals only happen at the standard period boundaries (start/end of month or level transitions), preventing confusion and ensuring accurate holiday balance tracking. This improves the reliability of holiday accrual calculations.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#245201
This update corrects a bug where currency conversion rates were incorrectly calculated through branch companies instead of the root company. Previously, multi-branch setups caused errors, but this fix ensures rates are always determined based on the parent company, improving reconciliation and data accuracy. This resolves a critical issue impacting financial reporting.
Original PR description
**Description of the issue/feature this PR addresses:** When branch companies are involved, currency conversion goes through the branch instead of its root company. Because currency rates in Odoo…
**Description of the issue/feature this PR addresses:** When branch companies are involved, currency conversion goes through the branch instead of its root company. Because currency rates in Odoo only ever live on the root company, resolving a rate through a branch is incorrect. Furthermore, when two sibling branches are active at the same time, it makes the computed company a multi-record set, breaking the reconciliation process with an "Expected singleton" error. This is grounded in how the rest of res.currency already behaves by design: res.currency._get_rates() looks up rates with company_id in (False, company.root_id.id). res.currency.rate._check_company_id() forbids setting a rate on a company that has a parent_id. Therefore, rates are, by design, only ever meant to live on the root company. The only place that still passed the raw company (branch included) into with_company() was res.currency._get_conversion_rate(). **Current behavior before PR:** _get_conversion_rate() forwarded the received company untouched to from_currency.with_company(company). As a result, Odoo looked up the conversion rate through the branch rather than its parent. When more than one branch of the same parent is active at the same time (resulting in a recordset of 2+ branches), company.currency_id inside _compute_current_rate() was no longer a singleton, causing the code to crash with ValueError: Expected singleton: res.company(...) — even though every branch shares the exact same currency and rate defined on their common root company. **Steps to reproduce:** 1) Enable multi-company and branches. 2) Create a parent company P (e.g., using ARS as main currency). 3) Create two branches under P: B1 and B2 (branches inherit P's currency). 4) On the parent company P, define a currency rate for a foreign currency, e.g., USD (Accounting > Configuration > Currencies > USD > Rates). 5) Log in with a user that has P, B1, and B2 all selected as active companies (all three checked in the top-right company switcher). 6) In branch B1, create a customer invoice in USD. 7) In branch B2, register a customer payment in USD. 8) Open the Auto-reconcile tool or try to reconcile the journal items directly. Result: A ValueError: Expected singleton is raised during the reconciliation because the conversion rate is resolved against the multi-company recordset B1 + B2 instead of P. **Desired behavior after PR is merged:** _get_conversion_rate() now resolves the company to its root_id before computing the rate. Branches will correctly fallback to their parent company, and multiple active sibling branches will collapse to a single root company, ensuring that company.currency_id remains a singleton. With the same steps described above, the invoice and the payment now reconcile normally, safely using the single USD rate defined on the parent root company. Non-branch (standalone) companies remain unaffected since a root company's root_id is itself. **video** https://drive.google.com/file/d/14NGTTzP28CgSiYFQdFZ6juHSsib_MDd9/view --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273758
This update resolves an issue where importing vendor bills from KSeF would fail if custom taxes were used. Now, the system automatically detects and processes KSeF bills with custom taxes, ensuring accurate import functionality. This improves the reliability of importing invoices from the Polish tax authority.
Original PR description
…oder signature When importing a vendor bill from KSeF, the system strictly relied on official Odoo tax XML IDs (e.g., `vz_kraj_23`). If a user had custom taxes (e.g., from a third-party localization), the import would crash with a UserError indicating the tax was not found. Allow manually uploading a FA(3) XML file to vendor bills, and it is detected automatically by the system This commit fixes these issues by: 1. Implementing a smart fallback tax search. If the official XML ID is not found, it dynamically searches for a matching purchase tax based on the KSeF tax code (e.g., '23' -> 23% purchase tax, 'zw' -> 0%). 2. Adding an adapter method that matches the expected EDI decoder signature, processes the file data, and writes the parsed values to the draft invoice. task-6067168 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256818
This update resolves an issue where receipts sometimes printed blank or were cut prematurely. By adding a brief delay and using more precise printer commands, the system now ensures the receipt is fully rendered before paper is advanced, resulting in consistently printed receipts. The update also enhances the printer SDK for stability and future tracking.
Original PR description
Previously, printing a receipt could sometimes result in blank paper being dispensed or the paper being cut prematurely. This occurred because the sequence of line feeds and cut commands was dispatched immediately after sending the image payload, before the printer hardware had sufficient time to process and spool the bitmap. To resolve this, a 200ms delay is introduced after the bitmap is sent. Additionally, the arbitrary `printAndLineFeed` calls are replaced with a precise `printAndFeedPaper` and explicit `partialCut` command. This ensures the hardware has fully rendered the receipt before advancing the paper and engaging the blade. Finally, the internal imin SDK (`lib/imin-printer/imin-printer.js`) is updated to handle websocket connection timeouts gracefully and to expose new hardware APIs for future tracking. owp-6242801 Forward-Port-Of: odoo/odoo#270765
This update allows invoices in Italy to include both the 0% Digital Tax (DoI) and other applicable taxes on a single line. Previously, the system incorrectly handled DoI taxes, preventing plafond updates and proper deductions. This change ensures accurate VAT calculations and invoice processing for Italian customers.
Original PR description
- Create a declaration of intent in the customer's contact - Issue an invoice that includes both the 0% E (DoI tax) and any other tax - You will see how the plafond is not updated and the amount of this invoice is not deducted from it The method _compute_l10n_it_edi_doi_amount specifically exclude from the doi amount lines with the doi tax and another tax. However it should be possible to use both on a single line. We can use the amount subtotal because the doi is always 0%. opw-6253475 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267669
This update fixes an issue where invoices weren't accurately reflecting timesheet hours after a partial refund was issued on a sales order. The change ensures that previously invoiced hours are properly deducted when generating new invoices, preventing over-invoicing and maintaining accurate record-keeping of service time. This improves the reliability of our invoicing process.
Original PR description
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h…
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h on timesheets - Invoice the SO - Create a credit note for 11 hours => only 9 hours are invoiced - Log 5h more on timesheets - Back to the SO > create invoice again > All the 25hrs are to invoiced, although 9 of them were invoiced before ### Cause of Issue: When generating the new invoice, `_recompute_qty_to_invoice` calls `_get_delivered_quantity_by_analytic` which retrieves the analytic values for the SO line. The values retrieved are later used to determine the delivered quantity, which is later assigned to be `line.qty_to_invoice` without taking into account the already invoiced hours. https://github.com/odoo/odoo/blob/7a6518e39d34575a3977e7c4a0053a45223e203c/addons/sale_timesheet/models/sale_order_line.py#L176-L186 ### Fix: Ensures that hours that have already been completely invoiced are deducted from the quantity to invoice. opw-6253650 Forward-Port-Of: odoo/odoo#273156 Forward-Port-Of: odoo/odoo#268025
This update resolves an issue preventing users from changing a product's bill of materials type (kit to manufacture) when sales orders are already linked across multiple companies. The fix corrects a data integrity check that incorrectly considered company differences, now allowing for more flexible product management in a multi-company environment. This ensures sales orders can be processed correctly regardless of the initial bom type.
Original PR description
### Steps to reproduce: - Have two companies: company1 and company2 - Create a producct P available in both company1 and company2 - with company1, create a kit bom for a product P - with company2,…
### Steps to reproduce: - Have two companies: company1 and company2 - Create a producct P available in both company1 and company2 - with company1, create a kit bom for a product P - with company2, create and confirm a sale order for 1 unit of P - with company1, change the bom type of P from kit to manufature #### > UserError: As long as there are some sale order lines that must be delivered/invoiced and are related to these bills of materials, you can not remove them. ### Cause of the issue: Changing the bom type from a kit (phantom type) to a non kit will launch a call of the `_ensure_bom_is_free` in order to ensure data integrity if the kit bom was used by a relevant sale order line: https://github.com/odoo/odoo/blob/f4c76be062bec47b68ee42505d7d42fed31ac0f2/addons/sale_mrp/models/mrp_bom.py#L15-L18 https://github.com/odoo/odoo/blob/f4c76be062bec47b68ee42505d7d42fed31ac0f2/addons/sale_mrp/models/mrp_bom.py#L24-L42 However, this check does not take the company of the bom into account and in the present flow, the company of the bom is different from the company of the supposedly problematic sol. opw-6290304 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273839 Forward-Port-Of: odoo/odoo#271119
This update resolves an issue where the standard price of products wasn't being correctly updated during purchase order creation, leading to false test failures. The fix ensures the standard price is accurately calculated by freezing the time of manual valuation, preventing discrepancies and improving data consistency.
Original PR description
The below test sometimes fail for an incorrect reason and leads to a false positive:…
The below test sometimes fail for an incorrect reason and leads to a false
positive:
https://github.com/odoo/odoo/blob/6dbeac3a42f46b42c638c05aea8285452c944c3f/addons/stock_dropshipping/tests/test_purchase_order.py#L21
Here is another way to reproduce the issue with a higher probability of
false positive (and it is actually easier to read and understand what the
test is doing and what's wrong). It needs to edit the following test:
https://github.com/odoo/odoo/blob/ec58c5e12987401659ea0d75d3be2905ad1d807d/addons/purchase_stock/tests/test_create_picking.py#L953
With the below diff:
```diff
--- a/addons/purchase_stock/tests/test_create_picking.py
+++ b/addons/purchase_stock/tests/test_create_picking.py
@@ -965,6 +965,7 @@ class TestCreatePicking(ProductVariantsCommon):
'price': 500.0,
'discount': 10,
})]
+ self.product_id_1.standard_price = 1.0
po = self.env['purchase.order'].create(self.po_vals) # create a PO for 5 units
po.button_confirm()
with Form(po) as po_form:
```
It will lead to:
```
Traceback (most recent call last):
File ".../test_create_picking.py", line 976, in test_average_cost_updated_after_po_with_discount
self.assertEqual(self.product_id_1.standard_price, 450.0)
AssertionError: 1.0 != 450.0
```
Here are the explanations: when receiving an AVCO product, at some point, we
recompute its standard price. To do so, among several operations, we take
the last manual update, and we ignore all previous SM:
https://github.com/odoo/odoo/blob/2dbd88657395da965125c8f085da93e04c9c8f0a/addons/stock_account/models/product.py#L463-L465
This is an issue when things are done too quickly. See the pattern:
```py
self.product_a.standard_price = 5.0 # -> define valuation_from_date
po.confirm() # with another cost
receipt.button_validate() # -> define move.date
```
In case of a fast execution, both dates will be equal. We therefore ignore
the SM and rely on the manual update to define the standard price, which is
not expected. This explains the above `AssertionError`.
Fixing the codebase is quite tricky since the opposite use case could also
happen, aka first processing a receipt and only then modifiying the standard
price.
Tests side, a more important solution should probably be implemented to ease
their redaction and avoid this basic pattern. Yet, a WIP task is changing
the valo for Odoo 20, so the whole logic may change. Second, the current
issue is impacting a lot of builds, so we need to move forward. For both
reason, the commit only "fixes" the current test.
runbot-939955
Forward-Port-Of: odoo/odoo#273078This update fixes an issue where cash rounding records were incorrectly shared across all Indian companies. Previously, a single record was duplicated, leading to errors when opening invoices. The change ensures each new Indian company has its own unique cash rounding record, resolving data inconsistencies and improving invoice processing.
Original PR description
### Issue: When creating a new Indian company, the `Half Up` cash rounding is reassigned to the new company instead of being duplicated Previous Indian companies lose access to it, causing errors…
### Issue: When creating a new Indian company, the `Half Up` cash rounding is reassigned to the new company instead of being duplicated Previous Indian companies lose access to it, causing errors when opening invoices that reference the cash rounding if the user doesn't have access to that company ### Cause: `cash_rounding_in_half_up` was defined as a `data` record with a fixed XML ID (`l10n_in.cash_rounding_in_half_up`) `_get_in_account_cash_rounding` referenced that XML ID directly and set `company_id` to the current company on each chart of accounts installation This reassigned the single shared record to the new company instead of creating a new one Moving the definition to the `@template` decorator without a module-prefixed XML ID lets the chart of accounts system create one record per company, as intended ### Steps to reproduce: - Install `l10n_in` and switch to `IN Company` - Check the Cash Rounding records grouped by company - Create a new Indian company - Enable both `IN Company` and the new company - Check the Cash Rounding records grouped by company again Before the fix, only the last created Indian company has the Cash Rounding record opw-6318857
This update fixes a potential issue where users could still register payments against blocked invoices, even though the payment button was hidden. Now, blocked invoices are correctly displayed as 'Blocked' in lists and payments cannot be registered through standard flows. This ensures accurate financial reporting and prevents incorrect payment processing.
Original PR description
When an invoice is blocked for payment, the form view hides the Pay button, but users could still register a payment from list/payment-item flows. Prevent payment registration for blocked invoices in both the invoice action path and the payment register wizard path. Also make blocked invoices display as Blocked in invoice lists instead of Posted or Sent. task-6310234 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270830
This update fixes an issue where the 'To Pay' section on point-of-sale receipts incorrectly displayed the total due instead of the actual cash payment amount. The change ensures the receipt accurately reflects the price plus tax, aligning with previous behavior and improving clarity for users.
Original PR description
**Steps to reproduce:** - Create a rounding method, only for cash, rounding of 100 - Create a product, costing 100 - Go to the PoS, order and pay for the product with cash - The "To Pay" section is the total due, and not what we actually paid - It is 115 but it should be 100 as this is what we pay for **Why the fix:** The current behavior is to display the total due, not rounded, just everything we have to pay for. Before 19.0, what we paid for was displayed, in this exemple it would display 100 and not 115. This is correct as it seems it is what this section of the receipt is about. We now use **total_amount_currency** which is computed like this https://github.com/odoo/odoo/blob/006a6a1cc6e50bd8b328d0cabb7abbcf610e34bb/addons/account/static/src/helpers/account_tax.js#L1411-L1414 So it is the price + the tax + the rounding, in this exemple it would be **100 + 15 + (-15)** opw-6225613 Forward-Port-Of: odoo/odoo#265298
This update resolves an issue where users with access restricted to a 'branch' company were unable to properly validate purchase orders for components linked to a different 'company1' company. The fix ensures the system correctly handles valuation calculations when components are associated with different company IDs, preventing access errors.
Original PR description
### Steps to reproduce: - Have a company with a branch say "company1" and "branch" - Create two products: Final product (FP), Component (Comp) - Set the company_id of FP to "branch" and of Comp to…
### Steps to reproduce: - Have a company with a branch say "company1" and "branch" - Create two products: Final product (FP), Component (Comp) - Set the company_id of FP to "branch" and of Comp to "company1" - Associate both products with a product category set to avco in company1 (the field is company dependant) - Create a bom for FP with company_id set to "branch": 1 X Comp - Impersonate a user whose only allowed and default is "branch" - Create and confirm an MO for 1 unit of FP - Set the qty_producing to 1 unit and validate #### > Access Error: Access to unauthorized or invalid companies. ### Cause of the issue: Validating the MO will, validate the component move and set its value: https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/stock_move.py#L168-L173 But, in order to determine this value, it is necessary to determine its `property_cost_method`: https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L60-L69 Now, the issue is that the `product_template` of the component belongs to "company1" so that the user is unauthorized to read the valuation method of the product category for "company1". opw-6216141 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272429
10 changes
Resolved issues and error corrections
This update fixes a potential issue where Point of Sale records weren't being fully initialized before setup processes. By ensuring all records are created first, this change improves the stability and reliability of the Point of Sale module, preventing errors during initial configuration or data loading. This resolves a previously reported problem impacting data consistency.
Original PR description
This commit change the behavior of loadData to ensure that all records are linked & created before calling setup method of each of them. related task: 4922193 Forward-Port-Of: odoo/odoo#229128 Forward-Port-Of: odoo/odoo#228820
This update fixes an unexpected accrual of holiday days that occurred when carryover balances were applied at the beginning of the year. The fix ensures accruals only happen at the standard period boundaries (start/end of month or level transitions), resolving a confusing and inaccurate calculation. This improves the reliability of holiday balance tracking.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#245201
This update resolves an issue where importing vendor bills from KSeF would fail if custom taxes were used. Now, the system automatically detects and processes FA(3) XML files, dynamically matching KSeF tax codes to the correct purchase tax rates. This ensures smoother and more accurate bill imports for users with customized tax configurations.
Original PR description
…oder signature When importing a vendor bill from KSeF, the system strictly relied on official Odoo tax XML IDs (e.g., `vz_kraj_23`). If a user had custom taxes (e.g., from a third-party localization), the import would crash with a UserError indicating the tax was not found. Allow manually uploading a FA(3) XML file to vendor bills, and it is detected automatically by the system This commit fixes these issues by: 1. Implementing a smart fallback tax search. If the official XML ID is not found, it dynamically searches for a matching purchase tax based on the KSeF tax code (e.g., '23' -> 23% purchase tax, 'zw' -> 0%). 2. Adding an adapter method that matches the expected EDI decoder signature, processes the file data, and writes the parsed values to the draft invoice. task-6067168 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256818
This update allows invoices in Italy to include both the 0% DoI tax and other applicable taxes on a single line. Previously, the system incorrectly excluded DoI tax amounts, leading to incorrect plafond calculations. This change ensures accurate tax calculations and reporting for Italian customers.
Original PR description
- Create a declaration of intent in the customer's contact - Issue an invoice that includes both the 0% E (DoI tax) and any other tax - You will see how the plafond is not updated and the amount of this invoice is not deducted from it The method _compute_l10n_it_edi_doi_amount specifically exclude from the doi amount lines with the doi tax and another tax. However it should be possible to use both on a single line. We can use the amount subtotal because the doi is always 0%. opw-6253475 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267669
This update resolves an issue where batch payment sequences were incorrectly created when a new company was initially set up. The change ensures sequences are properly associated with the correct company, preventing errors during payment processing. This improves the reliability of our payment system.
Original PR description
Previously, batch payment sequence will be created by simply select to create new company due to having lambda in default. Hence, the created sequence does not have a correct company_id set as company hasn't yet created. Switch to creating sequence in ``create`` function to avoid this issue. Also use ``range_year`` for payment prefix because it was set to use date range. 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#273487 Forward-Port-Of: odoo/odoo#268689
This update fixes an issue where invoices weren't accurately reflecting timesheet hours after a partial refund was issued on a sales order. The change ensures that previously invoiced hours are properly deducted when generating new invoices, preventing over-invoicing and ensuring accurate reporting. This improves the reliability of sales order billing.
Original PR description
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h…
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h on timesheets - Invoice the SO - Create a credit note for 11 hours => only 9 hours are invoiced - Log 5h more on timesheets - Back to the SO > create invoice again > All the 25hrs are to invoiced, although 9 of them were invoiced before ### Cause of Issue: When generating the new invoice, `_recompute_qty_to_invoice` calls `_get_delivered_quantity_by_analytic` which retrieves the analytic values for the SO line. The values retrieved are later used to determine the delivered quantity, which is later assigned to be `line.qty_to_invoice` without taking into account the already invoiced hours. https://github.com/odoo/odoo/blob/7a6518e39d34575a3977e7c4a0053a45223e203c/addons/sale_timesheet/models/sale_order_line.py#L176-L186 ### Fix: Ensures that hours that have already been completely invoiced are deducted from the quantity to invoice. opw-6253650 Forward-Port-Of: odoo/odoo#273156 Forward-Port-Of: odoo/odoo#268025
This update corrects a bug where unreserving a production order would prevent byproducts from being created. The fix ensures that byproducts are properly adjusted when an order is unreserved, maintaining accurate inventory levels. This resolves an issue impacting production planning and material availability.
Original PR description
On a mo that has byproducts, if you unreserve, it will also set the quantity of byproducts to 0 Steps to reproduce: ------------------- * Create a Products main, component and byproduct * Create a…
On a mo that has byproducts, if you unreserve, it will also set the quantity of byproducts to 0 Steps to reproduce: ------------------- * Create a Products main, component and byproduct * Create a bom for main with component as component and byproduct as byproduct * Create and confirm a mo for main * Set qty_producing to quantity ot produce * click on "Unreserve" (do_unreserve) * click on "Check availability" (action_assign) * Produce All -> the byproducts will not be produced. Observation: ------------- When updating the qty_producing value it will also update the quantity of the byproducts moves: https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/mrp/models/mrp_production.py#L892-L893 https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/mrp/models/mrp_production.py#L1350 https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/stock/models/stock_move.py#L2382 The quantity on the byproducts move has been updated. When clicking on Unreserve it will call do_unreserve, https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/mrp/models/mrp_production.py#L2297-L2298 It will filters the moves that do not need to be unreserved and select the others: https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/stock/models/stock_move.py#L900 and it will unlink all the sml from the moves: https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/stock/models/stock_move.py#L919 Which will set the quantity on the byproduct moves to 0. When Producing all (button_mark_done) since the qty_producing has already been set, it will simply mark the byproduct move has picked. https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/mrp/models/mrp_production.py#L1323-L1324 In our case, this means that the no byproducts will be created since, the quantity was previously set to 0 opw-6296562 Forward-Port-Of: odoo/odoo#272216
This update resolves an issue where reverting inventory adjustments with packages resulted in negative quantities appearing within those packages. The fix ensures that quantities are accurately restored after a revert, preventing inconsistencies in package inventory levels. This improves data accuracy and reliability for stock management.
Original PR description
Currently when the user reverts an inventory adjustment move line with a package the package contains extra line showing negative quantity of the product. ## Steps to produce: - Install Inventory…
Currently when the user reverts an inventory adjustment move line with a package the package contains extra line showing negative quantity of the product.
## Steps to produce:
- Install Inventory without demo data
- Settings Enable 'Packages'
- Create a product:
- Cheese burger
- On hand > Create a new quant
- Package: 'Burgerbox' and 'On Hand Quantity`: 1 and save
- Set the On Hand quantity to zero and save
- History > Revert the Inventory adjustment line from WH/stock to Inventory adjustment by selecting it and reverting via actions.
- Products > Packages > BurgerBox
## Observed Behaviour:
After reverting an inventory adjustment that set the product's physical quantity to 0, the package contains two lines for the same product with quantities 1 and -1.
This is inconsistent because a package should not contain a product with a negative quantity.
The package should be restored to its original state and contain only the expected positive quantity.
## Root cause:
When the user reverts the move line, `action_revert_inventory` is called. This method creates the revert move and then marks that move as done at [1].
Marking the move as done subsequently marks all related move lines as done at [2]. During this process, the system first unreserves the quantity from the virtual location / inventory adjustment and then removes the quantity from that location (resulting in a -1 quantity move line at that location). This is performed through `_synchronize_quant`, which is responsible for synchronizing the physical inventory with the move line at [3].
The `_synchronize_quant` method uses the move line's `package_id` when updating the corresponding quant at [4]. As a result, `_update_available_quantity` creates a new quant with the following values at [5]:
```
{
'product_id': 1,
'location_id': 14,
'lot_id': stock.lot(),
'package_id': 1,
'owner_id': res.partner(),
'in_date': datetime.datetime(2026, 6, 22, 12, 42, 11),
'quantity': -1.0,
}
```
This creates a quant with a negative quantity that is linked to the package because `package_id` is set on the newly created quant. Consequently, the move line with the negative quantity becomes associated with the package.
[1]-
https://github.com/odoo/odoo/blob/333c279be7cba9fda17d2818be36f4d96d8cd0f6/addons/stock/models/stock_move_line.py#L1016-L1035
[2]-
https://github.com/odoo/odoo/blob/333c279be7cba9fda17d2818be36f4d96d8cd0f6/addons/stock/models/stock_move.py#L1956 [3]-
https://github.com/odoo/odoo/blob/333c279be7cba9fda17d2818be36f4d96d8cd0f6/addons/stock/models/stock_move_line.py#L662-L666
[4]-
https://github.com/odoo/odoo/blob/333c279be7cba9fda17d2818be36f4d96d8cd0f6/addons/stock/models/stock_move_line.py#L678-L687
[5]-
https://github.com/odoo/odoo/blob/333c279be7cba9fda17d2818be36f4d96d8cd0f6/addons/stock/models/stock_quant.py#L1130-L1143
## Solution:
Remove the source `package_id` when creating revert moves for inventory adjustment locations.
When an inventory adjustment sets a product's quantity to 0, the adjustment is completed without a destination package, meaning the product is effectively removed from the package. Therefore, the corresponding revert move should not retain the package as its source. Keeping the package as the source is inconsistent because package information should not exist on a virtual inventory adjustment location, and the original inventory adjustment removes the product from the package (there is no destination package).
By removing the source `package_id` from the revert move, the system avoids creating negative quants associated with the package during quant synchronization. This also ensures that, after the inventory adjustment is reverted, the quantities of products inside the package are restored correctly and match their state prior to the adjustment.
opw-6285739
Forward-Port-Of: odoo/odoo#273632
Forward-Port-Of: odoo/odoo#271440This update fixes an issue where expense accounts weren't being correctly applied during Point of Sale transactions. Now, when a sale is made without invoicing, the system accurately maps expense accounts based on the fiscal position or product category, ensuring accurate financial reporting. This improves the reliability of financial data generated from Point of Sale.
Original PR description
**Steps to reproduce:** - Make a product with a category - In the category, make the inventory valuation to automated - Set the income and expense account - Set a cost for the product - Make a fiscal…
**Steps to reproduce:** - Make a product with a category - In the category, make the inventory valuation to automated - Set the income and expense account - Set a cost for the product - Make a fiscal position and set it as default for the PoS - In the Account Mapping tab, map the income and expense to two other accounts - Go to the PoS - Make a sale for that product, without invoice - Close the session and in the backend check the session - Check the journal entries - The income account has been mapped to the fiscal position's - The outcome account stayed the same as in the category's **Why the fix:** When we invoice an order, the income and expense accounts are immediately updated, in a different place than if it has not been invoiced. At the session's closure, we update the accounts for every order that hasn't been invoiced. In this flow, the account mapping defined on the fiscal position was not applied, so we took the one defined on the product's category. The income account was already mapped as we need to do it earlier than the session closure, so it had already been set as the right one before our flow. For the expense account, we only need it at this specific time, so we can map it as the session's closure. We now map the account depending on the fiscal position if we are able to find one, otherwise, we use the category's default as we did before. opw-6171677 Forward-Port-Of: odoo/odoo#266700
This update ensures that taxes and order totals are accurately displayed when customers select in-store pickup for international orders. Previously, the system incorrectly applied VAT, leading to inaccurate checkout summaries. This fix automatically recalculates taxes and updates the checkout summary to match the selected pickup location.
Original PR description
Backport of : https://github.com/odoo/odoo/pull/269057 to V18 When an international customer selects an in-store pickup location, the order's fiscal position changes to the fiscal position matching…
Backport of : https://github.com/odoo/odoo/pull/269057 to V18 When an international customer selects an in-store pickup location, the order's fiscal position changes to the fiscal position matching the pickup warehouse. However, the order-line taxes and checkout summary are not recomputed immediately. **Steps to reproduce:** 1. Configure a French company and website. 2. Configure a product priced at 100 ( just an example , any price will do ) EUR excluding 20% French VAT. 3. Configure an export fiscal position removing VAT for Japan. 4. Configure an international delivery method. 5. Configure an in-store pickup method with a warehouse located in France. 6. Checkout using a Japanese delivery address. 7. Select the international delivery method. 8. Switch to pickup in store. **Current behavior:** - The order fiscal position changes to the French fiscal position. - Product-line taxes and the checkout summary remain based on the export fiscal position. - French VAT only appears later on the payment step. - Switching back to international delivery can similarly leave stale totals. **Expected behavior:** - Selecting the French pickup location immediately applies French VAT. - Switching back to international delivery immediately removes French VAT. - Totals displayed during delivery selection match the payment-step totals. **Cause:** The Click & Collect flow explicitly recomputes `fiscal_position_id` when selecting or leaving an in-store pickup location, but it does not recompute the order-line taxes and prices. Additionally, the pickup-location route does not return updated order-summary values, so the checkout page cannot refresh its displayed totals. **Solution:** - Recompute taxes and prices when the in-store fiscal position changes. - Restrict the recomputation to draft website orders. - Return the updated order summary after selecting a pickup location. - Refresh the checkout summary using the returned values. **Tests cover:** - Japanese delivery with export fiscal position and no VAT. - Switching to a French pickup location immediately applying 20% VAT. - Switching back to international delivery removing VAT. - Delivery-step totals matching payment-step recomputation. - Pickup-location route returning updated summary values. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269086
9 changes
Resolved issues and error corrections
This update resolves a problem where PDF invoices generated by the Nilvera integration were not being correctly saved to the system. The previous code incorrectly handled the PDF data, resulting in a base64-encoded string instead of the actual PDF file. This fix ensures the invoices are stored as proper binary files, allowing for correct display and download in the browser.
Original PR description
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session…
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes.
The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` from Python 3.14's stricter base64 validation in the `datas` auto-decode path. That switch silently changed what ends up on disk (`datas` decodes its input, `raw` does not)
Storing that string in the binary `raw` field encodes it as UTF-8, so the file on disk ends up as the literal ASCII of the base64 text. The attachment is served as `application/pdf` but the browser receives base64 ASCII and cannot preview or download the PDF.
Call `b64decode(response)` before storing so the attachment contains the actual PDF bytes.
OPW-6302803
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#270759This update corrects a rounding issue that occurred when processing down payments on sales orders, specifically when dealing with multiple product lines. The fix involves splitting the down payment calculation into multiple lines to ensure accurate calculations and prevent discrepancies of up to one cent. This improves the reliability of financial transactions.
Original PR description
**Steps to reproduce:** - Make a quotation with 2 products - Product A with a unit price of 5503.26 with 15% taxes - Product B with a unit price of 4058.04 with 15% taxes - Order 7 of A and 2 of B,…
**Steps to reproduce:** - Make a quotation with 2 products - Product A with a unit price of 5503.26 with 15% taxes - Product B with a unit price of 4058.04 with 15% taxes - Order 7 of A and 2 of B, the total is 53634.73 - Go to the pos, make a downpayment for 100% of the price - The total is 53634.74, which is one cent more than it should **Why the fix:** When importing this order, we calculate the price again, to do it we compute the price unit and the taxes for it, then we will multiply it by the qty and add it all to a downpayment line and add it to the order. When we have multiple qty like this, we run the risk of having a rounding issue, as the price unit is rounded before being multiplied by the tax and the quantity of said line. Unfortunately in this exemple, we either have a price without the taxes of 46638.89 which gives a total of 53634.72 once multiplied or we have 46638.90 which gives a total of 53634.74 So with a decimal of 2 there is no way we could have the right amount with only one line, which is why we split it in 2 lines to get more precision when importing the sale order. The way it worked before this commit is to take the sum of all lines and to calculate the downpayment based on it. We now split it into multiple smaller lines to avoid rounding issues because that's how it's done on the Sale Order in the backend. By splitting the down payment into multiple lines we replicate the per-line rounding behavior, preventing 1-cent discrepancies. This is fixed in 18.3 onwards by using the accounting helper functions, but they are not yet implemented in 18.0 and it will be too big of a change for 18.0 so this solution is suggested. opw-6222937
This update fixes an issue where credit note imports were incorrectly processing negative values, leading to incorrect tax calculations. The fix ensures that price, quantity, and tax amounts align with standard refund line behavior, resolving a discrepancy in total calculations. This improves the accuracy of credit note processing.
Original PR description
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as a negative value which is corrected with a rounding line. - The 6% tax rate is applied to the negative invoice line, resulting in a negative tax amount being deducted from the total (e.g., 449.32 + (-26.96) = 422.36) instead of being added (449.32 + 26.96 = 476.28) Expected behavior: price_unit, quantity and the related tax amounts should all be positive, matching a normal in_refund/out_refund line. Why this happens: - In `_import_ubl_invoice_line_add_price_unit_quantity_discount`, `BaseQuantity` was multiplied by file_document_sign, unlike `PriceAmount` from the same node which is left untouched. This flips price_quantity to -1, which later flips price_unit to negative when `price_unit = price_subtotal / price_quantity`. opw-6310442
This update resolves an issue where XML data associated with purchase invoices received via email was being discarded due to errors. Now, even if the XML data is faulty, it's retained, ensuring complete invoice processing and preventing data loss. This improves the reliability of our purchase invoice system.
Original PR description
Issue: When receiveing an email on a purchase journal, if the XML raise an issue, it is discarded. Steps to reproduce: - Configure an incoming mail server - Set up an email alias for the Vendor Bill journal - Receive a mail with an XML (e.g. PEPPOL XML) which raise an issue Current Behavior: - XML is discarded Cause: To avoid keeping pictures,... from mail, every attachment from a mail that doesn't fill an account.move is discarded. As the XML is faulty, it doesn't fill the move and is discarded. opw-6288972 Forward-Port-Of: odoo/odoo#270347
This update fixes an issue where the quantity displayed for kit products in the Point of Sale picking process was incorrect. The fix ensures that the correct quantity, based on the kit's components, is accurately reflected, leading to more precise inventory management. This improves order fulfillment accuracy for kit products.
Original PR description
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component…
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L283 we always get the component's line, as the move's product is the component, even if it used to be the kit product's move. This is because when exploding a kit's moves, it gets the kit's component as a product instead of keeping the kit product. This was introducing a weird behavior because we took the quantity from the component line, and not from the kit line, meaning the kit would always have the same quantity as the component. We now check if the move is actually a kit product's move, and if it is we adapt the qty to correct one by fetching the correct line's qty, and adapting it with the correct UoM. Changing the line in itself would not work, as the kit itself is not tracked by lots, so we would not enter https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L284 and the move line would not be correctly created. opw-6153000 Forward-Port-Of: odoo/odoo#262551
This update fixes an issue where users with access to multiple companies were only appearing as interviewers for jobs within their default company. The change ensures that users with access to multiple companies can be selected as interviewers for job positions across all their allowed companies, improving recruitment efficiency.
Original PR description
Issue: ---------------------------------------- A user allowed in multiple companies will only show as an interviewer in job positions from its default company. Steps to reproduce: ---------------------------------------- - Configure a user with multiple allowed companies (A and B) - Set the user's default company to A - Create or open a job position belonging to company B. - Try to add the user as an interviewer Cause: ---------------------------------------- To compute `allowed_user_ids` we group the users by `company_id` (i.e. the default company), so the allowed companies are ignored. Solution: ---------------------------------------- Group the users by `company_ids`, the aggregate then separates the companies in case they're a recordset. opw-6314373 Forward-Port-Of: odoo/odoo#273602
This update corrects a bug where the Peppol demo mode wasn't correctly applied to databases that had previously been neutralized. Previously, the system defaulted to production mode, leading to incorrect document registration. Now, the demo mode is automatically set when a neutralized database receives the account_peppol module, ensuring accurate Peppol network interactions.
Original PR description
When Peppol is installed on a database that was already neutralized (ex: a staging database where the feature is enabled after the neutralization happened), the account_peppol.edi.mode parameter is not set: data/neutralize.sql only runs at neutralization time, not when the module is installed afterwards. The demo/ data that also sets this parameter is not loaded on databases without demo data (real production/staging databases). As a result, _get_peppol_edi_mode() falls back to 'prod' and the neutralized database registers and sends documents against the live Peppol network. Steps to reproduce: - Neutralize a database on which Peppol is not installed yet - Install the account_peppol module - Open the Peppol settings / registration wizard: the mode is Production instead of Demo Force the demo mode in the pre_init_hook when the database is neutralized, mirroring data/neutralize.sql opw-6307710 Forward-Port-Of: odoo/odoo#273019
This update ensures that leave hours are calculated accurately when employees use calendars with multiple defined time slots. Previously, the system struggled to handle complex calendar schedules, leading to incorrect leave duration calculations. This fix resolves this issue, guaranteeing accurate leave tracking based on defined calendar hours.
Original PR description
Define the correct hours in the leaves if the calendar has defined dates (`date_from` and `date_to`) Use case example: - Create a calendar and define on Friday (Morning: from 08:00 to 13.00,…
Define the correct hours in the leaves if the calendar has defined dates (`date_from` and `date_to`) Use case example: - Create a calendar and define on Friday (Morning: from 08:00 to 13.00, Afternoon: from 19:00 to 21:00) with date_to=2025-01-01. - Define another specific Friday (Morning: from 09:00 to 14.00, Afternoon: from 17:00 to 20:00) in the same calendar with date_from=2025-01-01. - Create an employee and define the calendar created for him/her. - Create a leaves for the employee and select a Friday (2025-05-02). - The start hour of the leave must be 2025-05-02 09:00:00 - The end hour of the leave must be 2025-05-02 20:00:00   Please @pedrobaeza can you review it? @Tecnativa TT56218 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#254249 Forward-Port-Of: odoo/odoo#208378
This update resolves a bug that caused errors when processing payments with withholding taxes in Argentina. Specifically, the system now correctly handles 0% withholding taxes and prevents the deletion of withholding lines during payment resets. This ensures accurate tax calculations and payment processing for Argentinian businesses.
Original PR description
**Steps to reproduce:** - Install Accounting and l10n_ar_withholding - Switch to an Argentinian company (e.g. (AR) Responsable Inscripto) - Create a 0% Payment Withholding tax: * Tax Type: Customer…
**Steps to reproduce:**
- Install Accounting and l10n_ar_withholding
- Switch to an Argentinian company (e.g. (AR) Responsable Inscripto)
- Create a 0% Payment Withholding tax:
* Tax Type: Customer Payment Withholding
* Amount: 0.00 %
* Add an account for the tax distribution lines
- Create an invoice with a tax
- Confirm the invoice
- Pay the invoice:
* Withholdings:
- Add a line with the created 0% Payment Withholding tax
- Add a line with another Payment Withholding tax
- Create Payment
- Go to the payment
**Issue 1:**
When clicking on the first withholding line, a JS error is raised due to a missing index (i.e. currency_id).
**Cause 1:**
One of the fields has an aggregate sum function applied on it (i.e. amount_currency).
As it is a monetary field, the corresponding currency field is required in the view.
**Issue 2:**
When resetting the payment to draft, the withholding line with the 0% tax is deleted.
As the withholding table is not editable, it is not possible to add the line again.
**Cause 2:**
When the payment is reset to draft, the state of the associated journal entry is also set to draft and a "_sync_dynamic_lines" is triggered, which remove tax lines having a zero amount during the process.
**Solution 2:**
Keep all the lines with a Customer Payment Withholding tax as it is not possible to add a withholding line in the payment afterwards.
opw-6298058
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr