Monday, August 24, 2026
45 changes · saas-19.1
Enhancements to existing features
Stock updates now reuse the same warehouse lookup when processing several products at once. This reduces unnecessary database work and should make batch stock adjustments more efficient without changing user-facing behavior.
Original PR description
When `_inverse_qty_available` processes multiple products, it performs the same warehouse search for every eligible product, resulting in redundant queries during batch operations. Look up the warehouse lazily once and reuse it for all products in the recordset. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283564
Resolved issues and error corrections
This fixes an issue where website pages could keep showing a cached version after a visitor changed cookie consent from denied to accepted. The update ensures the page is refreshed so visitors see content and behavior that matches their latest privacy choice.
Original PR description
Initially with [commit 958b41c4], when cookies were denied (the page is cached a 1st time), then accepted (the page cache must be invalidated), cached pages would be computed again. This behavior was lost with [6c8a90ec], since which website pages are cached more aggressively. [commit 958b41c4]: https://github.com/odoo/odoo/commit/958b41c4acec7e1700ca4d6e0b25ee0ad2aac9f1 [6c8a90ec]: https://www.github.com/odoo/odoo/commit/6c8a90ecba45fb99addf1b86fe237fd626fba650 task-6471290 Forward-Port-Of: odoo/odoo#282737
Documentation and clarification updates
This update records that GitHub user kshitij-nariya has signed Odoo's Individual Contributor License Agreement. It is an administrative legal update that allows their future contributions to be accepted and merged without CLA-related blockers.
Original PR description
Description of the issue/feature this PR addresses: Signed the Odoo Individual Contributor License Agreement to contribute to the Odoo repository. Current behaviour before PR: The CLA signature is missing for GitHub user `kshitij-nariya`, which will prevent future contributions from being accepted and merged. Desired behaviour after PR is merged: The CLA signature for `kshitij-nariya` is recorded in the repository, allowing future pull requests and contributions to be successfully merged. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282808
This fix ensures credit notes are properly included when calculating cost of goods sold after a delivery is returned and then re-delivered. It prevents overstated costs on later invoices, improving the accuracy of inventory valuation and financial reporting.
Original PR description
**Steps to reproduce:** - create a storable product with a positive quantity a cost of 10 and average perpetual category - confirm a SO for 1 quantity, validate delivery - confirm invoice for 1 (COGS…
**Steps to reproduce:** - create a storable product with a positive quantity a cost of 10 and average perpetual category - confirm a SO for 1 quantity, validate delivery - confirm invoice for 1 (COGS should be 10) - return the delivery and validate - create a credit note from the invoice for 1 and confirm (COGS should be 10) - return the return and validate - change the standard price to 100 - create an invoice from the SO for 1 and confirm **Current behavior:** cogs are 190 **Expected behavior:** cogs should be 100 **Cause of the issue:** _get_posted_cogs_value doesn't take into account the credit notes (only the account moves with type 'out_invoice' are taken into account in the sum) https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/sale_stock/models/account_move.py#L185-L186 So in our case the first invoice and the credit note don't cancel out each other. The same goes for _get_cogs_qty (which returns the total cogs past + current), in the past cogs it doesn't take into account the quantities of the credit note. https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/sale_stock/models/account_move.py#L172-L174 So the quantity of the first invoice and the one of the credit note don't cancel out each other. As a result, the return value from _get_cogs_value() for the second invoice is : price unit = 100 returned by _get_cogs_price_unit() https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/account_move_line.py#L68 which returned the standard price because the product has an average cost method https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/stock_move.py#L275-L280 cogs_qty = 2 (instead of 1 if credit was taken into account as -1 in the sum) self._get_posted_cogs_value() = 10 (instead of 0 if credit note cogs were taken into account in the sum as -10) return value = (100 * 2 -10)/1 = 190 https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/account_move_line.py#L75 **fix:** if we take into account the credit note the return value will be : (100 * 1 - 0)/1 = 100 the mechanism of the already posted cogs value is there for cases where we only delivered a part of the quantity and then delivered the rest, but in the case where we delivered and then returned (with credit notes) it shouldn't have an impact. Thus the idea to include the credit note so that it can cancel out the first invoice opw-6426111 Forward-Port-Of: odoo/odoo#282893
This fixes seven mislabeled entries in the Mexican chart of accounts so their names match the official SAT catalogue. The change helps ensure electronic accounting exports show the correct descriptions, reducing confusion and compliance discrepancies, while trial balance and journal policy exports remain unaffected.
Original PR description
Seven entries of the Mexican chart of accounts template carry a name belonging to a **different** group, copied from a neighbouring entry. Each record's XML ID still states the intended name, which…
Seven entries of the Mexican chart of accounts template carry a name belonging
to a **different** group, copied from a neighbouring entry. Each record's XML ID
still states the intended name, which is what this restores.
| Code | Field | Before | After |
|---|---|---|---|
| `6` | `name@es` | Gastos generales | Gastos |
| `252.07` | `name@es` | `account_subgroup_hipotecas_por_pagar_a_largo_plazo_nacional` | Hipotecas por pagar a largo plazo nacional |
| `602` | `name`, `name@es` | Cost of sales / Costo de venta | Selling expenses / Gastos de venta |
| `613` | `name@es` | Amortización contable | Depreciación contable |
| `614` | `name` | Accounting depreciation | Accounting amortisation |
| `701.06` | `name`, `name@es` | Interest on foreign bank charges / Intereses a cargo bancario extranjero | Interest payable by national natural persons / Intereses a cargo de personas físicas nacional |
| `702` | `name@es` | Utilidad cambiaria | Productos financieros |
### Why it is not cosmetic
The electronic accounting Chart of Accounts XML takes the `Desc` attribute of
every `<Ctas>` element from the *account group name* — `cfdicoa.xml`
(`t-att-Desc="account.get('name')"`), fed by `trial_balance.py`
`_l10n_mx_get_coa_values()`. Any `es_*` database therefore declares:
```xml
<catalogocuentas:Ctas CodAgrup="702" NumCta="702" Desc="Utilidad cambiaria" Nivel="1" Natur="A"/>
```
whereas the SAT catalogue (Anexo 24) publishes `702` as *Productos financieros*,
with `702.01 Utilidad cambiaria` … `702.10 Otros productos financieros` beneath
it. `CodAgrup` comes from `code_prefix_start` and stays correct, so the file
still validates against the XSD, but the declared description does not match the
official nomenclature. Trial Balance and Pólizas are unaffected — neither
exports group names.
### Evidence
- `252.07` contains its own XML ID as the Spanish name.
- `602` duplicates `501.01`, yet its children are `Sueldos y Salarios`,
`Compensaciones`, `Tiempos extras`.
- `613` and `614` are swapped in one language each: `613`'s children are
depreciations, `614`'s are amortisations.
- `701.06` duplicates `701.05` in both languages; the correct name is symmetric
to `701.07` and to `702.06`.
- `6` is the only single-digit root group whose Spanish name does not match its
XML ID (`account_group_gastos`).
### Notes
Introduced in d782b8b92557; correct in 15.0, where the names lived in
`account.account.tag.csv`. Still present in 18.0, 19.0 and master, hence
targeting 17.0. Template data only — existing databases are unaffected until the
chart is (re)installed, and renaming a group moves no balance.
Forward-Port-Of: odoo/odoo#277426
Forward-Port-Of: odoo/odoo#277891The HTML editor now keeps text background colors when users turn formatted text into a list or convert a list item back to normal text. This prevents accidental loss of formatting and makes editing styled content more reliable.
Original PR description
Problem: Background color was lost both when converting text with a background color into a list item and when converting a list item with a background color back into a paragraph. Cause: - `insertListAfter` only copied `color` from the font wrapper to `li.style.color`, ignoring `background-color`. - Unwrapping a list item (`ListPlugin`) extracted `color`, `font-size`, and `text-align`, but ignored `backgroundColor`. Solution: - Preserve `background-color` from font wrapper onto `li.style.backgroundColor` when creating a list. - Restore `li.style.backgroundColor` onto a `<font>` wrapper when unwrapping a list item. Steps to reproduce: - Apply background color to a paragraph and toggle list -> background color is lost. - Apply background color to a list item and toggle list off -> background color is lost. opw-6481665 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283158
The Nilvera PDF button is now shown only where relevant, for Turkish companies using Turkish e-invoicing. Invoicing users can send, fetch, and retrieve Nilvera e-invoice PDFs without needing administrator access, reducing delays and support issues.
Original PR description
## Description of the issue/feature this PR addresses: Two related Nilvera e-invoice issues affecting Turkish companies: - The "Fetch Nilvera PDF" button appeared for every company, not just Turkish…
## Description of the issue/feature this PR addresses:
Two related Nilvera e-invoice issues affecting Turkish companies:
- The "Fetch Nilvera PDF" button appeared for every company, not just Turkish ones.
- Sending/fetching e-invoices (or fetching the PDF) as a non-admin Invoicing user raised an
AccessError, because the company's Nilvera API key field is restricted to System/Settings users
and several call sites read it without `.sudo()`.
## Current behavior before PR:
- The PDF-fetch button shows on both the list view and form view of `account.move` regardless of
the company's country.
- An Invoicing-only user (no System/Settings access) gets an AccessError when sending/fetching
e-invoices or fetching the PDF, because `_get_nilvera_client` reads
`company.l10n_tr_nilvera_api_key` without `.sudo()`.
## Desired behavior after PR is merged:
- Both buttons are only visible for Turkish companies (the list-view button has no bound record, so
its gate is driven by a new `l10n_tr` session_info/JS context injection instead of a direct field
reference).
- Invoicing users can send/fetch e-invoices and fetch the PDF without hitting an AccessError.
## Things to add on forward-port
The `.sudo()` fix lives inside `_get_nilvera_client` itself (`l10n_tr_nilvera/lib/nilvera_client.py`),
so every caller that goes through it is already fixed automatically once this diff forward-ports.
Only call sites that read `company.l10n_tr_nilvera_api_key` **directly**, bypassing
`_get_nilvera_client`, still need their own `.sudo()`:
### 19.4
- [ ] Fix e-Dispatch/e-Receipt fetch gate-check access error (direct read of the API-key field,
sudo it the same way as `_l10n_tr_nilvera_company_get_documents` in this PR)
task-6328589
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274312The mail system now returns only the thread information that is actually needed for the current user and conversation. This reduces unnecessary data exposure and helps keep mail views consistent across access rights and company contexts.
Original PR description
This change cleans up the requested data from `/mail/thread/data` route, ensuring it aligns with what is actually needed depending on the user and thread. part of task-6452761 Forward-Port-Of: odoo/odoo#282932 Forward-Port-Of: odoo/odoo#280713
The website editor now shows the drag-and-drop move handle on the correct side when editing right-to-left content. This improves usability for languages such as Arabic and Hebrew by making block movement controls appear where users expect them.
Original PR description
Problem: In MoveNodePlugin, `setMovableElement` sets the position of the drag-and-drop handler without considering `this.config.direction === "rtl"`. The handler is placed on the left side regardless of text direction. Solution: - In RTL mode, calculate the handle position from the right edge of the element so it is placed on the right side with the same distance as in LTR. - Update hover hooks, editable bounds, and dropzone rectangles for RTL mode. Steps to reproduce: 1. Open the editor in RTL mode. 2. Hover over a movable block element (e.g. `<p>`). => The move handle appears on the left side of the element instead of the right. task-6442717 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283662 Forward-Port-Of: odoo/odoo#281249
When someone is mentioned in a discussion, Odoo now sends the inbox notification to an active user account instead of possibly choosing an archived one. This prevents mentions from being missed when a contact has old or inactive user records linked to them.
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#283980 Forward-Port-Of: odoo/odoo#283806
Fixes an issue where newly created onsite learning events could be hidden after creation because they did not yet meet the view's filtering rules. New onsite events are now easier to find right away, and the current employee is automatically registered when appropriate.
Original PR description
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the…
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the event to have multiple slots as well as to have at least one employee registered to it. Therefore, newly created records often fail these criteria and remain hidden. In further versions, this pr: https://github.com/odoo/odoo/pull/246285/ changes the domain of the event selector in the employee resume by removing the dependency on the multiple slots and filtering by the specific employee for registration. This change is not stable to backport as it indroduces the `employee_id` field as an invisible field in the xml to be able to compare in the domain. This commit partly changes both domains to not require the multiple slots anymore, while still showing all events for which an employee is registered. This commit also ensures that when an event is created from the Onsite view or selector, the current user's employee will be registered to it. Steps to reproduce - Go to employees->Learning->Onsite - Select New and create an event - Go back to Onsite Courses - You will not see the created event (unless it is multi_slot and an employee was registered) opw-5915686 Forward-Port-Of: odoo/odoo#258952
Recovered shopping carts now recalculate the selected delivery cost after product prices are refreshed. This prevents customers from incorrectly keeping free shipping when an order no longer meets the free-shipping threshold, improving billing accuracy.
Original PR description
Steps to reproduce ================== 1. Configure a delivery method with free shipping above a threshold 2. Add a product to the cart above that threshold, select the delivery method and leave the…
Steps to reproduce ================== 1. Configure a delivery method with free shipping above a threshold 2. Add a product to the cart above that threshold, select the delivery method and leave the cart unfinished 3. Lower the product price below the threshold 4. Recover the cart and confirm the order from /shop/checkout => The product prices are refreshed, but shipping stays free although the new total is below the threshold. Root cause ========== Since [1], nothing re-rates the carrier after /shop/confirm_order refreshes the cart prices: the delivery method is selected before the confirmation. In 17.0, the payment page auto-clicked the selected carrier on load, which re-rated the shipping cost and masked the issue. Fix === Re-rate the selected delivery method in `shop_confirm_order` after the prices have been recomputed, as `_cart_update` already does. [1]: https://github.com/odoo/odoo/commit/8e2b6cede55b51f7ccdbe7601aa7e6035fd6f9fe opw-6383849 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282963 Forward-Port-Of: odoo/odoo#276905
The property editor no longer crashes when a saved domain cannot be evaluated. Users can still open the editor to fix or delete the affected property, avoiding a workflow-blocking error.
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#281221 Forward-Port-Of: odoo/odoo#259886
This update removes unused custom logic from the Gulf invoice module because the related translation behavior was already disabled. It reduces maintenance overhead without changing expected invoice behavior for users.
Original PR description
Remove create() and _compute_narration() method overrides from l10n_gcc_invoice as they only existed to call _load_narration_translation(), which has already been disabled. The parent class implementations handle all required functionality. Keeps the codebase clean by removing unnecessary method overrides. Forward-Port-Of: odoo/odoo#281565 Forward-Port-Of: odoo/odoo#281395
This fixes an issue where pressing Shift+Enter in Safari created a full paragraph break instead of a simple line break in the HTML editor. Users editing Knowledge articles on Mac Safari can now format text as expected without disrupting document structure.
Original PR description
**Steps to reproduce:** - Use a Mac with Safari - Install Knowledge app - Go to any article - Press Shift+Enter to try to enter a soft line break - Hard split is done instead **Issue:** Shift+Enter causes a `insertParagraph` event instead of `insertLineBreak` in Safari, which triggers the `SplitPlugin` instead of the `LineBreakPlugin`. **Fix:** Check if the browser is Safari and call `insertLineBreak` from the `SplitPlugin` (when needed) by listening to the "keydown" events. (note: I was not able to find any other key combination to properly trigger the `insertLineBreak` event in Safari) opw-6413507 Forward-Port-Of: odoo/odoo#281458
This fix prevents an error when staff relocate stock that is already reserved inside a package. It helps warehouse teams move packaged inventory between locations without being blocked by an unexpected access error.
Original PR description
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track…
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track localization in settings - Create a new storable product. - Using an inventory adjustment, add some quantity of that product in Stock, in a new package X. - Create a sales order for that product and confirm it, so the quantity in Stock gets reserved. - Go to Inventory > Reporting > Locations. - Select the quant and try to relocate it, e.g. to WH/Input. -> Raises an AccessError: "Failed to write field stock.package.picking_ids" **Cause** Relocating a quant creates a move: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1531 which creates a new move line without a `picking_id`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1276-L1287 Since both move lines (the new one and the one linked to the SO delivery) share the same `result_package_id`, in `_compute_picking_ids`, both move lines are grouped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L174-L176 Thus, two "pickings" end up associated with the package: the SO's, and `None`. While setting those pickings on the package, it tries to access them: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L182 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1497-L1503 And since `self` isn't just `None`, this check won't be skipped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4152 This eventually raises an AccessError since `None` gets filtered out by `filtered_domain`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4154-L4156 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1504-L1505 opw-6427070 Forward-Port-Of: odoo/odoo#282209
This fix prevents errors when work-hour values round up to the next hour during time conversion. It improves reliability for computed or aggregated hour values, especially near hour or day boundaries.
Original PR description
### Bug `odoo.tools.date_utils.float_to_time` builds the minutes with a rounding step: ```python return time(int(integral), int(float_round(60 * fractional, precision_digits=0)), 0) ``` When the…
### Bug
`odoo.tools.date_utils.float_to_time` builds the minutes with a rounding step:
```python
return time(int(integral), int(float_round(60 * fractional, precision_digits=0)), 0)
```
When the fractional part of the hour is high enough, `round(60 * fractional)`
rounds up to a full **60**, and `time(hour, 60)` is invalid:
```python
>>> float_to_time(16.9959)
ValueError: minute must be in 0..59, not 60
>>> float_to_time(8.999)
ValueError: minute must be in 0..59, not 60
```
Any hours value whose fractional part is ≥ ~0.9917 hits this — which happens
easily with computed/aggregated work-hour floats.
### Fix
Carry the rounded-up minute into the hour, and return `time.max` when that carry
reaches the end of the day (mirroring the existing `hours == 24.0` case):
```python
if minute == 60:
hour += 1
minute = 0
if hour >= 24:
return time.max
```
`float_to_time(16.9959)` now returns `time(17, 0)`, `float_to_time(23.9959)`
returns `time.max`, and regular values are unchanged.
Adds `TestFloatToTime` in `test_date_utils.py` covering the carry, the
end-of-day carry, and regular values.
Forward-Port-Of: odoo/odoo#280807This update checks and cleans data submitted when users post messages, ensuring only appropriate information for the current user is accepted. It helps reduce errors and protects message integrity in the Discuss and mail features.
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#282812 Forward-Port-Of: odoo/odoo#280894
This fix makes a required invoicing component explicit for the Turkish Nilvera localization, preventing setup and test failures when automatic module installation is skipped. It improves reliability without changing day-to-day user workflows.
Original PR description
The view 'res.partner.property.form.inherit.ubl.tr' references external ID 'account_edi_ubl_cii.view_partner_property_form' but module 'account_edi_ubl_cii' is only available because of auto_install-links in the module graph. This causes an error when auto_install is skipped on runbot. The error is fixed since saas-19.2(https://github.com/odoo/odoo/pull/248034) where the view is moved but no backport for saas-19.1 and earlier has been made. Adding the explicit dependency is valid because 'account_edi_ubl_cii' is indirectly installed at the same time. This is the effect chain: 1. 'l10n_tr_nilvera_einvoice' is auto_install'ed after 'l10n_tr_nilvera' installs 2. 'l10n_tr_nilvera_einvoice' has explicit dependency on 'account_edi_ubl_cii'. REF Runbot; https://runbot.odoo.com/odoo/error/237861 Forward-Port-Of: odoo/odoo#283105 Forward-Port-Of: odoo/odoo#282475
Fixed an edge case where a company sending an electronic invoice to itself could have the incoming copy incorrectly treated as a duplicate. This ensures the related vendor bill can still be created, avoiding missing internal billing documents.
Original PR description
Edge case: self-addressed messages (sender == receiver). i.e. a company genuinely invoicing itself. The outgoing invoice already carries the message UUID, so the duplicate check would wrongly discard the incoming document. Exclude those messages from the check so the vendor bill can still be created. task-no --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283663 Forward-Port-Of: odoo/odoo#283574
Paid self-order customers now receive receipt emails with the receipt attached, instead of emails without a receipt image. Unpaid orders continue to receive the same standard email, helping keep communications accurate without changing the unpaid order flow.
Original PR description
Before this commit: ================= - Emails were sent from `/pos-self-order/process-order`. - Receipt image generation was not possible from the backend, so `fullTicketImage` and `basicTicketImage` were hardcoded to `false`. - This caused all emails to be sent without any receipt attachment, even for paid orders. After this commit: ==================== - Unpaid orders continue to send a normal email without attachment. - Paid orders now send an email with the receipt attachment. - Added a new controller `/pos-self-order/send_self_order_receipt` to handle sending receipt emails with or without attachments based on payment status. Task-5353350 Forward-Port-Of: odoo/odoo#281782 Forward-Port-Of: odoo/odoo#237688
This fix prevents Accounting payment terms from crashing when a user enters a zero or negative day value for an end-of-month due date. Instead of showing a technical error, the system can now handle the entry safely and present the expected validation message when saving.
Original PR description
Steps to reproduce: - Install `Accounting` module - Payment Terms > Create NEW - Add a new Due Term line with "Days end of month on the" and a negative amount of days(eg: -1) Traceback: `ValueError: day is out of range for month` When `days_next_month` is set to a negative value, it is passed directly to `relativedelta` as the 'day' value. Since a negative value is not a valid day of the month, the due-date computation raises a `ValueError`. Use the end of the month for the calculation when `days_next_month` is non-positive. This prevents the traceback while computing the payment term and allows the proper validation error to be raised when the record is saved. opw-6453640 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281904
Fixed a rounding issue where POS eWallet or gift card payments could discount an order by one cent less than the amount deducted from the card when certain tax overrides were used. This keeps the customer charge and the consumed wallet or gift card balance aligned.
Original PR description
When paying with an eWallet or gift card in POS, the reward line could end up 0.01 short of the actual card balance if the discount product's tax is configured as tax-excluded through a per-tax…
When paying with an eWallet or gift card in POS, the reward line could end up 0.01 short of the actual card balance if the discount product's tax is configured as tax-excluded through a per-tax override, regardless of the tax's own default configuration. The card is still debited for the full balance, but the order is only discounted by one cent less, so the amount charged to the customer no longer matches the amount consumed from the card. Steps to reproduce: ------------------- * Top up an eWallet (or gift card) with a balance of 10.00 * On the eWallet/gift card program's discount product, set an 18% tax whose Tax Computation is overridden to "Excluded" (price_include_override = tax_excluded), independently of the company's default tax configuration * In POS, add a product to an order and pay (partly) with that eWallet/gift card > Observation: Only 9.99 is deducted from the order total, while the backend correctly shows 10 consumed on the wallet/gift card. Why the fix: ------------ The reward line's price_unit was reconstructed from a one-time backward tax computation, then kept only the tax amount for taxes whose price_include field was true, dropping it for any tax forced excluded. That price_unit was later re-taxed forward using the tax's real (excluded) configuration, and the two roundings don't agree for rates like 18%, losing a cent. We now force special_mode "total_included" whenever an eWallet/gift card reward line's taxes are computed, not just at creation, so its tax-included total always equals the exact redeemed amount regardless of how the tax is configured, and store price_unit as that target amount directly. opw-5819389 Forward-Port-Of: odoo/odoo#278568
Restaurant point-of-sale bill splitting now correctly selects the full quantity when the same combo choice appears multiple times. This prevents under-selecting items during split bills, making restaurant billing more accurate and reliable.
Original PR description
Steps to reproduce: --- - Install `pos_restaurant` demo data. - Open a session for `Restaurant`. - Go to any table. - Add a Sushi Lunch Combo line with the same sushi choice multiple times. - Click the "More" button and select "Split". - Click on any combo product line. Issue: --- - Only one quantity is selected instead of the full combo choice quantity. Cause: --- - Combo child lines were incremented by a fixed value of `1` during split, without considering the quantity ratio between the combo root line and combo child lines. Fix: --- - Compute the selection step based on the combo line quantity relative to the combo root line quantity. - Properly update split quantities for repeated combo choices. - Added test coverage for combo lines with repeated quantities. task-6197879 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282717 Forward-Port-Of: odoo/odoo#264049
This fixes an editor display issue where gradient-colored text inside a selected link could disappear. Users can now see and edit selected linked text clearly, reducing confusion while creating or updating content.
Original PR description
Problem: When text formatted with `.text-gradient` is inside a link with `.o_link_in_selection`, the selected text becomes invisible. `.text-gradient` sets `-webkit-text-fill-color: transparent`, which prevents `color: black !important` on `.o_link_in_selection` from taking effect. Cause: `-webkit-text-fill-color: transparent` from `.text-gradient` overrides standard text `color` rendering, causing the text to stay transparent against the selection highlight background. Solution: Set `-webkit-text-fill-color: black` on `.o_link_in_selection` to ensure text inside gradient links is rendered in black and remains clearly visible when selected. Steps to reproduce: - Add text "ABCD". - Apply gradient color to all text. - Create a link on "BC". - Place cursor/selection inside the new link. - Observe that the text is not visible. opw-6479350 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282952
Website editors can now click and edit previously locked parts of mega menu templates, such as footers and logo areas. This makes customizing website navigation easier and reduces the need for technical workarounds.
Original PR description
### Issue: Some elements in mega menu templates are not editable inline in the website builder. ### Steps to reproduce: - Go to Website > Site > Menu Editor and add a mega menu item. - Edit the mega menu and set its template (e.g. 'Thumbnails' or 'Logos'). - Try to inline edit certain sections (e.g. footer or logos container). ### Reason: `BuilderContentEditablePlugin` does not apply `contenteditable="true"` to these elements because they do not match any of the selectors defined in `content_editable_selectors`. ### Fix: Add missing element classes to `content_editable_selectors` so that these elements become editable inline. task-[6116253](https://www.odoo.com/odoo/project/974/tasks/6116253) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263021
The web search now safely ignores a saved or action-provided filter when its linked record no longer exists. This prevents users from encountering a crash and lets them continue using the search view normally.
Original PR description
…'t exist Have a search view with a m2o field Have an action that sets search_default_m2o: [/BAD ID/] Before this commit there was a crash After this commit, we simply ignore the filter. task-6469841 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#282738
Fixed an issue where translating text edited inside a related-record dialog could show outdated or empty content. The translation window now saves the edited dialog record first, helping users translate the current text without losing context.
Original PR description
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened…
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened in an x2many form dialog keeps its changes for itself until the dialog is saved, see https://github.com/odoo/odoo/blob/242f6d3cf7288853f163ac6986a3b7aa4279efaf/addons/web/static/src/model/relational_model/static_list.js#L193. Its pending changes are not part of the root record changes, so saving the root sends nothing to the server, and the translation dialog then shows the stored terms instead of the current content, or no terms at all when the stored value is empty. The fix changes openTranslationDialog in translation_button.js, the place that decides which record to save. When the record keeps its changes for itself (record._noUpdateParent), the record is saved directly, like the button did before the commit above. The root record is still saved in the other cases, so the editable list case that commit fixed keeps working. Steps to reproduce: 1. Activate a second language in Settings > Translations > Languages 2. Open the Surveys app, open a survey and click a question in the Questions tab 3. In the Description tab, change the description 4. Click the EN button on the description field => the translation dialog shows the terms of the previous description, not the current one Ticket [link](https://www.odoo.com/odoo/project.task/6237291) opw-6237291 Forward-Port-Of: odoo/odoo#269507
Sales users limited to their own documents can now cancel confirmed sales orders that include loyalty programs without hitting an access error. This prevents blocked cancellations while still cleaning up related loyalty point records correctly.
Original PR description
Steps to produce: --- - Install `sale_management` and `sale_loyalty` module without demo. - From sales > products > discounts & loyalty, create new loyalty card program and save. - Now create new…
Steps to produce: --- - Install `sale_management` and `sale_loyalty` module without demo. - From sales > products > discounts & loyalty, create new loyalty card program and save. - Now create new product of 100$. - Create a user which have sales rights as `user: own documents only`. - With that user, create new sale order with product and confirm. - Try to cancel the order. Issue: --- - It shows the access error: ```py You are not allowed to delete 'Sale Order Coupon Points - Keeps track of how a sale order impacts a coupon' (sale.order.coupon.points) records. This operation is allowed for the following groups: - Sales/Administrator Contact your administrator to request access if necessary. ``` Root cause: --- - Users with the `Sales: Own Documents Only` access right only have read permissions ([1]). When they cancel a Sales Order, the `_action_cancel` method attempts to clean up the temporary pending points allocated to the order by calling `self.coupon_point_ids.unlink()`. Because this call is executed without elevated privileges, the system blocks the deletion and raises an Access Error Solution: --- - Added `.sudo()` to the `unlink()` call for `coupon_point_ids` in the `_action_cancel` method. This ensures the pending point records are cleaned up with the necessary elevated privileges. [1]https://github.com/odoo/odoo/blob/23af2b443735c6d3a2f64e44f9ea5da45638b052/addons/sale_loyalty/security/ir.model.access.csv#L16 opw-6453016 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283535 Forward-Port-Of: odoo/odoo#281477
This fixes two mislabeled account group names used in Mexican trial balance reporting checks. The change helps ensure the Chart of Accounts XML expected for SAT reporting matches the correct official account categories and keeps localization validation passing.
Original PR description
Enterprise companion of odoo/odoo#277615 — the forward-port to 19.0 of the `l10n_mx` fix that restores the SAT account group names copied from siblings. The `l10n_mx_reports` trial balance test asserts the full Chart of Accounts XML sent to the SAT with the group names hardcoded in the expected output. Two of them were wrong (copied from sibling groups) and are corrected by the community PR: - SAT group **602** (Gastos de venta): `Cost of sales` → `Selling expenses` (`Cost of sales` is 501.01). - SAT group **614** (Amortización contable): `Accounting depreciation` → `Accounting amortisation` (that name belongs to 613). Without this, `ci/l10n` fails on `TestL10nMXTrialBalanceReport.test_generate_coa_xml` and `...test_generate_coa_xml_with_prefix_7_accounts_having_debit_and_credit_tags`. Same branch name as the odoo PR so the mergebot pairs them. Forward-Port-Of: odoo/enterprise#125207
The Belgian salary configurator now handles cases where no company bike is configured. This prevents an error when recruiters or candidates select the company bike option and keeps the offer process running smoothly.
Original PR description
Steps to Reproduce: - install l10n_be_hr_contract_salary module. - make sure that there is no model with vehicle type bike in fleet. - create an offer in recruitment. - open salary configurator. - click on company bike checkbox. Issue: - traceback occurs when enabling the company bike option without a configured bike. Reason: - the company bike depreciated cost value is empty when no bike is available, but the code tries to split it into bike options and vehicle ID resulting in a traceback. Solution: - Use the condition to check if the company bike depreciated cost is available before spliting the value. - Set the depreciated cost to 0 when no bike is selected. task-6468987
This update prevents a rare crash when Intrastat reporting logic is triggered for a company that the current user cannot normally read. It has no expected impact on standard day-to-day use, but improves reliability for future versions or custom setups.
Original PR description
Due to some trouble with tests, we found that in some cases, this function is called on the root company, and if the user does not have the access rights to read data from the company (users with system rights have them by default), it will cause a crash. This situation is not possible with the standard UI, but we fix it in case it becomes possible in a future version or customization.
This fixes Peppol invoice reception for companies that must receive vendor bills through a journal rather than the Documents app, such as French companies using electronic invoicing. Settings will no longer clear the required incoming invoice journal, and received Peppol documents will be routed correctly as vendor bills.
Original PR description
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal…
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal stays required. This is the case of French companies (via l10n_fr_pdp). The onchange of the settings and the import did not check this method. So on a French company with the mode set to 'documents': - the Settings cleared the journal on each opening, while it was still required - the incoming documents were saved in Documents instead of vendor bills Steps to reproduce: - Create a Belgian company, with a purchase journal, and register it on Peppol as receiver - Set the reception mode to "Receive in Documents" - Change the fiscal position to France, and install l10n_fr_pdp, the Peppol part is replaced by "French Electronic Invoicing", so the radio button is not visible anymore, but the company still has peppol_reception_mode == 'documents'. - Open the Settings again: the field "Incoming Invoices Journal" is empty. opw-6429691 Forward-Port-Of: odoo/enterprise#126462
This fixes an issue in Brazilian point-of-sale electronic invoicing where the system could choose the wrong matching tax during setup, causing tax adjustment accounting entries to become unbalanced. The correction ensures the sales tax is selected consistently, helping avoid accounting errors for affected POS transactions.
Original PR description
The chart template gives the same Avatax code and price_include_override to the sale and the purchase tax, and creates both in the same transaction. Without an explicit type_tax_use the lookup used to return either of them at random, and picking the purchase one left the entry unbalanced. The purchase taxes got their Avatax code in 18.4+. https://github.com/odoo/enterprise/pull/101072 runbot-945969 Forward-Port-Of: odoo/enterprise#128818 Forward-Port-Of: odoo/enterprise#128476
The Peruvian sales ledger now reports the full gross sale amount when a 3% IGV withholding applies. This aligns the report with SUNAT expectations because the withholding is handled at payment time, not as a reduction of the sale total.
Original PR description
The 3% IGV withholding is a negative sale tax, so it reduced amount_total and the 14.4 ledger reported a net total. SUNAT expects the gross total of the operation, the withholding being a payment-time mechanism. task-5935227 Forward-Port-Of: odoo/enterprise#128849
Links added inside spreadsheet cell comments can now be opened with a regular click, as users would expect. This removes a frustrating interaction issue and makes shared references in comments easier to access.
Original PR description
Current behavior before PR: - Clicking a link in a cell comment did not work. A left click was blocked, while Ctrl+click (or Cmd+click) opened the link in a new tab. - This was caused by `t-on-click.prevent` on the comment thread and popover. It was originally added because the scroller service used the URL hash to scroll to anchors, which was removed in https://github.com/odoo/odoo/commit/711e9c9f24818714129f55283e2df64503d93605 Desired behavior after PR is merged: - `t-on-click.prevent` is removed and links in cell comments can be opened normally with both left click and Ctrl+click (Cmd+click on macOS). Task: [6448651](https://www.odoo.com/odoo/project/2328/tasks/6448651) Forward-Port-Of: odoo/enterprise#128448 Forward-Port-Of: odoo/enterprise#127473
This fix lets users enter and compare budget amounts on the Moroccan Profit and Loss report, even though it uses multiple columns. Budget values now remain visible and are compared against the correct balance column, improving financial planning accuracy for Moroccan companies.
Original PR description
Before this commit, it was impossible to use budget on the Moroccan P&L, for the following reasons: - The feature was designed for one-column reports. MA's P&L uses 3, one of which is the total of…
Before this commit, it was impossible to use budget on the Moroccan P&L, for the following reasons:
- The feature was designed for one-column reports. MA's P&L uses 3, one of which is the total of the two others.
=> We remove that requirement, and make sure to always select the 'balance' column as the reference for the budget comparison.
- When trying to input a budget amount in the report, the amount disappeared entirely.
=> This was because the total column of report was not using 'balance' as its expression label. We fix that by rewriting the expression labels of that report.
The fact we hardcode the use of 'balance' is arguable. It is however not possible here to rely on some custom handler to change a specific option key that would be used to generate the budget comparison data, since some of those data need to be generated in the get_options, before _custom_options_initializer even gets called. This is the simplest approach, and this case is rare enough for us to deem it acceptable.
opw-6385229
Forward-Port-Of: odoo/enterprise#128816
Forward-Port-Of: odoo/enterprise#128266Splitting a multi-page PDF in Documents could show the new pages in a random order because they were created at the same time. The update adds a consistent fallback ordering so users see split pages in the expected sequence.
Original PR description
steps: - upload a multi-page pdf - split all the pages -> they now show in a random order The issue is that the current documents are sorted by create_date desc, but the split creates all the different documents at the same time so they are sorted in the order they happen to be on the disk. We now add a sort by id to act as a tie-breaker. opw-6176840 Forward-Port-Of: odoo/enterprise#128410 Forward-Port-Of: odoo/enterprise#117255
Odoo no longer blocks tax return submission because of incomplete settings on tax groups that have not been used. This prevents unnecessary interruptions during tax closing while still checking the configuration that matters for actual transactions.
Original PR description
…ax closing Steps to reproduce: - Remove the tax payable and receivable accounts of a tax group for which no move exists. - Open the tax returns view, set the opening date and submit the tax return -> Odoo prevents going further because the tax group configuration isn't fully done, but it's useless to ensure that for tax groups that aren't used. Forward-Port-Of: odoo/enterprise#128197
The website builder now shows the correct preview image for the AI live chat snippet when it is not installed. This prevents a broken or missing preview from appearing while users configure website content.
Original PR description
Problem: 1) The preview image of the livechat snippet was removed in this [commit][1] and wasn't replaced with another image. As a result, the uninstalled livechat snippet doesn't display properly in the website builder. Solutions: 1) An image has been added `ai_livechat.png` which is shown on preview Note: This fix will change in master to be up to date with current website snippet previews. The location will be moved to `snippet_previews` and the file type will be changed to `.webp` [1]: https://github.com/odoo/enterprise/commit/df05441e469157890253b5550b5f8735723b28fb Task-5248712 Forward-Port-Of: odoo/enterprise#126361
Belgian Acerta payroll exports now include weekend days when an eligible leave period overlaps a weekend. This ensures sick leave and similar absences are reported in the format Acerta expects, reducing missing entries and manual corrections.
Original PR description
## Steps to reproduce: - Install l10n_be_hr_payroll_acerta - Create an employee in a belgian company - Create a sick time off for the created employee that overlaps with a weekend - Export acerta report for the employee - Notice the weekend that overlaps with the time off is not present in the report ## Cause: While exporting the report file we only loop over the created work entries' dates and since weekends doesn't have work entries we don't consider them in the report. ## Fix: When generating the line of a leave's start date we check if the leave overlaps with a WE, we fetch the WE's date and we generate a line for each day of the WE. According to Acerta this is the correct behavior for their reports for specific types of leaves. **opw-6313534** Forward-Port-Of: odoo/enterprise#128143 Forward-Port-Of: odoo/enterprise#124500
The LinkedIn social integration now handles accounts where LinkedIn returns no statistics, preventing the refresh process from failing. This improves reliability for users managing LinkedIn accounts with missing or unavailable analytics data.
Original PR description
Bug === When the LinkedIn API returns no statistics for the account, the refresh crashes. Task-6425391 Forward-Port-Of: odoo/enterprise#126326
Re-authorizing a Shopee shop can now correctly switch the shop to the newly selected Shopee account. This prevents shops from staying linked to an old API account after reconnecting, reducing setup errors and support needs.
Original PR description
Context: when a user re-authenticate a shop, they might use different shopee.account (API key). Currently Odoo will not change the shopee.account when they re-auth with another shopee.account. Enable a shopee.shop switches to another shopee.account when we run `create_or_update_shop` function. Forward-Port-Of: odoo/enterprise#128469 Forward-Port-Of: odoo/enterprise#92446
The project Sales button now shows all related sales records, including rental orders, so the list matches the count shown on the project. This prevents users from missing rental orders when reviewing project-related sales activity.
Original PR description
Steps to Reproduce --- 1. Install sale_renting_project. 2. Create a Project linked to 1 standard Sales Order and 1 Rental Order. 3. Observe the "Sales" stat button counts 2 Sales. 4. Click the stat button. Only the standard Sales Order is displayed. Issue --- In saas-18.4, the project Sales stat button calls action_view_sos without the from_embedded_action context key. As a result, _get_sale_orders_domain applies the non-rental filter by default, causing rental orders to be excluded from the action even though they are included in the displayed counter. Expected Behavior --- The Sales stat button should display all orders linked to the project, including both standard and rental orders, matching its total counter. Fix --- Return the base project domain unmodified when from_embedded_action is not set in the context. task-6140201 Forward-Port-Of: odoo/enterprise#128468 Forward-Port-Of: odoo/enterprise#121449
A contributor submitted their Individual Contributor License Agreement for Odoo. This is a legal administrative update that helps confirm contribution rights and does not change product functionality.
Original PR description
This pull request submits my Odoo Individual Contributor License Agreement signature. Forward-Port-Of: odoo/odoo#282045