Thursday, September 17, 2026
24 changes · 19.0
Enhancements to existing features
Product variant names are now generated more efficiently by avoiding unnecessary checks across all attribute values. This can significantly reduce loading time for large product catalogs, especially in Point of Sale sessions.
Original PR description
Issue --> `product.product's` display_name appends the variant's combination name, which `_get_combination_name` builds by dropping the values that come from single value lines. The check behind that, `_is_from_single_value_line`, only needs to know whether the line holds exactly one active value, but it filtered the line's entire set of values through `_only_active()` to find out. That check runs once per attribute value of every variant being named, so its cost follows the number of values on the template rather than the number of lines. Solution --> Stop at the second active value instead: finding two is enough to know the line is not single valued. The archived path (only_active=False) only measures the line's length and is unchanged, as is the returned name in both cases. Benchmark -> Reading display_name for the 23989 variants on the related database loads in the Point of Sale from approximately 200s to 70s. opw-6530735 Forward-Port-Of: odoo/odoo#288183
Inventory availability searches now run much faster by using stock quantity records directly instead of scanning broader stock movement data. This improves responsiveness for businesses with large product and warehouse datasets, especially when filtering products by free quantity.
Original PR description
**Problem:** Searching on free_qty can be slow when there are many stock.move and stock.quant. The search method uses _compute_quantities_dict() to compute the free_qty based on stock.move and…
**Problem:** Searching on free_qty can be slow when there are many stock.move and stock.quant. The search method uses _compute_quantities_dict() to compute the free_qty based on stock.move and stock.quant, which is expensive. **Solution:** A similar field, qty_available, has a faster search method based on stock.quant only. This method can be adapted to support fast searching on free_qty by also aggregating the reserved_qty and subtracting it from the qty_available. **Perf Table:** Record: product.template, with ~20k stock.picking and most products w/o moves. |Record count|Time before|Queries before|Time after|Queries after| |------------|-----------|--------------|----------|-------------| |10k |2.14s |214 |370ms |45 | |50k |6.06s |644 |829ms |50 | |100k |11.42s |1196 |1.05s |46 | opw-6266096 Forward-Port-Of: odoo/odoo#283098 Forward-Port-Of: odoo/odoo#270434
Adds support for GSTR-1 IFF returns used by Indian businesses under the QRMP scheme for monthly B2B invoice reporting in the first two months of a quarter. This helps users manually create the needed IFF periods and prevents invoices already reported through IFF from being included again in the later quarterly GSTR-1 filing.
Original PR description
Under the QRMP (Quarterly Return Monthly Payment) scheme, taxpayers file monthly returns only for B2B invoices during the first two months of a quarter using the Invoice Furnishing Facility (IFF), while the complete quarterly GSTR-1 — including all remaining invoices (B2B, B2C, etc.) — is filed in the third month. This commit introduces a new `GSTR-1 IFF` return type to handle these monthly filings. Unlike regular returns, IFF return periods are not auto-generated; users can manually create them as needed. Additionally, invoices linked to an IFF return are now tracked to ensure They are excluded from subsequent quarterly GSTR-1 filings. task-3376482
Resolved issues and error corrections
This fixes a Safari-specific issue where replacing selected text at the start of an editable note could delete the selection without inserting the typed character. Users can now reliably select text and type over it in the HTML editor, avoiding lost input while editing notes or other rich text content.
Original PR description
When using Safari, if the first character of the editable is selected and a character is pressed, the selection content is removed, but the character is not inserted. It seems that Safari does not trigger the actual `input` event, nor its native behavior, if the initial anchor node is detached from the DOM after `beforeinput`. This commit avoids this issue by preventing Safari from proceeding with the insertion right after the deletion by instead re-triggering the `insertText` command. Steps to reproduce: - Use Safari - Go to a To do note - Select the first word - Press a letter => The first word was deleted but the letter was not inserted. task-6445669 Forward-Port-Of: odoo/odoo#281223
The Time Off Ledger now shows expected hours based on each employee's actual weekday schedule instead of using a weekly average. This gives managers and HR teams more accurate reporting for employees with shorter or longer scheduled days, such as reduced Friday hours.
Original PR description
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to…
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to reproduce: 1) Install hr_holidays_attendance. 2) Create/assign a working schedule where daily hours vary across the week (e.g. Monday-Thursday 8.5h, Friday 5h). 3) Go to Attendance > Reporting > Time Off Ledger, filter on that employee. ### Observed behavior: "Expected Hours" is identical on every day (the schedule's average hours/day), regardless of which weekday the row is for. ### Expected behavior: "Expected Hours" should match the hours actually scheduled for that specific weekday, so a shorter Friday shows less than a full Monday. ### Root cause: `Time off Ledger` is a SQL view. Its `_select()` builds `expected_hours` from `rc.hours_per_day` at [1], i.e. `resource.calendar.hours_per_day`, a field explicitly labelled **"Average Hour per Day"** and is computed via [_get_hours_per_day] as `hours_per_week / days_per_week`, a single flat value for the whole calendar. The per-weekday hours are available in `resource.calendar.attendance` it stores `dayofweek`, `hour_from`/`hour_to` and a computed `duration_hours` per line, and can legitimately differ per weekday (e.g. Monday 8h vs Friday 4h) at [2]. The report's own [_cte_cal_workday()] CTE already reads this table to decide whether a weekday is a working day (`cal_workday`, joined as `cw` in `_from()`), but discarded the actual hours, keeping only `(calendar_id, dayofweek)`. `_select()` then fell back to the calendar-wide average via `rc.hours_per_day` instead of the per-day value. Example: calendar with Monday 8h and Tuesday 4h -> `hours_per_day` averages to 6h; the report showed 6h on both Monday and Tuesday instead of 8h and 4h respectively. [1]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L256 [_get_hours_per_day]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar.py#L703-L707 [2]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar_attendance.py#L15-L31 [_cte_cal_workday()]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L82-L89 ### Fix: `_cte_cal_workday()` now aggregates `SUM(duration_hours)` per `(calendar_id, dayofweek)` from `resource_calendar_attendance`, instead of just selecting the distinct keys. `_select()` reads this per-weekday value (`cw.hours_per_day`) instead of the calendar-wide `rc.hours_per_day` for both `expected_hours` and `difference_hours`. **opw-6425167**
Point of Sale product search now includes products whose variant has a single attribute value, such as Size M. This helps cashiers find the right item more reliably and avoids missed products during checkout.
Original PR description
In the POS search bar, searching for an attribute value (e.g., "M") returned products with multi-value attributes (e.g., Size: M - L), but omitted products with single-value attributes (e.g., Size: M). This occurred because backend `_compute_display_name` filters out single-value attribute lines from variant display names. As POS `searchString` relied on `display_name`, `name`, `default_code`, and `barcode`, the attribute value name was missing from single-value variant search strings. task-id: 6431718 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Colombian Libro Diario report now opens correctly when comparison options are enabled. It also includes journal entries without partners, helping ensure legally required accounting information is not omitted from the report.
Original PR description
## [FIX] l10n_co_reports: fix UNION ALL crash with multiple column groups ### Issue: Any comparison mode (Period, Analytic, etc.) in the Libro Diario raises an Odoo Server Error ### Cause: Each…
## [FIX] l10n_co_reports: fix UNION ALL crash with multiple column groups
### Issue:
Any comparison mode (Period, Analytic, etc.) in the Libro Diario raises an Odoo Server Error
### Cause:
Each sub-query in the `UNION ALL` had its own `ORDER BY` SQL only allows one global `ORDER BY` on a `UNION ALL`, or parentheses around each query — neither was the case
### Steps to reproduce:
- Install `l10n_co_reports` and `accountant`
- Enable Developer Mode in Settings
- Open the Libro Diario report and click the gear icon (top right)
- In the Options tab, enable Period Comparison
- Enable the Comparison for the Previous Period
Before the fix, an error is raised
------------------------------
## [FIX] l10n_co_reports: include partnerless entries in Libro Diario
### Issue:
Journal entries without a partner are excluded from the report but are legally required to appear
### Cause:
`_get_domain` called `super()` which adds `('partner_id', '!=', False)` to the domain, filtering out all partnerless entries
The SQL query also used a `JOIN` instead of `LEFT JOIN` on `res_partner`, excluding lines with no partner at the DB level
### Notes:
`NULL` values for `partner_name` or `line_label` caused the JS to hide the corresponding column headers
`header.js` matches columns to their header by `column_group_index`/`expression_label` and skips `None` values
Fixed by using `COALESCE` to return an empty string instead
### Steps to reproduce:
- Install `l10n_co_reports` and `accountant`
- Create a Journal Entry without a partner or label
- Open the Daily Journal Report
Before the fix, the entry doesn't appear
After the fix, check that PARTNER and LABEL headers are visible
opw-6430728
Forward-Port-Of: odoo/enterprise#129674Submitting a draft Denmark VAT report no longer triggers an error. The report now passes the needed prior settings when calculating VAT lines, helping Danish users complete VAT submissions reliably.
Original PR description
Currently, an error occurs when the user submits the draft Denmark VAT report. ``` TypeError: AccountReport.get_options() missing 1 required positional argument: 'previous_options' ``` When the user submits the draft Denmark VAT report, it gets the calculated lines of the current report by calling get_options method. However, get_options() requires the previous_options argument [1]. Since this argument is not passed [2], it raises the error. This commit ensures that an empty dictionary is passed as the previous_options argument when getting the report lines. [1]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/account_reports/models/account_report.py#L2126 [2]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/l10n_dk_reports/wizard/tax_report_wizard.py#L216 sentry-7380293202
This fix prevents website visitors from seeing the “Ask a Human” option when the selected live chat channel has no available human agents, avoiding a dead-end support experience. It also moves the related notice into the chat conversation so it remains visible even when the chat window overlaps on-screen notifications.
Original PR description
This commit aims to adapt the tests for this bugfix. How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created…
This commit aims to adapt the tests for this bugfix. How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created livechat channel as the default live chat channel. - Go to website, click bottom right bubble to start livechat. - Resize the Window on your screen so that the chat window obscures the top right of the page (where you get notifications) - Press 'Ask a Human' Current behavior: - 'Ask a Human' is available (despite having 0 agants in the channel -> will never reach a human) - Notification is obscured by the chat window (when it overlaps with the top right) Expected: - Remove 'Ask a Human' button, when there are no human (agents) in the livechat channel - Change the notification to a chat message. enterprise PR: https://github.com/odoo/enterprise/pull/125673 task: 6148037 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
The live chat now hides the “Ask a Human” option when no human agents are available, preventing visitors from requesting an unreachable handoff. If a handoff cannot happen, the message appears directly in the chat instead of as a notification that may be hidden behind the chat window.
Original PR description
How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created livechat channel as the default live chat channel. - Go to website, click bottom right bubble to start livechat. - Resize the Window on your screen so that the chat window obscures the top right of the page (where you get notifications) - Press 'Ask a Human' Current behavior: - 'Ask a Human' is available (despite having 0 agants in the channel -> will never reach a human) - Notification is obscured by the chat window (when it overlaps with the top right) Expected: - Remove 'Ask a Human' button, when there are no human (agents) in the livechat channel - Change the notification to a chat message. task: 6148037 community PR: https://github.com/odoo/odoo/pull/278834
The calendar view now updates properly when users resize their browser or switch between mobile and desktop layouts. This prevents the wrong filter panel from showing on small screens and avoids the calendar disappearing after returning to a wider view.
Original PR description
See commits
Employees can now split an already approved expense even when it has a receipt attached. The fix prevents an erroneous access warning during the split while keeping the normal restriction that blocks adding new receipts to approved expenses.
Original PR description
Steps to reproduce: ---------------------------------------- 1. Install the Expense module. 2. Create an expense with a receipt/attachment and approve it. 3. Try to split the approved expense using…
Steps to reproduce: ---------------------------------------- 1. Install the Expense module. 2. Create an expense with a receipt/attachment and approve it. 3. Try to split the approved expense using the "Split Expense" button. Observation: ---------------------------------------- An Access Error is raised: "You can't add attachments to an expense once it has been approved." Issue: ---------------------------------------- When splitting an approved expense, Odoo duplicates the original expense into multiple split parts. Since the original expense is already in the `approved` state, the newly created duplicate records also have `state == 'approved'`. During the split process, Odoo copies the attachments from the original expense to the newly created split records: https://github.com/odoo/odoo/blob/e696bc516c97bc7157d52c5dba0b42e7cb8bab48/addons/hr_expense/wizard/hr_expense_split_wizard.py#L59-L61 Because `copied_expense.state` is 'approved', the create method of `ir.attachment` triggers an AccessError via the security validation https://github.com/odoo/odoo/blob/e696bc516c97bc7157d52c5dba0b42e7cb8bab48/addons/hr_expense/models/ir_attachment.py#L23-L27 Solution: ---------------------------------------- Bypass the attachment restriction if the creation is initiated from the split wizard. It only bypasses the validation during the split operation, preserving the security constraints for normal user uploads to approved expenses opw-6373579
Point of Sale order reports now align their totals with the actual order total when taxes are rounded per tax group. This prevents small reporting discrepancies, such as one-cent differences, and improves trust in sales analysis figures.
Original PR description
Steps to reproduce: - Install `Point of Sale` - Settings > Invoicing > Taxes > Rounding Method > choose 'Round Per Tax' - Create 3 products - First one costs 16.73 and has 8% sales tax - Second one…
Steps to reproduce:
- Install `Point of Sale`
- Settings > Invoicing > Taxes > Rounding Method > choose 'Round Per Tax'
- Create 3 products
- First one costs 16.73 and has 8% sales tax
- Second one costs 7 and has no applied sales tax
- Finally, a third one that costs 25.99 and has 8% sales tax too
- Create an order with the following quantities
- 3 of the first product
- 3 of the second product
- 2 of the third one
_Notice that the total is 131.34, but if you sum the individual lines the total 131.35_
- Go to Reporting > Orders Analysis > pivot view
> Notice that the total price is 131.35, resulting in a discrepancy of 0.01.
### Cause of Issue:
The SQL query that shows the report calculates the prices on a "Round per Line" basis, since it retrives each individual line on its own from `pos_order_line` table https://github.com/odoo/odoo/blob/a236f67776616f6facdefb0117a6ffdde9b7c84c/addons/point_of_sale/report/pos_order_report.py#L72 If the order/invoice has products with different taxes, this is bound to cause discrepancies.
### Fix:
Since it is important to match the total value of the order to the total value in the report, while also retriveing the lines one by one, the safest approach was to calculate the tax for each tax group "_round per tax_" and compare it to the tax we get usually by just summing, and if there are any differences we subtract them from the last line of that tax group.
### Drawback:
This might cause some small price differences (in the price's 0.01 places) in different orders, but the display of a correct total value is of more importance.
opw-6141218This fixes an issue where some grid-based views could appear empty after the page refreshed or rebuilt part of the interface. The grid now recalculates immediately when its scroll area changes, so users see the expected rows and columns without needing to scroll or resize the window.
Original PR description
Manual fw-port of https://github.com/odoo/odoo/pull/281721 See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Theme updates now correctly remove website-specific copies and related dependent views when a theme view is no longer included. This prevents update failures that could block businesses from applying website theme changes or upgrades.
Original PR description
When a record disappears from a theme's data files, updating that theme deletes its per-website copies. The context built for `copy_ids` in `_process_end_unlink_record` used the literal `'MODULE_UNINSTALL_FLAG'` instead of the constant, and passed it as a positional dict, which replaces the whole context rather than extending it. `ir.ui.view.unlink` therefore did not cascade to the inheriting views and the `inherit_id` foreign key refused the deletion, aborting the theme update. Reported by: https://github.com/odoo/odoo/issues/286171 Forward-Port-Of: odoo/odoo#288548
Products with multiple variants now show the add-to-cart option when at least one variant is available to buy. This prevents shoppers from seeing an item as sold out just because the first variant has no stock, helping avoid missed sales.
Original PR description
Steps to reproduce: =================== 1. Create a storable product with two variants and untick "Continue selling when out-of-stock". 2. Leave the first variant at 0 in stock, put some stock on the…
Steps to reproduce: =================== 1. Create a storable product with two variants and untick "Continue selling when out-of-stock". 2. Leave the first variant at 0 in stock, put some stock on the second one. 3. try the "Add to Cart" option of the shop page. => The product has no add to cart button, as if it were sold out, while its second variant can be bought. Reordering the variants so that the one in stock comes first brings the button back. Root cause: =========== `product.template._is_sold_out()` delegates to `product_variant_id`, which is `product_variant_ids[:1]`, so a whole template is declared sold out on the sole basis of its first variant. `_website_show_quick_add()` then hides the button for the template, whatever the stock of the other variants. The helper has looked at that single variant since it was written: - [1] added it to hide the button of sold out products. Fix: ==== Consider the template sold out only when all of its variants are. The check stops at the first variant in stock, so a product that can be bought still costs a single stock lookup. [1]: https://github.com/odoo/odoo/commit/4ae197cc770cec2058e1f113209c4f93a4a0f4b2 opw-6520052 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286781
Fixed an issue that could prevent Arabic-English GCC invoice PDFs from generating when invoices included section or note lines. This helps users print and preview affected invoices without encountering an internal server error.
Original PR description
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not…
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not iterable ``` This happens because `account.move.line` records of type `line_section` or `line_note` have `name = False`. The template evaluates `arabic_name not in line.name` (and the same for `english_name`), which fails because Python cannot apply the `in` operator on a boolean value. ## Fix Add a `line.name and` guard before each `not in` check: ```xml <!-- Before --> <span t-if="arabic_name not in line.name" .../> <span t-if="(english_name != arabic_name) and (english_name not in line.name)" .../> <!-- After --> <span t-if="line.name and arabic_name not in line.name" .../> <span t-if="line.name and (english_name != arabic_name) and (english_name not in line.name)" .../> ``` ## Steps to reproduce 1. Install `l10n_gcc_invoice` on an Odoo 16.0 instance. 2. Create a customer invoice and add a **Section** line. 3. Print/preview the invoice PDF. 4. Observe `Internal Server Error` / `TypeError: argument of type 'bool' is not iterable`. Forward-Port-Of: odoo/odoo#278887 Forward-Port-Of: odoo/odoo#267147
Financial reports now display and scroll correctly on iPhones and iPads, preventing the scrollbar from appearing behind report content. This improves usability for mobile users viewing long accounting reports without changing the desktop experience.
Original PR description
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its…
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its rendering engine) on all of them, including Chrome and Firefox ### Steps to reproduce (on iPhone): - Install `l10n_es` (contains scrollable reports by default) - Switch to the ES company - Open the Tax Report and try to scroll Before the fix, the scrollbar renders behind the report content ### Cause: `o_content` was added unconditionally in commit https://github.com/odoo/enterprise/commit/d60ea6e0f3245e394fd0cee5f22f52aff2a79e2e But its role differs between desktop and mobile, and its presence on mobile triggers a WebKit compositor bug On desktop, `o_content` is required: `.o_action` stays `overflow: hidden`, so only `.o_content` (`overflow: auto`) can scroll the report Per the CSS flexbox spec, a flex item won't shrink below its content size unless its `overflow` is not `visible` Without `o_content`, the div keeps `overflow: visible`, refuses to shrink, overflows `.o_action`, and the excess is silently clipped — content becomes unreachable, not just visually different On mobile, the framework flips scroll responsibility to `.o_action` (`overflow: auto`) and forces `.o_content` back to `overflow: initial` `o_content` is therefore not needed on mobile On WebKit (iOS/iPadOS), keeping `o_content` on mobile is harmful: it sits as a non-scrolling `position: relative` node between the real scroll ancestor (`.o_action`) and descendants that require special compositing — the sticky `thead` and the fixed-position mobile chatter This configuration causes WebKit's compositor to miscalculate the Root layer bounding box This geometry mismatch is the most likely explanation for why the native scroll indicator renders behind the report instead of on top of it Removing `o_content` on mobile avoids this node entirely and restores correct compositor geometry ### Notes: Tested on Android (Blink) with and without `o_content`: no visual difference and identical compositor layer geometry confirmed via Chrome DevTools — no regression introduced The `padding-bottom` on `.o_account_report_scroll_container` is unrelated — it is applied unconditionally and exists for a separate bug (last row clipped on scroll) opw-6191827
Attendance approvers can once again adjust Extra Hours directly without changing an employee's check-in or check-out times. This restores the expected manager workflow for correcting overtime while keeping worked time calculations based on the actual attendance timestamps.
Original PR description
Issue: Attendance approvers cannot manually adjust the Extra Hours value anymore. The only available workaround is to change the check-in/check-out timestamps, although Extra Hours should be…
Issue: Attendance approvers cannot manually adjust the Extra Hours value anymore. The only available workaround is to change the check-in/check-out timestamps, although Extra Hours should be adjustable independently from the actually worked time. Steps to reproduce: - Enable overtime validation by manager in Attendances settings. - Configure an employee with the default overtime ruleset and a standard 40h working schedule. - Set the Attendance approver to the user used for the test. - Create an attendance that generates overtime, for example 08:00-20:00 on a regular 8h workday. - Open the attendance as the approver. - Try to edit Extra Hours without changing Check In or Check Out. - Approve the overtime and try to edit Extra Hours again. Cause: `validated_overtime_hours` was a readonly computed value based on linked overtime lines, while https://github.com/odoo/odoo/blob/ba708ca0cf926a5feaf4b1e50c7d0ead3db4391a/addons/hr_attendance/views/hr_attendance_view.xml#L112-L116 kept the form field readonly. The editable value had moved to `hr.attendance.overtime.line.manual_duration`, but the attendance level Extra Hours field no longer wrote back to it. Solution: Make `validated_overtime_hours` editable again and add an inverse function that writes manager edits to non-refused linked overtime lines' `manual_duration`. Keep `overtime_hours`/`duration` computed from the timestamps and rules, and keep the views editable only for managers while the overtime is not refused. Ignore the default `validated_overtime_hours = 0` sent by form creation, because creating an attendance through the UI does not accidentally override generated overtime, thus we needed to make it readonly when it doesn't have an id yet. opw-6271244 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Uploading a new version of a shared document no longer creates a duplicate behind the scenes. This prevents upload failures for users managing files that are also linked to other records, such as products.
Original PR description
Uploading a new version of a document whose attachment is shared with another record (e.g. a product template) fails with duplicate key value violates unique constraint "documents_document_attachment_unique", key (attachment_id) already exists. `_documents_upload` first writes res_model/res_id of the linked record onto the new attachment, which triggers ir.attachment's auto-create-document logic and spawns a competing document. The version-upload's own write then tries to claim the same attachment, causing the constraint violation. Steps to reproduce: 1. Create a product template and add an attachment. 2. Go to the document->proucts. 3. Click on the attachment, "action" and then "manage version" 4. Upload a new document and you'll see the issue. Fix: pass no_document=True when writing res_model/res_id on the new attachment, so no competing document is created. opw-6360307
Fixed an accounting issue where some batch payments could remain marked as sent even after the related bill and payment were paid. This keeps payment statuses aligned with the actual payment outcome and reduces manual follow-up for finance teams.
Original PR description
A payment on a journal without outstanding account has no journal entry, so it is matched as soon as it is paid. When the bill it pays is reconciled later on, _compute_state turns it to 'paid', but assigning a field from within a compute doesn't notify the fields depending on it: is_matched stays False and any batch payment holding that payment stays in 'sent' state. Mark is_matched for recomputation explicitly, as 18.0 already does since d8de234. Steps to reproduce: - On the Bank journal, leave the outstanding account empty on the outbound payment method line - Post a vendor bill - Register a payment on it - Put that payment in a batch payment and validate the batch - Create a MISC entry and reconcile the bill with it - Issue: bill and payment are 'Paid' but the batch stays 'Sent'.
The shop now blocks combo products from being added to the cart unless every required choice has been completed correctly. This prevents customers from checking out with incomplete combo selections, reducing incorrect orders and fulfillment issues.
Original PR description
Steps to reproduce: =================== 1. Publish a combo product whose choices each offer more than one item, so the combo configurator is shown. 2. On the shop, open the product and click "Add to…
Steps to reproduce: =================== 1. Publish a combo product whose choices each offer more than one item, so the combo configurator is shown. 2. On the shop, open the product and click "Add to Cart". 3. Select an item for one combo choice only. The dialog's "Add to cart" button stays disabled. 4. Remove the `disabled` attribute from that button with the browser developer tools and click it. => The combo is added to the cart with one of its choices unanswered, and the order can be paid in that state. Root cause: =========== The incomplete selection is only prevented on the client side, by disabling the button until every choice has been answered. Server side, `/website_sale/combo_configurator/update_cart` only rejects a completely empty selection, never that the selected items cover every choice. Fix: ==== Reject the request unless the selected combo items cover exactly the combo choices of the product. Comparing the set of choices rather than counting the items also rejects a selection answering the same choice twice while leaving another one unanswered. opw-6478837 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284704 Forward-Port-Of: odoo/odoo#283465
EU OSS sales are now reported correctly in French e-invoicing flows by treating them as non-French VAT while preserving the destination VAT on invoices and accounting records. This avoids compliance rejections caused by French reporting rules that only allow French VAT rates in this flow.
Original PR description
OSS sales are taxed in the customer's Member State but are not subject to French VAT. They must therefore be reported under TNT1, while the Flow 10 Schematron only accepts French VAT rates. Identify taxes generated for the EU OSS scheme through their OSS tag. Keep the destination VAT on the invoice and in accounting, but report the taxable base under TNT1 with a zero tax rate and amount. Continue rejecting unsupported rates for regular taxes and invalid OSS rates. no task id Forward-Port-Of: odoo/odoo#288013
The mail plugin now allows administrators to adjust how long authentication tokens remain valid, with a default duration of seven days. This reduces the need for users to sign in every day while keeping the tokens limited to Outlook-related access.
Original PR description
Purpose ======= Allow customizing the expiration time of tokens, so users don't need to login everyday in the plugin. This is customized with a system parameter, with a default of 7 days. Those tokens are limited to endpoints `auth="outlook"`. Task-6466253 Forward-Port-Of: odoo/odoo#287767