Thursday, March 12, 2026
20 changes · saas-18.4
Resolved issues and error corrections
This fix prevents crashes when users generate lot numbers for inventory receipts, especially for products created before lot tracking sequences were available or when the received quantity is zero. It helps warehouse users complete receipt processing reliably without unexpected error messages.
Original PR description
Currently, an error occurs when a user tries to generate lots while providing a lot number. **Steps to replicate:** - Install purchase (without demo). - Create a product `test`. - Install stock and…
Currently, an error occurs when a user tries to generate lots while providing a lot number.
**Steps to replicate:**
- Install purchase (without demo).
- Create a product `test`.
- Install stock and turn on `Lots and Serial Numbers`
- Open the product `test` and turn on `Track Inventory` `by Lots`.
- Open Receipts > add the product `test`> give demand as 3 > and go to its form view using view button.
- Click `Generate Lots` > type `lot1` in `First lot Number` > Generate > Error-1
- Click `Generate Lots` > type 0 in Quantity received > Generate > Error-2.
**Error-1:**
```
File '/home/odoo/odoo18/community/addons/stock/models/stock_move.py', line 1026, in action_generate_lot_line_vals
if (first_lot and first_lot == product.lot_sequence_id.get_next_char(first_number)):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File '/home/odoo/odoo18/community/odoo/addons/base/models/ir_sequence.py', line 237, in get_next_char
interpolated_prefix, interpolated_suffix = self._get_prefix_suffix()
^^^^^^^^^^^^^^^^^^^^^^^^^
File '/home/odoo/odoo18/community/odoo/addons/base/models/ir_sequence.py', line 227, in _get_prefix_suffix
self.ensure_one()
File '/home/odoo/odoo18/community/odoo/orm/models.py', line 5640, in ensure_one
raise ValueError('Expected singleton: %s' % self)
ValueError: Expected singleton: ir.sequence()
```
**Error-2:**
```
File '/home/odoo/odoo18/community/addons/stock/models/stock_move.py', line 1025, in action_generate_lot_line_vals
first_number = product.lot_sequence_id.number_next_actual - product.lot_sequence_id.number_increment
^^^^^^^
UnboundLocalError: cannot access local variable 'product' where it is not associated with a value
```
---
**Cause:**
- Both errors originated through a recent [PR].
**Error-1 (Expected singleton: ir.sequence()):**
- As the product was already created before Inventory was installed, the `lot_sequence_id` was empty. (Note:`lot_sequence_id` field has a default value , but default value
assignment triggers only during the record creation, any records created
before stock is installed will not be assigned any value for
`lot_sequence_id`.)
- As no `lot_sequence_id` is assigned to `test` product the line [1] calls `get_next_char()` on an empty recordset which further calls `_get_prefix_suffix()` [2] and raises singletonerror from [here].
**Error-2 (UnboundLocalError: cannot access local variable 'product'):**
- As the `Received Quantity` was given 0, the `count` argument is received as 0 and as a result the `lot_qties` [3] and `lot_names` [4] are received as empty lists.
- This causes their [zip] to be empty list too and the loop never runs, so assignment to [product] variable never happens and causes the error to occur from here [5].
---
**Solution:**
**Error-1:**
- Now we perform write on `product.lot_sequence_id` only if it exists, otherwise we skip it.
**Error-2:**
- Moved the static assignment of variable `product` and `location_dest_id` outside the loop, this will also prevent the browse being called multiple times for browsing the same record.
[PR]: https://github.com/odoo/odoo/pull/240368
[1]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/addons/stock/models/stock_move.py#L1026
[2]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/odoo/addons/base/models/ir_sequence.py#L237
[here]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/odoo/addons/base/models/ir_sequence.py#L227
[3]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/addons/stock/models/stock_move.py#L989
[4]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/addons/stock/models/stock_move.py#L994
[zip]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/addons/stock/models/stock_move.py#L1000
[product]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/addons/stock/models/stock_move.py#L1004
[5]: https://github.com/odoo/odoo/blob/7ab52c1675b9764d11454c7b5216064bec4628f8/addons/stock/models/stock_move.py#L1025
sentry-7254849206,7265844194
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#248846Expense reports created from incoming emails now use the employee's company instead of the linked user's default company. This prevents valid emailed expenses from being blocked when users work across a parent company and branch.
Original PR description
**Steps to reproduce:** - Install Expenses - Activate "Incoming Emails" in the settings - Configure the expense Alias - Configure an "Incoming Mail Server" - Create a Branch for a company - Create a…
**Steps to reproduce:** - Install Expenses - Activate "Incoming Emails" in the settings - Configure the expense Alias - Configure an "Incoming Mail Server" - Create a Branch for a company - Create a User: * Email Address: [an existing email address] * Allowed Companies: [the parent company + the branch company] * Default Company: [the branch company] * User Types: Internal User - Create an employee for the user in the parent company - From the email address, send a PDF to the expense alias **Issue:** The expense is not created in the database due to a UserError: "Incompatible companies on records". **Cause:** When the email is received and treated, the system tries to create an expense. From the email address, it retrieves an employee that is linked to the expense. For the company of the expense, if a user is linked to the employee, it takes the default company of the user. Otherwise, it takes the company of the employee. In this case, the company set on the expense is the default company of the user (i.e. the branch company) and the employee set on the expense belongs to the parent company ; which triggers the UserError during the company check. **Solution:** Always use the company of the employee, even if there is a user linked to the employee. opw-5346809 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#253313 Forward-Port-Of: odoo/odoo#249415
Customers booking appointments on a German website could hit an access error when paying fully with a gift card and automatic invoicing was enabled. The change keeps the checkout confirmation flow working while preserving the intended accounting behavior for stock valuation records.
Original PR description
**Steps to reproduce:** - Install `l10n_de, website_sale and appointment` modules. - Create a website for the `DE company` and make it default. (simply place it first in the sequence.) - From…
**Steps to reproduce:** - Install `l10n_de, website_sale and appointment` modules. - Create a website for the `DE company` and make it default. (simply place it first in the sequence.) - From settings enable `Automatic Invoice` and `Discounts, Loyalty & Gift Card`. - Go to the appointment module and create a new appointment for the DE company. - In the options tab, enable `Up-front payment` and publish it. - Create a new gift card program and generate a gift card to test (should cover the entire cost of the appointment booking). - Now go to an Incognito tab, go to the appointment, and book the currently created appointment. When confirming the order, use the gift card and then confirm the order. **Issue:** - When you confirm the order, an access error occurs. **Root cause:** - Since automatic invoicing is enabled, at [1] the method `get_product_accounts` is called for the DE company. Inside this method at [2], self.sudo(False) is used, which removes the elevated access rights. As a result, the `Public product template` record rule is triggered, and because the public user cannot access the product, an AccessError is raised. - In this [commit], we can see that `sudo(False)` was added when coming from the stock flow because the stock valuation layer was being created as sudo. **Solution:** - Instead of adding `sudo(False)` in **l10n_de**, we can apply `sudo(False)` at the point where the **stock valuation layer** is created using `sudo()`. [1]https://github.com/odoo/odoo/blob/9d5d5c08f950d32bf61c2186181e8eddad9036da/addons/account/models/account_move_line.py#L603-L604 [2]https://github.com/odoo/odoo/blob/9d5d5c08f950d32bf61c2186181e8eddad9036da/addons/l10n_de/models/datev.py#L20 [commit]: https://github.com/odoo/odoo/pull/236931/changes/0919774791bb998954b870798c88843f999f2de4 opw-5483472 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#253131 Forward-Port-Of: odoo/odoo#246920
This fix ensures landed costs on subcontracted receipts are accounted correctly when some received items have already been delivered. It prevents inventory and expense values from being overstated or missing, improving financial accuracy for subcontracted manufacturing flows.
Original PR description
…lues landed cost sbc **Problem:** account move line created from a landed cost on a subcontracted receipt do not take into account already out quantity. **Steps to reproduce:** - create a tracked…
…lues landed cost sbc **Problem:** account move line created from a landed cost on a subcontracted receipt do not take into account already out quantity. **Steps to reproduce:** - create a tracked product with avco auto category - create a subcontracted bom for this product with no comp - create and confirm a PO for 10 unit of this product with the same partner as the subcontractor of the bom - validate the receipt - create and validate a delivery for 4 unit of your product - navigate to inventory/operations/adjustments/landed costs - create a new landed cost - select the receipt from the PO - add a landed cost of 10$ and validate - select the valuation smart button - a 6$ svl was created (which is correct because 6 out 10 products of the receipt are still in stock) - click on the book widget to open the account move view **Current behavior:** Only two account move lines were created both with a value of 10 One crediting sotck interim received On debiting stock valuation **Expected behavior:** 4 extra account move lines (all with a value of 4) should have been created to compensate the out quantity like it is the case for non subcontracted product. One debiting stock interim delivered One crediting stock valuation One debiting expenses One crediting stock interim delivered **Cause of the issue:** _is_in() will return false for the move of a subcontracted receipt https://github.com/odoo/odoo/blob/7462ec423e8b106a5175a71ac009176d16cdf225/addons/stock_landed_costs/models/stock_landed_cost.py#L185 This is wanted and happens because _should_be_valued() will return true when called on the subcontracted location https://github.com/odoo/odoo/blob/7462ec423e8b106a5175a71ac009176d16cdf225/addons/stock_account/models/stock_move.py#L129 As a consequence, qty_out stays 0 https://github.com/odoo/odoo/blob/7462ec423e8b106a5175a71ac009176d16cdf225/addons/stock_landed_costs/models/stock_landed_cost.py#L185-L186 and we do not append the values for the extra amls inside _create_account_move_line() https://github.com/odoo/odoo/blob/7462ec423e8b106a5175a71ac009176d16cdf225/addons/stock_landed_costs/models/stock_landed_cost.py#L465-L466 **fix:** if we make sure the the adjustment line is linked to the move of the MO instead of the move of the receipt, this problem does not happen because _is_in() returns true for the move of the MO. Also in this case we don't need _get_stock_valuation_layer_ids() which was introduced by this PR https://github.com/odoo/odoo/pull/166107 to solve the same issue. That is because the move used in button_validate is the move linked to the adjustment line, which will, after this fix, be the one of the MO, so we can directly take its stock valuation layers. opw-5723126 Forward-Port-Of: odoo/odoo#252903 Forward-Port-Of: odoo/odoo#248231
This change restores the long-standing behavior where related stock moves can be grouped into the same warehouse transfer. It reduces extra operational work, such as handling or printing multiple separate pickings for the same supply chain.
Original PR description
This reverts [1]. Let's quote the commit: > - `Observation`: the next transfers for both receipts are merged into a single > transfer, even though both receipts were created manually and not generated > from any common source document like PO/SO. The above behavior was and is the expected one for years and should not suddenly change on stable. Even the tests were protecting the cases but [1] have changed the `assert`. Commit [1] quickly leads to the creation of tickets. For instance, in the mentioned OPW, where the user resupplies a warehouse from another one: he now has several pickings for the same supply chain, which lead to extra work (e.g., printing all the pickings) [1] https://github.com/odoo/odoo/commit/840b42fd2365a652e53d607f38ac78ccb8dd63dc OPW-6011532 Forward-Port-Of: odoo/odoo#253054
Odoo now maps the 'qt (US)' unit of measure to a valid international unit code when creating electronic invoices. This prevents Peppol invoice validation failures for businesses that sell or bill items measured in US quarts.
Original PR description
Currently, the Odoo UoM 'qt (US)' is converted to 'QT', which is not valid anymore. Based on investigation, this was originally set to QT following this link: https://unece.org/fileadmin/DAM/cefact/recommendations/rec20/rec20_rev3_Annex2e.pdf But this document seems dated from 2005. Step to reproduce: - Create an invoice with a line with 'qt (US)' as UoM - Try to send the invoice to Peppol - You will get a validation error: "[BR-CL-23]-Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21" Also removed the link to unece.org since the link is no longer valid. opw-5961476 Forward-Port-Of: odoo/odoo#252803 Forward-Port-Of: odoo/odoo#252174
This fixes an issue where monthly time off accruals could be skipped after the annual carry-over reset. Employees and HR teams will now see the correct leave balance progression from month to month.
Original PR description
steps to reproduce: ------------------- 1. Install Time Off 2. Go to Configuration > Accrual Plans 3. Create an accrual plan: * Set the accrued gain time to "At the start of the accrual period" * Set…
steps to reproduce: ------------------- 1. Install Time Off 2. Go to Configuration > Accrual Plans 3. Create an accrual plan: * Set the accrued gain time to "At the start of the accrual period" * Set the carry-over time to "At the start of the year" 4. Create a milestone: * Set the number of accrued days to 1 * Set the accrual frequency to "monthly" and the carry over to "None.Accrued time reset to 0" 5. Go to Management > Allocations 6. Create an allocation: * Set the start date to 2025-01-01 * Set the accrual plan to the one created above 7. Use future allocations to check accruals current behavior: ----------------- - On 2026-01-01 --> accrued days = 1 (correct) - On 2026-02-01 --> accrued days = 1 (should be 2) - On 2026-03-01 --> accrued days = 2 (delayed accrual, off by one month) cause of the issue: ------------------- Commit 30c7011 introduced a condition that accrues time off on the carry over date: https://github.com/odoo/odoo/blob/1416aad902a97ce56aaecc2aadc4dd9f7814ee53/addons/hr_holidays/models/hr_leave_allocation.py#L559 This incorrectly evaluates accruals across the carry over period instead of restricting to the current month, causing February accruals to be skipped. **Reason February accruals are skipped:** https://github.com/odoo/odoo/blob/dcb072f675c5630327d27d785b86e1ec8e2d442d/addons/hr_holidays/models/hr_leave_allocation.py#L559-L561 https://github.com/odoo/odoo/blob/dcb072f675c5630327d27d785b86e1ec8e2d442d/addons/hr_holidays/models/hr_leave_allocation.py#L541-L544 * After January, the last_executed_carryover_date is set to 2026-01-01. * Therefore, February uses last_executed_carryover_date = 2026-01-01. * The condition evaluates as true for February: ```python3 last_executed_carryover_date <= allocation.nextcall <= carryover_period_end 2026-01-01 <= 2026-02-01 <= 2026-02-01 ``` As a result, the February accrual is skipped. **Why it works correctly in March:** * After February, the last_executed_carryover_date is updated to 2027-01-01. * March now uses this updated date: ```python3 last_executed_carryover_date <= allocation.nextcall <= carryover_period_end 2027-01-01 <= 2026-03-01 <= 2027-02-01 ``` The condition is not satisfied, so accruals are processed correctly. solution: ---------- Add a condition to check if the loop has already run for the current carryover period. This ensures the system avoids applying the carryover twice, allowing subsequent accruals to process as expected. opw-5020834 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#253153 Forward-Port-Of: odoo/odoo#227646
When an approved time off request is deleted, refused, or cancelled, its related timesheet entries are now removed correctly. This prevents leftover or duplicate timesheet hours, especially when public holidays overlap with employee leave.
Original PR description
BUG 1: ----------- **Steps to reproduce:** 1. Install Time Off and Timesheets with demo data. 2. Create a time off for an employee and approve it. 3. Check the related timesheet entry for that…
BUG 1: ----------- **Steps to reproduce:** 1. Install Time Off and Timesheets with demo data. 2. Create a time off for an employee and approve it. 3. Check the related timesheet entry for that employee. 4. Delete the approved time off. 5. Check the timesheet entries again. **Issue:** The timesheet entry remains even after the related time off record is deleted. **Cause:** Following commit 944c11e, admins can delete [approved time off ](https://github.com/odoo/odoo/blob/f6cf0d067e5f30e2b22ea513071cd7c5e3d9f44c/addons/hr_holidays/models/hr_leave.py#L956-L959)records. The relationship between the leave and the analytic line (timesheet) did not have a deletion policy defined. When the leave was [unlinked](https://github.com/odoo/odoo/blob/f6cf0d067e5f30e2b22ea513071cd7c5e3d9f44c/addons/hr_holidays/models/hr_leave.py#L961-L964), the analytic line remained without its parent reference. **Solution:** Explicitly remove related timesheet entries before deleting the leave record. BUG 2: ----------- Currently, refusing/cancelling a time off record can lead to orphan timesheets/duplicated hours (16h instead of 8h) if a public holiday exists on the same day. **Root cause:** The issue comes from this write method: https://github.com/odoo/odoo/blob/79ff1d63caed2c1058aa338947b9af90ebb6cd20/addons/project_timesheet_holidays/models/hr_leave.py#L128-L130 The method first unlinks the holiday_id from the timesheets and then attempts to delete them. However, once the holiday_id is set to False, the timesheets are no longer linked to the leave. As a result, leave.timesheet_ids becomes empty, and nothing is deleted. This leads to orphan timesheet records. When the leave is later refused or cancelled, a new public holiday timesheet entry is generated (if applicable), resulting in duplicated timesheet entries for the same day. **Steps to reproduce:** 1. Create a time off for one day and validate it (8h timesheet generated). 2. Create a public holiday for the same day. 3. Observe that leave duration becomes 0, but the timesheet remains. 4. Refuse or cancel the time off. 5. Observe two timesheet entries for the same day (16h total). opw-5384428 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#247155
This fix prevents Odoo from creating unnecessary warehouse transfers when a returned kit product on a sales order is reduced to zero quantity. It improves accuracy for businesses using multi-step deliveries, avoiding confusing duplicate pickings and extra warehouse work.
Original PR description
Backport of 0432a982dfea6e68419f137502c018c0a9241181 ### Steps to reproduce: 1. In the settings enable: Multi-steps route 2. Put your warehouse in 2-step deliveries 3. Create a kit product: - With…
Backport of 0432a982dfea6e68419f137502c018c0a9241181 ### Steps to reproduce: 1. In the settings enable: Multi-steps route 2. Put your warehouse in 2-step deliveries 3. Create a kit product: - With one component - There is one component in the stock 4. Create and confirm a SO with 1 x K 5. Process the pick and ship 6. Return the delivery 7. Set the sol qty to 0 ### > Two unexpected pickings are created to put the kit in output Decreasing the sol quantity to 0 will call the `_action_launch_stock_rule` in order to create and run procurements related to that quantity change. However, the quantity currently handled by other procurements is determined here by the `_compute_kit_quantities`: https://github.com/odoo/odoo/blob/87e176ad76c9d7b87cd622ae38a8b9a62813b1cb/addons/sale_stock/models/sale_order_line.py#L388 https://github.com/odoo/odoo/blob/87e176ad76c9d7b87cd622ae38a8b9a62813b1cb/addons/sale_mrp/models/sale_order_line.py#L154-L166 https://github.com/odoo/odoo/blob/87e176ad76c9d7b87cd622ae38a8b9a62813b1cb/addons/mrp/models/stock_move.py#L578-L580 Now, the issue is that `_compute_kit_quantities` does not handle move chains properly, as all delivery moves contribute to the `incoming_qty` and all return moves contribute to the `outgoing_qty`. This results in an `incoming_qty` of 1 (for the pick) + 1 (for the ship) and an `outgoing_qty` of 1 (for the 1-step return), that is a `qty_processed` of 1. As a result, the procurement will be generated for a quantity of `0 - 1` (rather than 0): https://github.com/odoo/odoo/blob/87e176ad76c9d7b87cd622ae38a8b9a62813b1cb/addons/sale_stock/models/sale_order_line.py#L388-L402 which leads to the unexpected picking creations. opw-6006543 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#253061
Manufacturing orders that automatically create backorders could record the same labour cost twice, inflating accounting entries. This fix ensures each workorder time entry is posted only once, keeping labour costs accurate during partial production flows.
Original PR description
When the Manufacturing operation type is configured with Create Backorder = Always, `mrp.production.pre_button_mark_done()` re-enters `button_mark_done()` in the same server execution path. This can trigger `_post_labour()` twice, causing two posted “<MO> - Labour” journal entries and doubling labour costs in Accounting. Labour costs should be posted only once per workorder time entry. Ensure labour is only posted once by skipping workorder times that were already linked to an accounting entry. This prevents a second execution from creating a duplicate labour journal entry. A test was added to cover the partial production flow when backorder is set to “Always.” Related ticket: opw-5931754 --- Forward-Port-Of: odoo/odoo#250237
This fix prevents registered Peppol company identifiers from being recalculated when VAT details are changed. It helps avoid invoice refusals caused by mismatched Peppol data and reduces the need for companies to unregister and register again.
Original PR description
To reproduce: - Register your company on Peppol (even sender). - Change the VAT on your company (will be done if you choose your document layout). - Send an invoice on the network - => The invoice…
To reproduce: - Register your company on Peppol (even sender). - Change the VAT on your company (will be done if you choose your document layout). - Send an invoice on the network - => The invoice will be refused by the AP. The issue is that the EAS and Endpoint are automatically re-computed when you write on the VAT. It causes issues as the UBL will be filled with the values on the partner, resulting in a non-synchronised SBD and UBL, which will result in all new invoices to be in error. Their only solution would be to revert back the EAS/Endpoint on the partner, which will sometimes be blocked meaning they have to un-register->re-register Also adapt the query count, as we need to do a search in each compute. opw-5923552 opw-5924552 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#252902 Forward-Port-Of: odoo/odoo#249017
This update fixes a bug where employee skills weren't automatically added to appraisals created by the system's automated scheduling process. The fix ensures that skills are correctly copied to new appraisals, regardless of how they're initially created, improving appraisal accuracy and data consistency. This resolves a previous issue impacting appraisal reporting.
Original PR description
Steps to reproduce: ------------------------------------- 1. Install `hr_appraisal_skills` module 2. Create a new employee and assign at least one skill to the employee 3. Set the Next Appraisal Date…
Steps to reproduce: ------------------------------------- 1. Install `hr_appraisal_skills` module 2. Create a new employee and assign at least one skill to the employee 3. Set the Next Appraisal Date to today 4. Go to Scheduled Actions > Appraisal: Run employee appraisal > Run Manually 5. Open the newly created appraisal for the employee Observation: ------------------------------------- In the Skills tab, the employee's skills are not populated even though the appraisal is already in the confirmed stage Issue: ------------------------------------- When the cron `_run_employee_appraisal_plans` creates an appraisal, it is created directly in `pending` state via `create()`. The skill-copying logic only lived in the `write()` override, which triggers on state transitions from 'new' to 'pending'. Since `create()` bypasses `write()`, Employee skills were never copied to cron-created appraisals https://github.com/odoo/enterprise/blob/451dce92a087086fc3d5d5f610626312f32bcd13/hr_appraisal_skills/models/hr_skills.py#L12-L15 Solution: ------------------------------------- Add a `create()` override to call `_copy_skills_when_confirmed` when an appraisal is created directly in the `pending` state, ensuring employee skills are properly copied. opw-5491433 Forward-Port-Of: odoo/enterprise#110013 Forward-Port-Of: odoo/enterprise#107760
This update fixes an issue where the MPS wasn't accurately reflecting demand for dependent components. Previously, the system defaulted to the oldest BoM, regardless of user-defined preferences. Now, the system uses the BoM selected by the user within the MPS, ensuring correct component demand calculations and updates.
Original PR description
## Issue: When computing the product tree, the system would use `_bom_find` to find the BoM. However, it's possible to have multiple BoM for the same product, and the user should have chosen which…
## Issue:
When computing the product tree, the system would use `_bom_find` to find the BoM. However, it's possible to have multiple BoM for the same product, and the user should have chosen which BoM he wants to use. `_bom_find` ignores the user configuration in MPS, and simply select the first (oldest) BoM in the list. This means that the components in the MPS would not be correctly updated.
---
## How to reproduce:
https://github.com/user-attachments/assets/c7e6f4d4-332a-4e2b-a40a-1b831daeb6c8
- Create Products FNS & CMP
- Create BoM for FNS without bom line (V1)
- Create BoM for FNS with CMP in bom lines (V2)
- Add FNS to MPS using bom V2
- Set Forecast Qty of FNS to 10
- => Indirect Demand Qty for CMP is not shown (because it's 0)
---
## Test Result without fix:
```
2026-03-05 15:00:24,601 52396 INFO oes_test_18.0 odoo.addons.mrp_mps.tests.test_mrp_mps: Starting TestMpsMps.test_indirect_multiple_boms ...
2026-03-05 15:00:24,742 52396 INFO oes_test_18.0 odoo.addons.mrp_mps.tests.test_mrp_mps: ======================================================================
2026-03-05 15:00:24,742 52396 ERROR oes_test_18.0 odoo.addons.mrp_mps.tests.test_mrp_mps: FAIL: TestMpsMps.test_indirect_multiple_boms
Traceback (most recent call last):
File "/home/odoo/Odoo/src/18.0/enterprise/mrp_mps/tests/test_mrp_mps.py", line 1556, in test_indirect_multiple_boms
self.assertEqual(forecast_cmp['forecast_ids'][0]['indirect_demand_qty'], 10)
AssertionError: 0.0 != 10
```
---
OPW-5979738
Forward-Port-Of: odoo/enterprise#109693This update resolves conflicts in transaction states related to Fiskaly processing in the German POS system. By isolating transactions to specific terminals and intelligently handling errors, the system now correctly manages transaction states, preventing data inconsistencies and ensuring accurate financial reporting. This improves the reliability of the POS system for our German customers.
Original PR description
Changes:
- cancelActiveTransactions: use the TSS-scoped endpoint
/tss/{tss_id}/tx and filter results by client_id so only orphaned
transactions from this terminal are cancelled, never those from
other POS sessions sharing the same TSS
- transactionCall: on non-retryable errors (400 revision conflict or
terminal state mismatch), call _handleTransactionStateConflict which
GETs the actual transaction state and recovers:
- Cancelling already CANCELLED → silent success
- Finishing already FINISHED → return existing tx data
- Finishing a CANCELLED tx → create a fresh transaction and finish it
- handleFiskalyCancellation: correctly reset transactionState to
inactive on the uiState after cancellation
opw-5972708
Forward-Port-Of: odoo/enterprise#109996This update resolves a bug where the barcode scanning app incorrectly identified products when using barcodes that include product prices (starting with '23'). The fix mirrors the functionality in the Point of Sale app, ensuring accurate product recognition for these common barcode formats. This improves the reliability of inventory management.
Original PR description
Issue ----- Barcode app doesn't match products when using price-embedded barcodes. Steps to reproduce ----- - Use default nomenclature (so price embedded barcodes are 23...) - Create a product with barcode 2355555000004 - Go to barcode and scan 2355555009502 > The product isn't recognised Cause ----- There is no logic in place to handle such barcodes, but it can be added to mimic how it works in POS. https://github.com/odoo/odoo/blob/0fe2023dc57b6cc02bd399d3c8fc5d6c8ed6e833/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js#L212 ----- Ticket: opw-5901412 Forward-Port-Of: odoo/enterprise#110034 Forward-Port-Of: odoo/enterprise#109627
This update fixes an issue where bank statement reconciliation in foreign currency journals incorrectly converted amounts. When reconciling batch payments, the system now uses the correct payment currency, ensuring accurate balance calculations and reporting. This improves the reliability of financial reconciliation processes.
Original PR description
When reconciling a batch payment in a foreign currency journal where payments do not have outstanding accounts, the resulting bank statement lines could use the wrong currency for balance conversion. Steps to reproduce: - Create a journal in a foreign currency (e.g., CHF) - Create two invoices in company currency (e.g., EUR) - Pay both invoices using the foreign journal - Create a batch payment for these payments. - Reconcile a bank statement line against this batch payment. Issue: Reconciliation make use of the payments amount in the wrong currency. Analysis: During the reconciliation of a batch payment, the system creates new amls from the payment values. However, the currency of the computed amount should be the source payment currency, and not the invoice line currency. opw-5887218 Forward-Port-Of: odoo/enterprise#108745
This update corrects a bug where customers could set subscription start dates to 'false', leading to incorrect invoicing. The change prevents users from removing the start date, ensuring subscriptions are properly billed moving forward. This resolves a potential revenue issue and maintains accurate subscription tracking.
Original PR description
**Issue** Some customers were removing the `start_date` of subscriptions, leading to the subscription being considered free on the next invoicing. While there are legitimate use cases to edit the `start_date` of a running subscription, it should probably not be removed. opw-5325303 Forward-Port-Of: odoo/enterprise#104925
This update ensures Odoo invoices sent to the AFIP web service (ARCA) comply with their strict requirements for numeric fields like price and quantity. By limiting these values to a maximum of 3 decimal places, we prevent invoice rejections and maintain accurate accounting. This change aligns with the AFIP specifications and ensures consistent data formatting.
Original PR description
… request ARCA requires numeric fields such as unit price and quantity to have a maximum of 12 integer digits and 6 decimal places. If these fields are sent with more than 6 decimals, AFIP rejects…
… request ARCA requires numeric fields such as unit price and quantity to have a maximum of 12 integer digits and 6 decimal places. If these fields are sent with more than 6 decimals, AFIP rejects the invoice with errors like: `Code 1814: Campo Cmp.Items.Pro_precio_uni invalido. El valor debe tener 12 enteros y 6 decimales como máximo.` To ensure compliance, values are formatted before sending the request to ARCA. **Precision rationale** ARCA WS documentation mentions 4 decimal places, while the WS error message itself refers to 6 decimals, and in practice the service accepts up to 6 decimals without rejection. In this implementation, we intentionally use 2 decimal places. The reason is consistency with the rest of the monetary amounts in the invoice: line totals, invoice total, taxes, and related amounts are all rounded to 2 decimals, even in cases where the documentation allows higher precision (e.g., 3 decimals). Before the changes in rounding precision, the stable version already rounded values according to line rounding. In real-world accounting scenarios, the vast majority of use cases operate with 2 decimal places. Keeping this behavior ensures consistency across calculations and avoids discrepancies caused by mixed rounding strategies. For a stable release, this was considered the safest and most predictable option, even though the WS technically allows higher precision. Stable version changes are covered in the following commits: https://github.com/odoo/odoo/pull/243987/changes/8a21ec45f9d72a7c80d9c1f8398fe01e298ae775 https://github.com/odoo/odoo/pull/246347/changes/79ceeed707ef274f19a04e741f6cb8ac60c44321 <img width="780" height="435" alt="image" src="https://github.com/user-attachments/assets/9f25a0e8-b9d2-4ad2-bbcf-e988c7f8a4c9" /> [WSFEX - Manual de desarrollador](https://www.afip.gob.ar/ws/WSFEX/WSFEX-Manualparaeldesarrollador.pdf) Forward-Port-Of: odoo/enterprise#106509
This update resolves a memory issue in the account asset module that occurred when processing large numbers of accounting records. The fix uses a more efficient method to calculate depreciation, reducing the amount of data stored in memory and preventing the system from crashing. This ensures smoother operation for businesses with extensive asset records.
Original PR description
The previous compute method loaded all moves records into memory, which caused an out-of-memory issue for large number of record. Replaced the logic with read_group aggregation to perform the…
The previous compute method loaded all moves records into memory, which caused an out-of-memory issue for large number of record. Replaced the logic with read_group aggregation to perform the calculation using sql and reduce memory usage.
Note: the issue is faced during 16.0 version too but as 16.0 is no more supported for bug fix. So, doing it from 17.0 version.
```
Traceback (most recent call last):
File "/tmp/tmpkvo8a96w/migrations/base/tests/test_mock_crawl.py", line 333, in crawl_menu
self.mock_action(action_vals)
File "/tmp/tmpkvo8a96w/migrations/base/tests/test_mock_crawl.py", line 346, in mock_action
return self.mock_act_window(action)
File "/tmp/tmpkvo8a96w/migrations/base/tests/test_mock_crawl.py", line 506, in mock_act_window
mock_method(model, view, fields_list, domain, group_by)
File "/tmp/tmpkvo8a96w/migrations/base/tests/test_mock_crawl.py", line 657, in mock_view_tree
self.mock_web_search_read(model, view, [domain], fields_list)
File "/tmp/tmpkvo8a96w/migrations/base/tests/test_mock_crawl.py", line 691, in mock_web_search_read
data = model.search_read(domain=domain, fields=fields_list, limit=80, order=filter_order(model))
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 5074, in search_read
result = records.read(fields, **read_kwargs)
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 3038, in read
return self._read_format(fnames=fields, load=load)
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 3219, in _read_format
vals[name] = convert(record[name], record, use_name_get)
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 6007, in __getitem__
return self._fields[key].__get__(self, self.env.registry[self._name])
File "/home/odoo/src/odoo/16.0/odoo/fields.py", line 1222, in __get__
self.compute_value(recs)
File "/home/odoo/src/odoo/16.0/odoo/fields.py", line 1404, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/16.0/addons/mail/models/mail_thread.py", line 403, in _compute_field_value
return super()._compute_field_value(field)
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 4276, in _compute_field_value
fields.determine(field.compute, self)
File "/home/odoo/src/odoo/16.0/odoo/fields.py", line 98, in determine
return needle(*args)
File "/home/odoo/src/enterprise/16.0/account_asset/models/account_asset.py", line 293, in _compute_value_residual
posted_depreciation_moves = record.depreciation_move_ids.filtered(lambda mv: mv.state == 'posted')
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 5496, in filtered
return self.browse([rec.id for rec in self if func(rec)])
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 5496, in <listcomp>
return self.browse([rec.id for rec in self if func(rec)])
File "/home/odoo/src/enterprise/16.0/account_asset/models/account_asset.py", line 293, in <lambda>
posted_depreciation_moves = record.depreciation_move_ids.filtered(lambda mv: mv.state == 'posted')
File "/home/odoo/src/odoo/16.0/odoo/fields.py", line 1187, in __get__
recs._fetch_field(self)
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 3245, in _fetch_field
self._read(fnames)
File "/home/odoo/src/odoo/16.0/odoo/models.py", line 3351, in _read
self.env.cache.insert_missing(fetched, field, values)
File "/home/odoo/src/odoo/16.0/odoo/api.py", line 1123, in insert_missing
field_cache.setdefault(id_, val)
MemoryError
```
opw-5921410
upg-3891767
Forward-Port-Of: odoo/enterprise#109008This update ensures that our UY CFE (electronic invoice) validation process correctly accesses company-specific data, resolving a previous issue where users without specific permissions wouldn't be able to validate CFEs. Adding `sudo()` access guarantees accurate data retrieval, preventing validation errors and improving the reliability of this key functionality. This change is a critical fix for our Uruguay clients.
Original PR description
This pull request makes a small update to the `_ucfe_inbox` method in `l10n_uy_edi_document.py` to ensure that company-specific fields are always accessed with the appropriate permissions. This is achieved by using the `sudo()` method when retrieving the `l10n_uy_edi_ucfe_commerce_code` and `l10n_uy_edi_ucfe_terminal_code` fields from the `company` record. * Ensured that `l10n_uy_edi_ucfe_commerce_code` and `l10n_uy_edi_ucfe_terminal_code` fields are accessed with elevated permissions by calling `company.sudo()` in the `_ucfe_inbox` method (`l10n_uy_edi_document.py`). Without this fix, if the user doesn't belong to group "base system", it won't be able to validate CFEs, receiving the following message: <img width="1272" height="400" alt="image" src="https://github.com/user-attachments/assets/ec4223fb-5b96-4a3e-babf-2f6a35ecd123" /> Forward-Port-Of: odoo/enterprise#105918