Friday, May 15, 2026
13 changes · saas-19.2
Resolved issues and error corrections
This update fixes an issue where unreconciling payments on recurring invoices would incorrectly generate a new invoice for the following month. The fix adds a context flag to prevent the automatic creation of these duplicate invoices, ensuring accurate invoice generation and reducing potential accounting errors. This improves the reliability of recurring invoice processing.
Original PR description
Issue: Unreconciling a payment in a batch payment from a recurring invoice will cause an invoice for the next recurring period to be generated Steps to reproduce: 1. Create and confirm a monthly…
Issue: Unreconciling a payment in a batch payment from a recurring invoice will cause an invoice for the next recurring period to be generated Steps to reproduce: 1. Create and confirm a monthly recurring invoice 2. Create a payment for the invoice 3. Create a batch payment and add the payment created in step 2 then validate it 4. Create a bank statement line and reconcile it with the batch payment created in step 3 5. Unreconcile the payment from the invoice from the invoice form view 6. Notice that a draft invoice for the next month’s recurring invoice is created Cause: When unreconciling the payment from the invoice via the invoice form view, the method “delete_reconciled_line” is called. In the “account_accountant_batch_payment” override of that method, it will reset the invoice back to draft and repost it. However, when posting a recurring invoice, the default behavior is to create the invoice for the next recurrence period Solution: Adding a new context flag called “skip_recurring_copy” will prevent the next period’s recurring invoice from being generated when invoices are posted through “delete_reconciled_line” opw-6158881 Forward-Port-Of: odoo/odoo#263991
This update fixes an issue where early payment discounts with cash rounding were incorrectly calculating tax amounts in payment journal entries. The change ensures that tax amounts are accurately added when using the 'Modify tax amount' cash rounding strategy, resolving discrepancies in discount calculations. This improves the reliability of financial reporting.
Original PR description
Issue: When creating a payment with an early payment discount, on an invoice using a cash rounding with a strategy of “Modify tax amount”, the early payment discount lines on the payment journal…
Issue: When creating a payment with an early payment discount, on an invoice using a cash rounding with a strategy of “Modify tax amount”, the early payment discount lines on the payment journal entry will be far off from the correct amount Steps to reproduce: 1. Create a sales tax for 8.1% 2. Create a cash rounding record for 0.05 as the rounding precision, “Modify tax amount” as the rounding strategy, and “Nearest” as the rounding method 3. Create a payment term with early payment discount of 2% if paid within 18 days. And reduced tax on early payment. With a due term of 100% 30 days after the invoice date 4. Create an invoice on 1/1 with a subtotal of 339.60 and the tax of 8.1% and add the cash rounding method and payment term created earlier 5. Create a payment for it 9 days later on 1/10 for the full amount after the early payment discount is applied Cause: tax_amounts is grabbing the amount for a certain tax from the last line on the invoice with the same tax_repartition_line_id. Usually there is only one tax line representing a certain tax on an invoice. However, when a cash rounding is applied to the invoice with a strategy of “Modify tax amount”, the cash rounding line that is created will also have the same tax_repartition_line_id. In that case, it will grab the amount on the cash rounding line instead of adding the first tax amount with the cash rounding line amount Solution: In tax_amounts, add to the accumulating value if a tax repartition line id already exists as a key otherwise, insert it into tax_amounts opw-5998497 Forward-Port-Of: odoo/odoo#259025
This fix resolves an issue where Kanban progress bars for date/datetime grouped tasks appeared grey and inaccurate. The problem stemmed from a mismatch in how date/time keys were formatted on the server and client. By standardizing the key formatting process, the progress bars now accurately reflect the state of tasks within each group.
Original PR description
Steps to reproduce 1. Open any kanban view declaring a <progressbar> (e.g. Project > Tasks, or Field Service > My Tasks). 2. Group by a date or datetime field with a granularity (e.g. Creation Date:…
Steps to reproduce 1. Open any kanban view declaring a <progressbar> (e.g. Project > Tasks, or Field Service > My Tasks). 2. Group by a date or datetime field with a granularity (e.g. Creation Date: Year, planned_date_begin:day). 3. Have records spread across several groups with different state values (done, in progress, canceled, ...). 4. Observe the colored progress bar at the top of each column. Issue Every column's progress bar renders as a uniform grey block regardless of the actual state distribution of its records. The effect is visible only when grouping by a date/datetime field; grouping by stage or assignees still works. The kanban relies on two separate RPCs whose keys must match: formatted_read_group (used by web_read_group) returns the columns the client stores, and the client uses each column's formatted groupby value as the lookup key; read_progress_bar returns a dict mapping that same key to per-state counts. For a datetime group by with a time granularity, formatted_read_group goes through _web_read_group_groupby_formatter, which localizes the naive timestamp in the user's timezone and then converts it to UTC before strftime, see https://github.com/odoo/odoo/blob/8d80ed39a2b6250bc5dfbe1f0fe4c84cc1b778ce/addons/web/models/models.py#L698-L726. On the client, serializeDateTime also formats in UTC, see https://github.com/odoo/odoo/blob/8d80ed39a2b6250bc5dfbe1f0fe4c84cc1b778ce/addons/web/static/src/core/l10n/dates.js#L455-L462, so group.serverValue is the UTC-shifted string the server sent, e.g. "2025-01-01 08:00:00" for a Pacific-tz user on a 2025-year group. read_progress_bar called _read_group directly, which returns the raw naive datetime out of date_trunc, see https://github.com/odoo/odoo/blob/8d80ed39a2b6250bc5dfbe1f0fe4c84cc1b778ce/odoo/orm/models.py#L1764, so its dict was keyed by str(datetime(2025, 1, 1, 0, 0)) = "2025-01-01 00:00:00". The UTC-formatted key the client looks up in _pbCounts[groupValue], see https://github.com/odoo/odoo/blob/8d80ed39a2b6250bc5dfbe1f0fe4c84cc1b778ce/addons/web/static/src/views/kanban/progress_bar_hook.js#L69, never matched, per-state counts stayed at zero, and the synthetic "Other" bucket with color "200" (grey) absorbed the whole group.count, see https://github.com/odoo/odoo/blob/8d80ed39a2b6250bc5dfbe1f0fe4c84cc1b778ce/addons/web/static/src/views/kanban/progress_bar_hook.js#L88, hence the uniform grey bar. Non-date groupbys (many2one, selection, boolean) are unaffected because their keys are scalar ids / selection codes that round-trip identically on both sides. The "None" column also works because both sides emit the literal string "False". The regression was introduced by d5a6e97abab04282c7a69093cd790b539a958241 when read_progress_bar was switched from self.read_group(...), whose output was pre-formatted the same way the client received it, to self._read_group(...) Solution Route read_progress_bar through formatted_read_group, the same method web_read_group uses to build groups for the client. Both sides now run through _web_read_group_groupby_formatter, so the keys are produced by the same code path and match by construction for every field type, including date/datetime granularities. formatted_read_group wraps relational and date/datetime groupby values as (id, display_name) / (utc_str, label) tuples, so the adapt helper unwraps the first element to restore the scalar key shape the result dict expects. opw-6148736 Forward-Port-Of: odoo/odoo#261024
This update fixes an issue where the average sale price was incorrectly calculated due to tax inclusion/exclusion. The change ensures the sale average price always uses the net amount (excluding tax) from the invoice line, leading to more accurate reporting and pricing. This improves the reliability of sales data.
Original PR description
The price_unit of a account.move.line can be with or without tax. The sale_avg_price should be either incl. or excl. tax. To ensure the avg price is always excl. tax the price_subtotal can be used. Forward-Port-Of: odoo/odoo#263671 Forward-Port-Of: odoo/odoo#199209
This update resolves an issue where changes made to timesheet data in one Odoo tab weren't consistently reflected in other tabs. The fix ensures that all modifications are saved and synchronized across different windows, improving data accuracy and reliability for users.
Original PR description
Steps to reproduce: - Open Odoo in two tabs - Open the systray in tab 1 - Change some fields - Close the systray to save - Open the systray in tab 2 All fields are not in line in both tabs. This commit, hence, ensures that all data changed in the inline form is saved and consistent across different windows. When switching window, the systray is closed and the data is saved to the local storage. When opening the systray in another tab, the local storage will be accessed to read the latests changes (i.e., the modifications done in the other tab). task-6180394
This update resolves an issue where creating two overtime shifts on a Saturday (set to end at midnight) would trigger an error. The fix addresses a timing discrepancy in how overtime start and end times are calculated, preventing overlapping shifts and ensuring accurate overtime recording.
Original PR description
__ ## Short functional explanation of the error When we create 2 shifts for the same day for an employee, on a non-working day for their schedule. When trying to create the second one after setting…
__ ## Short functional explanation of the error When we create 2 shifts for the same day for an employee, on a non-working day for their schedule. When trying to create the second one after setting the end date to midnight, we get the error: `ValueError: Expected singleton: hr.attendance.overtime.line(2, 3)` ## Reproduction Steps 1. Create an Employee. In the Payroll tab, Make sure they have an active contract. Set their Working Hours to a fixed schedule, where they have saturdays as non-working days. In the Settings tab, set an Overtime Ruleset. 2. Click on the overtime ruleset. Then, for each rule, under Action, set the Work Entry Type To Use as Overtime Hours. 3. Go to Attendances. In Configuration > Settings, under Extra Hours, set the Extra Hours Validation as Approved By Manager. 4. Create an attendance for your Employee on a Saturday, from 12h to 18h. 5. Create a second attendance for your Employee on that same Saturday, from 18h to 00h00. Try to Save. Note: the timezone of your computer, the working schedule and the employee should be set at Brussels time. ### Expected behavior The Overtime is registered. ### Unexpected behavior An error occurs: `ValueError: Expected singleton: hr.attendance.overtime.line(2, 3)` ## Origin of the issue The end time of the overtime is defined as follows: https://github.com/odoo/enterprise/blob/47faff7d6c9da5572e3bad3ff5a55b40c2ba81ac/hr_work_entry_attendance/models/hr_version.py#L54-L56 However, in the case where our shift ends after the computed end of the day (in our case, the end time of the shift is 00:00:00 and the end of the day is set at 23:59:59), it creates some problems. The end time of the overtime is set 1 second too early. Later we compute the start time of the overtime as follows: https://github.com/odoo/enterprise/blob/47faff7d6c9da5572e3bad3ff5a55b40c2ba81ac/hr_work_entry_attendance/models/hr_version.py#L57 Thus, the time start of the overtime is also set one second too early. As our second shift starts right after the first one, after the execution of this code, we will get a second shift that starts before the end of the first one. Then, we add these values in a list: https://github.com/odoo/enterprise/blob/47faff7d6c9da5572e3bad3ff5a55b40c2ba81ac/hr_work_entry_attendance/models/hr_version.py#L59 which will contain overlapping timeframes, and with which we create an Interval: https://github.com/odoo/enterprise/blob/47faff7d6c9da5572e3bad3ff5a55b40c2ba81ac/hr_work_entry_attendance/models/hr_version.py#L60 But when we create an Interval with overlapping timeframes, we obtain only one interval as the timeframes are merged. https://github.com/odoo/enterprise/blob/47faff7d6c9da5572e3bad3ff5a55b40c2ba81ac/hr_work_entry_attendance/models/hr_version.py#L173 As a result, `overtime_intervals` will contain only one time frame with 2 different corresponding overtimes, which causes a singleton error when reaching: https://github.com/odoo/enterprise/blob/47faff7d6c9da5572e3bad3ff5a55b40c2ba81ac/hr_work_entry_attendance/models/hr_version.py#L179 __ opw-6096454 Forward-Port-Of: odoo/enterprise#117094 Forward-Port-Of: odoo/enterprise#114147
This update fixes an issue where dependent taxes weren't correctly recalculated after a base tax was removed from a sales order or invoice. The fix ensures that tax amounts are accurately computed, particularly when 'Affect Base of Subsequent Taxes' is enabled, preventing financial discrepancies. This improves the reliability of tax calculations.
Original PR description
**Steps to reproduce:** * Install the *Accounting* module with French localization (*l10n_fr_account*). * Create a *Sales Tax* with: * A new tax group (e.g., 'Codifab'). * Enable *Affect Base of…
**Steps to reproduce:** * Install the *Accounting* module with French localization (*l10n_fr_account*). * Create a *Sales Tax* with: * A new tax group (e.g., 'Codifab'). * Enable *Affect Base of Subsequent Taxes*. * Create a *Sales Order*: * Add the first tax (with *Affect Base of Subsequent Taxes*). * Then add the second tax (eg VAT tax). * Confirm the *Sales Order*. * Create a *Down Payment Invoice* (percentage-based). * Open the generated invoice and: * Remove the first tax (the one affecting the base). **Observed behavior:** * The amount of the second tax group does not update after removing the first tax, leading to incorrect tax computation. **Cause:** * In `_import_base_line_extra_tax_data`, the condition: `all(str(tax.id) in extra_tax_data['manual_tax_amounts'] for tax in sorted_taxes)` only ensured partial matching of taxes. * This allowed reuse of stale `manual_tax_amounts` when taxes were removed or modified, causing incorrect base values for dependent taxes (e.g., *Affect Base of Subsequent Taxes*). **Fix:** * Update the condition to enforce an exact match between current taxes and cached `manual_tax_amounts` by checking both size and membership. * Prevent reuse of outdated tax data when taxes change, ensuring proper recomputation of dependent taxes. * Align Python logic with the JS implementation for consistency between `account_tax.py` and `account_tax.js`. opw-6063970 Forward-Port-Of: odoo/odoo#264434 Forward-Port-Of: odoo/odoo#259566
This update fixes an issue where pasting content into the composer created excessive nested divs, preventing users from correctly deleting pasted text. The fix also adds necessary plugins to properly handle links within the composer, improving the overall editing experience.
Original PR description
Currently, when pasting content into the composer, we sanitize it by stripping all tags except a few allowed ones, and this creates a lot of nested divs in the pasted content. This prevents the content from being deleted correctly when the user is in the nested divs and presses backspace. For links, we currently missing the plugin that correctly handles them in the composer, this commit adds it and also adds the missing plugin LinkSelectionPlugin and OdooLinkSelectionPlugin for the composer. task-6214020 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix ensures that the departure date is correctly associated with employee versions, addressing an issue where the dismissal date was being used instead. This accurately reflects scenarios like employee notices and prevents incorrect version tracking, improving payroll and HR data accuracy.
Original PR description
__ ## Short functional explanation of the error When we set the departure of an employee. The version is retrieved using the dismissal date. However, employees can work after their dismissal date,…
__ ## Short functional explanation of the error When we set the departure of an employee. The version is retrieved using the dismissal date. However, employees can work after their dismissal date, until their departure (in the case of a notice, for example). Therefore, the departure date should be chosen instead. ## Reproduction Steps 1. Go to Employees and create a new employee. In the Payroll tab, set a start date for their contract. Hit save. 2. This will create a version. You can see it top right, with the contract date. Click on the '+' next to it and set a date later. 3. Click on the cog in the top left and click End of Collaboration. Set an End Reason. Set the Dismissal Date to occur during the first version and the Departure Date to occur during the second version. Then, click Schedule. ### Expected behavior The Departure tab should appear when clicking on the second version, top right. ### Unexpected behavior The departure tab appears on the first version. ## Origin of the issue To select the version on which the departure occurs, we use this line of code: https://github.com/odoo/odoo/blob/be8b1bbad757fda27df579ce36cbc97324f58f62/addons/hr/models/hr_employee_departure.py#L117 `departure_date` should be used instead. __ opw-6079675
This update fixes an issue where event tickets with fixed prices were incorrectly showing a struck-through original price, making them appear as discounts. The change ensures that fixed price rules are displayed accurately, aligning with standard eCommerce behavior and providing a clearer price representation to customers. This improves the user experience and avoids confusion regarding pricing.
Original PR description
Event tickets show a struck-through original price even when a "Fixed Price" pricelist rule is applied, making it incorrectly appear as a discount. This is inconsistent with eCommerce shop behavior.…
Event tickets show a struck-through original price even when a "Fixed Price" pricelist rule is applied, making it incorrectly appear as a discount. This is inconsistent with eCommerce shop behavior. ### Steps to reproduce 1. Create an event with a paid ticket (e.g., 100 EUR). 2. Create a pricelist with a "Fixed Price" rule for that ticket (e.g., 80 EUR). 3. Open the event registration page. 4. The 100 EUR appears struck-through next to 80 EUR. ### Cause Odoo's website only shows a struck-through original price for discount rules, not fixed price rules. By design, a fixed price replaces the original rather than reducing it. However, the event registration page used a simplified check: it compared the final price to the original and assumed any difference was a discount. This ignored the rule type, incorrectly flagging fixed price rules as discounts. ### Fix Rationale A new helper method on the event ticket model now queries the applied pricelist rule to determine if it qualifies as a discount. opw-5993477 Forward-Port-Of: odoo/odoo#264470 Forward-Port-Of: odoo/odoo#263586
This update now allows customers to cancel their Stripe payments directly through the payment terminal, both on the POS system and self-order kiosks. Previously, cancellation was only possible through the POS interface, creating a frustrating experience for customers. This change improves customer satisfaction and streamlines the payment process.
Original PR description
Before this commit, when making a payment on a Stripe terminal, the only way to cancel the payment was from the POS interface. In the self order kiosk, it was impossible to cancel the payment. After this commit, a cancel button will appear on the payment terminal for both POS and kiosk Stripe payments. task-6166789 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#264665 Forward-Port-Of: odoo/odoo#264270
This update ensures that livechat conversations are automatically marked as read when they end, resolving a previous issue where agents saw persistent unread indicators. The change adjusts how the chat window focuses, triggering the read state correctly regardless of whether the composer is visible. This improves agent efficiency and provides a cleaner user experience.
Original PR description
**Description of the issue this PR addresses:** Previously, when a livechat conversation ended, it was never automatically marked as read. The existing `mark_as_read` mechanism depends on the…
**Description of the issue this PR addresses:** Previously, when a livechat conversation ended, it was never automatically marked as read. The existing `mark_as_read` mechanism depends on the composer being focused, but ended livechat conversations hides the composer, and the chat window does not focus the thread automatically (focus only happens on explicit click). This made it impossible for the read state to be triggered through the normal path, leaving agents with persistent unread indicators on closed livechat conversations. **Desired behavior after PR is merged:** - Focus the composer when present. - Focus the conversation otherwise. This ensures the read state is correctly triggered when the conversation is effectively in focus. task-[5900038](https://www.odoo.com/odoo/project/1519/tasks/5900038) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263355 Forward-Port-Of: odoo/odoo#253609
This update corrects a problem where invoices with many items caused rounding errors during discount calculations, leading to validation failures with Mexican tax authorities (SAT). The fix ensures accurate discount distribution across all invoice lines, resolving previously reported errors and improving compliance.
Original PR description
…any lines Fix SAT validation errors CFDI40111 and CFDI40108 that occur when invoices with many lines contain a small negative line, causing per-line discounts to be hidden due to currency precision. opw-6187014 Forward-Port-Of: odoo/enterprise#117375 Forward-Port-Of: odoo/enterprise#117297