Daily updates from Odoo
Tuesday, August 18, 2026
61 changes
12 changes
Enhancements to existing features
Odoo now checks an institution's maximum allowed payment amount before initiating single or batch payments through Odoo/Odoofin. This helps prevent failed payment attempts when a bank or provider enforces transaction limits.
Original PR description
Before trying to initiate payments through Odoo/Odoofin, we should check that the total amount for the (batch) payment does not exceed the maximum payment amount allowed by the institution (some Powens institutions introduced that limit). task-6310729 Forward-Port-Of: odoo/enterprise#127110 Forward-Port-Of: odoo/enterprise#121513
The Timesheet Assistant now shows clearer Helpdesk ticket suggestions using the actual ticket name, making entries easier to recognize. When users add a suggested ticket, the timesheet form is filled with the right ticket automatically, reducing manual entry and avoiding incorrect grouping of unrelated events.
Original PR description
Before this commit, the Timesheet Assistant displayed static labels for Helpdesk Tickets. Furthermore, when a user clicked "Add" on a ticket suggestion, the Timesheet Inline Form did not auto-populate the ticket name, as the source ID was lost during the grouping phase. Task: 6320652 Forward-Port-Of: odoo/enterprise#121662
Resolved issues and error corrections
Starting a timesheet timer from a task now correctly carries over the task's sales order item, so the resulting timesheet remains billable as expected. This prevents missing billing information and ensures the Billable option is visible immediately when relevant.
Original PR description
**Problem:** Starting the timer on a task that is linked to a sale order item produces a timesheet that is not linked to it, and the Billable toggle is missing from the timer. **Steps to reproduce:**…
**Problem:** Starting the timer on a task that is linked to a sale order item produces a timesheet that is not linked to it, and the Billable toggle is missing from the timer. **Steps to reproduce:** 1. Install Timesheets and Sales 2. Open a task whose Sales Order Item is set 3. Start the timer from the Timesheets systray 4. Save it and open the resulting timesheet **Current behavior:** The Sales Order Item is empty. The Billable toggle only appears after removing and re-adding the task in the timer. **Expected behavior:** The timer is billable on the task's sale order item as soon as it is opened. **Cause of the issue:** `_get_timesheet_pre_filled_form_data` returns only `project_id` and `task_id`. The timer form merges that pre-fill over `timesheet_default_values`, which `lazy_session_info` computes once per session from `account.analytic.line.new()` - a record with no project and no task, so `so_line`, `allow_billable` and `has_available_so` are all `False` in it. Because the pre-fill carries none of those keys, they keep the task-independent session values: the timesheet stays unlinked from the sale order item, and the Billable toggle stays hidden since it is displayed from `has_available_so`. **Fix:** The pre-fill endpoint is the only place that knows which task the timer is being opened on, so it is where the task-dependent values have to be resolved. Reading them off a new timesheet built with that project and task keeps the endpoint generic - it returns whatever `_get_aw_timesheet_fields_specification` declares, so the sale fields stay owned by sale_timesheet_enterprise rather than being named in timesheet_grid. Dropping the session defaults instead was rejected: they are also the only source of `date`, `user_id` and `company_id` for the timer record, and removing them makes saving fail on the required Date field. opw-6423577 Forward-Port-Of: odoo/enterprise#127800
Fixed an issue where payroll correction batches could be created under the wrong active company when correcting a paid payslip for an employee in another company. Correction batches now use the company of the payslips they contain and avoid mixing payslips from different companies, improving accuracy in multi-company payroll processing.
Original PR description
Steps to reproduce: - Have an employee in company B, with a paid payslip - Log in with company A active (company B allowed but not selected) - Open the employee's paid payslip and click "Correct" The refund and correction payslips are computed in company B (their company follows the employee), but the pay run created for them by the wizard has no explicit company and falls back to the active company A. Set the pay run's company from the payslips it contains, and group the payslips by company as well as by structure so that a batch never mixes companies. task-6428755 Forward-Port-Of: odoo/enterprise#126064
Odoo now correctly recognizes valid Brazilian NF-e invoice XML files even when the main invoice tag has no attributes. This prevents eligible vendor bills from being silently skipped during import, improving reliability for Brazilian accounting workflows.
Original PR description
### Issue before this commit: Certain valid Brazilian NF-e (electronic invoice) XML files fail to import because the system silently ignores them during the initial EDI recognition phase. ### Steps to reproduce the issue: 1. Download Accounting and l10n_br_edi 2. Go to Vendor > Bills 3. Try to import both xmls in the ticket 4. One of the two will not be imported correctly ### Cause of the issue: https://github.com/odoo/enterprise/blob/3ed1721b702555e96c9774969927f6517e855704/l10n_br_edi/models/account_move.py#L819-L827 This function relies on a strict byte string search for b"<NFe " while it's also correct if the tag is only `<NFe>`. ### Reason to introduce the fix: To make the initial NF-e file recognition more robust and compliant with standard XML namespace rules, ensuring Odoo successfully processes all valid Brazilian invoices regardless of attribute formatting. opw-6402843 Forward-Port-Of: odoo/enterprise#127851 Forward-Port-Of: odoo/enterprise#126881
The timesheet menu has been adjusted to display more cleanly on mobile devices. This makes it easier for users to view and enter timesheet information from phones without dealing with a clunky interface.
Original PR description
In this commit, we improve the display of the timesheet systray in mobile view as it was clunky. task-6332208 Forward-Port-Of: odoo/enterprise#122491
Completed kitchen orders are now removed from the Order Status Display instead of reappearing under "Almost There." This keeps customer-facing order progress accurate and avoids confusion after an order has finished preparation.
Original PR description
Steps to reproduce ------------------ - Open a PoS session, the Kitchen Display, and the Order Status Display. - Create an order and send it to the Kitchen. - Process the order through all the stages until it reaches the final (completed) stage. Issue ----- - Once the order reaches the completed stage, it reappears in the "Almost There" section of the Order Status Display instead of being removed. Cause ----- - The applied domain fetched all kitchen orders, including completed ones. The display logic only distinguishes whether an order is in the second last preparation stage than show it as "Ready", all other orders are shown as "Almost There". As a result, completed orders fall back into the "Almost There" section.. Fix --- - Updated the domain to fetch only active kitchen orders and exclude completed ones from the Order Status Display. Task: 6394865
The payment registration flow now uses the bank account selected in the wizard when none is set directly on the invoice or move. This helps ensure customers can see and use the “Pay Now” option when a valid bank account is available, reducing payment friction.
Original PR description
Before this commit, it could happens that when we don't put a partner_bank_id on the move it self. The "pay now" button was never displayed. It was because partner_bank_ids was only checking the value from the move and not the wizard. Now if there is no partner_bank_id in the wizard.batches then we look at the value in the wizard and we use it. task-6374002 Forward-Port-Of: odoo/enterprise#123759
International DPD shipments through Sendcloud now include the recipient tax number in the required customs details. This prevents delivery validation failures and adds safer fallback values for required customs fields when creating new deliveries.
Original PR description
### This is a revision of #119399 which had to be reverted. Original issue ----- Deliveries cannot be validated using DPD with Sendcloud, users get an error. Steps to reproduce ----- - Set up…
### This is a revision of #119399 which had to be reverted. Original issue ----- Deliveries cannot be validated using DPD with Sendcloud, users get an error. Steps to reproduce ----- - Set up Sendcloud DPD - Create a SO - Interntional customer - Some VAT number - Some product - Add sendcloud delivery - Confirm SO - Validate the linked picking > Error: “The receiver VAT number is missing; please provide it to continue” Issue's cause ----- Tax numbers should be included in the `customs_information` field of the request as per the API https://sendcloud.dev/api/v2/parcels/create-a-parcel-or-parcels#body-one-of-0-parcel-customs-information-tax-numbers For the `vat_label` field, we have to force the language to English in the context because the field is translated by default, but sendcloud only accepts the english names (eg French "TVA" is not accepted, expected value is "VAT"). https://github.com/odoo/odoo/blob/d1d1610332a1596d026fb0a42ec236d1a79c71cc/odoo/addons/base/models/res_country.py#L75 Revert cause ----- The vat_label field is marked for translation (translate=True) https://github.com/odoo/odoo/blob/d1d1610332a1596d026fb0a42ec236d1a79c71cc/odoo/addons/base/models/res_country.py#L75 So if the user has the DB in french for example, we are sending "TVA" instead of "VAT" in the name field. Other issues ----- - We need to provide an actual fallback for `customs_invoice_nr`. As it stands, if we create a new delivery it cannot be validated because Sendcloud doesn't accept for the field to be empty. - Same for `name`, we need to provide an actual fallback. ----- Ticket: opw-6250860 Forward-Port-Of: odoo/enterprise#127425 Forward-Port-Of: odoo/enterprise#124245
Fixes a timer issue where task or ticket timers could jump between values, show negative seconds, or track the wrong duration after repeated stop, start, and page reload cycles. This improves confidence in recorded work time by ensuring only the active timer on screen updates the displayed elapsed time.
Original PR description
A running task/ticket timer can sometimes stutter, show negative values, and track the wrong elapsed time. ### Steps to reproduce On a record with a timesheet timer (for example, a Field Service…
A running task/ticket timer can sometimes stutter, show negative values, and track the wrong elapsed time. ### Steps to reproduce On a record with a timesheet timer (for example, a Field Service task): 1. Start the timer and let it run for about 15-20 seconds. 2. Stop it and confirm the dialog. 3. Start it again. This creates a new `timer_start`. 4. Reload the page. The timer starts jumping every second between two different values. As it keeps running, it can even show negative values such as `00:00:-57`. If the problem does not appear right away, repeat steps 2-4 a few times. It usually shows up after a few stop/start/reload cycles. ### Cause The timer shown in the button bar is the `timer_start_field` widget. It starts a `setInterval` that updates a shared `TimerReactive` object once per second. While a form is loading, Odoo renders it several times in a row (for example: a first render, another when the chatter is loaded, and another when the record data comes back from the server). Rendering a form builds all of its fields to produce the display, so each of these renders creates its own `timer_start_field`. Odoo keeps and mounts only the render that ends up on screen; the earlier ones are thrown away before being mounted. The interval is started while the field renders, from the record observer set up in `setup`, before the field is mounted. So the fields that are later thrown away also start an interval. Those intervals keep running for the rest of the session. Each one updates the same shared `TimerReactive` object using the `timer_start` it was created with. As long as every instance has the same `timer_start`, they all write the same value and the problem stays hidden. After the timer is stopped and started again, the old instances keep the old `timer_start` while the mounted one uses the new one. Every second they overwrite each other's value, so the timer jumps between two different elapsed times. When the instance with the newer `timer_start` writes right after one with an older start, it tries to show a smaller elapsed time than what is already there, and the subtraction in `TimerReactive` produces a negative number of seconds. ### Fix Move the per-second timer update into a `useEffect`. The effect only runs after the field is mounted, and Owl automatically cleans it up when the field is unmounted or when `timer_start` or `timer_pause` change. This means fields that are destroyed before they are mounted never start an interval, so only the mounted field updates the shared timer. `onRecordChange` no longer starts or stops the interval. It only updates the displayed timer value to match the current record. opw-6209405 Forward-Port-Of: odoo/enterprise#128071 Forward-Port-Of: odoo/enterprise#126242
Belgian payroll now correctly splits a new long sickness leave after the first 30 days, even when it occurs during a relapse window but is not marked as related to the previous illness. This helps ensure payroll calculations classify sick leave consistently and avoid overcounting the initial sick leave category.
Original PR description
Steps to reproduce: - Create a STO for an employee of more than 30 days -> this period is split in 30 days STO and x days SGS. - Within the relapse period, create a second STO of more than 30 days and leave the relapse field empty (which is fine if the second STO is not related to the first sickness) -> the period should be split after the first 30 days just like the first STO, but it remains an STO for the whole duration. task-6296152
Users can now add AI-generated images to Documents without encountering a crash. The fix also improves handling for attachments that are not linked to another business record, making document creation more reliable in similar cases.
Original PR description
Bug === Since 907f1029e8abb38c1ddd3fa9493fa866322706ca a crash happen when we generate image with AI, and try to add it to documents. Task-6453430
15 changes
Resolved issues and error corrections
Fixes an issue where task or ticket timers could jump between values, show negative time, or record the wrong elapsed time after repeated stop/start and page reload cycles. This helps users trust the displayed timer and improves timesheet accuracy.
Original PR description
A running task/ticket timer can sometimes stutter, show negative values, and track the wrong elapsed time. ### Steps to reproduce On a record with a timesheet timer (for example, a Field Service…
A running task/ticket timer can sometimes stutter, show negative values, and track the wrong elapsed time. ### Steps to reproduce On a record with a timesheet timer (for example, a Field Service task): 1. Start the timer and let it run for about 15-20 seconds. 2. Stop it and confirm the dialog. 3. Start it again. This creates a new `timer_start`. 4. Reload the page. The timer starts jumping every second between two different values. As it keeps running, it can even show negative values such as `00:00:-57`. If the problem does not appear right away, repeat steps 2-4 a few times. It usually shows up after a few stop/start/reload cycles. ### Cause The timer shown in the button bar is the `timer_start_field` widget. It starts a `setInterval` that updates a shared `TimerReactive` object once per second. While a form is loading, Odoo renders it several times in a row (for example: a first render, another when the chatter is loaded, and another when the record data comes back from the server). Rendering a form builds all of its fields to produce the display, so each of these renders creates its own `timer_start_field`. Odoo keeps and mounts only the render that ends up on screen; the earlier ones are thrown away before being mounted. The interval is started while the field renders, from the record observer set up in `setup`, before the field is mounted. So the fields that are later thrown away also start an interval. Those intervals keep running for the rest of the session. Each one updates the same shared `TimerReactive` object using the `timer_start` it was created with. As long as every instance has the same `timer_start`, they all write the same value and the problem stays hidden. After the timer is stopped and started again, the old instances keep the old `timer_start` while the mounted one uses the new one. Every second they overwrite each other's value, so the timer jumps between two different elapsed times. When the instance with the newer `timer_start` writes right after one with an older start, it tries to show a smaller elapsed time than what is already there, and the subtraction in `TimerReactive` produces a negative number of seconds. ### Fix Move the per-second timer update into a `useEffect`. The effect only runs after the field is mounted, and Owl automatically cleans it up when the field is unmounted or when `timer_start` or `timer_pause` change. This means fields that are destroyed before they are mounted never start an interval, so only the mounted field updates the shared timer. `onRecordChange` no longer starts or stops the interval. It only updates the displayed timer value to match the current record. opw-6209405 Forward-Port-Of: odoo/enterprise#126242
The payment flow now checks the bank account selected in the payment wizard when it is missing from the invoice or move. This helps ensure customers can see and use the “Pay Now” button in more situations, reducing failed or delayed online payment attempts.
Original PR description
Before this commit, it could happens that when we don't put a partner_bank_id on the move it self. The "pay now" button was never displayed. It was because partner_bank_ids was only checking the value from the move and not the wizard. Now if there is no partner_bank_id in the wizard.batches then we look at the value in the wizard and we use it. task-6374002
Odoo now correctly recognizes Brazilian NF-e invoice XML files even when the main invoice tag has no attributes. This prevents valid vendor bill files from being silently skipped during import, helping accounting teams process supplier invoices more reliably.
Original PR description
### Issue before this commit: Certain valid Brazilian NF-e (electronic invoice) XML files fail to import because the system silently ignores them during the initial EDI recognition phase. ### Steps to reproduce the issue: 1. Download Accounting and l10n_br_edi 2. Go to Vendor > Bills 3. Try to import both xmls in the ticket 4. One of the two will not be imported correctly ### Cause of the issue: https://github.com/odoo/enterprise/blob/3ed1721b702555e96c9774969927f6517e855704/l10n_br_edi/models/account_move.py#L819-L827 This function relies on a strict byte string search for b"<NFe " while it's also correct if the tag is only `<NFe>`. ### Reason to introduce the fix: To make the initial NF-e file recognition more robust and compliant with standard XML namespace rules, ensuring Odoo successfully processes all valid Brazilian invoices regardless of attribute formatting. opw-6402843 Forward-Port-Of: odoo/enterprise#127851 Forward-Port-Of: odoo/enterprise#126881
This fix prevents Dutch Digipoort tax return status updates from failing when an old or incomplete status record has no linked closing entry. Other tax return status records can now continue processing normally, improving reliability for Dutch reporting workflows.
Original PR description
The `l10n_nl_reports_sbr_status_info` contains the `l10n_nl_reports_sbr.status.service` class. The class is responsible for fetching the status of sent Digipoort tax returns. The status is then posted as a chatter message to the tax return's closing entry. Issues can arise when one of the status service records is, for whatever reason, missing a closing entry. In such case, the message cannot be posted, resulting in an exception being raised. Since the records are processed in a loop without a try-catch, this causes the whole action to fail. This can lead to one broken record effectively shutting down the whole module's functionality. This PR adds some if-else checks to gracefully handle the case where the closing entry is missing. Related tickets: opw-5901446 and opw-6410082 Forward-Port-Of: odoo/enterprise#127844 Forward-Port-Of: odoo/enterprise#125996
This update corrects payroll category setup for Belgian HR payroll so salary and reporting data are classified more accurately. It helps reduce payroll validation and declaration errors, particularly for Belgian payroll accounting checks.
Partial time off entries on flexible work schedules now keep their actual duration instead of being treated as a full day. This prevents attendance reports from overstating overtime and ensures the Time Off Gantt view only marks the real leave hours.
Original PR description
Problem: On a flexible working schedule, a time off of a few hours (neither a full nor a half day) was treated as a full day off. The Attendance list then reported the whole day's attendance as…
Problem: On a flexible working schedule, a time off of a few hours (neither a full nor a half day) was treated as a full day off. The Attendance list then reported the whole day's attendance as overtime, and the Time Off gantt grayed out the entire day instead of only the leave's hours. Steps to reproduce: 1. Give an employee a flexible working schedule (e.g. 8h/day) with an overtime ruleset based on the contract's expected hours. 2. Record a 2-hour time off, then an 8-hour attendance on the same day. 3. Observe the attendance reports 8 hours of overtime instead of 2. Current behavior: A partial time off makes the whole day count as extra hours. Expected behavior: Only the hours actually taken off reduce the day's expected hours. Cause: For a flexible schedule, _handle_flexible_leave_interval expands a leave to the whole day. The override already narrows full-day and half-day leaves, but any other number of hours fell through to that full-day expansion. The expanded interval is subtracted from the day's expected hours in _work_intervals_batch, so they drop to zero and every worked hour becomes overtime. Fix: A leave of an arbitrary number of hours should only remove the hours it actually covers, so it keeps its requested interval instead of being stretched to the whole day. opw-6291536 Forward-Port-Of: odoo/enterprise#127449 Forward-Port-Of: odoo/enterprise#123778
The IoT device list now opens device details using the standard application behavior, which restores pagination after it was previously broken. This helps users browse and manage IoT devices reliably across multiple pages.
Original PR description
Since #72351, the pagination on IoT devices was broken due to how we were getting to the full device form when clicking on a record. We now change the override to use the existing method from the framework `switchToForm` which handles it better. opw-6058532 Forward-Port-Of: odoo/enterprise#127215 Forward-Port-Of: odoo/enterprise#126486
Leads created from WhatsApp conversations in Discuss now automatically include the correct customer contact. This prevents sales teams from receiving incomplete leads and reduces manual cleanup after WhatsApp interactions.
Original PR description
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss -…
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss - Click on the 'Create Lead' smart button - Check the created lead > The Customer/Contact field is empty ### Cause of Issue: When a lead is created from a Discuss conversation, `_convert_visitor_to_lead` in `crm_livechat` attempts to set the lead's customer. It does this by checking if the channel has `livechat_customer_partner_ids`. However, in whatsapp discuss conversations, the client is saved in `whatsapp_partner_id` and `livechat_customer_partner_ids` is empty. https://github.com/odoo/odoo/blob/29556fda44b9f1e6cf08129443ca47fa6cda34f9/addons/crm_livechat/models/discuss_channel.py#L54-L64 ### Fix: Overrode `_convert_visitor_to_lead` in the `whatsapp` module to properly populate the `partner_id` of the created lead if it wasn't already set and the channel has a `whatsapp_partner_id` instead of checking for the client in `crm_livechat`, which will raise an "Attribute Error", since `crm_livechat` isn't dependent on `whatsapp`. **Note**: No automated test could be added for this fix. Testing the `_convert_visitor_to_lead` method from the `whatsapp` module requires the `crm_livechat` module to be installed to access the base method implementation opw-6371274
Belgian payroll now handles salary and work schedule changes that occur within the same month more accurately. This prevents incorrect GPA prorations by splitting the month into the relevant periods and balancing the hours, helping ensure payslips reflect the employee's actual situation.
Original PR description
Handle salary and schedule changes occurring in the same month by splitting the month into salary periods, using the longest period as the anchor, valuing the shorter periods normally, and balancing the longest period with the remaining theoretical hours. Task Id: 6107129
The accounting reports date filter now keeps the correct fiscal year range when users switch between companies with different fiscal year calendars. This prevents reports from showing misleading periods caused by reusing dates from the previously selected company.
Original PR description
Fix year-mode date filter when switching between companies with different fiscal years With two companies configured: one using a standard fiscal year and one using an offset fiscal year, switching between them could produce incorrect date ranges. This happened because the previous company’s `date_to` value was reused to compute the current period for the newly selected company, and vice versa. The fix is to use the `date_to` year instead and select the latest fiscal year ending in that same year. Forward-Port-Of: odoo/enterprise#117603
The salary simulation for Indian regular pay structures no longer triggers a background tax calculation that clears newly entered fields. This prevents confusing “Missing required fields” errors and lets HR users complete simulations reliably.
Original PR description
Steps :- - On opening the Simulation when India: Regular pay structure is selected, throws "Missing required fields" when fields are changes on form view. Fix:- - For Indian company, the TDS calculation ran in the background while opening the popup, and it was clearing the values just entered. This calculation isn't needed for a simulation, so it is now skipped. task-6392171
Belgian payroll now correctly splits a second long sick leave into the standard paid sick leave and subsequent sickness periods, even when it happens during a relapse window but is not linked to the earlier sickness. This helps ensure payroll calculations reflect the intended leave rules and avoids overstating the initial sick leave period.
Original PR description
Steps to reproduce: - Create a STO for an employee of more than 30 days -> this period is split in 30 days STO and x days SGS. - Within the relapse period, create a second STO of more than 30 days and leave the relapse field empty (which is fine if the second STO is not related to the first sickness) -> the period should be split after the first 30 days just like the first STO, but it remains an STO for the whole duration. task-6296152
Fixes how Belgian salary packages account for the mobility budget when employees take extra-legal leave. The configurator now calculates gross salary and mobility budget consistently with standard payroll rules, improving accuracy for employer cost planning.
Original PR description
Forward-Port-Of: odoo/enterprise#112723
Confirmed manufacturing orders now update their work orders correctly when related bill of materials operations are changed or removed. This helps production teams avoid outdated or extra operations after refreshing a manufacturing order from its bill of materials.
Original PR description
Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation…
Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation on anything else than the company, name or workcenter - Go back to the MO, click the "Update Bom" button > The second operation is not unlinked and the first operation is not updated Cause of the issue: The `action_update_bom` updates the move raws and operations of the MO via the `_link_bom`: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L1214-L1218 For draft MO's all the work of these updates is done via the compute methods and by deleting all the records unrelevant to the new bom: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2603-L2626 And, in that case all the workorders that are not linked to an operation of the bom are expected to be deleted. However, when the MO is not in draft, the update of operations is expected to be performed here: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2647-L2664 However, since the operation of the bom has been deleted, the workorder that is expected to be deleted is not linked to any operation and hence does not satisfy the condition to be deleted: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2663-L2664 Concerning the non update of operations, it happens because the MO's operation are only updated on the three fields: `company_id`, `workcenter_id`, `name`: https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2647-L2664 https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2628-L2629 However, many other cahnges can and are actually relevant. Note: Prior to commit 80e6ed658fb43584bc2fad673ca40d9af6cf0ab6 operations were archived on boms rather than deleted: https://github.com/odoo/odoo/blob/4a5270218fe6fd7d30edb6d684b3340dc7423bab/addons/mrp/views/mrp_routing_views.xml#L53-L55 As such they would still be linked to an operation (but unrelated to the present values of the bom) and hence would fall into the condition of being unlinked from the MO. Since the bom operations are no longer archived there is no way to determine if an operation used to be linked to a bom and we therefore need to chose between deleting all operations unrelated to the present bom or to keep them all (when the MO has been confirmed). Community: https://github.com/odoo/odoo/pull/269747 opw-6285878 opw-6261738 Forward-Port-Of: odoo/enterprise#126938 Forward-Port-Of: odoo/enterprise#120709
Timesheet suggestions based on calendar events now map more reliably to the right project or task. The fix also corrects duration calculations when overlapping events are adjusted, helping users see more accurate suggested entries.
Original PR description
task: 6435164 Forward-Port-Of: odoo/enterprise#126680
1 change
Resolved issues and error corrections
The IoT device list now opens device details using the standard navigation flow, which restores broken pagination. This helps users browse and manage larger device lists without getting stuck or losing expected navigation behavior.
Original PR description
Since #72351, the pagination on IoT devices was broken due to how we were getting to the full device form when clicking on a record. We now change the override to use the existing method from the framework `switchToForm` which handles it better. opw-6058532 Forward-Port-Of: odoo/enterprise#127215 Forward-Port-Of: odoo/enterprise#126486
5 changes
Enhancements to existing features
The Dutch payroll module now includes the 2026 income tax rates for residents. This keeps payroll calculations aligned with the latest tax parameters and helps businesses prepare accurate payslips for the new year.
Original PR description
Added 2026 values for the residents' income tax rates rule parameter. task-6462877 Forward-Port-Of: odoo/enterprise#127556
Resolved issues and error corrections
Odoo now recognizes valid Brazilian electronic invoice XML files even when the main invoice tag has no extra attributes. This prevents eligible vendor bill files from being skipped during import, helping accounting teams process supplier invoices more consistently.
Original PR description
### Issue before this commit: Certain valid Brazilian NF-e (electronic invoice) XML files fail to import because the system silently ignores them during the initial EDI recognition phase. ### Steps to reproduce the issue: 1. Download Accounting and l10n_br_edi 2. Go to Vendor > Bills 3. Try to import both xmls in the ticket 4. One of the two will not be imported correctly ### Cause of the issue: https://github.com/odoo/enterprise/blob/3ed1721b702555e96c9774969927f6517e855704/l10n_br_edi/models/account_move.py#L819-L827 This function relies on a strict byte string search for b"<NFe " while it's also correct if the tag is only `<NFe>`. ### Reason to introduce the fix: To make the initial NF-e file recognition more robust and compliant with standard XML namespace rules, ensuring Odoo successfully processes all valid Brazilian invoices regardless of attribute formatting. opw-6402843 Forward-Port-Of: odoo/enterprise#127851 Forward-Port-Of: odoo/enterprise#126881
Swiss Payroll now uses the employee's requested time off dates when calculating absences, avoiding accidental extra absence days caused by timezone conversion. This prevents one-day accident leaves from being counted as two days for employees without a working schedule, helping keep regular wage and accident salary calculations accurate.
Original PR description
Issue: Swiss payslips count one extra absence day for employees without a working schedule. A one day accident leave can therefore be prorated as two days, reducing the regular wage and overstating…
Issue: Swiss payslips count one extra absence day for employees without a working schedule. A one day accident leave can therefore be prorated as two days, reducing the regular wage and overstating the accident salary. Steps to reproduce: * Install Swiss Payroll. * Configure a monthly employee without a working schedule. * Assign one day of accident time off. * Generate the payslip for that month. Cause: The Swiss wage computation derives absence boundaries from the date portion of the leave's UTC datetimes: https://github.com/odoo/enterprise/blob/16c29e1bab34b5bcb2b001477d928ec5eb294a97/l10n_ch_hr_payroll/models/hr_payslip.py#L313-L330 A fully flexible employee's full day leave starts at local midnight. In timezones ahead of UTC, that start is stored on the previous UTC date, so the inclusive calendar day computation adds an extra day. Solution: We need to use the requested time off dates for both payslip range filtering and absence proration. These fields preserve the calendar days selected by the user independently of timezone conversion, while leaving the UTC datetimes and half-day handling unchanged. opw-6435086 Forward-Port-Of: odoo/enterprise#127513
Salary package configuration now correctly offers all relevant contract benefit fields, including fields provided by country-specific payroll modules. This prevents setup errors and avoids a crash when saving a selected public benefit field.
Original PR description
1- The benefit fields related to the hr.version have a domain that limits them to the whitelisted fields used to copy values from a template, which does not always include benefit fields. The…
1- The benefit fields related to the hr.version have a domain that limits them to the whitelisted fields used to copy values from a template, which does not always include benefit fields. The advantage of the whitelist is that it factored in for the allowed countries, so instead of duplicating this logic to benefit fields and implementing it in every l10n, we can check which module the field comes from.
example:
The field [`company_car_total_depreciated_cost`](https://github.com/odoo/enterprise/blob/ce691cd6aaacfb86cd866698d2fcc3fe930912cb/l10n_be_hr_payroll_fleet/models/hr_version.py#L62) cannot be selected as `res_field_id` when it should be possible as we see in the [data](https://github.com/odoo/enterprise/blob/ce691cd6aaacfb86cd866698d2fcc3fe930912cb/l10n_be_hr_contract_salary/data/hr_contract_salary_benefit_data.xml#L6), it is not whitelisted because we dont want to copy its value from a template.
2- Another fix is the inverse of the public field, there's a traceback because the selection field is always converted to a string and cannot be used to browse as is.
```py
File "/data/build/enterprise/hr_contract_salary/models/hr_contract_salary_benefit.py", line 238, in _inverse_res_field_public
record.res_field_id = self.sudo().env['ir.model.fields'].browse(record.res_field_public)
^^^^^^^^^^^^^^^^^^^
File "/data/build/odoo/odoo/orm/fields.py", line 1890, in __set__
write_value = self.convert_to_write(value, records)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/build/odoo/odoo/orm/fields_relational.py", line 387, in convert_to_write
return value.id
^^^^^^^^
File "/data/build/odoo/odoo/orm/fields_misc.py", line 115, in __get__
raise ValueError("Expected singleton: %s" % record) from None
ValueError: Expected singleton: ir.model.fields('1', '7', '3', '8', '4')
```The salary calculator now keeps simulations separate from existing payroll documents. This prevents employees with draft payslips from seeing calculator fields cleared or missing-field errors, improving reliability during salary planning.
Original PR description
Steps:- 1. Navigate to Payroll->Employees menu->Salary Calculator 2. Select Employee who already have a draft payslip. 3. You will see all the fields will get emptied and give "Missing required fields". Root cause:- Opening the salary simulator temporarily writes the simulated values onto the employee's record. If that employee already had a draft payslip, this write also refreshed that payslip behind the scenes, even though the payslip had nothing to do with the simulation. Fix:- Mark the simulation clearly as a simulation so it no longer refreshes the employee's existing payslip. task-6392171
2 changes
Resolved issues and error corrections
DIN 5008 PDF documents now show dates in the expected day.month.year format for Germany, Austria, and Switzerland, regardless of the user's language settings. This prevents invoices, quotations, purchase orders, follow-ups, and field service worksheets from displaying confusing or non-compliant date formats.
Original PR description
* = din5008_account_followup, din5008_industry_fsm **Steps to reproduce:** * Install the **Germany - Accounting** (`l10n_de`) module (which pulls in `l10n_din5008`) * Set the document layout to **DIN…
* = din5008_account_followup, din5008_industry_fsm
**Steps to reproduce:**
* Install the **Germany - Accounting** (`l10n_de`) module (which pulls in `l10n_din5008`)
* Set the document layout to **DIN 5008** and generate any PDF report (invoice, quotation, purchase order, etc.).
**Observed behavior (date format):**
* All dates in the information block (Invoice Date, Due Date, Delivery Date, Order Date, etc.) are rendered in `yyyy-mm-dd` format instead of the expected `dd.MM.yyyy` format used in DE, AT, and CH.
**Cause (date format):**
* All `t-options="{'widget': 'date'}"` directives across the DIN 5008 template family rely on the active user's language locale for date formatting. If the user language is not `de_DE`, dates render in the locale's default format (e.g. `yyyy-mm-dd` for `en_US`).
**Fix (date format):**
* Add `'format': 'dd.MM.yyyy'` explicitly to all `t-options` date widgets across all DIN 5008 report templates (`l10n_din5008`, `l10n_din5008_sale`, `l10n_din5008_purchase`, `l10n_din5008_sale_subscription`, `l10n_din5008_repair`, `l10n_din5008_account_followup`, `l10n_din5008_industry_fsm`).
* This is correct for all three countries using DIN 5008 (DE, AT, CH), which all follow the `dd.MM.yyyy` convention.
opw-6392649
Forward-Port-Of: odoo/enterprise#126006This fix prevents the incoming invoice journal from being cleared for companies that cannot receive Peppol documents through the Documents app, such as French companies using electronic invoicing. Incoming Peppol documents are now kept as vendor bills in the required journal instead of being incorrectly routed to Documents.
Original PR description
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal…
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal stays required. This is the case of French companies (via l10n_fr_pdp). The onchange of the settings and the import did not check this method. So on a French company with the mode set to 'documents': - the Settings cleared the journal on each opening, while it was still required - the incoming documents were saved in Documents instead of vendor bills Steps to reproduce: - Create a Belgian company, with a purchase journal, and register it on Peppol as receiver - Set the reception mode to "Receive in Documents" - Change the fiscal position to France, and install l10n_fr_pdp, the Peppol part is replaced by "French Electronic Invoicing", so the radio button is not visible anymore, but the company still has peppol_reception_mode == 'documents'. - Open the Settings again: the field "Incoming Invoices Journal" is empty. opw-6429691
16 changes
Enhancements to existing features
Belgian payroll now includes the required Journalist Pension Fund contributions for both employees and employers. This helps payroll calculations reflect the additional 1% employee contribution and 2% employer cost based on the NSSO gross base.
Original PR description
**What:** - Added the Journalist Pension Fund (Employee) salary rule to calculate an additional 1% contribution based on the employee's NSSO gross base. - Added the Accounting: ONSS Journalist Pension Fund (Employer) salary rule to compute the corresponding 2% employer contribution based on the NSSO base, correctly impacting the total employer cost. task-6424251
Employee-related documents now stay better connected to employee records, including automatic linking when files are uploaded from an employee profile. Access rights are also synchronized with employee folders, helping ensure documents uploaded through employee discussions follow the correct permissions.
Original PR description
This PR adds more synchronization between the documents and the employee by implementing the points below. - Add the support of the `hr.employee` model in the res_model of documents. - When coming from the context of the employee, uploading the file leads to linking the employee by default to the res_model. - Synced the access rights of the employee folder when the parent folder changes or when the employee folder is created. - Now, documents uploaded from the chatter of the employee will inherit the access rights from the folder. Task-6072094
Belgian payroll now includes employees' holiday attest balance when calculating paid time off to allocate for the next year. December allocations will require approval instead of being automatically approved, giving HR teams better oversight before finalizing leave balances.
Original PR description
- changed allocations from december to be in "to approve" state instead of approved - added a column for Holiday Attest Balance in holiday pay step and included it in the time off to allocate for next year task-id: 6394281
Payslip lines that rely on quarterly calculations or direct totals now hide misleading amount values and show explanatory text instead. This makes Belgian payslips easier to understand and reduces confusion for payroll users and employees reviewing payslip details.
Original PR description
As some numbers is calculated based on quarter and total amounts are calculated directly we should hide the value of amount col and show info text for those lines Task: 6431856
UrbanPiper point-of-sale preparation tickets can now follow printer settings that split tickets by individual product. This helps kitchen or preparation teams receive clearer, item-specific tickets instead of one combined order ticket.
Original PR description
Preparation ticket generation now takes an is_split_per_product flag, read from the printer configuration, to print one ticket per product instead of one ticket grouping the whole order. pos_urban_piper overrides _generate_preparation_receipt_data to add its own data on top of the generated receipts. related-https://github.com/odoo/odoo/pull/267412 task-6227300
The empty Commission Plan screen now shows an illustrated explanation of how commission plans are calculated instead of a generic placeholder image. This helps sales and compensation teams understand the setup flow more quickly, with support for mobile layouts and right-to-left languages.
Original PR description
For the Commission Plan's empty screen we've replaced the smiling face guy with an explanation on how a commission plan is calculated in the form of an illustrated diagram. Adapts for mobile and rtl. ⚠️ Note for RTL translations: The `x` position on the translatable lines in the SVG needs to be changed to `75` and `95` respectively ```diff - <tspan x="24.9952" y="109.264">Invoices, </tspan> - <tspan x="10.1241" y="124.264">Sale Orders, ...</tspan> + <tspan x="75" y="109.264">[translation for "Invoices, " ]</tspan> + <tspan x="95" y="124.264">[translation for "Sale Orders, ..."]</tspan> ``` task-6369345
Mexican payroll teams can now choose an alternative ISR withholding method that converts each pay period’s taxable income to a monthly equivalent, applies the monthly tax table, then scales the result back. This helps companies align payslip tax calculations with their preferred local practice while keeping the existing method available.
Original PR description
Some Mexican companies compute the income tax withheld on each payslip scaling up the period taxable income to its monthly equivalent. Here the monthly table is applied, and the resulting tax is scaled back down to the period. This adds a company level switch between the two methods. Both are exposed in the Payroll settings, the days per month being configurable: - 'standard' keeps the current behaviour, looking up the tax table matching the employee's pay schedule. - 'monthly_with_period_factor' derives a period factor by dividing 'l10n_mx_isr_days_per_month' (30.4 by default, i.e. 365 / 12) by the number of days in the pay period, and applies the monthly table to the scaled income. The monthly table itself is already in the database, as the 'monthly' key of the 'l10n_mx_isr_tables' rule parameter, so no new data is introduced and the new method follows the yearly table updates like the existing one does. task-6433348
Resolved issues and error corrections
Invoice and bill numbers now reflect fiscal years that span more than 12 months, such as using a 25-26 year range instead of only 2026. This prevents misleading document numbering and keeps accounting sequences aligned with the company’s configured fiscal year records.
Original PR description
Issue: When a fiscal year begins, for example, the 01/12/2025 and ends the 31/12/2026, the sequence mechanism for the invoices and bills do not take into account that the fiscal year covers more than a year and the sequence starts at INV/2026/0001 instead of INV/25-26/0001. Source of the issue: when computing if the year is stagerred, we took into account only the fiscalyear_last_day and fiscalyear_last_month of the company, instead of checking if there exists any record of account.fiscal.year, and if there is, prioritize the existing records. task-4951257
Shopfloor users can now unplan manufacturing work orders without being blocked by an access error linked to Belgian payroll leave records. This fixes a disruption caused when removing planned work time triggered a payroll-related check the user was not allowed to perform.
Original PR description
After https://github.com/odoo/enterprise/pull/104335 , A search on `hr.leave.allocation` is done, which the normal shopfloor user doesn't have access to. In shopfloor,`leave_id` (a field related to `resource_calender_leaves` in `mrp_workorder`) gets unlinked in a couple of different actions, which then triggers the unlink method added that performs the search consequently. Simple steps to reproduce: - Use shopfloor user - Create MO - Plan workorders - Unplan At unplanning, `leave_id` is unlinked, which then triggers the `on_delete` method in `l10n_be_hr_payroll` that performs the search and throws the access error.
Luxembourg payroll now calculates historical payslips using the wage index that applied at the payslip date, rather than today’s index. This helps ensure past salary calculations remain accurate when wage index values change over time.
Original PR description
Historical payslips incorrectly used today's wage index instead of the index active during the payslip period. Now, salary rules evaluate the indexed wage using `payslip.date_to` via the new `_get_l10n_lu_indexed_wage(date)` contract method. Task: 6395557 Forward-Port-Of: odoo/enterprise#125861
Rental orders using custom routes now correctly generate the expected return transfer, even when the route is configured for make-to-order purchasing. This prevents missing return logistics after a rental delivery, helping teams track rented products through the full rental cycle.
Original PR description
### Steps to reproduce: - In the settings enable: Multi-Steps Routes and rental transfers - Unarchive the MTO route - Create a rental product P with a buy route and a set vendor - Create a rental…
### Steps to reproduce: - In the settings enable: Multi-Steps Routes and rental transfers - Unarchive the MTO route - Create a rental product P with a buy route and a set vendor - Create a rental order for 1 x P and set the the MTO route on the sol - Confirm the order #### > The delivery as well as the purchase for 1 unit of P was generated but the return was not. ### Cause of the issue: The procurement generated to handle both the delivery and the return rental picking are handled by the `_create_procurements`: https://github.com/odoo/enterprise/blob/b0e48baaf99bdc4faefd2ffdd3bd5637fb548593/sale_stock_renting/models/sale_order_line.py#L353-L374 The `route_ids` set and used is the `mto_route` set on the sol: https://github.com/odoo/odoo/blob/0f061503e26ac8c441d62d91947419119e48c47a/addons/sale_stock/models/sale_order_line.py#L415-L422 https://github.com/odoo/odoo/blob/0f061503e26ac8c441d62d91947419119e48c47a/addons/sale_stock/models/sale_order_line.py#L282-L297 However, in the present case, the mto route does not contain any rule with a relevant `location_src_id` in the rental location so that the return will not be generated. opw-6361322 Forward-Port-Of: odoo/enterprise#126410 Forward-Port-Of: odoo/enterprise#124097
Barcode receipts now keep the correct putaway destination when users scan multiple lots for the same product. This prevents items from being shown or processed as going to the default stock location instead of the intended shelf, reducing warehouse handling errors.
Original PR description
Steps to reproduce --- 1. Enable Storage Locations and Lots & Serial Numbers. 2. Add a putaway rule sending a lot-tracked product from WH/Stock to WH/Stock/Shelf 1. 3. Confirm a receipt reserving 2…
Steps to reproduce --- 1. Enable Storage Locations and Lots & Serial Numbers. 2. Add a putaway rule sending a lot-tracked product from WH/Stock to WH/Stock/Shelf 1. 3. Confirm a receipt reserving 2 units of that product; putaway sets the reserved move line destination to WH/Stock/Shelf 1. 4. In the Barcode app, scan a first lot, then a second lot. The second lot lands on a separate line at WH/Stock instead of WH/Stock/Shelf 1. Issue --- The first lot reuses the reserved line and keeps its Shelf 1 destination. The second lot cannot reuse it because its tracking number differs, so `_findLine` returns nothing and `_getNewLineDefaultValues` builds a new line with `location_dest_id` set to `_defaultDestLocation()`, the picking's default destination (WH/Stock). https://github.com/odoo/enterprise/blob/314a79b774f30dc9377b2971492576c4b84483e1/stock_barcode/static/src/models/barcode_picking_model.js#L1591-L1601 Putaway relocates the destination on the move line at reservation, never on the picking, so only the reserved line carries Shelf 1. Since `groupKey` includes `location_dest_id`, the new line does not group with the first lot and shows separately at WH/Stock. This is not a regression: new lines have always defaulted to the operation destination. https://github.com/odoo/enterprise/blob/314a79b774f30dc9377b2971492576c4b84483e1/stock_barcode/static/src/models/barcode_picking_model.js#L239-L241 The new line now inherits the selected line's `location_dest_id`, already relocated by putaway, instead of the default. opw-6317077 Forward-Port-Of: odoo/enterprise#127906 Forward-Port-Of: odoo/enterprise#125309
Odoo now recognizes valid Brazilian electronic invoice XML files even when the main invoice tag has no extra attributes. This prevents legitimate vendor bills from being skipped during import, helping accounting teams process supplier invoices more consistently.
Original PR description
### Issue before this commit: Certain valid Brazilian NF-e (electronic invoice) XML files fail to import because the system silently ignores them during the initial EDI recognition phase. ### Steps to reproduce the issue: 1. Download Accounting and l10n_br_edi 2. Go to Vendor > Bills 3. Try to import both xmls in the ticket 4. One of the two will not be imported correctly ### Cause of the issue: https://github.com/odoo/enterprise/blob/3ed1721b702555e96c9774969927f6517e855704/l10n_br_edi/models/account_move.py#L819-L827 This function relies on a strict byte string search for b"<NFe " while it's also correct if the tag is only `<NFe>`. ### Reason to introduce the fix: To make the initial NF-e file recognition more robust and compliant with standard XML namespace rules, ensuring Odoo successfully processes all valid Brazilian invoices regardless of attribute formatting. opw-6402843 Forward-Port-Of: odoo/enterprise#127851 Forward-Port-Of: odoo/enterprise#126881
Belgian DIMONA fields are now shown when employee types are relevant to Belgium or not limited to a country, avoiding missing setup options. The update also prevents mismatched employee type and company countries, reducing payroll configuration errors.
Original PR description
company/country validation DIMONA category and sub-types were hidden whenever the employee type's country was not exactly 'BE', including when no country was set at all. They should be visible whenever the type has no country or BE, and no company or a BE company. Add a `company_country_code` related field to expose the company's country for use in the view invisible domain, and add a constraint raising a validation error if an employee type's country and its related company's country don't match. Task: 6442738 PR community: [odoo/odoo#281923](https://github.com/odoo/odoo/pull/281923)
The Timesheets Assistant now opens in a chronological view so employees can match activities to work more naturally. It also avoids repeated loading, keeps dismissed suggestions from coming back, reduces visual flicker when selecting suggestions, and better recognizes Discord activity from a browser.
Original PR description
## [FIX] timesheet_grid: remove duplicate rpc call Before this commit, the `loadTimesheets` method is called 2 times in a row, that method does a rpc call to load the existing timesheets and so, it…
## [FIX] timesheet_grid: remove duplicate rpc call Before this commit, the `loadTimesheets` method is called 2 times in a row, that method does a rpc call to load the existing timesheets and so, it is not needed to call it 2 times since the rpc will return the exact same result. This commit removes the rpc call when we compute the suggestions to only load the timesheets when we load all the data. ## [FIX] timesheet_grid: show chronological view instead of project view Before this commit, the `by project` view were loaded first in the timesheet assistant action, to group the suggestion by project, the problem is at the beginning the view will not really show a perfect matching and so the user could think the feature does not work and he will not understand how to correctly match the suggestions shown in the view. This commit changes the view loaded by default in Timesheets Assistant to first show the chronological view, that view is more logical for the current user to rethink what he did in the past to correctly map the events to a project and a task when he generates his timesheets thanks to those events. The by project view is still useful afterwards when the system has learned the choices made by the current user. ## [FIX] timesheet_grid: ensure events are consumed forever Before this commit, the suggestions removed by the current user comes back when he changes the date and come back to the day he removes the suggestions. The reason is because a shallow copy of events consumed is made and that copy alters the duration of the initial object. This commit avoids copying the consumed events object to make sure the initial object is not altered when processing the events to remove them if they are removed before by the user. ### Steps to reproduce the issue: 1. install timesheet_grid and Activity watch, makes sure Activity watch collects some activities on your computer. 2. Go to Assistant menu in timesheets app. 3. Remove some suggestions displayed in the right panel. 4. Go to next date. 5. Come back to previous date. ### Expected Behavior: The suggestions removed should not appear again. ### Actual Behavior: The suggestions removed come back in the view. ## [FIX] timesheet_grid: fix flicker when suggestion selected Before this commit, when the user selects a suggestion in timesheet assistant, there is a small flicker appears because the height of the row grows because of the border added to highlight the suggestion selected. This commit reviews a bit the style to make sure the border bottom in the previous element is removed if the element is not selected or if the 2 consecutives suggestions are selected. ## [FIX] timesheet_grid: fix discord rules to handle discord in web Before this commit, when the user uses discord in its browser instead of the app on his computer, the discord rules don't catch the activity watch events because the tab title is different than the windows name in the app. This commit adapts the regex of Discord rules to handle the both use cases. task-[6385639](https://www.odoo.com/odoo/project.task/6385639) Forward-Port-Of: odoo/enterprise#126435 Forward-Port-Of: odoo/enterprise#124855
Spreadsheet pivot tables now apply currency exchange rates when they are inserted. This helps users working with multi-currency data see more accurate financial figures in their reports.
Original PR description
apply currency exchange rates when inserting a pivot in spreadsheet Task: 6022608
2 changes
Resolved issues and error corrections
The AI assistant now waits until pivot reports are fully ready before applying changes, preventing crashes or blank pivot views when switching views. It also keeps the default measures when the AI has not requested specific ones, so users continue to see meaningful report data.
Original PR description
When the AI agent switched from another view to a pivot view, the pivot view could crash or open without any active measures. The AI controller patch applies the agent's adjustments upon receiving…
When the AI agent switched from another view to a pivot view, the pivot view could crash or open without any active measures. The AI controller patch applies the agent's adjustments upon receiving the `APPLY_AI_ADJUST_MODEL` bus event. However, the event could be processed while the pivot model was still executing `_loadData()`. In that case, the following sequence occurred: * `_loadData()` started and awaited. * The controller patch was executed. * The patch called `toggleMeasures()`. * `toggleMeasures()` waited for `_loadData()` to complete. * `_loadData()` finished and updated the metadata with the available measures. * `toggleMeasures()` resumed and wrote back the metadata snapshot it had taken before waiting. Since `toggleMeasures()` operates on a snapshot of the metadata, the measures populated by `_loadData()` were lost when the snapshot replaced the current metadata, leaving the pivot model without its `measures` metadata and causing the view to crash. Prevent this race condition by waiting for the pivot model initialization to complete before applying the AI adjustments. Also preserve the default active measures when the AI agent does not explicitly request any measures instead of clearing them and opening an empty pivot view. task-6384368
Odoo now recognizes valid Brazilian NF-e invoice XML files even when the main invoice tag has no extra attributes. This prevents some vendor bills from being silently skipped during import, helping accounting teams process compliant Brazilian invoices more consistently.
Original PR description
### Issue before this commit: Certain valid Brazilian NF-e (electronic invoice) XML files fail to import because the system silently ignores them during the initial EDI recognition phase. ### Steps to reproduce the issue: 1. Download Accounting and l10n_br_edi 2. Go to Vendor > Bills 3. Try to import both xmls in the ticket 4. One of the two will not be imported correctly ### Cause of the issue: https://github.com/odoo/enterprise/blob/3ed1721b702555e96c9774969927f6517e855704/l10n_br_edi/models/account_move.py#L819-L827 This function relies on a strict byte string search for b"<NFe " while it's also correct if the tag is only `<NFe>`. ### Reason to introduce the fix: To make the initial NF-e file recognition more robust and compliant with standard XML namespace rules, ensuring Odoo successfully processes all valid Brazilian invoices regardless of attribute formatting. opw-6402843 Forward-Port-Of: odoo/enterprise#127851 Forward-Port-Of: odoo/enterprise#126881
5 changes
Enhancements to existing features
Signer email addresses on signature requests can now only be changed by the person who created the request. This helps prevent unintended or unauthorized recipient changes and keeps the signing process more controlled.
Original PR description
Forward-Port-Of: odoo/enterprise#126675
Resolved issues and error corrections
Odoo now correctly reads Colombian vendor bill XML files that include withholding taxes. This ensures ReteRenta, ReteIVA, and ReteICA amounts are added to invoice lines, reducing manual corrections and improving tax accuracy.
Original PR description
Currently, Odoo doesn't detect Withholdings taxes for Colombia while uploading vendor bill XML, causing ReteRenta, ReteIVA, ReteICA withholdings to not being added in invoice lines, for versions under 19.2 Fix: Backport commit https://github.com/odoo/enterprise/commit/edc67858350d1b8408019e51751240026d931dab Versions: 18.0 -> 19.1 Task-id: [6417175](https://www.odoo.com/odoo/project/49/tasks/6417175)
DIN 5008 PDF documents now consistently show dates in the expected day.month.year format for Germany, Austria, and Switzerland, regardless of the user's language settings. This prevents customer-facing documents such as invoices, quotations, purchase orders, follow-ups, and field service worksheets from displaying confusing or non-compliant date formats.
Original PR description
* = din5008_account_followup, din5008_industry_fsm **Steps to reproduce:** * Install the **Germany - Accounting** (`l10n_de`) module (which pulls in `l10n_din5008`) * Set the document layout to **DIN…
* = din5008_account_followup, din5008_industry_fsm
**Steps to reproduce:**
* Install the **Germany - Accounting** (`l10n_de`) module (which pulls in `l10n_din5008`)
* Set the document layout to **DIN 5008** and generate any PDF report (invoice, quotation, purchase order, etc.).
**Observed behavior (date format):**
* All dates in the information block (Invoice Date, Due Date, Delivery Date, Order Date, etc.) are rendered in `yyyy-mm-dd` format instead of the expected `dd.MM.yyyy` format used in DE, AT, and CH.
**Cause (date format):**
* All `t-options="{'widget': 'date'}"` directives across the DIN 5008 template family rely on the active user's language locale for date formatting. If the user language is not `de_DE`, dates render in the locale's default format (e.g. `yyyy-mm-dd` for `en_US`).
**Fix (date format):**
* Add `'format': 'dd.MM.yyyy'` explicitly to all `t-options` date widgets across all DIN 5008 report templates (`l10n_din5008`, `l10n_din5008_sale`, `l10n_din5008_purchase`, `l10n_din5008_sale_subscription`, `l10n_din5008_repair`, `l10n_din5008_account_followup`, `l10n_din5008_industry_fsm`).
* This is correct for all three countries using DIN 5008 (DE, AT, CH), which all follow the `dd.MM.yyyy` convention.
opw-6392649Fixes the Moroccan Profit and Loss report so budget figures are included instead of appearing empty. The report also now shows the related budget percentage column, giving users a more complete view for financial planning and comparison.
Original PR description
Issue: Budgets are not working on Profit and Loss in the l10n_ma. Steps to Reproduce: - Install l10n_ma - Go to Profit and Loss, create a budget - Select Profit and Loss Statement (MA) - Budget is empty. Reason: For l10n_ma we use different values on `target_line_res_dict` (`cur_op` `prev_op` `cur_tot`) rather than the usual `balance`. This causes our budget column to have those same 3 types instead of the usual `balance`. When we then search for the dict entry `balance` in `target_line_res_dict`, we won't find anything giving us `None` and causing the current issue where nothing appears. https://github.com/odoo/enterprise/blob/61e3272430b4adeefb90a0a224bd308a3267decf/account_reports/models/account_report.py#L3125-L3129 opw-6385229
This fixes German SEPA credit transfer exports so they no longer include an unsupported LEI field in older bank file formats. The change keeps generated payment files compliant with bank requirements, reducing the risk of rejected vendor payment batches.
Original PR description
### Issue before this commit: When generating a SEPA Credit Transfer batch using the German XML format (pain.001.001.03), the <LEI> tag is erroneously included in the exported file if an LEI is…
### Issue before this commit: When generating a SEPA Credit Transfer batch using the German XML format (pain.001.001.03), the <LEI> tag is erroneously included in the exported file if an LEI is configured on the company. This invalidates the XML, causing banks to reject the file. ### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Go to Settins > Vendor Payments > SEPA Credit Transfer / ISO20022 and set Name Identification as 529900T8BM49AURSDO55 and Issuer as LEIMAN 3. Go to companies and set 529900T8BM49AURSDO55 as LEI in the DE company 4. Go to Accounting dashboard and click the 3 dots of the bank group, go to Configuration and set the Account Number and be sure in the Outgoing Payments tab XML Format is German 5. Create a new German company from Contacts with: 1. Country as Germany 2. VAT 3. Account Number in the Bank Accounts by adding one line: 1. example Account Number: DE65100500007201811026 2. example Bank: BNP Paribas 3. activate the Send Money button 7. Then go to Vendor > Payments and create a new one with Payment Method as SEPA Credit Transfer for the German company created 8. Go back and select the new payment from the list and click create batch and print it 9. In the XML of pain.001.001.03.(DE) file, the LEI tag should not be included. ### Cause of the issue: The XML generation logic does not filter out the <LEI> element for older schema versions like pain.001.001.03, which do not support this tag. ### Reason to introduce the fix: To ensure strict schema compliance and prevent bank rejections. The <LEI> element is now properly omitted from pain.001.001.03 files and restricted only to newer formats (e.g., pain.001.001.09) where it is valid. opw-6428150
3 changes
Enhancements to existing features
Austria’s SEPA credit transfer payments now use the newer standard payment file format by default, replacing a local older format that will soon be unsupported. This helps businesses stay compliant with bank requirements and reduces the risk of future payment file rejections.
Original PR description
Austria currently uses a local variant of SCT, which is actually a restricted version of pain.001.001.03 (the old SEPA format). This version will no longer be supported within a year. We now use pain.001.001.09 as the default format (for Austria and generic SEPA) and update existing values accordingly in the migration scripts (only for Austria, see upgrade PR). task-5157755
Resolved issues and error corrections
German DATEV exports now correctly handle bank settlements involving a payer currency, bank account currency, and company currency. The transaction is split through the standard DATEV clearing account so each exported line uses only one foreign currency, preventing export issues while leaving simpler transactions unchanged.
Original PR description
Issue: - DATEV does not support multiple foreign currencies on a single journal line. - This can occur when reconciling a bank transaction where: - the payer uses one currency (C1), - the bank…
Issue: - DATEV does not support multiple foreign currencies on a single journal line. - This can occur when reconciling a bank transaction where: - the payer uses one currency (C1), - the bank journal is held in another currency (C2), - the company uses a third currency (C3). - The existing export logic could not represent the bank liquidity and foreign AR/AP currencies separately in such cases. Fix: - Detect 3-currency cases from bank statement transactions and their liquidity line. - Use the DATEV clearing account (1360 SKR03 / 1460 SKR04) to split the transaction into two logical legs: - Bank → Clearing (bank journal currency) - AR/AP → Clearing (payer currency) - Emit the liquidity leg only once when multiple foreign AR/AP lines are reconciled against the same bank transaction. - Keep regular 1- and 2-currency transactions on the existing export path. Impact: - Correctly represents 3-currency bank settlements in DATEV. - Keeps each exported line in a single foreign currency. - Leaves manual entries and payment transactions outside this specific handling, as the scenario is specific to the bank liquidity line. taskID-5457547
Mexican electronic invoices now report the original unit price and discount correctly when invoice lines have extremely high discounts. This prevents incorrect CFDI XML amounts that could affect compliance and customer-facing invoice documents.
Original PR description
### Steps to reproduce issue: 1. Activate the Mexican localization and configure a company able to sign 2. Create an invoice with one line: a product with a valid UNSPSC code, Price 250.00, VAT 16%,…
### Steps to reproduce issue:
1. Activate the Mexican localization and configure a company able to sign
2. Create an invoice with one line: a product with a valid UNSPSC code,
Price 250.00, VAT 16%, Discount 99.99%
3. Confirm, then Send & Print with the CFDI checked
4. In the generated XML, the following values are off:
- In node "Comprobante": SubTotal="200.00" Descuento="199.98"
- In node "Concepto": ValorUnitario="200.00" Importe="200.00"
Descuento="199.98"
- Expected are 250.00 and 249.98
### Explanation:
`discount` is a Float(digits=(16, 2)) and both conversions of the field don't give the same float: `convert_to_column` writes it as '99.99' while `convert_to_cache` rounds it to 99.99000000000001. Hence, 250 * (1 - 99.99000000000001 / 100) = 0.0249999999999695, rounded to 0.02, but 250 * (1 - 99.99 / 100) = 0.0250000000000250, rounded to 0.03. `price_subtotal` is therefore stored as 0.02 when the invoice is posted, while any later request, such as the one generating the CFDI, recomputes 0.03.
`gross_price_subtotal` is computed by dividing the rounded `price_subtotal` by `discount_factor`, which amplifies the rounding error of `price_subtotal` by 1 / discount_factor, so 10 000 times with a 99.99% discount: 0.02 / 0.0001 = 200.00 instead of 250.00. That difference is exactly half a cent, which is also the tolerance of the condition added by 771d3e3cf, so the condition fails and the amount computed from `price_subtotal` wins over `price_unit`. Every price whose net lands on a half cent is affected, i.e. the odd multiples of 50 with a 99.99% discount.
### Fix reasoning:
`price_unit` is the source of truth for `ValorUnitario`. Tolerating one rounding unit instead of half of one is enough to cover the rounding of `price_subtotal`, so `price_unit` keeps the priority. The fallback still applies to the cases it was added for, where something other than the discount affects `price_subtotal` (e.g. price-included taxes), since those differ by much more than one rounding unit. The discount amount is derived from the gross, so it absorbs the difference: `Importe` - `Descuento` still gives `price_subtotal` and the total of the document is unchanged.
ticket-id: [6386895](https://www.odoo.com/odoo/project/49/tasks/6386895)