Daily updates from Odoo
Tuesday, June 17, 2025
16 changes · 18.0
Enhancements to existing features
Accounting tax summaries can now group lines without taxes under a chosen category, such as exempt tax, instead of leaving them unclassified. This helps produce clearer totals and tax reporting in accounting, sales, and localized electronic invoicing flows.
Original PR description
At the moment, the tax aggregators always give a base line with no tax the `None` grouping key. Sometimes, we want base lines with no tax to be grouped as if they had a particular tax, such as an exempt one. This commit extends the aggregator behaviour so that for base lines with no tax, the grouping function is called with an empty tax_data dict, and the returned grouping key is used to group the base line. Enterprise PR: https://github.com/odoo/enterprise/pull/87223 task-4242065
Employees with flexible working schedules are no longer included in automatic check-out and absence management rules. This prevents attendance settings from incorrectly affecting people whose work hours are intentionally variable.
Original PR description
If the automatic check out and absence managment settings in attendance are set, it will not take into account the employees that have a flexible working schedule. These settings were not compatible with the notion of flexibility in hours. Backport of https://github.com/odoo/odoo/pull/196822 task-4816736
Untaxed invoice lines in Colombian and Mexican electronic invoicing can now be grouped using the appropriate tax category, such as exempt tax, instead of always being treated as uncategorized. This improves the accuracy of tax reporting and electronic invoice data for local compliance needs.
Original PR description
At the moment, the tax aggregators always give a base line with no tax the `None` grouping key. Sometimes, we want base lines with no tax to be grouped as if they had a particular tax, such as an exempt one. This commit extends the aggregator behaviour so that for base lines with no tax, the grouping function is called with an empty tax_data dict, and the returned grouping key is used to group the base line. Community PR: https://github.com/odoo/odoo/pull/213405 task-4242065
Duplicated Indian GST purchase records and related debit or credit notes will no longer carry over the original IRN number. This helps prevent the same official reference from appearing on multiple documents, reducing reporting confusion and compliance risk.
Original PR description
Before this PR: - IRN number was also copied when a record was duplicated or while creating a debit/credit note for purchase document. - This caused the same IRN to appear on multiple records. After this PR: - Now, when a record is duplicated or a debit/credit note is created, the IRN number will be empty to prevent duplication. Task Id: 4854568
Resolved issues and error corrections
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 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
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
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
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
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-4778429