Wednesday, August 26, 2026
32 changes · master
Resolved issues and error corrections
Restores the page state used when mobile bottom sheets, such as the blog mobile menu, are open. This fixes incorrect styling and prevents the page behind the sheet from scrolling, improving the mobile browsing experience.
Original PR description
Regression from #281432: the `bottom-sheet-open` class the bottom sheet plugin set on the body was removed there, following [a review comment of…
Regression from #281432: the `bottom-sheet-open` class the bottom sheet plugin set on the body was removed there, following [a review comment of mine](https://github.com/odoo/odoo/pull/281432#discussion_r3756639100) that wrongly concluded it was dead code — my grep missed `website.scss`, which keys the frontend bottom sheet styles on `body.bottom-sheet-open` and uses it to lock the page scroll behind an open sheet:
```scss
body.bottom-sheet-open {
--BottomSheetStatusBar__entry-background--active: #{$primary-bg-subtle};
...
// Prevent the page from being scrollable when a bottom sheet is open.
overflow: hidden;
}
```
Since then the blog mobile menu is styled wrong and the page scrolls behind the sheet on mobile.
This restores @jucop-odoo's original code from the PR, with two changes:
- `bottom-sheet-open-multiple` is not restored: nothing in odoo/enterprise ever read it.
- the count is decremented from the overlay's `onRemove`, which runs exactly once, rather than from the returned `remove`, which can be called several times for the same sheet.This update lets saved searches and default filters correctly use newer relative date options such as today or this week. It also improves date display consistency for locales using non-Western numerals and keeps older customized views from breaking.
Original PR description
In the previous [PR](https://github.com/odoo/odoo/pull/267980) we introduced relative filters (such as `today`, `this week`, ...) and removed the hardcoded filters that duplicated them…
In the previous [PR](https://github.com/odoo/odoo/pull/267980) we introduced relative filters (such as `today`, `this week`, ...) and removed the hardcoded filters that duplicated them (`3e7106201cfe`), for example:
```xml
<!-- BEFORE (planning/views/planning_views.xml) -->
<filter string="Start Date" name="start_datetime" date="start_datetime" end_month="1">
<filter name="start_today" string="Today" domain="[
('start_datetime', '<', 'today +1d'),
('start_datetime', '>=', 'today'),
]"/>
<filter name="start_this_week" string="This Week" domain="[...]"/>
<filter name="start_next_week" string="Next Week" domain="[...]"/>
</filter>
<!-- NOW -->
<filter string="Start Date" name="start_datetime" date="start_datetime"/>
```
However this would now break if trying to target the new relative filter:
```xml
<field name="context"> { 'search_default_start_datetime': 'custom_start_today' }</field>
```
(This example was not in production code but this PR tried to do it, and it broke on rebase:
https://github.com/odoo/enterprise/pull/125775)
In this PR we make it so that relative filters (not only today) can be activates through context keys and `default_period=`. We also make a number of small improvements / refactoring and 1 localisation fix.
**Commit 1** - [REF] web: tidy relative filter internals
- Make filter and option lookups throw on an unknown id instead of returning `undefined` and failing later.
- Centralize the "period + year" concatenation in one `joinWithYear` helper (2 call sites, one from the previous PR).
- Build `displayedFilterItems` in a single pass, attaching each date filter's relative twin to it.
- Drop a missed hardcoded "Today" in `stock_fleet`'s batch transfer view.
- Re-add the four range attributes to the RNG as deprecated-but-accepted, so a custo view no longer crashes.
(Will do a follow-up PR with an upgrade script to remove it)
**Commit 2** - [IMP] web, base: smart dates as date filter defaults
- Accept relative option ids in both `default_period` and `search_default_<filter>` (`today`, `this_week`, `this_month`, `this_quarter`, `this_year`).
**Commit 3** - [FIX] web: use the active numbering system for hand-built date labels
- The week number of the `formatLocalWeekRange` was not being translated to locale numbering system.
**Enterprise commit** - [REM] helpdesk, frontdesk: drop Today filters duplicating the smart date
- Remove the Today filters on Closed On / Creation Date / Rated On (helpdesk) and next to Date (frontdesk), that are redundant with the relative filters we introduced in the previous [PR](https://github.com/odoo/odoo/pull/267980)
Follow up to: https://github.com/odoo/odoo/pull/267980
Enterprise: https://github.com/odoo/enterprise/pull/128934When users start a timesheet timer from a task linked to a sales order item, the timer now automatically keeps that sales link and shows the billable option immediately. This prevents saved timesheets from losing billing information and reduces manual correction work.
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
Financial reports now use a generic Date column instead of Invoice Date, so payments and journal entries show the relevant payment or entry date rather than appearing blank. This makes Aged Payable, Aged Receivable, and Partner Ledger reports clearer and more consistent for users reviewing balances.
Original PR description
- In Aged Payable and Aged Receivable show payment date for payments and journal entry date for misc entries instead of leaving them empty, just like Partner Ledger. - In Partner Ledger, Aged Payable and Aged Receivable, rename 'Invoice Date' column to 'Date', because former will be appropriate for invoice but in case of payment or misc entry, we'll show payment date or JE date. So more generic column name 'Date' is more appropriate. - In tests setup, move for month 11th is created before 10th and no invoice_date was being set, so in report invoice_date was empty, and in tests where we have sort by invoice_date ASC and where nothing is given report sorted it with creation date ASC , because invoice_date was missing. But now when we have added fallback for invoice_date, the report is using that Date column for sorting and therefore tests are modified to show 10th month entries before 11th. [task- 6467119](https://www.odoo.com/odoo/project/967/tasks/6467119)
The payroll employee card now shows the wage from the employee's contract for the relevant payslip period, rather than relying on the payslip value. This gives payroll users a more accurate historical view and correctly presents hourly wages without converting them to a monthly format.
Original PR description
The wage displayed in the payroll information was coming from the payslip itself instead of the employee's contract version at that specific period. And if the employee had an hourly wage, it was forced into a monthly format. This fetches the historical wage directly from the contract version linked to the payslip period and fixes the hourly display. task-6361600
Mexican payroll users can now refresh SAT status directly from a payslip. The change also ensures payslip tax documents are included in automatic SAT status checks, so validated CFDIs no longer remain shown as undefined in Odoo.
Original PR description
Add a new button to update SAT info on MX payslips. target: master task-6231563
This fixes an issue where the same contact assigned to sign multiple times could be prompted to sign again before other required signers had completed their turn. Signing requests now only show the next document to a user when it is actually their turn, helping businesses keep approval flows in the intended order.
Original PR description
### Steps to Reproduce: 1. Create a sign request and have 3 total signers (User, Customer, Employee) 2. Enable Signing Order and make the order as follows: (1) User, (2) Customer, (3) Employee But…
### Steps to Reproduce: 1. Create a sign request and have 3 total signers (User, Customer, Employee) 2. Enable Signing Order and make the order as follows: (1) User, (2) Customer, (3) Employee But make the User and Employee the same contact 3. Send and sign the request > Notice that (1) is able to sign for (3) immediately after, (2) has not signed yet. ### Description of the issue/feature this PR addresses: **Issue:** The signing order is ignored when the same user has to sign multiple times on a document, even if it is configured for a different person to sign in between. This happens because all signature request items are initialized in the 'sent' state upon creation, rather than strictly advancing based on the order. As a result, the system prematurely allows users to sign out of order and prompts them with their next turn too early. **Solution:** To resolve this, the controller was updated to include an `is_mail_sent = True` domain filter. This ensures that the UI's post-sign popup only displays documents where it is explicitly the user's active turn, rather than prompting a premature sign. ### Current behavior before PR: Users are able to sign prematurely, and the system will disregard the configured signing order. ### Desired behavior after PR: Users will only be prompted and able to sign a document when it is explicitly their turn, per the `mail_sent_order`. This way, documents are signed in order. opw-6417327 Forward-Port-Of: odoo/enterprise#128487 Forward-Port-Of: odoo/enterprise#125573
The Inventory Valuation report now includes accounting differences for products that currently have no stock on hand. This helps businesses spot and correct unbalanced valuation or variation accounts without losing the performance benefit of skipping full value calculations for zero-quantity products.
Original PR description
## Problem If multiple valuation/variation accounts are used, the Inventory Valuation report will not show the balance of accounts attached to products that have 0 quantity available if a different…
## Problem If multiple valuation/variation accounts are used, the Inventory Valuation report will not show the balance of accounts attached to products that have 0 quantity available if a different account has quantity. ## Solution In order to maintain the performance improvements intended by the commit that introduced the `qty_available != 0` filter, we will avoid calculating `total_value` for products with 0 quantity. We will still run `stock_accounting_value` on these products in order to capture interim accounting value on the Inventory Valuation report. ## Steps to Reproduce (Runbot v19) (defer to the test for more info) 1. Create an extra set of valuation/variation accounts 2. Create a product, avco perpetual accounting the default valuation/variation accounts 3. Create a second product, avco perpetual accounting the new valuation/variation accounts 4. Purchase 1 unit of each of the products and receive, bill both 5. Sell 1 unit of the product attached to the new valuation/variation 6. Go to Accounting > Review > Inventory Valuation, and note that the new valuation/variation accounts are not present. If you click Generate Entry, you will see that these accounts need to be balanced opw-6473319 Forward-Port-Of: odoo/odoo#283787 Forward-Port-Of: odoo/odoo#282819
This fixes an issue where users could not create custom fields from the technical settings after installing AI Fields. The system now ignores an empty model value submitted by the form, preventing an unnecessary validation error and restoring the expected setup flow.
Original PR description
**Steps to reproduce** - Install `ai_fields` - In debug mode, go to Settings > Technical > Fields - Trying to create any field results in a `Validation Error` **Cause** Commit ebeab340264af42450b74a76d69fc284b600f94c introduced a check on `model` to prevent a mismatch. `ai_studio` adds the `model` as an invisible field to the form, sending a `False` value to the `create`. https://github.com/odoo/enterprise/blob/bafa674d287577a0f32e08af165dd9561b1667c0/ai_fields/views/ir_model_views.xml#L39 **Change** Ignore `model` if it is `False` opw-6463182 Forward-Port-Of: odoo/odoo#283018
This fix restores the ability to delegate Studio approval responsibilities as intended. It helps teams keep approval workflows moving when the original approver is unavailable or responsibilities need to be reassigned.
Original PR description
opw-6321766 Forward-Port-Of: odoo/enterprise#128666 Forward-Port-Of: odoo/enterprise#122441
This fix prevents discounts from being counted twice when calculating withholding tax amounts. Businesses using withholding taxes will see more accurate tax totals on affected accounting documents.
Original PR description
When calculating tax details for `withholding_total_amount_currency`, the discount was already applied to `price_unit` before passing it to `_add_tax_details_in_base_line`. However, `_add_tax_details_in_base_line` already applies the discount itself. As a result, the discount was applied twice, causing the withholding amount to be calculated incorrectly. In this commit, remove the initial discount calculation and pass the original price_unit so that the discount is applied only once. ref- https://github.com/odoo/odoo/blob/master/addons/account/models/account_tax.py#L1804C9-L1804C34 task-6471414 Forward-Port-Of: odoo/odoo#282419
The accounting reports date filter now keeps the correct fiscal year when users switch between companies with different fiscal year calendars. This prevents reports from showing incorrect date ranges, helping businesses compare and review financial data more reliably.
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#127945 Forward-Port-Of: odoo/enterprise#117603
Point of sale orders using online payments now validate correctly after items are added or changed. The system updates the order total before checking payment status, preventing incorrect payment errors and reducing checkout disruption.
Original PR description
When products were added after selecting an online payment method and going back to the floor plan, the subsequent validation failed with "Invalid online payments" because the server's amount_unpaid was based on the old order total. Fix: sync the order to the server before querying amount_unpaid, so the server always has the latest total when checkRemainingOnlinePaymentLines is called. Also guard cancelPayment against calling the payment terminal interface on online payment methods that do not use one. task-id: 6330704 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The point of sale customer display now updates more consistently during checkout, ensuring shoppers see the latest order information. The change also reduces unnecessary refreshes and communication behind the scenes, improving responsiveness and stability.
Original PR description
Fixes customer display updates by moving the data format and preparation synchronously inside the useEffect hook. This allows OWL 3 to correctly register reactive dependencies, while only the final dispatch remains debounced (100ms). Optimizations: - Persist CustomerDisplayPosAdapter on Chrome. - Add JSON serialization diffing before dispatch to skip redundant RPC/broadcasts. - Use recursive deepUpdate in-place to avoid complete DOM re-renders. task-id: 6292603 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Point of Sale receipts now show the correct cash rounding line even when a customer pays more than the order total. This improves receipt accuracy and keeps payment totals consistent for cash transactions that require rounding.
Original PR description
**Steps to reproduce:** - Make a rounding method, Nearest and 0.05 of rounding - Make a product that costs $4.99, don't set a tax - Go to the PoS - Order the product - Before paying click the +10…
**Steps to reproduce:** - Make a rounding method, Nearest and 0.05 of rounding - Make a product that costs $4.99, don't set a tax - Go to the PoS - Order the product - Before paying click the +10 button, then pay - The rounding line is not present on the ticket **Why the fix:** When making a normal rounded purchase, by just clicking the "Cash" button, the rounding line will be displayed. This is because we do not try to apply the rounding if the rounding of the remaining is not equal to zero. https://github.com/odoo/odoo/blob/995629db3231de944710751c3184bf1b8b1355c7/addons/point_of_sale/static/src/app/models/accounting/pos_order_accounting.js#L118-L123 When we try to over pay, the remaining will be negative by the amount we overpay, so the amount will be set to zero, and the rounding will not be set. We now take the amount we overpay into account, and deduct it from the amount we paid to then correctly compute the remaining amount without having to deal with the amount overpaid. Some tests were not taking the rounding as it was not working correctly, so it has now been changed now that it works as it should. opw-6025807 Forward-Port-Of: odoo/odoo#283413 Forward-Port-Of: odoo/odoo#256117
Project margin reports now correctly exclude company-paid expense payment lines from revenue totals. This prevents expenses from appearing as positive “Other Revenues,” giving teams a more accurate view of actual project profitability.
Original PR description
Steps to reproduce --- 1. Install Sales (with Margins) and Project, and open a billable project linked to a sale order. 2. From the project's Expenses view, create an expense paid by the Company and…
Steps to reproduce --- 1. Install Sales (with Margins) and Project, and open a billable project linked to a sale order. 2. From the project's Expenses view, create an expense paid by the Company and post it. 3. Open the project's Actual Margins: the expense shows under Other Revenues as a positive amount. Issue --- Creating an expense from a project's Expenses view keeps `project_id` in the context until its journal entry is built. For a company-paid expense that entry is a payment, and `AccountMoveLine._compute_analytic_distribution` puts the project's analytic distribution on every line that is not receivable or payable, which also covers the Outstanding Payments liquidity line. An analytic amount is the opposite of the move line balance, so the negative liquidity balance becomes a positive analytic line, classified as Other Revenues, on top of the real cost already booked on the expense account. The receivable/payable filter from b6200026ecf1 narrowed the override introduced in ac1995ad6ddf but ignored the liquidity counterpart of a payment; since only profit and loss lines make up a project margin, restricting the distribution to `income` and `expense` accounts keeps the settlement line out of the report. https://github.com/odoo/odoo/blob/f037dead17eb6a74d1ea9c56b0861b285be17516/addons/sale_project/models/account_move_line.py#L10-L19 opw-6326551 Forward-Port-Of: odoo/odoo#283938 Forward-Port-Of: odoo/odoo#273273
This update fixes an issue where selecting a suggested word on mobile with Gboard could place the replacement text incorrectly and delete only part of the original word. It improves text editing reliability for users working in Odoo's HTML editor on mobile devices.
Original PR description
Before this commit: on mobile, when typing using Gboard and select a word suggestion will only delete the last character and put the new word at the beginning of the word to be replaced. This is because Gboard extends the selection to the text to be corrected, then deletes it, and inserts the corrected text. This flow falls in our previous fix for MS Swiftkey's delete backward, and wrongly uses cached old selection instead of using extended new selection from Gboard. After this commit: We strict the Swiftkey fix further, and only execute it when the cursor is at the beginning of the p element. Related commit: https://github.com/odoo/odoo/commit/822fd4e8fec7e114e6748dd8c9b4969f423fb290 task-6233756 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278266
The web interface now handles invalid filters on relational properties without crashing the editor. Users can still open the property settings to correct or remove the problematic filter, preventing them from being locked out of editing or deleting the property.
Original PR description
When a relational property has a domain the server cannot evaluate, opening its definition editor crashes. The editor calls search_count on that domain to show how many records match, both when it…
When a relational property has a domain the server cannot evaluate, opening its definition editor crashes. The editor calls search_count on that domain to show how many records match, both when it opens and on every later render. The server raises a ValueError and the call has no error handling, so the whole editor goes down. The bad domain stays saved on the property, so reopening the editor fails the same way and the property can no longer be edited or deleted. Wrap the search_count call in _updateMatchingRecordsCount (property_definition.js) in a try/catch and show no count when it fails. This is the only place the editor counts matching records, so guarding it here handles a bad domain from any source, the field selector or the code editor. The field selector still shows its warning on the invalid path, so the user can fix or delete the property. Steps to reproduce: 1. On a model that has a Properties field, add a Many2one property and set its Model to a model that itself has a Properties field. 2. Open the property Domain and click New Rule. 3. In the field selector pick the Properties entry, then close the selector. => An error dialog appears and the property can no longer be edited or deleted. Ticket [link](https://www.odoo.com/odoo/project.task/6101311) opw-6101311 Forward-Port-Of: odoo/odoo#283802 Forward-Port-Of: odoo/odoo#259886
This fix ensures Odoo correctly refreshes whether taxes and related accounting rules are marked as used when invoices, expenses, purchases, or point-of-sale records change. This helps prevent outdated tax status information from appearing in accounting-related workflows.
Original PR description
Currently, `is_used` is computed using queries on `account.move.line`, `account.reconcile.model.line`, etc. As a result, it has no depends and is not automatically updated when records in either model are created, modified, or deleted. This commit reverse M2M fields for respective models and use it as dependency to `_compute_is_used`. It also adds a missing dependency of `is_used` to `_compute_repartition_lines_str`. Forward-Port-Of: odoo/odoo#283406
Mentions in Odoo Mail now choose an active user account for each recipient instead of potentially selecting an archived account. This ensures mentioned people receive their inbox notifications reliably when they have both archived and active user records.
Original PR description
Before this commit, mentioning a partner that has an archived user sent the inbox notification to that archived user, so the mentioned person never saw the mention. This happens because the query picking the user of a recipient joins res_users without filtering on active, and keeps one row per partner with DISTINCT ON and no ORDER BY, so which row survives is arbitrary. One solution could have been to keep every active user of the partner, which is what we want as each of them has its own notification type, but a notification is stored per partner, so the type of a single user applies to all of them. Picking one user is a current limitation. This commit fixes the issue by taking the first active user of each partner in a lateral join, ordered as mail.followers._get_recipient_data already does: internal users first, then the lowest id. Forward-Port-Of: odoo/odoo#284214 Forward-Port-Of: odoo/odoo#283806
Aged Receivables and Aged Payables now calculate aging periods correctly when horizontal groups are applied. This prevents incorrect amounts from appearing in older-period columns, improving reliability of customer and vendor aging reports.
Original PR description
Problem: When using horizontal groups in Aged Receivables / Aged Payables reports, the amounts shown in the Older periods are incorrect. Steps to reproduce: 1. Activate debug mode 2. Go to Accounting…
Problem: When using horizontal groups in Aged Receivables / Aged Payables reports, the amounts shown in the Older periods are incorrect. Steps to reproduce: 1. Activate debug mode 2. Go to Accounting > Configuration > Horizontal Groups 3. Add a new horizontal group that results in at least 2 groups 4. Go to Accounting > Reporting > Aged Receivables / Aged Payables 5. Apply the horizontal group created 6. Notice how the amount in the Older period is incorrect, different from before applying the horizontal group. (It may be coincidentally correct, you can check by applying different aging intervals until you find one that shows the issue) Cause: The periods were not correctly calculated. The number of periods was calculated based on the number of period columns, without taking into account the number of column groups. When using horizontal groups, period columns are duplicated for each group that exists after applying the horziontal group. This is not considered when calculating the number of periods, which results in calculating too many periods and therefore having incorrect durations for each period. opw-6374639 Forward-Port-Of: odoo/enterprise#127570
This fixes monthly auto-planning so work slots can be scheduled through the last working day of the selected month. It prevents sales planning from under-scheduling hours due to timezone conversion issues, improving planning accuracy for service orders.
Original PR description
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To…
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To Plan" button, then click "Auto Plan". 4. Make sure the "Month" filter is selected in the scale options and observe the planned slots. Issue: -------- When auto planning slots for a month, the last day of the month is excluded. For example, slots are scheduled only until July 30th, even though July 31st is a working day. Cause: -------- While preparing the context, `stopDate` is set to July 31st at 00:00. It is then passed to [serializeDateTime()](https://github.com/odoo/odoo/blob/dacaad91bba8f959daf5d89a046c5a1c11e48eec/addons/web/static/src/core/l10n/dates.js#L553-L560), which converts the datetime to UTC. Depending on the user's timezone, this can shift the date to the previous day, causing the last day of the month to be excluded. Solution: ------------ Use `localEndOf()` to set `stopDate` to the local end of the selected range before passing it to `serializeDateTime()`. This ensures the last day of the month is preserved during UTC conversion. **NOTE:** Forward-port the solution from the 18.0 version, which was adapted to the publish shift use case in 18.3 and introduced this issue. Add a HOOT test case to prevent this regression in future versions. References: [18](https://github.com/odoo/enterprise/commit/bc3db24f83473d5646f7c2cfca8ed1c5b064ea2e) and [saas-18.3](https://github.com/odoo/enterprise/commit/c81fba31780869940f726b695ad46a87f69798fb) opw-6391495 Forward-Port-Of: odoo/enterprise#128775 Forward-Port-Of: odoo/enterprise#127950
Mexican point-of-sale global invoices now ignore cancelled refund orders when calculating refund amounts. This prevents valid invoices from failing when a cancelled refund exists alongside a paid refund.
Original PR description
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click…
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click "Return Products" to make a refund, but don't pay it, cancel it instead. 4. From the same order, click "Return Products" again to make a second refund, and pay it normally. 5. Go to the orders list, select the main order and the paid refund (not the cancelled one), then Actions > Create Global Invoice. -> Observation: error in the global invoice. In the CFDI tab of the main order the line is "Send Global In Error", and hovering on it the detail says "Failed to distribute some negative lines". Why: ---- When we make the global invoice, we remove the refunds from the order. A cancelled refund was never paid, so we should not count it. But we were counting it too. So we removed the refund amount twice in our case, one for the paid refund, and one for the cancelled one, and we end up with an order with negative amount that cannot be distributed. The fix: -------- We now skip the cancelled orders when we search the refunds, the same way it is done above when we collect the refunded orders. opw-6261404 Forward-Port-Of: odoo/enterprise#128026 Forward-Port-Of: odoo/enterprise#120996
This fix restores the default grouping behavior in Gantt views after it was unintentionally removed. Business users will see planned work grouped as expected again, reducing confusion when reviewing schedules and resource planning.
Original PR description
This reverts commit d35390a534fc60354a3c0174d6aa7cd158805f63 (#117918) task-6425644 Forward-Port-Of: odoo/enterprise#125983
Generated PDFs now render Arabic, Persian, and Hebrew text more reliably, including when mixed with English words or numbers. This improves readability of business documents for users working in right-to-left languages while keeping a safe fallback if the supporting library is unavailable.
Original PR description
# Summary Fixes incorrect rendering of Arabic/Persian/Hebrew (RTL) text in generated PDFs, especially when mixed with Latin digits or English (LTR). Uses python-bidi to compute display order and…
# Summary Fixes incorrect rendering of Arabic/Persian/Hebrew (RTL) text in generated PDFs, especially when mixed with Latin digits or English (LTR). Uses python-bidi to compute display order and introduces a safe fallback when that dependency isn’t available. # Problem - RTL strings were displayed in reverse order in PDFs. - Mixed content (e.g., Arabic + numbers/English) produced garbled ordering. - Existing logic flipped text only when the first character was RTL and no LTR chars existed, which broke mixed cases. # Solution python-bidi follows the Unicode Bidirectional Algorithm and correctly handles real-world mixed strings (Arabic + Latin + numbers + punctuation). Fallback ensures no hard runtime breakage on instances where new dependencies is missing. If python-bidi is missing, behavior is similar to previous “hotfix”: - Pure RTL: reverse text for legible display. - Mixed RTL/LTR: don’t reverse (favor readable English segments over fully correct RTL order). --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix ensures that approved leave properly clears negative extra hours created automatically for missing attendance. Employees and HR teams will see more accurate overtime balances when leave is added for a day that was previously marked as an absence.
Original PR description
Before this commit: --- When [`absence_management`](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/res_company.py#L42) is enabled, a [scheduled…
Before this commit:
---
When [`absence_management`](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/res_company.py#L42) is enabled, a [scheduled action](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/hr_attendance.py#L645) automatically creates an attendance record at [**12:00:00 AM**](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/hr_attendance.py#L649) to mark negative extra hours for employees with missing attendance.
<img width="1147" height="474" alt="image" src="https://github.com/user-attachments/assets/833a4387-bc20-4bb7-817d-9ebe9afa7d71" />
If an employee later creates a leave covering this autogenerated attendance, the extra hours should be reset to `0`. However, this does not happen.
#### Video demonstration:
https://drive.google.com/file/d/1DTNQuQ3uV5nOUVMBazCDo0hBZbKJZUIW/view
This happens because the [domain](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_holidays_attendance/models/resource_calendar_leaves.py#L8) used to fetch attendances for [`_update_overtime`](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_holidays_attendance/models/resource_calendar_leaves.py#L34) compares the attendance `check_in` and `check_out` datetimes with the leave `date_from` and `date_to` datetimes.
The leave datetimes are aligned with the employee's working schedule. For example, if the working hours are **8:00 AM–5:00 PM**, the leave is stored from `{date, 8:00 AM}` to `{date, 5:00 PM}`. In contrast, the scheduled action creates the autogenerated absence attendance at **12:00:00 AM** (in the user's timezone). Since this attendance falls outside the leave datetime range, it is excluded from the domain, and `_update_overtime` is never called for it.
After this fix:
---
Instead of building the domain using the leave datetime range, the domain is built using the leave date range. This ensures that all attendances for the affected dates, including autogenerated absence attendances created at midnight, are included and their extra hours are updated correctly.
OPW: 6385811
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#281123Odoo Studio now handles cases where fields shown on a form are generated behind the scenes and cannot be matched during view optimization. This prevents crashes when users edit affected accounting and Dutch reporting views, falling back safely instead.
Original PR description
* = account_invoice_extract, l10n_nl_reports Example of steps: - Install web_studio and `accountant` - Open the corresponding form view in Studio - With debug mode toggle "Show invisible Elements"…
* = account_invoice_extract, l10n_nl_reports Example of steps: - Install web_studio and `accountant` - Open the corresponding form view in Studio - With debug mode toggle "Show invisible Elements" and edit "Invisible" on the second partner_id field - Traceback `normalize()` compares the combined arch without the studio customization to the one with it, in order to compute the smallest possible set of xpaths. To do so, it calls `apply_inheritance_specs` (the low-level function from `odoo.tools.template_inheritance`) directly on the statically combined arch. Some models add or duplicate nodes dynamically in `_get_view()` (Python postprocessing, run after the static view combination). A studio operation can target such a node, since it is what the user actually sees and clicks on. But that node has no counterpart in the purely static combined arch used by `normalize()`, so `apply_inheritance_specs` raises a ValueError. `edit_view()` only catches `ValidationError` to fall back to an un-optimized (but valid) studio arch instead of failing the request. Since the low-level function raises a plain `ValueError` here, that fallback never triggers, and the exception is not caught anywhere. To fix this, we will keep the behavior from version 18.0 and catch the ValueError raised by `apply_inheritance_specs` in `normalize_with_keyed_tree` and re-raise it as a ValidationError, like `ir.ui.view.apply_inheritance_specs` already does elsewhere. This lets `edit_view()`'s existing fallback handle the case gracefully instead of crashing. Additionally, the two models responsible for the dynamically-added nodes described above are fixed at the source. `account_invoice_extract`'s duplicated `partner_id` field and `l10n_nl_reports`'s injected `company_id` field are now marked with `data-used-by`, the same attribute `_add_missing_fields` already sets in `ir_ui_view.py` for the fields it adds. Studio already skip rendering and computing xpaths for any node carrying this attribute (since https://github.com/odoo/enterprise/pull/92862), so these nodes are no longer exposed to the user and can no longer produce a studio operation that `normalize()` is unable to locate. opw-6332911 Forward-Port-Of: odoo/enterprise#127009 Forward-Port-Of: odoo/enterprise#122829
This fixes keyboard navigation in the Discuss Channels view so users can move from search results to channel cards without errors. It improves accessibility and lets users browse and open channels using arrow keys and Enter as expected.
Original PR description
**Description of the issue/feature this PR addresses:** ---------------------------------------------- Currently, in Discuss > Channels, pressing the down arrow while the search bar is focused raises…
**Description of the issue/feature this PR addresses:** ---------------------------------------------- Currently, in Discuss > Channels, pressing the down arrow while the search bar is focused raises a traceback instead of moving the focus to the first card. The channels kanban is rendered by a custom template written from scratch, which omits the t-ref="this.rootRef" of the standard kanban renderer, so this.rootRef() is null when the focus-view handler looks for the first card. A second traceback follows once the focus reaches a card, as focusNextCard looks for .o_kanban_group wrappers whenever the list is grouped, while the categories are rendered as plain rows, and the resulting empty list is then accessed by index. **Current behavior before PR:** ---------------------------------------------- - Pressing the down arrow from the search bar raises a traceback - Pressing the arrow keys again from a card raises a second traceback - Keyboard users cannot browse the channels kanban at all **Desired behavior after PR is merged:** ---------------------------------------------- - Pressing the down arrow from the search bar moves the focus to the first card - The arrow keys browse the cards in reading order, across categories - Pressing the up arrow on the first card returns the focus to the search bar - Pressing Enter on a card opens the corresponding channel - Keyboard navigation is consistent with the other kanban views Task-6439024 ---------------------------------------------- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Confirmed manufacturing orders now correctly update their operations when the related bill of materials changes. This prevents removed or edited production steps from staying incorrectly on existing orders, helping teams keep shop floor instructions aligned with the latest manufacturing plan.
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…
### 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 changes 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). Enterprise: https://github.com/odoo/enterprise/pull/120709 opw-6285878 opw-6261738 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280803 Forward-Port-Of: odoo/odoo#269747
The mail system now checks and cleans message posting data before a message is created, based on the current user’s permissions. This helps prevent inappropriate or unexpected data from being included in discussions, improving reliability and data handling.
Original PR description
This change sanitizes some post data before allowing the post, making sure the data received by `message_post` is clean based on the current user. part of task-6452761 Forward-Port-Of: odoo/odoo#284201 Forward-Port-Of: odoo/odoo#280894
Confirmed manufacturing orders now correctly reflect changes made to their bill of materials when users choose to update them. This prevents obsolete operations from staying on production orders and ensures relevant operation changes are applied, reducing planning and execution errors on the shop floor.
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#128120 Forward-Port-Of: odoo/enterprise#120709
Product videos in the website shop now load only when their carousel slide is shown, so the preview image loads at the correct size. This prevents blurry video thumbnails and improves the product page experience for shoppers.
Original PR description
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video…
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video preview cover is blurry. Root cause: =========== The product images are rendered in a carousel (the shop_product_carousel template in ) where only the first slide gets the "active" class; https://github.com/odoo/odoo/blob/af1b3ee2e7ac56a35bff5e030c3a831c27dbcf24/addons/website_sale/views/templates.xml#L3224-L3226 every other slide is "display: none". A product video is rendered as a live <iframe> inside its slide, so when the video is not the first media its iframe loads while its container has no dimensions (0x0). The embedded player then initializes as a small mobile player and loads a low resolution cover thumbnail (120x90), which looks blurry once the slide is shown at full size. Reloading only the iframe while the slide is visible fixes it, a full page reload does not. Fix: ==== Defer loading the video iframes located on hidden slides their src is moved to a data-src attribute on start and restored once the slide becomes visible. The player then initializes at full size and loads a high resolution cover. opw-6349394 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282508 Forward-Port-Of: odoo/odoo#274002