Daily updates from Odoo
Tuesday, June 17, 2025
22 changes · 18.0
Resolved issues and error corrections
This fixes cases where Odoo could send duplicate or inconsistently formatted Date headers in web responses, which created unnecessary warnings in server logs. It also standardizes date-related headers for generated editor images, helping caching behave more predictably without changing user-facing features.
Original PR description
Forward-port of https://github.com/odoo/odoo/pull/206505
This fixes an error that occurred when refunding Point of Sale orders containing multiple products valued with FIFO or average cost and processed with the ship later plus invoice option. Businesses can now complete these refunds without disruption, reducing checkout and back-office support issues.
Original PR description
When refunding a PoS order with mulitple lines containing a product with a category that use FIFO/AVCO valuation method, there was a traceback Steps to reproduce: ------------------- * Create a category CAT that use FIFO/AVCO valuation method * Create a product P1 with category CAT * Create a product P2 with category CAT * Create a PoS order with P1 and P2 * Validate the order using the shiplater and invoice option * Refund the order using the shiplater and invoice option > Observation: You get a traceback opw-4848667
This fix ensures Point of Sale buy X get Y promotions correctly increase the number of free items as customers add more qualifying products. It helps businesses apply loyalty rewards accurately at checkout and avoids under-delivering promised promotions.
Original PR description
When activating a loyalty program with a buy X get Y promotion. For example for buy 10 get 3, if you have 10 products in your cart and add 1 more product, it will add a reward for 1 free product. But if you add another product, the reward will stay the same, it will not give 2 free product. Steps to reproduce: ------------------- * Create a buy X get Y loyalty program (10 get 3 free) where X and Y are the same product. * Add 10 products to the cart. * Add 1 more product to the cart, it will add a reward for 1 free product. * Add another product to the cart > Observation: The reward will not change, it will still be 1 free product. Why the fix: ------------ If possible we will try to maximize the reward quantity. That's why we don't do it in cases where multiple rewards are possible. opw-4762083
Manufacturing unbuild operations no longer fail when a component move quantity is reduced. The fix keeps inventory valuation consistent by using the component cost from the original manufacturing order, even if prices changed later.
Original PR description
…nbuild **Problem:** When decreaseing the quantity of the stock move created for the component of the bom when unbuilding, it triggers an error message **Steps to reproduce:** - create a product…
…nbuild **Problem:** When decreaseing the quantity of the stock move created for the component of the bom when unbuilding, it triggers an error message **Steps to reproduce:** - create a product tracked by quantity (the "component product") - in the category field select an avco category - set an on hand quantity of 1 - create another product tracked by quantity (the "final product") - create a bom for this product and select your first product as the component - create a manufacture order for the final product - confirm and produce all - unbuild it and click on the "Unbuilds" smart button - select the line of the manufacture order - click on "product moves" - select the line of the component product - set the quantity to 0 **Current behavior:** an error message appears **Expected behavior:** a stock valuation layer should be created with the unit cost of the component product at the time of the manufacture order **Cause of the issue:** price_unit_map is created to make sure that when unbuilding a non standard final product, the outgoing stock valuation layer created for the final product has the same value as in the MO (the current standard_price could have changed due to POs since the MO happened for instance) https://github.com/odoo/odoo/blob/05cff3b7d866f6bc95c4b32f343ae14a4da946f2/addons/mrp_account/models/stock_move.py#L47-L56 when reducing the quantity of the stock move linked to the component of the unbuild an outgoing stock valuation layer is created and _get_out_svl_vals is triggered, those 2 conditions are true https://github.com/odoo/odoo/blob/05cff3b7d866f6bc95c4b32f343ae14a4da946f2/addons/mrp_account/models/stock_move.py#L55-L56 but the product is not the same as the one of move_finished_ids of the MO (this product is final product of the MO) so the filter result in an empty record set and [0] creates an index out of range error https://github.com/odoo/odoo/blob/05cff3b7d866f6bc95c4b32f343ae14a4da946f2/addons/mrp_account/models/stock_move.py#L49-L51 **Fix:** the unit_cost of the stock valuation layer for the component product created from unbuild is the same as : the unit_cost from the stock valuation layer created from the MO for the component (even if the standard_price changed inbetween). https://github.com/odoo/odoo/blob/4fd9ae6ccd96ac13475e7e5aa9805e6f529cd609/addons/stock_account/models/stock_move.py#L516-L517 https://github.com/odoo/odoo/blob/4fd9ae6ccd96ac13475e7e5aa9805e6f529cd609/addons/stock_account/models/stock_move.py#L51 So to be consistent an ajustement to the the stock move created from the unbuild should create a stock valuation layer which also has the same unit cost opw-4747920
This fix prevents payment status updates from failing when multiple accounting accounts share the same type. It improves reliability for accounting workflows by correctly checking reconciliation status across all relevant accounts.
Original PR description
Issue: Before this commit, sometimes we might have different accounts with the same type, which throws a singleton error Fix: Check if all the accounts are not reconciled oes-4798698
This fix makes the Accounting area check that a menu item exists before including it in results. It prevents unnecessary errors when a menu is unavailable, helping users continue working without interruption.
Original PR description
Check that the menu actually exists before adding it to the results. Also don't raise error if not found. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update makes an automated test for image transformation more stable, reducing random failures in Odoo's validation process. It helps developers get more reliable test results without changing how users interact with the HTML editor.
Original PR description
Purpose of this PR: - The image transformation test was failing non-deterministically on runbot due to bare `expect(...).toHaveCount` calls. This commit replaces them with the `expectElementCount` utility. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents employees with flexible schedules from being marked absent when they had approved leave. It corrects day-end time handling and avoids counting tiny timing differences as work or overtime, improving accuracy in attendance and leave records.
Original PR description
### Steps to reproduce: - Enable Absence Management from general settings - Create a leave for the previous day for a flexible employee - Run the 'Detect Absences for employees' cron job - Navigate…
### Steps to reproduce: - Enable Absence Management from general settings - Create a leave for the previous day for a flexible employee - Run the 'Detect Absences for employees' cron job - Navigate to the attendance gantt view - Notice a negative attendance created for the employee on the day of his leave ### Cause: This is happening because when checking the expected attendances for a flexible employee there is a difference between the end of the attendance interval (e.g. 2025-04-03 00:00:00 UTC) and the of the leave interval (e.g. 2025-04-02 23:59:59.9999 Europe/Burssels). This difference considered to be an expected attendance. Also when checking the attendances to calculate working hours and overtime duration we take the attendance created to cover the absence into account and assume that it is working hours https://github.com/odoo/odoo/blob/6beb3ea82d75513803ae78f5bc71024313938a97/addons/hr_attendance/models/hr_attendance.py#L357-L366 ### Fix: We are now setting the end datetime of the attendance interval to be the end of the day in the user's timezone not in UTC. Also set the end of the leave interval to be the minimum time of the next day instead of the max time in the same day of the leave. Also when calculating the overtime duration for flexible employee we check if it is a very small amount of time. opw-4545023
This fixes downloads for files whose extensions include an underscore, such as Parasolid .x_t files. Odoo will now preserve the original filename instead of adding an extra .txt extension, reducing confusion for users sharing or retrieving documents.
Original PR description
Step to reproduce; - install documents - upload a parasolid file, it has extension `x_t` - download it Observation: if a file is uploaded with file named as `file.x_t`, it will be downloaded as `file.x_t.txt` Issue: currently, the `get_extension` method only allow alphanumeric values for extension, which do not consider extension having '_' https://github.com/odoo/odoo/blob/15fd3f62769b137cb9a0625f47b333424b851a27/odoo/tools/mimetypes.py#L212-L218 when such file is downloaded, `_get_stream_from` appends extra extension to the filename, depending upon its mimetype. https://github.com/odoo/odoo/blob/15fd3f62769b137cb9a0625f47b333424b851a27/odoo/addons/base/models/ir_binary.py#L148-L150 Fix: instead of `isalnum()`, use a regex that allows alphanumeric and underscore in extension. opw-4801263 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Italian electronic invoices now include cash rounding adjustments in the total amount shown in the exported XML. This prevents mismatches between the invoice total and the official XML, supporting compliance with Italian fiscal rules for cash payments.
Original PR description
The Italian Law requires cash payments to be rounded up to 5 cents (art. 13 del D.Lgs. 231/2007, updated in 2018). The exported XML invoice didn't have any cash rounding lines and so the sum was wrong in ImportoTotaleDocumento. This was happening because cash rounding lines (`display_type == 'rounding'`) were excluded during the export function's computation.
This fix makes the function include `rounding` lines into the computation, making the invoice compliant.
Steps:
1. Activate cash rounding management.
2. Create a `0.05E` cash rounding.
3. Create an invoice with a total that is not rounded.
4. Set up the cash rounding on the invoice:
Other information > Cash rounding method
An invoice cash rounding line will be created, the total will be rounded.
5. Generate the XML and download it.
6. The amount in `ImportoTotaleDocumento` tag will not match the total of the invoice.
Task [link](https://www.odoo.com/odoo/project/967/tasks/4816355)
task-4816355Sales users without stock permissions can now access the forecast report from sales documents instead of seeing an access error. They can view the information needed for customer commitments, while stock reservation actions remain restricted to users with the right inventory permissions.
Original PR description
Task: 4640948 Currently, a sales user with no stock permissions can see the little forecast icon and, upon clicking on it, a link to the forecast report. However, trying to access the report results in an "Access Error" message. The forecast report is important for sales users. This commit will allow them to access the report, but without the option to edit stock pickings (reserve/unreserve buttons). --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Returned products that were not part of the original sale are now placed after existing sales order lines instead of appearing in the middle of the order. This keeps sales orders easier to read and avoids confusion when reviewing returns involving replacement or different products.
Original PR description
Issue ----- When returning different products than the ones sold on the SO, the returned products have their sequence set to 10 so they all appear between the first and second sold products. Steps to reproduce ----- - Install both Sale & Stock apps - Create & confirm a sale for 2 different products - Confirm the delivery - Create & validate a return for a third product - Open the SO -> The returned product sol is second in sequence Cause ----- The return SOL are created with no specified sequence value, so they all have the default (10). See [review](https://github.com/odoo/odoo/pull/209091#pullrequestreview-2828126823) for details. ----- Ticket: opw-4564504
Posting an invoice or bill will now only automatically mark it as checked when the related journal is configured to do so. This prevents a user’s existing checked status from being unintentionally cleared during posting, helping preserve review decisions and reduce accounting errors.
Original PR description
Description of the issue/feature this PR addresses: Posting an account move will unconditionally overwrite any value in the `checked` field to the value of the `journal_id.autocheck_on_post` field. I.e. whatever the user has selected in the `checked` will be overwritten. This PR will change the functionality to work as the original comment for the feature states. Current behavior before PR: The `checked` field on an account move is always updated to the value of the `journal_id.autocheck_on_post` field, when posting an account move. Desired behavior after PR is merged: The checked field on an account move is updated to the value of the journal_id.autocheck_on_post, when posting a move, only when the journal_id.autocheck_on_post is `True`. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The slide sharing dialog now explains that email shares may be sent later because messages are queued and processed in batches. This avoids confusing users into thinking the share failed and helps prevent duplicate email sends.
Original PR description
This PR updates the share dialog message shown when a slide is shared via email. Since emails are queued and sent later in batches, the previous message could be misleading and prompt users to send multiple emails unnecessarily. Task-4797324
Manufacturing work order time entered through the work order widget now updates project analytic accounts with the correct labor cost. This prevents misleading gross margin figures caused by near-zero or incorrect cost entries.
Original PR description
…rrect data from MO operation **Problem:** lines of mrp.workcenter.productivity added from the workorder widget do not update the linked analytic account with the right amount **Steps to reproduce:**…
…rrect data from MO operation **Problem:** lines of mrp.workcenter.productivity added from the workorder widget do not update the linked analytic account with the right amount **Steps to reproduce:** - enable "analytic accounting" setting - Unarchive MTO route - Create a new product - In the inventory page check MTO and Manufacture routes - click on the Bill of materials smart button and create a new BOM - add a component and an operation - Create another product, set "product type" as service - in the "create on order" field select "project & task" - create a new quotation with these two products and confirm - click on the "manufacturing" smart button - in the work orders page click on the "open work order" button at the right of the line - click on add a line - set a duration of 30:10 and clik elsewhere on the screen - save - click on the "analytic account" smart button - select the only line and click on the "gross margin" smart button **Current behavior:** There is a single line with an amount of 0.01 **Expected behavior:** The price should reflect the hourly rate of the employee for the task (that's 50.28 with a 100$/hours rate for instance) **Cause of the issue:** There is two issue here. First Issue : When clicking on save on the widget, this will modify the time_ids field of the mrp.workorder this will create a new mrp.workcenter.productivity with the values we wrote. Consequently, on the vals_list when the create method is triggered duration is set to the duration we wrote (so here 30.17) Because of this, when the _compute_duration is the triggered previous_durations will be [30.17] and _create_analytic_entry will be called with a parameter of 30.17 https://github.com/odoo/enterprise/blob/8a21b4f8ee8d24ebbc9ac2eb08696a0de056357f/project_mrp_workorder_account/models/mrp_workcenter_productivity.py#L10-L14 Therefore, inside _create_analytic_entry duration will be 0 and amount will also be 0 This issue does not happen if the duration is set by changing the "real duration" in the "work orders" page of the manufacturing order. This is because when doing it this way, we modify the duration of the mrp.workorder which triggers the set_duration_method which also creates a new mrp.workcenter.productivity but here the _prepare_timeline_vals returns a dictonary without a duration field https://github.com/odoo/odoo/blob/4fe90167266a92c3a5941eac9a6a2084a80056ac/addons/mrp/models/mrp_workorder.py#L352-L353 Second Issue: The line we see with a 0.01 amount is actually created before we save. It's created from the onchange method when we change the duration on the form. The onchange of duration triggers an onchange of start date which gets the value of duration and triggers of recomputation of duration https://github.com/odoo/odoo/blob/4fe90167266a92c3a5941eac9a6a2084a80056ac/addons/mrp/models/mrp_workcenter.py#L522-L531 As a consequence, _compute_duration is called. self.duration is initially not rounded so in our exemple previous duration will be equal to 30.1666666666668 https://github.com/odoo/enterprise/blob/8a21b4f8ee8d24ebbc9ac2eb08696a0de056357f/project_mrp_workorder_account/models/mrp_workcenter_productivity.py#L10-L11 However after the call to super()._compute_duration(), self.duration will be rounded thanks to the use of convert to duration inside the super method https://github.com/odoo/odoo/blob/4fe90167266a92c3a5941eac9a6a2084a80056ac/addons/mrp/models/mrp_workcenter.py#L472 So when doing the difference here inside _create_analytic_entry https://github.com/odoo/enterprise/blob/8a21b4f8ee8d24ebbc9ac2eb08696a0de056357f/project_mrp_workorder_account/models/mrp_workcenter_productivity.py#L38 duration will be 5.555555555556424e-05 and amount will be -0.005555555555556424 which results in the creation of this line with an amount of 0.01 **fix:** Calling the creation of account.analytic.line from the compute method leads to other problems : For instance, if a line is added in the "open work order" widget and we change the end date but change our mind and don't save the line, the duration will have been computed and a new account analytic line will have been created Whereas calling the method _create_analytic_entries from create() and write() ensures it's only called when the model is saved opw-4715690
This fix prevents payroll pivot reports from failing when a custom salary rule field without a country code is added through Studio. Businesses can now use these custom payroll measures in reporting without encountering an error.
Original PR description
**Steps to reproduce:** 1. Install hr_payroll. 2. Create a salary rule (with a structure where country_id is null) and set `View on Payroll Reporting?` is checked for a custom field (x_l10n_xx_%). 3. Add the custom field to the payroll report's pivot view using Studio. **Issue:** - The pivot view fails to open, resulting in a traceback. ``` UncaughtPromiseError > OwlError Uncaught Promise > The following error occurred in onWillStart: "Cannot read properties of undefined (reading 'string')" ``` **Cause:** - Custom field that not start with `l10n_<country_code>` or `x_l10n_<country_code>` are removed, causing undefined fields in the pivot view. https://github.com/odoo/enterprise/blob/ccb5ed4779db4bdf6944f43eb2e4a7aa978b9272/hr_payroll/static/src/js/hr_payroll_report_pivot_model.js#L13-L15 **Solution:** - Allow to add custom fields created from salary rules without a country code to be used as custom measures in the pivot view. opw-4688376
Changing a sales quotation template from one with a recurring plan to one without now correctly clears the recurring plan field. This prevents outdated subscription plan information from remaining on sale orders and helps users avoid incorrect subscription setup.
Original PR description
**Issue:** - In sale order, first select the quotation template with a recurring plan, then, change the quotation template to without a recurring plan. After changing the quotation template, the…
**Issue:** - In sale order, first select the quotation template with a recurring plan, then, change the quotation template to without a recurring plan. After changing the quotation template, the 'Recurring Plan' (`plan_id`) field still shows the value from the previously selected template. This happens because the `plan_id` is not reset when the new template has no recurring plan. - In version 17, this case was handled in the else part and set [default value](https://github.com/odoo/enterprise/blob/17.0/sale_subscription/models/sale_order.py#L490) (company_id.subscription_default_plan_id), but in version 18.0, the field was [removed](https://github.com/odoo/enterprise/pull/55355/files). **Steps to reproduce:** - Install the Sales and Subscriptions modules. - Create two quotation templates — one with a recurring plan and one without. - Create a Sale Order and first select the quotation template 'with a recurring plan'. - Then, change the quotation template to 'without a recurring plan'. **Solution:** - Reset `plan_id` to `None` before computing the new value to ensure accurate computation. If the selected quotation template has a recurring plan, it will be assigned; otherwise, the plan_id field will remain empty. **Runbot v18.0 (without fix)**   OPW: [4845478](https://www.odoo.com/odoo/70/tasks/4845478)
Creating a payslip for Belgian employees could fail when a work entry covered a long period, such as a full month. This fix ensures those long work entries are handled correctly so payroll processing can continue without errors.
Original PR description
There is a bug in the module that made the creation of payslip triggers an error. Steps to reproduce it :
1) Use a Belgian company and an employee with a contract;
2) Create a work entry for a whole month (from 05/01/2025 09:00:00 to 06/01/2025 17:00:00 for example);
3) Try to create a payslip for this employee.
Error :
File "/data/build/enterprise/l10n_be_hr_payroll/models/hr_contract.py", line 517, in _get_work_hours_split_half
work_data[('half', work_entry.work_entry_type_id.id)] += dt.days * 24 + dt.seconds / 3600 # Number of hours
TypeError: 'float' object is not iterable
[opw-4752109](www.odoo.com/odoo/project/49/tasks/4752109)Users can now upload a file to a document request successfully the first time they try. This removes a frustrating retry step and makes document collection smoother and more reliable.
Original PR description
Before this commit, when one tries to upload a file to a document request for the first time it does nothing.
This is caused by an async memoize call which causes the file to be cleared between the memoize rpc call and the file processing after that call... After that first call the user can upload a new file without issue but it's still annoying.
This commit fix this issue by storing the file in a variable before doing the memoize rpc call.
Task-4778429Hong Kong payroll tax report exports no longer fail when a married employee has no spouse name recorded. This allows businesses to generate required IRD reports from confirmed payslips without needing unnecessary placeholder spouse details.
Original PR description
Steps: - Create an employee with marital status is married - Keep the spouse name empty - Try to export ird reports with a confirmed payslip Current behavior: - Error raised Expected behavior: - Should be able to export the report even spouse name is empty
This fix prevents users from clearing or editing currency settings in Studio when doing so would cause an error. It keeps monetary fields stable while users customize list views, reducing failed saves and support issues.
Original PR description
Currently an error occurs when the user tries to remove the currency on any monetary field through web studio.
Steps to replicate:
- Open any list view with web_studio.
- Drag and drop a monetary field and remove the currency field and save.
Error:
`TypeError: Argument must be bytes or unicode, got 'NoneType'`
This error occurs because the `attrs` dictionary contains `{'name': None}`, and `etree.Element()` doesn't accept attributes with None values, causing it to raise a `TypeError`.
This commit fixes the issue by making the field required so that user cant remove the value from currency field and hence preventing error at line [1].
[1]-https://github.com/odoo/enterprise/blob/a41cca06bc1e25614390f9207c937566557350a0/web_studio/controllers/main.py#L928
sentry-4244801243Importing bank statement lines from CSV now preserves an included statement reference instead of always creating a new bank statement. This prevents duplicate statements and keeps imported reconciliation data tied to the correct existing statement.
Original PR description
…id during import In the override of execute_import of account_bank_statement_import_csv.py a new account.bank.statement was always created even if the import contained statement_id. Steps to reproduce: - Export an account.bank.statement.line in bank rec with the statement_id included. - Import it back. The statement_id of the imported lines is a new one. opw-4753864