Wednesday, July 8, 2026
33 changes · saas-19.2
Enhancements to existing features
Hong Kong payroll data updates will now include the relevant salary rule files. This helps keep standard, non-customized payroll rules up to date automatically, reducing manual maintenance for users.
Original PR description
Currently, the "Payroll: Update data" cron doesn't work for HK payroll as we never set up the _get_data_files_to_update. We can set up the list of data files to keep up to date to better support our users by automatically keeping non-edited salary rules up to date. task-6360339 Forward-Port-Of: odoo/enterprise#122777
This update speeds up the creation of backorders when validating receipts with many operations. It also prevents database memory issues by reducing the size of the conditions sent to PostgreSQL, making large stock operations complete more reliably and much faster.
Original PR description
## The Problem Validating receipts with a large number of operations by creating a backorder was timing out due to $O(N*M)$ filtering inside `_get_qty_received_without_self`, where N is the number of…
## The Problem Validating receipts with a large number of operations by creating a backorder was timing out due to $O(N*M)$ filtering inside `_get_qty_received_without_self`, where N is the number of moves in the picking, and M is the number of moves being processed for valuation. This happened while computing price units for moves one by one: each move filtered all picking moves to find the ones with the same `product_id`. Another issue was a PostgreSQL `"memory exhausted"` error caused by generating a large number of OR'ed conditions, equal to the number of processed moves. ## The Solution The massive filtering was fixed by filtering moves of the `purchase_line` instead of the `picking`, which is typically associated with only a few moves. This is still correct as the loop just after already ignores moves that don't have the same `purchase_line_id` of `self` anyways. The PostgreSQL error was fixed by grouping moves by `location_dest_id` and generating one condition per location using an `in` clause, which is typically much smaller than generating one condition per move. ## Benchmark Benchmark on a customer database, validating a receipt with 10k+ operations by creating a backorder: ```text Time: timeout -> 6 min ``` OPW-6272667 Forward-Port-Of: odoo/odoo#273555 Forward-Port-Of: odoo/odoo#269350
This update makes the tax supply date available for German accounting entries. It helps businesses record the correct tax timing more easily and brings Germany in line with other localizations that already support this field.
Original PR description
Forward-Port-Of: odoo/odoo#272461
Resolved issues and error corrections
Manufacturing shopfloor users can now see real employee profile pictures instead of generic placeholders when viewing or changing operators. This makes it easier for operators to identify themselves and colleagues during shopfloor work without requiring HR-level access.
Original PR description
Currently when a shopfloor operator modifies the operator, operator images are not visible and only place holder images are visible. ## Steps to produce: - Install Manufacturing with demo data -…
Currently when a shopfloor operator modifies the operator, operator images are not visible and only place holder images are visible. ## Steps to produce: - Install Manufacturing with demo data - Login as Marc Demo - Open shopfloor ## Observed Behavior: Operator is unable to see their own profile picture and when you press the Edit Operator the operator is not able to see their as well as other operators images as well. ## Root cause: This issue occurs because by default an operator's user does not have access right for `Employees` in Human Resources section as they are not an HR officer nor an administrator. So when the user tries to access the image url that is meant for users with those access rights (HR officer / Administrator) at [1] and at [2]. The system returns a placeholder image instead of the employee image since the operator does not have access to hr employee model but does have access to hr employee public as per this [commit](https://github.com/odoo/odoo/commit/c9ca3761464413327d2beb697553a3ccd7eef4d1) [1]- https://github.com/odoo/enterprise/blob/5fe2fb44f4c8a938390343f7cdc90674cbf09391/mrp_workorder/static/src/mrp_display/dialog/mrp_employee_dialog.js#L16 [2]- https://github.com/odoo/enterprise/blob/5fe2fb44f4c8a938390343f7cdc90674cbf09391/mrp_workorder/static/src/mrp_display/employees_panel.xml#L9-L10 ## Solution: Allowing operators to see images can be done by modifying the links to use the public model instead of the private model. This will allow operators see other operators based on images and should improve user experience. | Before | |--------| | <img width="1865" height="844" alt="image" src="https://github.com/user-attachments/assets/c28f4209-0a60-486a-bfec-2a4db39cd946" /> | | <img width="995" height="899" alt="image" src="https://github.com/user-attachments/assets/7541edd9-6cc9-42d1-b5c5-39e0740d9a93" /> | | After | |--------| | <img width="1881" height="903" alt="image" src="https://github.com/user-attachments/assets/df3f8357-5cc2-4688-83dd-9a009fd56957" /> | | <img width="1221" height="862" alt="image" src="https://github.com/user-attachments/assets/c9d72b07-e1ad-43e4-85f5-00d8f1db9095" /> | opw-6321989 Forward-Port-Of: odoo/enterprise#121751
This fixes an error that could stop Belgian CODA bank statement imports when the file included a specific type of summary block. Businesses using Belgian bank feeds can now import these CODA files without interruption.
Original PR description
### Issue: After the fix in commit (https://github.com/odoo/enterprise/commit/3ef8ae7a6b8eb362e18c74dfab9aadce792b5dc2), importing a CODA file containing a type 4 block raises a traceback ### Cause: That commit introduced `communication_struct_by_ref_move`, which iterates over all lines and accesses `line['communication_struct']` Type 4 lines are not assigned a `communication_struct` value by the parser in `_get_coda_file_statements` Accessing the key directly raises a `KeyError` in `_get_coda_final_statements` in `communication_struct_by_ref_move` ### Steps to reproduce: - Install `l10n_be_coda` - Switch to the BE company - Create a Bank Journal with account `BE33737018595246` - Go to the Accounting Dashboard and import a CODA file containing a type 4 block (Like the one on the ticket) Before the fix, a traceback is raised on import opw-6363148 Forward-Port-Of: odoo/enterprise#123222
Sales orders now consistently block recurring products when no subscription plan is set, including when products are added from the catalog view. This prevents invalid subscription orders from being created through an alternate workflow and keeps the user experience aligned across entry points.
Original PR description
Steps to reproduce: --------------------------------------- 1. Install Subscription Module 2. Create and Confirm SO with no recurring plan and a non-recurring product 3. Add a recurring product >…
Steps to reproduce: --------------------------------------- 1. Install Subscription Module 2. Create and Confirm SO with no recurring plan and a non-recurring product 3. Add a recurring product > Save SO > Observe the User Error 4. Now add the same recurring product through Catalog View Observation: --------------------------------------- No User Error raised stating 'You cannot save a sale order with recurring product and no subscription plan.' Issue: --------------------------------------- When you manually add a line and click 'Save', the constraint (`_constraint_subscription_plan`) is triggered and raised `UserError` https://github.com/odoo/enterprise/blob/434d88960abb5e424fdc1106fc93935d328bff78/sale_subscription/models/sale_order.py#L176-L177 When you add a product via the catalog view, it calls `_update_order_line_info` which directly creates/updates order lines, Which do not trigger the python constraint. https://github.com/odoo/odoo/blob/ef9772bba1515bdaf5410c3af5a3e395f562d513/addons/sale/models/sale_order.py#L1926-L1933 Solution: --------------------------------------- Two private helpers are introduced: * `_is_exempt_from_subscription_plan_check`: single source of truth for all exempt states (draft, cancelled, upsell, and legacy upgrade orders). * `_check_recurring_plan_mismatch`: raises a `UserError` when the order has or will have a recurring product but no subscription plan, reusing the exemption helper so both call sites stay in sync. `_constraint_subscription_plan` is refactored to delegate to these helpers, and `_update_order_line_info` is overridden to call `_check_recurring_plan_mismatch` before the catalog update is applied, ensuring consistent validation across both entry points. opw-6194865 Forward-Port-Of: odoo/enterprise#122799 Forward-Port-Of: odoo/enterprise#117879
Lazada order syncing now avoids changing ordered quantities based on individual item statuses. This prevents sync failures when an item was already delivered and later appears canceled, keeping order imports more reliable for merchants.
Original PR description
Lazada stores order statuses at the item level. When an item is canceled, we mirrored this by decreasing the ordered quantity on the sale order line. But if the item was already delivered, decreasing the quantity below the delivered amount is forbidden and raises a `UserError`, which aborts the whole order sync:
```python
File ".../sale_stock/models/sale_order_line.py", line 420, in _update_line_quantity
raise UserError(_('The ordered quantity of a sale order line cannot be decreased below the amount already delivered. [...]'))
```
In practice, item-level statuses only differ from the order status in exceptional cases. Stop syncing statuses at the item level and assume the entire order shares a single status, which avoids the quantity decrease and the resulting traceback.
opw-6267730
Forward-Port-Of: odoo/enterprise#122851The timesheet grid now marks public holidays and approved personal time off according to each employee's own working schedule, instead of always using the company default. This helps employees and managers see accurate unavailable days when entering or reviewing timesheets.
Original PR description
Steps to Reproduce --- - Create two different working schedules with different public holidays - Assign employees to specific working schedules - Set company default working schedule to a different…
Steps to Reproduce --- - Create two different working schedules with different public holidays - Assign employees to specific working schedules - Set company default working schedule to a different schedule - Login as employee with specific working schedule - Navigate to Timesheets app -> My Timesheets - Observe public holidays and personal time-off displayed in the timesheet grid Issue --- - The timesheet grid displays unavailable dates (public holidays, weekends) from the company's default working schedule instead of the employee's assigned working schedule. - Personal time-off requests are not reflected as unavailable dates in the timesheet grid. Current Behaviour --- - Public holidays shown are always from the company's default working schedule, ignoring employee-specific working schedule assignments. - Employee's approved time-off requests don't appear as unavailable in the timesheet. Expected Behaviour --- - Public holidays should display based on the employee's assigned working schedule, with company schedule as fallback only when no specific schedule is assigned. - Employee's personal time-off requests should appear as unavailable dates. - This should align with Time Off app behavior. Fix --- - Included employee-specific work interval calculation with personal time-off requests. - Added support for contract-based calendar changes and calendar validity periods. - Implemented proper fallback when valid intervals are not found. task-4997080 Forward-Port-Of: odoo/enterprise#95458
Fixed an issue where scanning a component product barcode on the shopfloor did nothing when that component was tied to a manufacturing operation. Operators can now scan these barcodes and have the correct product action triggered, reducing manual work and production delays.
Original PR description
When scanning a product barcode in the shopfloor, it would not simulate a click on the product if the corresponding BoM line was linked to an operation. Steps to reproduce: ------------------- * Create a BoM for product A with a BoM line for product B and link it to any operation. * Create a WO for product A and confirm it * Open the shopfloor with the WO and scan the barcode of product B > Observation: Nothing happens Why the fix: ------------ We simulate the onClick for the product even if the move is not marked manual_consumption. opw-6268665 Forward-Port-Of: odoo/enterprise#122337
This fixes an issue where website dynamic snippets could point to the wrong filters when modules were installed in a different order. The change ensures the correct database filter is used, helping generated website content display as intended.
Original PR description
Our default dynamic snippets filter ids are set based on the order that we install our modules. This can cause issues if the user installs their modules in a different order. To fix this, we need to update the data-filter-id value to the correct value of the DB. To be able to do this, we also change the regex replacement to use lxml instead since it's much simpler. Lxml part from 799f83575e162eb683cfaebb4eb602ccc1fbe466. Forward-Port-Of: odoo/enterprise#123143 Forward-Port-Of: odoo/enterprise#122671
The Belgian payroll rules no longer apply a special public holiday eligibility condition for time credit contracts because it had no legal basis. This helps ensure payroll calculations follow the correct legal interpretation and avoids incorrect treatment of affected employees.
Original PR description
The specific code related to the eligibility to public holiday for time credit contracts has no legal base. This commit removes it. task-6370653
DHL shipments could fail for customers or warehouses in regions whose province code is stored as a single character, such as Barcelona. The update converts those short region codes into the longer format DHL expects, helping deliveries validate successfully for affected countries.
Original PR description
Steps:
- Install delivery_dhl_rest
- Create a new customer with barcelona as address
- Create a new Delivery
- Set DHL
- Validate de delivery
- Validation error #/customerDetails/receiverDetails/postalAddress/provinceCode: expected minLength: 2, actual: 1
DHL requires `provinceCode` to be at least 2 characters. Several countries in `res.country.state` data use single-character codes (e.g. ES: B, M, A…; AR: C, B, S…; CN: 京, 沪…). This caused API validation errors when shipping from or to addresses in those regions.
Add `PROVINCE_CODE_MAP`, a dict keyed by `(country_ISO2, state_code)`, mapping each offending code to its ISO 3166-2 form (e.g. ('ES', 'B') -> 'ES-B'). Both `_get_consignee_vals` and `_get_shipper_vals` now look up the map before sending `provinceCode`, falling back to the raw code for countries not in the map.
links: https://developer.dhl.com/api-reference/mydhl-api-dhl-express#shipments
opw-6341745
Forward-Port-Of: odoo/enterprise#122138This fixes an issue where some users could receive an access error when opening WhatsApp messages linked to restricted business documents. The change restores the expected behavior while keeping WhatsApp message visibility controlled by existing access rules.
Original PR description
The `body` field on `whatsapp.message` was defined with `related_sudo=False` with the intent of restricting access to messages from restricted records. However, this was never actually providing any…
The `body` field on `whatsapp.message` was defined with `related_sudo=False` with the intent of restricting access to messages from restricted records. However, this was never actually providing any security value because [`mail.message.fetch()`] was overriding it with `self.sudo()` till `v19.1`, meaning the body was always fetched as superuser regardless:
```py
web_search_read() -> search_fetch()
-> fields.py _compute_related()
-> record[self.related_field.name] # triggers fetch of mail.message.body
-> models.py _fetch_field()
-> mail_message.py fetch()
-> self = self.sudo() # sudo hack overrides related_sudo=False silently
```
In `v19.2`, the `fetch()` sudo hack was intentionally removed (see commit odoo/odoo@4727f12d274a0b2d7c455363d189565bd8fb2e7a) as access rights are now cached and can be checked without a performance penalty. This exposed the broken `related_sudo=False` which now causes an `AccessError` when trying to read the body of a `whatsapp.message` whose linked `mail.message` points to a document the current user cannot access (e.g. `purchase.order`).
Access control on `whatsapp.message` is already correctly enforced at the `ir.rule` level:
- Regular users can only see messages they created (`create_uid = user.id`)
- WA Admins can see all messages
We have upgrade requests failing on this issue: TBG-[2765]
[`mail.message.fetch()`]: https://github.com/odoo/odoo/blob/saas-19.1/addons/mail/models/mail_message.py#L812-L819
[2765]: https://upgrade.odoo.com/odoo/tbg/2765?debug=1The Turkish Central Bank exchange rate provider now uses the official selling rate instead of averaging buying and selling rates. This improves accuracy for accounting and import valuation, helping align currency calculations with Turkish customs requirements.
Original PR description
## Short fix summary: The TCMB (Central Bank of Turkey) provider computed the exchange rate as an average of the buying and selling rates (`2 / (ForexBuying + ForexSelling)`). This is inaccurate for real accounting flows and does not follow Turkish customs regulation (Customs Law No. 4458, Art. 30), which requires the Central Bank's selling rate for goods import valuation. This now uses the selling rate (`ForexSelling`) only. task-6227500 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#122770
International Easypost shipments now use the sale order currency for commercial invoices when that information is available. This prevents customers and customs documents from showing the company currency instead of the currency agreed on the order.
Original PR description
Issue ----- When shipping internationally with Easypost, the currency on the commercial invoice does not always match the one of the sale order. Steps to reproduce ----- - Install Easypost - Create a…
Issue ----- When shipping internationally with Easypost, the currency on the commercial invoice does not always match the one of the sale order. Steps to reproduce ----- - Install Easypost - Create a new pricelist using a different currency from the company - Create a SO - Some product with a weight & HS code - Customer must be in another country from company (for commercial invoice) - Use the new pricelist - Add easypost delivery - Confirm SO - Validate linked picking > Commercial invoice uses company currency instead of SO's Cause ----- The currency being sent to Easypost is retrieved from the package in https://github.com/odoo/enterprise/blob/4c540f450d4de8b59b871662123f85ed54cca2a9/delivery_easypost/models/easypost_request.py#L146 The package object is actually created by calling the carrier's `_get_packages_from_picking` method https://github.com/odoo/enterprise/blob/4c540f450d4de8b59b871662123f85ed54cca2a9/delivery_easypost/models/easypost_request.py#L266-L270 Solution ----- We could be fixing this in `stock_delivery` by creating the package with the correct currency when calling `_get_packages_from_picking`. The problem with this approach is that this might negatively affect other carrier services, as discussed in https://github.com/odoo/odoo/pull/268224. Instead, we can apply a band-aid fix to take the currency from the picking's sale in the `delivery_easypost` module, which is the only one where the problem was reported. ----- Ticket: opw-6224883 Forward-Port-Of: odoo/enterprise#123083
Fixed an issue that could cause the Balance Sheet report to crash when analytic accounting was enabled and a custom Studio field linked to journal items existed. Users can now apply analytic account filters to the report without hitting an error, improving reliability for accounting reporting.
Original PR description
Steps to reproduce ================== - Activate Analytic Accounting. - Go to Accounting > Accounting > Reconcile. - Open Studio. - Add a new many2many field. - Set Journal Item as the related model. - Go to Reporting > Balance Sheet. - Select an analytic account. => RecursionError: maximum recursion depth exceeded Cause of the issue ================== Calling `self.env['account.move.line'].fields_get()` will cause a recursion error. `account.report::_prepare_lines_for_analytic_groupby()` calls `account.move.line::_where_calc()` which in turns calls _prepare_lines_for_analytic_groupby again Solution ======== It turns out we don't actually need to retrieve the groupable attribute, thus bypassing the error. opw-6129149 Forward-Port-Of: odoo/enterprise#122244 Forward-Port-Of: odoo/enterprise#116251
The Peru Profit and Loss report now includes Other Operating Income when calculating gross profit and related profit totals. This ensures business results such as operating profit, pre-tax result, and net profit reflect all relevant income correctly.
Original PR description
**Steps to reproduce:** 1. Install `l10n_pe` and switching to the Peru company. 2. Create and post a journal entry with a line on account 7520000 (Other Operating Income). 3. Open the Profit and Loss report (PE). 4. The amount appears correctly under "Other operating income" (`PE_PNL_A_5`). 5. "Gross profit", "Operating profit" , "Result before taxes" and "Net Profit" do not change when this amount is added or removed. **Issue:** The "Other operating income" line is excluded from the Gross Profit calculation, and consequently from Operating Profit and every downstream total in the PE Profit and Loss report. **Why this happens:** Gross Profit (`PE_PNL_A_4`) balance expression uses the aggregation with formula `PE_PNL_A.balance - PE_PNL_A_3.balance`, which doesn't include `PE_PNL_A_5.balance` as a term opw-6283907 Forward-Port-Of: odoo/enterprise#123063
The Project smart button on a confirmed sales order now opens the linked project as expected, even when the order has no sales order lines. This fixes a case where the button was visible but did not respond, improving access to the related project information.
Original PR description
Steps to reproduce: - - Create a sale order. - Link a project using the Project field. - Confirm the sale order. - Click on the Project smart button. Issue: - The Project smart button is displayed since the sale order has a linked project. However, clicking on it does nothing. Cause: - A sale order without order lines can still have projects linked through the project_id field. The action should not assume that no order lines means there are no projects to display. Solution: - Remove the unnecessary order line check and allow the existing logic to open the linked projects. task-6209658 Forward-Port-Of: odoo/odoo#270752
This fix prevents a crash in live chat when a chatbot tries to hand a visitor over to an operator but none are configured. Instead of showing an error, the conversation now ends gracefully, improving reliability for visitors and support teams.
Original PR description
When a live chat channel has no operators configured and the chatbot script ends with a Forward to operator step, triggering that step causes a traceback. Steps to reproduce the error: - Install…
When a live chat channel has no operators configured and the chatbot script ends with a Forward to operator step, triggering that step causes a traceback. Steps to reproduce the error: - Install ``im_livechat`` module with demo data - Go to Live Chat > Configuration > Chatbots > Create a new chatbot > Add script > Step Type: Question > Set answers > Save > Add script > Step Type: Forward to operator > Only If: Set one of the above answers > Save - Go to Live chat > Channel > Click the configure channel on YourWebsite.com > Remove the operators > Save - Go to the chatbot > test > select the configured answer Traceback: ```py StopIteration ``` https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/addons/im_livechat/controllers/chatbot.py#L65-L70 When the chatbot script reaches a Forward to operator step while no operator is configured in the live chat channel, no chatbot message is created. As a result, the generator iterates over an empty iterator, and the ``next()`` call raises a ``StopIteration`` exception, causing a traceback during the conversation. sentry-7435424405 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274615 Forward-Port-Of: odoo/odoo#261726
The Point of Sale receipt now shows the rounded amount actually paid in the “To Pay” section, instead of the unrounded total due. This makes cash-rounded receipts clearer and matches the behavior users expected before, avoiding confusion when the receipt shows a higher amount than what was paid.
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#274176 Forward-Port-Of: odoo/odoo#265298
This fix ensures invoices sent through Nilvera are checked again even when they temporarily show as “Unknown.” That prevents documents from getting stuck in an unfinished state and helps the final status appear correctly once Nilvera completes its processing.
Original PR description
## Short fix summary:
Nilvera reports `Unknown` as a normal, transient `StatusCode` value (their own e-Archive API docs
list the enum as `unknown`/`waiting`/`succeed`/`error`) right after a document is sent, before their
daily batch resolves the final status. But `_cron_nilvera_get_invoice_status`'s search domain only
matches `l10n_tr_nilvera_send_status in ('waiting', 'sent')`, so once an invoice lands on `unknown` it
is never polled again — even after Nilvera later resolves the real status on their side. This adds
`unknown` to that domain so these invoices keep getting polled until Nilvera reports a final status.
task-6328589
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274494
Forward-Port-Of: odoo/odoo#274311This change prevents the timesheet attendance report from failing when custom fields with common names are added to employee records. It makes the database query more specific, so the report keeps working reliably in customized systems.
Original PR description
In `hr_timesheet_attendance_report`, the SQL query was using unqualified columns (e.g. `date` instead of `ts.date`)
It was not an issue in standard, but if a customer adds a `date` or `check_in` column to `hr_employee`, the query becomes ambiguous and fails.
To solve the issue, we explicitly qualify `ts.date` and `hr_attendance.check_in`.
upg-4445460
```python
File "/home/odoo/src/odoo/19.0/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py", line 24, in init
self.env.cr.execute("""CREATE OR REPLACE VIEW %s AS (
File "/home/odoo/src/odoo/19.0/odoo/sql_db.py", line 440, in execute
self._obj.execute(query, params)
psycopg2.errors.AmbiguousColumn: column reference "date" is ambiguous
LINE 44: AND date <= CURRENT_DATE
```
Forward-Port-Of: odoo/odoo#274482
Forward-Port-Of: odoo/odoo#274341This change fixes an issue where some stock items could be lost from the re-reservation process when validating packed products. As a result, the system now correctly keeps track of all affected items and updates pack status consistently after validation.
Original PR description
This reverts commit 5d70f75f1d27577ee4e2121497ce477cfa6cda53. `free_reservation` is called once per move line to validate. The goal is to unlink potential move lines that have the same reservation. After finding them, a force re-reservation is triggered. The idea of the previous commit was to call `check_entire_pack` (caused by the re-reservation) only once and not at each move line `free_reservation`. The issue is the stock move that has been unreserved then re-reserved are lost in the process and only the picking that had at least one move line validated are actually calling `check_entire_pack`. 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#273813 Forward-Port-Of: odoo/odoo#273658
This fix ensures that FIFO cost calculations are accurate when products are tracked by lot and received at different prices. As a result, both the lot’s average cost and the product’s standard price now reflect the real inventory value, avoiding incorrect valuation in stock reporting and product costing.
Original PR description
This PR is needed for the fix of https://github.com/odoo/odoo/pull/272411 **Problem:** lot's standard price are not correct when the product is fifo and move have different values and multiple lots…
This PR is needed for the fix of https://github.com/odoo/odoo/pull/272411 **Problem:** lot's standard price are not correct when the product is fifo and move have different values and multiple lots **Steps to reproduce:** - product fifo tracked and valued by lots - 20 IN @ 100 (all in lot 1) - 10 IN @ 10 (5 in lot 1 and 5 in lot 2) - on the product form click on the lot/serial number smart button and select lot 1 **Current behavior:** the average cost of lot1 is 64 back on the product form the standard price is 55 **Expected behavior:** the average cost of lot 1 should be 20 * 100 (from move1) + 5 * 10 (from move 2) / 25 = 2050 / 25 = 82 the standard price of the product should be 2100 / 30 = 70 **Cause of the issue:** Because the product is fifo, to compute the avg_cost of the lot we call _run_fifo() https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/stock_lot.py#L47 which calls _run_fifo_get_stack() to get the fifo stack specific to this lot. https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L545 Issue 1) run_fifo_get_stack() stores the on hand quantity (for the lot if a lot is given as param) in fifo_stack_size and, as long as there is moves and fifo_stack_size>0, adds move (starting from the last one in date) to the stack and removes the quantity of the move from fifo_stack_size. It then returns the moves stack and the remaning quantity on the first move of the stack (for the rest we know it's the full quantity) https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L612-L618 Inside run_fifo_get_stack(), to do this, because we're only considering the quantities from this specific lot we should only remove the quantity from the move that went in lot, but currently we're removing the quantity from the entire move. https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L615-L618 So, at the first iteration of the while loop (for the move with 10 quantities), instead of doing fifo_stack_size(25) -= 5, we do fifo_stack_size(25) -= 10 The next move is the last one, so it's the one on which remaining_qty_on_first_stack_move will be based on. remaining_qty_on_first_stack_move will be the minimum between the move's quantity and the fifo_stack_size. So because the fifo_stack_size is now wrongfully 15 instead of 20 that's the value that will be returned by _run_fifo_get_stack. So inside run_fifo(), qty_on_first_move will be 15 instead of 20 https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L545 Issue 2) Another issue is that inside _run_fifo when calling _get_valued on the move, we don't use the lot parameter. So we use the entire quantity of the move instead of the quantity specific to the lot. https://github.com/odoo/odoo/blob/b07ff5843ee87741b293d9e67f72a77a2ed2ed88/addons/stock_account/models/product.py#L561-L562 And we use the full value of the move instead of the pro rata of the value for the quantity specific to the lot As a consequence, inside _run_fifo the computation for the fifo_cost will be 15 (because of issue1) * 100 $ [first iteration of the while loop] \+ 10 (because of issue 2) * 10$ [second iteration of the while loop] = 1600$ Instead of 20 *100 + 5 *10$ = 2050$ Therefore the avg_cost of the lot is wrong and the standard price of the product will also be false. side note: those two issues balance each other if the price unit of the moves are the same needed for PR of opw-6311341 Forward-Port-Of: odoo/odoo#273728
When a POS order is changed after it was already sent to the server, adding the online payment option again now updates the server with the latest order total. This ensures customers pay the correct amount instead of an outdated one.
Original PR description
When an online payment line is added, the order is synced to the server so the customer can pay it online. This sync was only performed when the order did not yet exist on the server (string id). As a result, once an order had already been synced, modifying it (e.g. increasing the amount) and adding the online payment line again did not push the new amount to the server. opw-6314690 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272961 Forward-Port-Of: odoo/odoo#271542