Tuesday, September 1, 2026
45 changes · saas-19.1
Resolved issues and error corrections
This fixes an issue where opening tickets from a Helpdesk Team reached through an email alias could fail because the system reused the wrong navigation details. Users can now open the Tickets button normally, improving reliability for support teams managing aliases and helpdesk workflows.
Original PR description
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe…
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe error ### Description of the issue/feature this PR addresses: **Issue:** When navigating from an email alias to its parent document (e.g., a Helpdesk Team), the web client incorrectly retains the `active_id` and `active_model` of the alias in the context. **Solution:** We updated the `action_view_ticket` method in `helpdesk.team` to explicitly inject the correct `active_model` and `active_id` into the context before calling `_for_xml_id`. This overwrites the polluted Alias data before the window action is evaluated. ### Current behavior before PR: Clicking the "Tickets" smart button passes the old alias context into the action. This bad data flows into the ticket view, which attempts to look up a Helpdesk Team using the Alias's ID to generate the empty list help message, resulting in a `MissingError`. ### Desired behavior after PR: The Python action sanitizes the context at the source, ensuring that the XML action and subsequent view evaluations receive the correct Helpdesk Team ID. Ultimately, the view will load normally without crashing. opw-6395638 Forward-Port-Of: odoo/enterprise#129901 Forward-Port-Of: odoo/enterprise#125232
Mobile invoice views now show the invoice line description when no product is selected. This prevents blank cards from appearing, making invoice review clearer for users working on phones or small screens.
Original PR description
Invoice lines can be created without a product. However, the mobile kanban view does not handle such lines properly and displays an empty card without a name or image. This commit adapts mobile kanban view to show invoice line's name when `product_id` is not set. Before | After -- | -- <img width="469" height="277" alt="image" src="https://github.com/user-attachments/assets/3b367581-b73d-4b12-a487-1a54c3b88470" /> | <img width="406" height="300" alt="image" src="https://github.com/user-attachments/assets/107d7294-90e8-49c6-826a-9be75c6b799f" /> --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285608 Forward-Port-Of: odoo/odoo#284755
The Guatemala localization now reflects the new tax treatment for regular gasoline containing 10% alcohol from August 22. New customer accounting setups will tax only 90% of regular gasoline gallons, matching the government exemption for the alcohol portion; existing databases can update their tax formula manually if needed.
Original PR description
Starting August 22nd Regular Gasoline is changing to a version that contains 10% alcohol. Because of this, the government is only taxing 90% of the gallons since the 10% alcohol portion is exempt. This will update COA for new customers, any current dbs who need to use the new formula can manually update theirs to include the * 0.9 portion. task-6483303 Forward-Port-Of: odoo/enterprise#129483 Forward-Port-Of: odoo/enterprise#129412
Italian point-of-sale refunds now take the cashier to the payment page for the refund order, rather than the product screen or the original order payment page. This prevents confusion during refund processing and helps stores complete fiscal-printer refunds correctly.
Original PR description
**Issue**: 1. From versions 18.4 to 19.1 inclusive, the system redirects to the product screen; 2. From version 19.2 onward, the redirection targets the payment page of the original order instead of the refund order. **Expected behavior**: The system navigates to the payment page for the refund order. **Steps to reproduce**: - Set up an Italian fiscal printer; - Open a POS session and process an order; - Create a refund for the order. [Ticket link](https://www.odoo.com/odoo/project/49/tasks/6499079) opw-6499079 Forward-Port-Of: odoo/enterprise#129758
Fixes an issue in the HTML editor where replacing a selected URL link with a pasted URL updated the destination but kept the old visible text. Users now see the newly pasted URL as the link label too, reducing confusion and preventing misleading links in edited content.
Original PR description
**Current behavior before PR:** Steps to reproduce the issue: - Create a link via typing a valid URL + space. - Copy/Paste a different URL from the browser. - Select the entire link you just created. - Paste the copied URL on top of it. Notice that the label is still the old URL even though the URL actually changed. This happens because after commit [1] When pasting a URL over an active text selection, selected content is converted into a link pointing to the pasted URL. This should not be the case if selected content is a link with same label and URL. **Desired behavior after PR is merged:** If a link is entirely selected and its label is the same as URL then it should replace the existing link label with new URL. [1]: https://github.com/odoo/odoo/commit/d356043a67e1d7291bd1302b1e90a2d9a07718da task-6456004 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixed an issue where vendor bills received by email could show the saved original email file as the main document instead of the actual invoice PDF. This ensures users see the correct invoice preview when incoming mail is configured to keep a copy of the original message.
Original PR description
When an incoming mail server has the "Keep Original" option enabled, a copy of every incoming mail is stored as original_email.eml. Because it is not a document of the invoice, the system unattaches it, however it may still be used as main attachment in case no other PDF or image was attached to the message. Steps to reproduce: - Configure an incoming mail server with "Keep Original" enabled, using an alias pointing to a vendor bill journal. - Send a mail to that alias containing an xml embedding a PDF. - Open that bill Issue: The invoice's main attachment points to the .eml file. This occurs because it is set before the PDF is extracted from the xml. Then, when import extracts the PDF, it is added as attachment on the invoice, but we already have a main attachment that won't be overwritten. However, once a pdf or image is added as attachment, the system will show the (broken) preview. opw-6431726 Forward-Port-Of: odoo/odoo#280318
Fixed an accounting issue where bill references could disappear in the bank reconciliation view when a foreign-currency payment also created an exchange difference entry. This helps users more easily identify matched bills and verify reconciled transactions.
Original PR description
In the bank reconciliation widget, a reconciled transaction matched with a bill and a loss exchange entry no longer displays the bill reference next to the reconciled line. Steps to reproduce: - Have…
In the bank reconciliation widget, a reconciled transaction matched with a bill and a loss exchange entry no longer displays the bill reference next to the reconciled line. Steps to reproduce: - Have company currency USD and foreign currency EUR - Create a vendor bill in foreign currency (100 EUR, rate 1 USD = 1 EUR) - Create a bank transaction in the foreign currency for less than the bill total, at a rate making the amount higher in company currency (90 EUR, 108 USD at 1 EUR = 1.2 USD) - Open the bank reconciliation widget, reconcile transaction and bill - Unfold the reconciled transaction Issue: A bill reference is missing Analysis: The exchange difference line is reconciled with the transaction line. Being reconciled with two lines (bill and exch), the widget relies on `*_reconciled_lines_excluding_exchange_diff` to show the fields. However, `count_reconciled_lines_excluding_exchange_diff` has been defined as boolean instead of int, faulting the comparison. opw-6479015 Forward-Port-Of: odoo/odoo#283855
The Polish bank account verification process no longer fails when checking several partners where some are missing tax or bank details. This prevents an error from interrupting the verification of valid partners and makes the process more reliable.
Original PR description
When we check for multiple partners with some valid and some being incomplete (no vat or no bank account), a traceback is raised This was due to a bracket accessor, changed into a get in this commit. no-task Forward-Port-Of: odoo/odoo#285631
This fix prevents Odoo from showing a misleading consumption warning when manufacturing a serial-tracked product whose make-to-order purchased component arrives after the finished product serial number was assigned. It ensures correctly reserved components are recognized as consumed, reducing unnecessary warnings and smoother completion of manufacturing orders.
Original PR description
Steps to reproduce the bug: - Warehouse configured for 2-Step Manufacturing - Component "C1": - Routes: MTO + Buy - Tracking: By Unique Serial Number - Create a Finished product: - Route: Manufacture…
Steps to reproduce the bug:
- Warehouse configured for 2-Step Manufacturing
- Component "C1":
- Routes: MTO + Buy
- Tracking: By Unique Serial Number
- Create a Finished product:
- Route: Manufacture
- Tracking: By Unique Serial Number
- BoM:
- component "C1": 1 unit
- Create and confirm the Manufacturing Order
- Generate/assign the serial number for the finished product immediately, before the related purchase order is even confirmed
- Confirm the Purchase Order
- Receive the component
- Transfer the component to WH/Pre-Production
- Click Produce All
Problem:
A Consumption Warning was displayed stating that the consumed quantity differs from the expected quantity, even though the consumed quantity was exactly equal to the BoM quantity and no manual quantity change was performed. Clicking "Set Quantities & Validate" completed the MO successfully, hiding the inconsistency.
`_get_consumption_issues()` only counts a raw move's quantity as "consumed" when `move.picked` is `True`
[(odoo/addons/mrp/models/mrp_production.py#L1791)](https://github.com/odoo/odoo/blob/19.0/addons/mrp/models/mrp_production.py#L1791).
Generating the finished product's serial number calls `_set_qty_producing()`
[(odoo/addons/mrp/models/mrp_production.py#L1402)](https://github.com/odoo/odoo/blob/19.0/addons/mrp/models/mrp_production.py#L1402).
, which itself only sets `move.picked = True` when `move.quantity` is truthy
[(odoo/addons/mrp/models/mrp_production.py#L1443)](https://github.com/odoo/odoo/blob/19.0/addons/mrp/models/mrp_production.py#L1443).
At that point the MTO component had not been received yet, so `move.quantity` was still 0 and `picked` was never set. Once the component was later received and transferred to Pre-Production, `_action_assign()` correctly reserved the raw move (`quantity` became correct), but nothing ever went back to flip `picked` to `True`, since `_set_quantities()`
[(odoo/addons/mrp/models/mrp_production.py#L2932)](https://github.com/odoo/odoo/blob/19.0/addons/mrp/models/mrp_production.py#L2932).
only calls `_set_qty_producing()` again when `qty_producing` is still falsy, which was no longer the case. `_get_consumption_issues()` therefore still counted the consumed quantity as 0 against the expected BoM quantity.
Solution:
In `pre_button_mark_done()`, for auto productions (single unit being produced), after `_set_quantities()` runs, also mark as `picked` any non-manual-consumption raw move that is not yet `picked` but whose reserved `quantity` already exactly matches its own `product_uom_qty`. This is scoped to auto productions only, since on a multi-unit production a raw move's aggregate `quantity` can equal its `product_uom_qty` by coincidence (reservation for future backorder steps) even though only part of it is meant to be consumed for the current step.
opw-6421531
Forward-Port-Of: odoo/odoo#281258The update helps prevent reporting differences between depreciation schedules and balance sheets when asset-linked journal item accounts are changed. Users are now warned when a mismatch is detected and can update affected assets in bulk to keep reports aligned.
Original PR description
… and balance sheet When a user changes the account on a journal item that was linked to an asset, it creates discrepancy between the Depreciation Schedule, which relies on the asset's Fixed Asset Account, and the Balance Sheet that relies on the journal item's account. We created a warning when such a discrepancy is detected, and allowed the user to mass edit the assets to change those accounts. task-4314894 Forward-Port-Of: odoo/enterprise#128463 Forward-Port-Of: odoo/enterprise#122241
Accountants with standard accounting access can now generate BOE files for Spanish Modelo 115 tax reports without needing company access rights. This removes an unnecessary permissions error and lets the right users complete tax reporting without extra administrative privileges.
Original PR description
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax…
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax reports, even though exporting a BOE file has nothing to do with editing company configuration and should be available to any accountant. Steps to reproduce: ------------------- * Log in as a user with accounting rights only (not part of the Companies > Access Rights group) * Go to Accounting > Reporting > Tax Return, open the Mod 115 report * Click the gear icon > BOE > Generate BOE > Observation: An AccessError is raised: "You are not allowed to modify 'Companies' (res.company) ... This operation is allowed for the following groups: Access Rights", even though the user is not trying to edit the company. Why the fix: ------------ The BOE wizard shared by Mod 111/115/303 declares a `company_partner_id` field, related to `company_id.partner_id`, with `readonly=False`. That field is invisible in every view and only exists to compute the domain of `partner_bank_id`; it was never meant to be edited by the user. Because it is declared writable, the ORM attaches an inverse to the related field, so saving the wizard (which happens when generating the BOE, since the field, though invisible, is still part of the view and thus of the saved values) writes `company.partner_id` back onto `res.company`, even though the value never actually changes. That implicit write requires write access on res.company, which is only granted to the Access Rights group, causing the error for regular accountants. Dropping `readonly=False` keeps the field as a plain readonly related field, still usable for the bank account domain, without ever triggering that spurious write. opw-6388869 Forward-Port-Of: odoo/enterprise#129841 Forward-Port-Of: odoo/enterprise#126661
Fixed an issue where slower mobile tap actions in automated tests could be mistaken for long presses, causing expected save actions to be skipped. This improves test stability for mobile workflows without changing end-user behavior.
Original PR description
Before this commit, MobileWebSuite.test_unit_mobile failed at random on the mail test "HtmlMail add icon and save inline html", which taps the save button of an html_mail field and waits for the…
Before this commit, MobileWebSuite.test_unit_mobile failed at random on the mail test "HtmlMail add icon and save inline html", which taps the save button of an html_mail field and waits for the write:
[verifySteps] expected the following steps
> Expected: [
"web_save",
]
> Received: []
This happens because a tap held longer than `LONG_TAP_DELAY` counts as a long press, and `_pointerUp` then dispatches no click at all. That delay spans the synthetic pointerdown and pointerup of a single `click()`, so the work a listener does during the press counts towards that delay: tapping save blurs the html field, which converts the whole editor content to inline styles, and under the load of a runbot build that conversion alone takes longer than the delay.
One solution could have been to raise `LONG_TAP_DELAY`, but a listener slower than the new value suppresses the click again.
This commit counts a tap as long only when the test releases the pointer itself, with `pointerUp`, so the work a listener does during a `click()` no longer suppresses the click.
https://runbot.odoo.com/odoo/error/946581
Forward-Port-Of: odoo/odoo#285640
Forward-Port-Of: odoo/odoo#284971Fixed an issue where spreadsheets containing images could not be downloaded as Excel files. The export process now uses the proper image access information, so users can reliably export spreadsheets with embedded images.
Original PR description
Since https://github.com/odoo/enterprise/pull/97488, the images are shared across spreadsheets and the attachments are no longer related to a specific record. Due to the management of the…
Since https://github.com/odoo/enterprise/pull/97488, the images are shared across spreadsheets and the attachments are no longer related to a specific record. Due to the management of the IrAttachment access rights, it means that those attachments are not accessible by anyone by default and we rely on the access_token to display them in the webclient. However, the method that builds the final xlsx file fetches the images from the server and did not use the access token, meaning that it could never access the attachment. Such situation raised a UserError that was caught by the webclient. How to reproduce: - As admin, create a spreadsheet and insert an image inside of it - try to download the spreadsheet as an xlsx file counterpart of https://github.com/odoo/enterprise/pull/126384 Task-6432724 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixed an issue where expanding a newly created Calendar event opened a blank form instead of the event just saved. This prevents users from accidentally creating duplicate events for the same time slot.
Original PR description
Problem: In Calendar, dragging a slot opens the quick create dialog. Typing a subject and clicking the expand button in the dialog header creates the event, but the form that opens is empty, and…
Problem: In Calendar, dragging a slot opens the quick create dialog. Typing a subject and clicking the expand button in the dialog header creates the event, but the form that opens is empty, and saving it creates a second event for the same slot. Cause: `FormViewDialog` stores the id of the record it saved in `currentResId`, which `onExpand` passes as `res_id`. `saveRecord` sets it only in the branch that runs when no `onRecordSave` prop is given. `AttendeeCalendarController` passes one since 30478fe855cd (odoo/odoo#239435), so `currentResId` stays false and the action opens a new record. Solution: Set `currentResId` in the shared branch of `saveRecord`. It is private to `FormViewDialog`, so a consumer passing `onRecordSave` cannot set it. Steps to reproduce: - Open Calendar. - Drag a slot in the week view to open the quick create dialog. - Type a meeting subject. - Click the expand button in the dialog header. - Observe that the form opens on a new record while the event is created. Ticket [link](https://www.odoo.com/odoo/project/49/tasks/6476930) opw-6476930
Manufacturing bills of materials now prevent configurable combo products from being selected as components. This avoids using sales bundle items where physical manufacturing parts are expected, reducing confusion and incorrect production setup.
Original PR description
Version: --------- - 19.0+ Steps to reproduce: ------------------- - Install `mrp` and `sale_management` - Create a product of type `combo` - Go to Manufacturing > Products > Bills of Materials - Create or edit a BoM - Add a component line - Select the combo product Issue: ------ Combo products can be selected as BoM components. Since combo products represent configurable sales bundles rather than physical products, they are not meaningful manufacturing components. Before Commit: ------------------- - It was possible to select a product of type 'combo' as a component in a Bill of Materials. After Commit: ----------------- - Combo products are filtered out of the component picker and cannot be added as BoM components --- opw-6425024 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278861
Fixed an issue in the HTML editor where pressing Enter in a bullet list item containing a table did not split the list as expected. This makes editing list content with embedded tables more predictable and prevents unrelated content from being moved out of the list.
Original PR description
### Steps to reproduce: - Insert a bullet list - Inside of the list, insert a table - Write before and/or after the table (in the same list item) - Press enter before and/or after the inserted text -…
### Steps to reproduce: - Insert a bullet list - Inside of the list, insert a table - Write before and/or after the table (in the same list item) - Press enter before and/or after the inserted text - Notice that the bullet is not split like in a normal list ### Root Cause: - On Enter, list plugin checked whether the list item contained unsplittable element. Since the table was inside the list item, it always treated the list item as unsplittable, even when the cursor was outside the table. As a result, the list item could never be split. ### Solution: - Instead of checking the whole list item, walk up from the split target to the list item and look for an unsplittable element along the way. This allows the list item to split normally when the cursor is outside the unsplittable. - Also fixed the outdent check (from commit [1](https://github.com/odoo/odoo/commit/59dc5393bdf79953eb85ccdc292b0b6dc3b59304)) which only checked if the cursor's block was empty. For example, in a list item like: `<li><p>ab</p><table>...</table><p>[]<br></p></li>` pressing Enter in the empty `<p>` would outdent the entire list item (including "ab" and the table) out of the list. Now also verify that list item has no real content before outdenting. task-6449843 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280903
This fix prevents users from deleting the image inside a website card cover while leaving behind an empty placeholder. It avoids broken card settings and potential errors when editing website snippets.
Original PR description
It was possible to remove the image inside a card cover while keeping the figure wrapper. The card option would then still consider that there was a cover image even though the image was gone, which could also lead to a traceback. Steps to reproduce: - Insert the `s_three_columns` snippet - Click on the image of one card - Either press "Enter", "Delete", "Backspace" - Hover the "Cover Image" options => The image is removed but the `<figure>` is still there, so the option is still considered active (leading to a traceback) task-6081728 Forward-Port-Of: odoo/odoo#283106 Forward-Port-Of: odoo/odoo#280086
Fixes a subcontracting receipt issue where entering a partial quantity could incorrectly mark related work as ready, causing the non-subcontracted item to be left entirely for backorder. This helps ensure mixed receipts from subcontractors are validated with the correct delivered and remaining quantities.
Original PR description
### Steps to reproduce: - Create a subcontracted product P1 - Create a storable product P2 - Buy 5 units of both products from your subcontractor - On the receipt set both moves quantity to 2 Units -…
### Steps to reproduce: - Create a subcontracted product P1 - Create a storable product P2 - Buy 5 units of both products from your subcontractor - On the receipt set both moves quantity to 2 Units - Validate the receipt and create a backorder #### > Only the subcontracted move has been kept on the receipt and a backorder was created for 3 units of P1 and 5 of P2. ### Cause of the issue: Setting the quantity of the subcontracted move will automatically record the quantities on the subcontracted MO: https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/stock_move.py#L83 https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/stock_move.py#L123 https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/mrp_production.py#L91 However, the `_update_finished_move` method adds and update the related subcontracted move lines marking them as *picked* to adapt the related reservation: https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/mrp_production.py#L118-L164 This is problematic since picking a move line will also pick the move: https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/stock/models/stock_move.py#L261-L267 And only picked moves are considered to be processed at picking validation. ### Note: The exact same issue had already been fixed in 17.0: db8b33ebb9fe23507bcba30b12741e4d688ae549 However, the fix had an issue concerning the barcode behavior as it removed the picked computation for subcontracted moves which made hybrid pickings such as the above one (with one subcontracted and one non-subcontracted move) impossible to process in the barcode app. As such, the fix and test where reverted in cf2d18c92bee55ef79db1a338e9baf12f258ee5b The present commit provides an alternative fix of the original issue keeping subcontracted moves unpicked by quantity changes without affecting the picked computation of subcontracted moves (e.g. adding a picked move line on a subcontracted move will still pick that move). Enterprise: https://github.com/odoo/enterprise/pull/123884 opw-6330584 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283927 Forward-Port-Of: odoo/odoo#275304
This update fixes typos and wording inconsistencies in the Point of Sale LNA checklist. It improves clarity for users reviewing the checklist without changing any business process or functionality.
Original PR description
Fixed some typos and inconsistencies on the LNA checklist document Task-[6330795](https://www.odoo.com/odoo/project/1737/tasks/6330795) Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281950
Responses to invoices received through the French PDP system are now sent through only the PDP channel instead of both PDP and classic Peppol. This prevents duplicate chatter messages and reduces confusion for users following invoice communication history.
Original PR description
When responding to an invoice received through PDP, 2 responses were actually sent: one through PDP and the other through the 'classic' Peppol. This was done to allow more reliability, in case one channel is down when trying to respond, but this mainly creates cluttah' in the chattah' (call me busta rhymes yo) task-6522652 Forward-Port-Of: odoo/odoo#285596
Maintenance requests now create follow-up activities with the correct deadline based on the user's local timezone. This prevents activities scheduled for tomorrow from incorrectly appearing due today for users in timezones far ahead of UTC.
Original PR description
Steps to reproduce: =============== - Install `maintenance` module - Set your user's timezone to Pacific/Auckland (UTC+12/+13). - Go to Maintenance > Maintenance Requests > New. - Set the Scheduled…
Steps to reproduce:
===============
- Install `maintenance` module
- Set your user's timezone to Pacific/Auckland (UTC+12/+13).
- Go to Maintenance > Maintenance Requests > New.
- Set the Scheduled Date to tomorrow morning (e.g. 8:00 am).
- Save and check the auto-generated activity in the chatter.
Issue:
====
The activity generated for the maintenance request's Scheduled Date is
created with a deadline of Today instead of Tomorrow, even though the
Scheduled Date was explicitly set to Tomorrow.
Cause:
=====
`schedule_date` is a `Datetime` field, always stored in UTC. In
`MaintenanceRequest.activity_update()`, the deadline for the activity
(a `Date` field) was computed with:
```py
fields.Datetime.from_string(request.schedule_date).date()
```
`from_string()` only parses the stored value; it does not convert it
to any timezone. Calling `.date()` on it therefore truncates the raw
UTC datetime directly, ignoring the timezone of the user who picked
the date.
For a user ahead of UTC by a large enough offset (e.g. Pacific/Auckland,
UTC+12/+13), picking "Tomorrow 8:00 am" in their local timezone can
resolve to a UTC instant that still falls on Today's date (Tomorrow
8:00 local minus 13 hours lands at 19:00 UTC on Today). Truncating
that UTC value straight to a date silently shifts the deadline back
by one full day versus what the user selected.
Fix:
===
The deadline is now computed from the schedule date after converting
it to the record's (user's) timezone, using
`fields.Datetime.context_timestamp()`, instead of truncating the raw
UTC value. This fixes the reported issue for any user whose timezone
offset makes the truncation land on the wrong side of midnight UTC.
---
Note - automatic activity creation is remove from this [commit](https://github.com/odoo/odoo/commit/ab63e9709d8ea790a8a72a0a86b8bccc478c02d5) in saas-19.2
---
opw-6363868
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#275330This update adds test coverage to confirm that shared spreadsheets and spreadsheet editing continue to export correctly to Excel when images are included. It helps prevent regressions that could affect users sharing or downloading spreadsheet files with visual content.
Original PR description
Counterpart of https://github.com/odoo/odoo/pull/279788 Task-6432724
Users can no longer create order boxes from the point-of-sale printer settings. This helps ensure each order box is only set up when paired with a real physical device, reducing configuration mistakes.
Original PR description
We prevent users from creating oboxes from pos.printer model, as oboxes should be paired with an actual device.
Swiss payroll no longer records automatic pension mutation refreshes in employee history. This prevents routine background updates from cluttering the activity log, making important changes easier to find.
Original PR description
Calling `_create_or_update_snapshot` after writing on an employee recomputes `lpp_mutations`, deleting and recreating the linked records. Because `lpp_mutations` was tracked, every `write` on an employee generated unhelpful chatter entries, cluttering important history. This was especially noisy during frequent writes in hourly crons. Disable field tracking on `lpp_mutations` to improve chatter clarity and overall user experience. opw-6285407 --- Forward-Port-Of: odoo/enterprise#129481 Forward-Port-Of: odoo/enterprise#128033
Fixes an issue in Restaurant Point of Sale where clearing an internal note could make the preparation display cancel an existing item and send it again as new. Kitchen staff now see the original preparation line updated correctly, reducing duplicate cooking and order confusion.
Original PR description
Steps to reproduce ------------------ 1. Open a restaurant PoS with a preparation display. 2. Add a product, put the internal note "A" on the line, press Send. 3. Change the note from "A" to "B",…
Steps to reproduce ------------------ 1. Open a restaurant PoS with a preparation display. 2. Add a product, put the internal note "A" on the line, press Send. 3. Change the note from "A" to "B", then open the note again and click "discard", that will clear it. 4. Press Send. -> The line is fully cancelled on the preparation display and the same quantity is sent again as a new order. Expected Behavour ----------------- The existing line should be updated in-place instead of cancelling and re-creating a new one. Why it's happening ------------------ In `_process_preparation_changes` we calculate a key for every line and the `note` is inside this key. For the order lines we take `line.note or "[]"`, but for the note history we take `note['new'] or ''`, in that case, the keys don't match, so we cancel the current one and create a new line!! The fix ------- We use `"[]"` now, in many places (for completness), a follow up of 484ef2e8b2b. opw-6467346 Forward-Port-Of: odoo/enterprise#128601
This fix prevents automated test failures from being reported twice because a final memory log was written after the browser test had already ended. The memory information is still captured, but it is now logged before the final test result so teams see the real failure more clearly.
Original PR description
Before this commit, a build whose unit tests suite fails collects a second runbot error on top of the real one: odoo.addons.web.tests.test_js.WebSuite.test_unit_desktop.browser: Error received after…
Before this commit, a build whose unit tests suite fails collects a second runbot error on top of the real one:
odoo.addons.web.tests.test_js.WebSuite.test_unit_desktop.browser:
Error received after termination: [MEMINFO] tests done (after GC)
- used: 220535896 - total: 341990868 - limit: 4395630592
Note that the figures themselves are fine (220MB used of a 4.4GB limit): the error is not about memory, only about when the line is logged.
This happens because the runner logs that line after `stop()`, and `stop()` is what reports the suite result: the server settles the test on that report and kills the browser. The final major GC outlasts the report. A passing build escapes because the browser dies within milliseconds; a failing one stays up for the screenshot, long enough for the GC to finish and the line to land after termination.
This commit fixes the issue by moving `stop()` after the final cleanups and their memory log: the [MEMINFO] line is still logged, but before the result report, while the server still waits on the browser.
https://runbot.odoo.com/odoo/error/229655
Forward-Port-Of: odoo/odoo#284957This fixes an internal testing issue where a failed setup request could leave automated web tests waiting until a long timeout. Test failures are now reported promptly, helping teams identify build problems faster and reducing wasted CI time.
Original PR description
Before this commit, a unit test suite could go silent between two test files, and `browser_js` then waited out its whole timeout before failing with:
[ERROR] TypeError: Failed to fetch
at orm (web.assets_unit_tests.min.js)
FAIL: WebSuite.test_unit_desktop
AssertionError: Script timeout exceeded
This happens because `fetchDependencies` gives every addon a `Deferred` that only the success handler of the batched `all_dependencies` call resolves, and a loaded runbot host answers that call with `RuntimeError: can't start new thread`. Nothing then settles those deferreds, so the `Promise.all` waiting on them never returns, `stop()` is never called, and the browser holds the build until the timeout.
This commit rejects the deferreds of a failed batch and reports an error escaping `runTests` on the console, so the build fails on the fetch error instead of running out its timeout.
https://runbot.odoo.com/odoo/error/243504
Forward-Port-Of: odoo/odoo#284980This fix prevents restaurant POS orders from losing their items when the same table is edited or refreshed across multiple POS devices before payment. It helps ensure paid orders keep the correct products, totals, and records, reducing reconciliation issues and data loss in multi-device sessions.
Original PR description
Steps to reproduce: - Restaurant config with two POS devices on the same session - Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids) -…
Steps to reproduce:
- Restaurant config with two POS devices on the same session
- Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids)
- Device A: remove both lines, without syncing
- Device B: touch the same order, so device A re-reads it from the server through the synchronisation websocket
- Device A: pay and validate the order
Issue:
The order is saved as paid, with its total and its payment, but without any orderline. The lines are deleted on the server: odoo.models.unlink: deleted pos.order.line records with IDs: [...]
Cause:
Removing an orderline queues an unlink command in
models.commands['pos.order'].unlink['lines_<order id>'] (delete_ in related_models.js). That command is only discarded by clearCommands(), which syncAllOrders() calls after a successful sync, so it stays pending in between.
Any read of the order in that window puts the line back: a deleted record counts as missing in missingRecursive(), so it is fetched again and loadData() re-creates it with its server id.
serialize() then emits both the update of the live line and the still pending unlink, and sync_from_ui writes
lines: [[1, id, {...}], [3, id]]. The ORM applies commands in order, and pos.order.line.order_id is ondelete='cascade', so Command.UNLINK deletes the line right after writing it.
Fix:
Skip a removal command when the record is still linked to the parent at serialization time. A record cannot be both linked and removed in the same payload, so the pending command is stale and dropping it keeps the local state. Genuine removals, where the record is no longer linked, are still sent.
opw-6401146
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285427
Forward-Port-Of: odoo/odoo#284695This fixes the identification type sent in QR data for Argentine export invoices. As a result, customers can validate these invoices correctly on the ARCA verification page as legal documents.
Original PR description
Before this change we were sending id type code 0 and this generate two problems * ARCA verification page it was wrongly taking "CI Policia Federal" as the identification type of the receptor * We were not able to validate the expo invoice, we get always an error With this change the expo invoice can be checked as a real legal document in the ARCA page https://servicioscf.afip.gob.ar/publico/comprobantes/cae.aspx Forward-Port-Of: odoo/enterprise#126704
Product variant prices now keep the same minimum decimal display as the product template price they come from. This prevents reports and Studio customizations from showing shortened prices, such as 19.5 instead of 19.50, improving consistency in customer-facing documents.
Original PR description
Issue: --- Record's `min_display_digits` is not inherited in case of a model inheritance. e.g. `list_price` is inherited to `product.product` from `product.template`, however it renders with fewer decimals than `product.template.list_price` in reports. Steps to reproduce: 1- Set a product's Sales Price to 19.5. 2- Add `product.product.list_price` to a report using studio. The variant's field renders with 1 decimal while `decimal.precision` is 2. Cause: --- This is missed in ee32a17495a10af18f0b5de0a295283c5059d3c2 which introduces minimum precision. opw-6465159 Forward-Port-Of: odoo/odoo#285336 Forward-Port-Of: odoo/odoo#285064
A mail test helper now stops repeating checks after a timeout instead of letting one failed test trigger many unrelated failures. This makes automated test results clearer and helps teams identify the real cause of a problem faster.
Original PR description
Before this commit, one test contains() reaching its 10 seconds budget made the 129 tests that ran after it fail with its own assertion, over 14 suites:
[HOOT] Test "@mail/message/link_preview/Delete all link previews at
once" failed:
Failed assertion:
3. [toBe] expected values to be strictly equal (Failed to find 1 of ".o-mail-Message-body:text(...)" (Timeout of 87 seconds). Found 0 instead.)
This happens because the tick re-schedules itself on the result of runOnce(), which is undefined on a failure, the crashing one included. The check keeps selecting every 500ms and logs its assertion against whichever test is running, until a tick lands between two suites and getFixture() throws.
This commit re-schedules a tick only while the check is not done, so the crashing one is the last.
https://runbot.odoo.com/odoo/error/946650
Forward-Port-Of: odoo/odoo#285585Vendor bills created from expenses in Mexico now correctly display their CFDI XML attachments when available. This helps users access required tax documents directly from the bill and ensures these documents can be checked with SAT when needed.
Original PR description
**Current behavior:** Currently, account moves created from expenses (type receipt) don't include the XML when it is a CFDI. Causing users cannot see their attachment even though it is created on DB. https://docs.google.com/videos/d/1eFSAA-wvUzPDwIJDeiS2dkne94lGM7QBxRfjN3HxaLw/play **Versions:** 19+ **Fix:** Implementing a new helper to identify those moves that can actually hold a CFDI document (for now, all `is_invoice()` documents + vendor bill receipt, `in_receipt`), so now, we include 'in_receipts' in _compute_l10n_mx_edi_cfdi_state_and_attachment, _compute_l10n_mx_edi_document_ids, _compute_l10n_mx_edi_update_sat_needed and l10n_mx_edi_cfdi_try_sat. As these documents might need to fetch SAT services as well. Task-id: [6397080](https://www.odoo.com/odoo/project/49/tasks/6397080) Forward-Port-Of: odoo/enterprise#126493
The Kenya e-invoicing form now hides the validation message area when there is no message to show. This removes an unnecessary gap in the invoice form, making the screen cleaner and easier to read.
Original PR description
When there is no validation message, an empty div causes a whitespace gap between the header and the sheet in the form view. This commit adds `invisible="not l10n_ke_validation_message"` to the div to prevent this issue. Forward-Port-Of: odoo/enterprise#129364 Forward-Port-Of: odoo/enterprise#129318
This fix prevents upgrade warnings when employee records temporarily have no version during the HR migration process. It helps keep upgrade logs cleaner and avoids a harmless but confusing error while employee data is being backfilled.
Original PR description
### Issue: A warning `IndexError: tuple index out of range` is logged on RunBot when upgrading from 18.0 to 19.0 for employees without any version ### Cause: `_get_version` on `hr.employee` falls…
### Issue: A warning `IndexError: tuple index out of range` is logged on RunBot when upgrading from 18.0 to 19.0 for employees without any version ### Cause: `_get_version` on `hr.employee` falls back to `versions[0]` when no version matches the given date But if the employee has no versions at all, `versions` is empty and `versions[0]` raises an `IndexError` ### Steps to reproduce: - Create a database with `hr_attendance` installed in 18.0 - Upgrade to 19.0 Before the fix, a warning is logged during the upgrade ### Notes: An employee without any version is a transient state during upgrade `hr/saas~18.4.1.1/end-migrate.py` backfills a version for every employee still missing one (`current_version_id IS NULL`) once the whole upgrade chain is done Since it only runs at the very end, an employee can still be found without a version by earlier steps (e.g. modules reloading their demo data), which is when this warning was logged Confirmed on an upgraded RunBot database that employees without a version before the upgrade do end up with one once fully completed runbot-241187 Forward-Port-Of: odoo/odoo#284467
This fixes how Nilvera refund documents are handled in the Turkish e-invoice integration. Refunds sent with positive amounts but refund-specific labels are now imported as refunds and linked to the original invoice when there is a clear match, improving accounting accuracy.
Original PR description
# Description of the issue/feature this PR addresses: Nilvera can send refund documents as Invoice with refund-specific InvoiceTypeCode values. # Current behavior before PR: The default UBL import logic only treats an Invoice as a refund when its amount is negative. As a result, Nilvera refund documents sent as Invoice with positive amounts are imported as invoices. Also, imported refunds are not linked to their original invoice. # Desired behavior after PR is merged: Nilvera refund documents using refund-specific InvoiceTypeCode values are imported as refunds. When a unique match is found, the imported refund is linked to its original invoice through reversed_entry_id. task-id-5948275 I confirm I have signed the CLA and read the PR guidelines at [www.odoo.com/submit-pr](http://www.odoo.com/submit-pr) Forward-Port-Of: odoo/odoo#259672
This fixes an issue where Argentine ecommerce product pages could show a tax-excluded price with a discount applied twice. Customers now see consistent tax-excluded prices between product listings and product detail pages, reducing pricing confusion at checkout.
Original PR description
Steps to produce: --- - Create a company with `Argentina` as the country. - Switch to the newly created company. - Create a new website for the `Argentina company`. - Create a pricelist with a `23%`…
Steps to produce: --- - Create a company with `Argentina` as the country. - Switch to the newly created company. - Create a new website for the `Argentina company`. - Create a pricelist with a `23%` discount on the sales price for all products. - Create a new product, set the sale price to 100, remove all tax, and publish. - Go to the website and switch to the newly created Argentina company website. - Go to the Shop page and open the product. Issue: --- - The tax-excluded price (`Precio s/Imp. Nac.`) displayed on the shop catalog card differs from the tax-excluded price displayed on the product detail page. Root cause: --- - In `_get_additionnal_combination_info`, [1] returns the unit price with the pricelist discount already applied. However, when `combination_info['has_discounted_price']` [2] is `True`, the method applies the discount again manually, resulting in the pricelist discount being applied twice on the product detail page. Solution: --- - Remove the redundant discount calculation block. This ensures that the tax-excluded price displayed on the product detail page matches the price shown on the shop catalog card. [1]https://github.com/odoo/odoo/blob/7f9560bd0ff66882459593a2d043c0197ceec0bb/addons/l10n_ar_website_sale/models/product_template.py#L61-L62 [2]https://github.com/odoo/odoo/blob/7f9560bd0ff66882459593a2d043c0197ceec0bb/addons/l10n_ar_website_sale/models/product_template.py#L71-L74 opw-6480531 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283761
This update keeps Odoo compatible with newer system versions of a certificate library used for secure connections. It prevents unnecessary warning messages and helps certificate-based email and connection features continue working reliably across different deployments.
Original PR description
pyOpenSSL 24.3.0 deprecated passing its own X509/PKey objects to Context.use_certificate()/use_privatekey(), and started accepting cryptography objects instead. Odoo pins pyopenssl 24.1.0, but the distro builds run the version shipped by the OS: since the test added by f0fb287c6502 covers that path, they now add a warning in the logs. Load the certificate and the key as cryptography objects when the installed pyOpenSSL supports them, keep the previous loaders otherwise. Reference: https://github.com/pyca/pyopenssl/commit/b0cb4b4 This fix is based on https://github.com/odoo/odoo/blob/a2b4f618328f3ce3f654fd2c1ee4410365706a7e/odoo/addons/base/models/ir_mail_server.py#L34-L46 runbot-944176 Forward-Port-Of: odoo/odoo#284802 Forward-Port-Of: odoo/odoo#277449
Event agenda pages now scroll horizontally more smoothly in Firefox and Safari when many agenda items are shown. This prevents visible shaking or stuttering, improving the browsing experience for event visitors.
Original PR description
On Firefox and Safari, horizontal scrolling on an agenda with many items stuttered. This was likely caused by triggering too many scroll events, without throttling them. Steps to reproduce: - On Firefox or Safari, run with demo data - Go to the "Design Fair Los Angeles" agenda (click on the event > Talks > Agenda. Alternatively, enter the URL directly: `/event/ID/agenda`, with the right event ID) - Resize the page (or open the dev tools) so that there is a horizontal scrollbar. - Scroll horizontally with a trackpad or with shift + mouse wheel. => The agenda stutters/shakes: as you go to the right, it sometimes slightly goes back left, and vice-versa. You might have to scroll from left to right and right to left a few times to see the issue. Forward-Port-Of: odoo/odoo#285237
Opening a page from the Website Pages list now keeps users on their current Odoo address when the page belongs to the default website. This prevents users from being sent to another domain, where they could lose context or be asked to log in again.
Original PR description
Steps to reproduce: =================== 1. Go to Website > Pages 2. Set a real domain on the default website 3. Open any page linked to that website => User is redirected to the real domain When clicking on a page linked to the default website from the Website > Pages list, if that website had a real domain configured, the action was redirecting the user to that domain instead of staying on the current host (e.g. <db>.odoo.com). since it's a different domain the user would then end up in the login page and lose the context of the page they wanted to open. Solution: ========= Now we change the behavior as requested by PO. On <domain>.odoo.com, in the Pages list view, if the page is linked to the default website, keep <domain>.odoo.com and don't redirect to the real domain => User should stay on the current host opw-6141457 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261698
This fixes Polish e-invoicing so certain 0% EU reverse charge taxes are correctly marked in the KSeF XML. It helps ensure invoices contain the right reverse charge indicator and tax base, reducing compliance and reporting errors.
Original PR description
**PROBLEM** K_12 taxes needs to be reported as reverse charge tax. **STEP TO REPRODUCE** 1. Create an invoice with the tax 0% EU U. 2. Send the invoice the ksef. 3. Open the xml, an notice P_18 value is 2 (meaning no reverse charge), and there is no tag P_13_10. expected behavior: P_18 = 1, P_13_10 = base for the tax. opw-6460338 Forward-Port-Of: odoo/odoo#281934
Frontdesk now explicitly includes the component needed to load its planning timeline views. This prevents installation failures in setups where automatic optional apps are skipped, making deployments more reliable.
Original PR description
Installation of this module fails in single-app skip auto-install. Gantt is not a valid root element in views when web_gantt isn't installed. There is no error on other builds because of the auto-install dependency using the following chain; [frontdesk] ──[depends]──> [hr] ──[depends]──> [web] ──⚡[AUTOLOAD]──> [web_gantt] This change needs forward porting to v19. saas-19.1 already has a fix. REF Runbot; https://runbot.odoo.com/odoo/error/237890 Forward-Port-Of: odoo/enterprise#128592
Closing a Point of Sale session no longer fails when orders include both a tracked product and kit products that use it as a component. The fix correctly totals quantities across multiple matching order lines, helping cashiers complete sessions reliably.
Original PR description
**Step to reproduce :** 1. Create Product A and enable lot tracking for it. 2. Create a Kit product and add Product A as one of its components. 3. Go to PoS and create orders using: - Product A - The…
**Step to reproduce :** 1. Create Product A and enable lot tracking for it. 2. Create a Kit product and add Product A as one of its components. 3. Go to PoS and create orders using: - Product A - The Kit product - A combination of Product A and the Kit product 4. Create three different orders with these combinations. 5. Try to close the PoS session. **Issue :** An error occurs when attempting to close the PoS session. The issue is in the pos_mrp module, specifically in the _get_lot_line_qty method. When accessing the following line: `lines_data[move.bom_line_id.bom_id.product_tmpl_id.product_variant_id.id]['order_lines'].qty` we expect order_lines to contain a single record. However, in this scenario, multiple order lines can be returned for the same product. As a result, accessing .qty directly on order_lines raises a singleton error. **Solution :** With this fix, we first retrieve the quantity from each order line and thensum the quantities together. This ensures that multiple matching order lines are handled correctly and prevents the singleton error when closing the PoS session. opw-6442765 Forward-Port-Of: odoo/odoo#284746 Forward-Port-Of: odoo/odoo#281622
Opening Project Update items from the activity menu now shows only updates that have the current user's activities for the selected timing bucket. This prevents users from seeing unrelated project updates and makes activity follow-up clearer and more accurate.
Original PR description
**Problem:** Opening a Project Update activity from the activity menu (the clock icon in the systray) lists every project update of every user, instead of the ones carrying the current user's…
**Problem:**
Opening a Project Update activity from the activity menu (the clock icon in the systray) lists every project update of every user, instead of the ones carrying the current user's activities.
**Steps to reproduce:**
1. Schedule an overdue activity on a project update
2. Have a colleague create another project update with no activity
3. Click the clock icon in the systray
4. Under "Project Update", click the "Late" count
**Current behavior:**
The list opens unfiltered and shows all project updates, including the ones of other users and the ones carrying no activity at all.
**Expected behavior:**
The list shows only the updates carrying the user's own activities, restricted to the bucket that was clicked.
**Cause of the issue:**
The activity menu never builds a "my activities" domain. `openActivityGroup` in `mail/static/src/core/web/activity_menu.js` narrows the generic act_window it opens purely through context keys — `search_default_filter_activities_my` plus `search_default_activities_overdue` / `_today` / `_upcoming_all` — and the only domain it forwards comes from `_get_activity_groups`, which is limited to `[('active', 'in', [True, False])]`. A `search_default_<name>` key is resolved against a filter of that name in the model's search view, and is silently dropped when no such filter exists. `project.update`'s search view never declared them, so every key the menu sends is discarded and the action opens on an empty domain.
**Fix:**
`project.project` and `project.task` — the addon's two other `mail.activity.mixin` models — already declare this block of invisible activity filters, as does every other model reachable from the activity menu. Declaring them on `project.update` is what makes the menu's context keys resolvable, and keeps the model consistent with the rest of the codebase instead of special-casing `project.update` on the client side.
opw-6416397
Forward-Port-Of: odoo/odoo#281511Project updates now show the same invoicing and billing profitability figures as the project dashboard. This prevents already invoiced or bill-related amounts from appearing incorrectly, giving managers more reliable project margin information.
Original PR description
Steps to reproduce: ------------------- 1. Install sale_timesheet. 2. Create a service product with: - Invoicing Policy: Prepaid/Fixed Price - Create on Order: Project & Task 3. Create a sales order…
Steps to reproduce: ------------------- 1. Install sale_timesheet. 2. Create a service product with: - Invoicing Policy: Prepaid/Fixed Price - Create on Order: Project & Task 3. Create a sales order with this product. 4. Open the generated project > dashboard and verify that "To Invoice" shows $1. 5. Create a project update and observe that "To Invoice" also shows $1. 6. Return to the sales order, create and post the invoice. 7. Open the project dashboard again and verify that "To Invoice" is now $0. 8. Create another project update. Issue: ------ The project update margin still displays $1 under "To Invoice" even though the sales order has already been fully invoiced. Cause: ------ The project update template displays the aggregated profitability totals (`profitability['total']['revenues']` and `profitability['total']['costs']`) instead of the dedicated `to_bill_to_invoice` and `billed_invoiced` values, causing stale "to invoice" amounts to persist after invoicing. Solution: --------- Use the `to_bill_to_invoice` and `billed_invoiced` values when rendering the project update profitability report. opw-6323869 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278646 Forward-Port-Of: odoo/odoo#273605
This fixes week grouping when Monday is selected as the first day of the week in locales that normally start on another day, such as Sunday. It prevents work hours from being split across the wrong weeks, helping weekly limits and reporting apply to the correct dates.
Original PR description
**Description of the issue/feature this PR addresses:** `weeknumber` takes a `first_week_day` override, documented as `(0 = Monday, ..., 6 = Sunday). If None, derived from the locale.`, but tests it…
**Description of the issue/feature this PR addresses:** `weeknumber` takes a `first_week_day` override, documented as `(0 = Monday, ..., 6 = Sunday). If None, derived from the locale.`, but tests it with `if not first_week_day`, so Monday (`0`) is discarded and the locale's own day is used. `resource` passes `int(get_lang(self.env).week_start) - 1`, which is exactly `0` for Monday, at three call sites. #259600 added the parameter for the opposite case, a Monday locale with a Sunday-start user, and that direction works; this one never has. **Current behavior before PR:** With `en_US` (first day Sunday) and week start set to Monday, `resource_resource.py:299` builds `week_start_date` from Monday while `:303` buckets by Sunday. Monday 2026-08-24 to Sunday 2026-08-30 comes back as W35 for six days and W36 for the Sunday, so one week's hours are split across two buckets and the `hours_per_week` cap lands on the wrong days. **Desired behavior after PR is merged:** The override is honoured and those seven days are all W35. I checked this with a partition property: every day of a `first_week_day`-aligned week must share one `(year, week)`. Over 9 locales x 7 values of `first_week_day` x 12 years, 275940 combinations, 5 locales failed and every failure was in the `first_week_day=0` bucket. After the change there are none. I also diffed every output before and after, 315360 combinations of locale, `first_week_day` and date: 4460 rows change, all of them `first_week_day=0` on a non-Monday locale. `None` and values 1 to 6 are byte identical, so the change is contained to the broken case. Targeting 19.0 because that is the oldest branch carrying the parameter; 17.0 and 18.0 do not have it. AI-assisted: I used Claude to sweep the parameter space and write the tests. Forward-Port-Of: odoo/odoo#285508