Tuesday, August 18, 2026
38 changes · saas-19.3
Enhancements to existing features
Cached website generation requests now schedule a quick follow-up task instead of calling webhooks immediately. This helps ensure notifications happen after the generation work is safely saved, without tying up system workers unnecessarily.
Original PR description
When a request is cached a webhook won't be called because if we did it immediatly it might arrive before the generator commited. And we don't want to delay the webhook call to not stall a worker just for that. So we simply scheldule a trigger if a request was cached.
Bulgaria made it mandatory for large companies to present a monthly file to report their VAT to the administration. To streamline that process, when the VAT return is validated and PDF is added to the attachments, the monthly General Ledger SAF-T file, the POKUPKI Purchase Report and PRODAGBI Sale Report are produced and added as well. Simplify the report file download error wizard's visuals and descriptions to improve readability. task-6007963 --- I confirm I have signed the CLA an
Original PR description
Bulgaria made it mandatory for large companies to present a monthly file to report their VAT to the administration. To streamline that process, when the VAT return is validated and PDF is added to the attachments, the monthly General Ledger SAF-T file, the POKUPKI Purchase Report and PRODAGBI Sale Report are produced and added as well. Simplify the report file download error wizard's visuals and descriptions to improve readability. task-6007963 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269132
Before this commit: - Refund orders of scheduled (with shipping date) orders always generated a new picking with negative quantities, even when the original delivery had not been completed. - This could lead to incorrect stock movements and negative quantity computations for undelivered pickings. After this commit: - When processing a refund of a scheduled (with shipping date) order, the behavior now depends on the state of the original picking: - If the picking has already been del
Original PR description
Before this commit: - Refund orders of scheduled (with shipping date) orders always generated a new picking with negative quantities, even when the original delivery had not been completed. - This…
Before this commit:
- Refund orders of scheduled (with shipping date) orders always generated a new picking with negative quantities, even when the original delivery had not been completed.
- This could lead to incorrect stock movements and negative quantity computations for undelivered pickings.
After this commit:
- When processing a refund of a scheduled (with shipping date) order, the behavior now depends on the state of the original picking:
- If the picking has already been delivered, a return picking is created with the corresponding negative quantities.
- If the picking has not been delivered, the original picking is updated instead:
- The picking is cancelled for a full refund.
- Refunded product moves are removed from the picking for a partial refund.
- This prevents unnecessary negative stock movements and ensures stock operations remain consistent with the delivery status.
Task-5902424
Forward-Port-Of: odoo/odoo#282087
Forward-Port-Of: odoo/odoo#271506Resolved issues and error corrections
The document tests now verify that GIF thumbnails are created and marked correctly instead of requiring an exact byte-for-byte match. This keeps quality checks aligned with improved GIF resizing behavior and reduces false test failures without changing user-facing functionality.
Original PR description
Previously, test_document_thumbnail_status asserted that document thumbnails were byte-for-byte identical to the raw GIF content. Following improvements to GIF handling in image_process(), thumbnails are now correctly resized, causing the raw byte assertion to fail. Update the test to assert the presence and status of the thumbnail rather than matching exact raw unresized payload bytes. [odoo/odoo#281883](https://github.com/odoo/odoo/pull/281883) opw-6232841 Forward-Port-Of: odoo/enterprise#128032
Miscellaneous changes
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associate
Original PR description
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the…
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associated with the product, including those that are not visible on ecommerce. **Current behavior before PR:** When a product only has non-visible tags, the tags table is displayed without any content. <img width="669" height="350" alt="image" src="https://github.com/user-attachments/assets/4758ea76-5186-4035-a065-aa4c71ce7053" /> **Desired behavior after PR is merged:** The tags table is only displayed when the product has at least one tag visible on ecommerce. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282348 Forward-Port-Of: odoo/odoo#278604
This fix removes ambiguity in how Studio approval rule conditions are interpreted. Approval rules with empty conditions now correctly apply to all relevant records, reducing the risk of inconsistent approval behavior for users.
Original PR description
Before this commit, there was an ambiguity with the usage of filtered_domain ie ``` self.assertTrue(record.filtered_domain(False)) self.assertFalse(record.filtered_domain(Domain(False))) ``` This is because in that case the API of filtered_domain was not respected After this commit, there is no ambiguity as we cast to a Domain the value we obtain from the rule: - False or None: all records should be impacted by the rule => Domain(True) - otherwise, let the domain do its job opw-6431607 Forward-Port-Of: odoo/enterprise#127813 Forward-Port-Of: odoo/enterprise#127676
Fixed an issue in spreadsheets where clearing a global filter could also remove its default value from the edit panel. This keeps the displayed filter setting reliable and prevents confusion when users adjust spreadsheet filters.
Original PR description
Current behavior before PR: - In [da282d4](https://github.com/odoo-dev/enterprise/commit/da282d4a031fa1c1377a6836acf662100c68bde0), date filter values were cleared when selecting 'All time'. - Based on this, we handled the crash by relying on the active filter value, but forgot that the same component is also used in the edit panel. - As a result, clearing a filter could make its default value disappear from the edit panel. Desired behavior after PR is merged: - Always use the `globalFilterValue` passed to `GlobalFilterInput`. - Keep the current filter value independent from the filter's default value. - This makes the component independent of the active filter state. Task: [6388147](https://www.odoo.com/odoo/project/2328/tasks/6388147)
Fixed an issue that could cause payroll salary attachment names to fail when several attachments were processed together. This helps HR and payroll users view records reliably without unexpected errors.
Original PR description
Currently, an error occurs when the display name is computed for multiple salary attachments. `ValueError: Expected singleton: hr.employee(58, 56)` After [recent commit], when computing the display name, the employee's display name is accessed through multiple attachment records at once. This results in accessing the display name of multiple employees simultaneously, which raises a singleton error. This commit ensures that the employee is accessed from each individual attachment record when computing the display name. [recent commit]: https://github.com/odoo/enterprise/commit/d9648ef695903113d6ed2f40cd4fcfdd68221fa8 [1]- https://github.com/odoo/enterprise/blob/d913de3097d06e723a08d24907024f83769b1fb8/hr_payroll/models/hr_salary_attachment.py#L150-L153 sentry-7665634830
Users can now see and select WhatsApp templates that are not yet approved when configuring WhatsApp signing. This makes it clearer that templates must be submitted and approved before the related signing flow can be used, while keeping the flow disabled until approval is complete.
Original PR description
Previously, users couldn’t see or select unapproved WhatsApp templates in Settings, which made it unclear that they needed to submit templates for approval first. Now, users can select unapproved templates, making the process clearer. The flow will still remain disabled until the template is approved. task-6306091
The salary configurator now correctly pre-selects Yes for optional benefits that are already included in a contract offer. This prevents employees from seeing misleading benefit choices when reviewing their offer.
Original PR description
Issue: When an employee opens the salary configurator for a contract offer, optional benefits configured with Yes/No radio choices (such as Medical Insurance) fail to pre-select 'Yes' even when the benefit is already included in the offer. Steps to Reproduce: 1. Go to Salary Configurator and open a contract offer that has an active benefit 2. Observe that 'Yes' is not selected for the benefit on initial load. Fix: Ensure that when a contract offer includes a benefit, the salary configurator automatically defaults the radio selection to `Yes`. task-6392064
Peruvian accounting reports now use the currency rate already saved on each accounting entry instead of recalculating it later. This reduces rounding discrepancies and helps produce more accurate statutory report figures.
Original PR description
Previously, the `_get_ple_report_data` method computed the currency rate when called. Since the calculation was based on the entry totals, it was prone to rounding errors. This PR makes it use the rate stored in the entry itself. This should lead to more accurate results. opw-6411322 Forward-Port-Of: odoo/enterprise#128027 Forward-Port-Of: odoo/enterprise#126882
Dominican Republic electronic consumer invoice PDFs no longer show the company name twice when the company document layout or address details are not configured. This keeps invoice headers clean and avoids confusion for customers receiving e-CF invoices.
Original PR description
Steps to Reproduce: 1. Install l10n_do_edi on a company configured with Dominican Republic localization. 2. Leave the company's address / Document Layout details unconfigured (Settings > Companies >…
Steps to Reproduce:
1. Install l10n_do_edi on a company configured with Dominican Republic localization.
2. Leave the company's address / Document Layout details unconfigured (Settings > Companies > Configure Document Layout).
3. Enable 'Use Documents' on the sales journal and create a Customer Invoice with Document Type = Electronic Consumer Invoice (e-CF).
4. Confirm/post the invoice and print/preview the PDF.
5. Observe the company name is printed twice in the header.
Root Cause:
l10n_do_edi.custom_header explicitly renders the company name via:
[`<span t-field='o.company_id.partner_id.name'/>`](https://github.com/odoo-dev/enterprise/blob/c23d526f53402471dccbbf9460cfd04ce377b5d0/l10n_do_edi/views/report_invoice.xml#L10)
immediately followed by a call to the core template web.company_address_list:
[` <t t-call='web.company_address_list'/>`](https://github.com/odoo-dev/enterprise/blob/c23d526f53402471dccbbf9460cfd04ce377b5d0/l10n_do_edi/views/report_invoice.xml#L11)
When the company's address/Document Layout is empty, `company.is_company_details_empty` evaluates True, causing `web.company_address_list` to fall back to a contact widget with `fields=['address', 'name']` — which renders the company name a second time. custom_header assumed `company_address_list` would never render the name itself, which does not hold in this empty-address state, resulting in the duplicate. This only surfaces on e-CF document types, since custom_header is only set via l10n_do_edi.report_invoice_document's routing (_get_name_invoice_report()), which standard invoices never hit.
Solution:
Made the explicit name span in `l10n_do_edi.custom_header` conditional on whether the company's details are configured. `web.company_address_list` (core, shared across other reports, left untouched) already renders the company name via its contact widget fallback when the address is empty — so the explicit span now only prints the name when `company_details` is populated, avoiding both sources printing it at once.
Result:
Company name now renders exactly once on e-CF invoices, regardless of whether the company's address/Document Layout is configured.
opw-6430918At installation of the l10n_pl_bank_verification module or when upgrading from a lower version, the field l10n_pl_verification_id on the account.payment model gets computed and it makes the upgrade crash out. Adding a init to the model to create the column to prevent the ORM from computing the field at module installation no-task Forward-Port-Of: odoo/odoo#282504
Original PR description
At installation of the l10n_pl_bank_verification module or when upgrading from a lower version, the field l10n_pl_verification_id on the account.payment model gets computed and it makes the upgrade crash out. Adding a init to the model to create the column to prevent the ORM from computing the field at module installation no-task Forward-Port-Of: odoo/odoo#282504
**Problem**: When creating a percentage down payment with a Brazilian tax, the divide by zero error occurs because the factor contains a zero value. **Steps to reproduce**: 1. Set up Brazil localization with ```l10n_br_avatax```, ```l10n_br_edi```. 2. Create a service product with ```LC116``` code set up. 3. Create a sales order with the customer has ```Automatic Tax Mapping (Avalara Brazil)``` in their Fiscal Position. 4. Add the service product to the sales order and confirm it. 5. Cr
Original PR description
**Problem**: When creating a percentage down payment with a Brazilian tax, the divide by zero error occurs because the factor contains a zero value. **Steps to reproduce**: 1. Set up Brazil localization with ```l10n_br_avatax```, ```l10n_br_edi```. 2. Create a service product with ```LC116``` code set up. 3. Create a sales order with the customer has ```Automatic Tax Mapping (Avalara Brazil)``` in their Fiscal Position. 4. Add the service product to the sales order and confirm it. 5. Create a percentage down payment for the sales order. 6. Divide by zero error occurs. **Fix**: Check if the ```sum_of_factors``` is zero and if so, set the ```delta_factor``` to zero. before ```normalize_results['plus_sum_of_factors'] / normalize_results['sum_of_factors']``` opw-6446986 Forward-Port-Of: odoo/odoo#281571
Steps to reproduce: - Make sure you have 2 or more languages installed - Install events and website app - For the website, apply the languages you have in settings - Make sure website default language is different from backend language - Create an event - Go to the event's website page through the smart button - Switch the language of the frontend to one that is different from backend - Translate (alter) the description for this language - Click on the outlook icon to download the ics
Original PR description
Steps to reproduce: - Make sure you have 2 or more languages installed - Install events and website app - For the website, apply the languages you have in settings - Make sure website default…
Steps to reproduce: - Make sure you have 2 or more languages installed - Install events and website app - For the website, apply the languages you have in settings - Make sure website default language is different from backend language - Create an event - Go to the event's website page through the smart button - Switch the language of the frontend to one that is different from backend - Translate (alter) the description for this language - Click on the outlook icon to download the ics Current Behavior: The ics will contain a description based on the backend's chosen language Expected Behavior: The ics will contain a description based on the website's chosen language. If there's no website, it will use the browser's language Clarification: The current behavior is confusing for users as they will expect the ics to match the description of the current webpage which is dependent on the frontend's selected language. This current behavior also contradicts the google calendar behavior which grabs the description based on the frontend's selected language. The endpoints for retrieving the ics file now utilize the website=True flag to take advantage of IrHttp._match which will automatically set the context language to be the frontend's rather than the backend language. We want the ics file to be in the frontend language for visual consistency. Also, send_email_reminder function does not require with context for this reason opw-6235320 Forward-Port-Of: odoo/odoo#280520 Forward-Port-Of: odoo/odoo#271968
### Current behavior: When exporting a quotation PDF from the Sales Order a multi-word product name, e.g. "Pick Up", is split into 2 lines ### Expected behavior: When exporting a quotation PDF from the Sales Order a multi-word product name, e.g. "Pick Up", should stay on a single line when there is enough horizontal space in the Description column ### Steps to reproduce: 1. Sales > new Sales Order 2. Add a product whose name has at least two words, e.g. "Pick Up" 3. Export the Sales O
Original PR description
### Current behavior: When exporting a quotation PDF from the Sales Order a multi-word product name, e.g. "Pick Up", is split into 2 lines ### Expected behavior: When exporting a quotation PDF from…
### Current behavior: When exporting a quotation PDF from the Sales Order a multi-word product name, e.g. "Pick Up", is split into 2 lines ### Expected behavior: When exporting a quotation PDF from the Sales Order a multi-word product name, e.g. "Pick Up", should stay on a single line when there is enough horizontal space in the Description column ### Steps to reproduce: 1. Sales > new Sales Order 2. Add a product whose name has at least two words, e.g. "Pick Up" 3. Export the Sales Order report to PDF 4. Observe the second word is pushed to a new line, even with free horizontal space ### Cause of the issue: Commit https://github.com/odoo/odoo/commit/6d695e53743a58560a00c163b3ac16ee1f149761 added a `d-flex` in 19.3 to keep product image and name on the same line. The `o_product_name_cell` uses default shrink wrap. Under wkhtmltopdf's old QtWebKit engine the shrink-wrapped width is computed marginally short of the real text width; this margin is font-metric dependent, so with the slightly wider font like Open Sans the last word crosses the boundary and wraps onto a new line, even though the surrounding cell has room. ### Fix: - Add the Bootstrap `flex-grow-1` utility class to the o_product_name_cell div so it grows to fill the remaining width of the flex row - The image keeps its fixed 48px width and stays on the same line - Small UI change, so no unit test. Only manual test: Before: <img width="357" height="211" alt="image" src="https://github.com/user-attachments/assets/29458867-c7d0-4cc8-8397-4c97b7a2b91d" /> After: <img width="342" height="196" alt="image" src="https://github.com/user-attachments/assets/9a1ca99c-916f-4179-b9b9-67879885e9d8" /> opw-6434410
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The `move_raw_ids` field on the MO form sets a context with `form_view_ref: 'mrp.view_mrp_stock_move_operations'`, so that editing a component's `stock.move` line in place opens that dedicated view: https://github.com/odoo/odoo/blob/20db2910d16fa6ebd07794fb9ed3fb1c8f57b0fc/addons/mrp/views/mrp_prod
Original PR description
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The…
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The `move_raw_ids` field on the MO form sets a context with `form_view_ref: 'mrp.view_mrp_stock_move_operations'`, so that editing a component's `stock.move` line in place opens that dedicated view: https://github.com/odoo/odoo/blob/20db2910d16fa6ebd07794fb9ed3fb1c8f57b0fc/addons/mrp/views/mrp_production_views.xml#L411-L417 The 'Catalog' button lives inside that same field, and calls `action_add_from_catalog_raw`, which delegates to the mixin's `action_add_from_catalog`: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L17 Which conserve the `'form_view_ref'` from context: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L28 and that action requests its form view with `view_id=False`: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L25 Which will try to load `view_mrp_stock_move_operations`, since: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/odoo/addons/base/models/ir_ui_view.py#L3002-L3005 `_get_view` never checks that the resolved view's `model` matches the model it was asked for, so it returns `view_mrp_stock_move_operations`for the `product.product` model: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/mrp/views/stock_move_views.xml#L41 https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/stock/views/stock_move_views.xml#L128 `state` doesn't exist on `product.product`, hence the crash. opw-6433620 Forward-Port-Of: odoo/odoo#279811
This removes the duplicate table tracker number on the receipt when ordering on kiosk. There is already a floating_order_name field that shows the table tracker number on the receipt when ordering on kiosk. task-6439063
Original PR description
This removes the duplicate table tracker number on the receipt when ordering on kiosk. There is already a floating_order_name field that shows the table tracker number on the receipt when ordering on kiosk. task-6439063
The PoS client draws the QR code itself, but `showQR` was feeding it the result of `build_qr_code_url`, which is not a payment payload but the URL of the report rendering one: /report/barcode/?barcode_type=QR&quiet=0&width=128&height=128&value=... Scanning the code therefore gave the bank app a report path instead of the payment data. All methods going through `res.partner.bank` were affected: `sct_qr`, `emv_qr`, `id_qr` and `ch_qr`. Add `build_qr_code_value`, returning the value th
Original PR description
The PoS client draws the QR code itself, but `showQR` was feeding it the result of `build_qr_code_url`, which is not a payment payload but the URL of the report rendering one:
/report/barcode/?barcode_type=QR&quiet=0&width=128&height=128&value=...
Scanning the code therefore gave the bank app a report path instead of the payment data. All methods going through `res.partner.bank` were affected: `sct_qr`, `emv_qr`, `id_qr` and `ch_qr`.
Add `build_qr_code_value`, returning the value the barcode controller would have encoded, and use it in the PoS. This also fixes `default_qr`, the offline fallback. `get_qr_code_url` is renamed to `get_qr_code_value` as it no longer returns a URL.
task-6465442# How to reproduce - Enable dev mode - In the website editor, add a Video Inner Content to the page - Enable atleast one option that is not "Start at" (e.g. Autoplay) - Click on the Video, and in the Style tab, click on Replace # The problem A traceback is shown # Cause When you replace a video, a pop up appears with the different options associated to that video. These options are rendered with the `VideoOption` component : https://github.com/odoo/odoo/blob/46d3fee5812b8406aac22
Original PR description
# How to reproduce - Enable dev mode - In the website editor, add a Video Inner Content to the page - Enable atleast one option that is not "Start at" (e.g. Autoplay) - Click on the Video, and in the…
# How to reproduce - Enable dev mode - In the website editor, add a Video Inner Content to the page - Enable atleast one option that is not "Start at" (e.g. Autoplay) - Click on the Video, and in the Style tab, click on Replace # The problem A traceback is shown # Cause When you replace a video, a pop up appears with the different options associated to that video. These options are rendered with the `VideoOption` component : https://github.com/odoo/odoo/blob/46d3fee5812b8406aac2244baa76b0daeef358c2/addons/html_editor/static/src/main/media/media_dialog/video_selector.js#L9 The props for that component accept a `value`, which type has been changed from `Boolean` to `String` in : https://github.com/odoo/odoo/commit/ae894892e048bcdf9c3efcf41b53b44f2fd9e88b The issue is that some option's value are defined as boolean : https://github.com/odoo/odoo/blob/46d3fee5812b8406aac2244baa76b0daeef358c2/addons/html_editor/static/src/main/media/media_dialog/video_selector.js#L352 So the props validation fail, but only in dev mode : https://github.com/odoo/odoo/blob/46d3fee5812b8406aac2244baa76b0daeef358c2/addons/web/static/lib/owl/owl.js#L3227-L3231 # Proposed solution When changing the options value in the popup, the trucy values are replaced by "00:00", which is why the traceback only appears when replacing the video : https://github.com/odoo/odoo/blob/46d3fee5812b8406aac2244baa76b0daeef358c2/addons/html_editor/static/src/main/media/media_dialog/video_selector.js#L182-L186 We now use the same trucy represetation in `syncOptionsWithUrl()` opw-6369196 Forward-Port-Of: odoo/odoo#280375 Forward-Port-Of: odoo/odoo#276585
`cbc:RoundingAmount` was the sum of `raw_total_excluded` over the non-fixed taxes plus the sum of `raw_tax_amount` over all the taxes, so the base of the line was counted once per non-fixed tax, inflating the line total. `cbc:TaxableAmount` was taken from the tax details of each grouping key, which is the base of that specific tax not the net amount of the line expected by JoFotara. Both amounts are now read from the base line tax details (`raw_total_included` and `raw_total_excluded`), so
Original PR description
`cbc:RoundingAmount` was the sum of `raw_total_excluded` over the non-fixed taxes plus the sum of `raw_tax_amount` over all the taxes, so the base of the line was counted once per non-fixed tax, inflating the line total. `cbc:TaxableAmount` was taken from the tax details of each grouping key, which is the base of that specific tax not the net amount of the line expected by JoFotara. Both amounts are now read from the base line tax details (`raw_total_included` and `raw_total_excluded`), so they describe the line itself regardless of the number of taxes set on it. The document level `cbc:TaxableAmount` keeps using the aggregated tax details. Even tho the problems were hidden because in Jordan, a line wouldn't have more than 1 percent tax + 1 fixed tax, it emerged during the development of the fix in this PR: https://github.com/odoo/odoo/pull/279335 Forward-Port-Of: odoo/odoo#281262
When using multiple devices sharing draft orders, a race condition can happen where one device reuses another device's empty synced draft order. This leads to duplicate UUIDs, which triggers automatic order merging in `sync_from_ui` on the server and clears the table association. To prevent this: - Filter out synced orders (`!order.isSynced`) in `getEmptyOrder()`, `createOrderIfNeeded()`, and `setTable()` when looking for reusable empty orders. - This ensures each terminal only reuses its o
Original PR description
When using multiple devices sharing draft orders, a race condition can happen where one device reuses another device's empty synced draft order. This leads to duplicate UUIDs, which triggers automatic order merging in `sync_from_ui` on the server and clears the table association. To prevent this: - Filter out synced orders (`!order.isSynced`) in `getEmptyOrder()`, `createOrderIfNeeded()`, and `setTable()` when looking for reusable empty orders. - This ensures each terminal only reuses its own locally created, unsynced empty orders, guaranteeing unique UUIDs per device session. task-id: 6296661 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269551
`test_prepare_order_vals_rights` builds its PoS user with stock.`group_stock_user`. Until 19.2 `point_of_sale` depended on `stock_account`, so stock was always installed and the xmlid resolved. Since 6a56bd10cec7 split stock out of PoS, the module no longer pulls in stock, and `env.ref `raises "External ID not found in the system" when the module is tested alone. The group was never needed: the test only calls `_prepare_invoice_vals`, which reads the symbol as sudo and touches no stock record
Original PR description
`test_prepare_order_vals_rights` builds its PoS user with stock.`group_stock_user`. Until 19.2 `point_of_sale` depended on `stock_account`, so stock was always installed and the xmlid resolved. Since 6a56bd10cec7 split stock out of PoS, the module no longer pulls in stock, and `env.ref `raises "External ID not found in the system" when the module is tested alone. The group was never needed: the test only calls `_prepare_invoice_vals`, which reads the symbol as sudo and touches no stock record. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Currently an error occurs when user tries to send a pdf of a invoice to a customer. Steps to replicate: (Make sure to have `python 3.14.4` and `pypdf=5.4.0`) - Install `l10n_sa_edi` with demo and switch to `My Saudi Arabia Company`. - Open invoices and create an invoice with customer and an invoice line. - Click `Send` > Again Click `Send`. Error: ``` AttributeError: 'PageObject' object has no attribute 'getObject' AttributeError: No attribute getObject found in IndirectObject or poi
Original PR description
Currently an error occurs when user tries to send a pdf of a invoice to a customer. Steps to replicate: (Make sure to have `python 3.14.4` and `pypdf=5.4.0`) - Install `l10n_sa_edi` with demo and…
Currently an error occurs when user tries to send a pdf of a invoice to a customer. Steps to replicate: (Make sure to have `python 3.14.4` and `pypdf=5.4.0`) - Install `l10n_sa_edi` with demo and switch to `My Saudi Arabia Company`. - Open invoices and create an invoice with customer and an invoice line. - Click `Send` > Again Click `Send`. Error: ``` AttributeError: 'PageObject' object has no attribute 'getObject' AttributeError: No attribute getObject found in IndirectObject or pointed object ``` - A recent [PR] introduced the old `getObject()` API in the PDF/A conversion code, even though it has been renamed to `get_object()` in the modern pypdf API. - As PyPDF2 1.x compatibility has already been removed, `getObject()` is no longer available and causes the error to log on the terminal. - Please refer to [1] and [2]. [PR]: https://github.com/odoo/odoo/pull/281275 [1]: https://github.com/odoo/odoo/pull/248197/files#diff-f3528e61bb9aa2d24d9b57cddafb7429d21216ba29f9dc7e08c92bb617949911L218 [2]: https://pypdf.readthedocs.io/en/stable/meta/changelog-v1.html#details sentry-7663263079 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7
Original PR description
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10…
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7 instead of 10. `mrp_subcontracting` overrides `_prepare_move_default_values` to force the move `location_dest_id` to the subcontractor location for every `is_subcontract` move: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/wizard/stock_picking_return.py#L20-L25 That is correct for the return, but the same override also runs for the exchange re-receipt, an `incoming` picking whose destination should be the stock location from `return_type.default_location_dest_id`: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/wizard/stock_picking_return.py#L137-L153 The exchange move then goes from the subcontracting location back to itself, so validating it nets zero and `WH/Stock` never receives the units. Skipping the override when `new_picking.picking_type_id.code` is `incoming` lets the exchange land in stock. The received quantity must also count that receipt. `_should_count_for_quantity_received` only counts `supplier` or `transit` sources: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/models/stock_move.py#L330-L331 so the exchange, sourced from the internal subcontracting location, is skipped while the return still subtracts its quantity. Counting subcontracting-sourced moves: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/models/stock_move.py#L312-L314 restores `qty_received` to 10. opw-6410978 Forward-Port-Of: odoo/odoo#282373 Forward-Port-Of: odoo/odoo#279431
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss - Click on the 'Create Lead' smart button - Check the created lead > The Customer/Contact field is empty ### Cause of Issue: When a lead is created from a Discuss conversation, `_convert_visitor_to_lead` in `crm_livechat` attempts to set the lead's customer. It does this by checking if the c
Original PR description
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss -…
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss - Click on the 'Create Lead' smart button - Check the created lead > The Customer/Contact field is empty ### Cause of Issue: When a lead is created from a Discuss conversation, `_convert_visitor_to_lead` in `crm_livechat` attempts to set the lead's customer. It does this by checking if the channel has `livechat_customer_partner_ids`. However, in whatsapp discuss conversations, the client is saved in `whatsapp_partner_id` and `livechat_customer_partner_ids` is empty. https://github.com/odoo/odoo/blob/29556fda44b9f1e6cf08129443ca47fa6cda34f9/addons/crm_livechat/models/discuss_channel.py#L54-L64 ### Fix: Kept the fix local to `crm_livechat` and checked whether `whatsapp` is installed before reading `whatsapp_partner_id`, which would otherwise raise an `AttributeError` since `crm_livechat` does not depend on `whatsapp` and vice versa. opw-6371274 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Repro steps: 1) Create 2 accounts in 2 different companies, both with no code 2) Merge the 2 accounts 3) On the merged account, attempt to a) add a code b) unmerge the accounts Problems: a) psycopg2.errors.InvalidParameterValue: cannot call jsonb_each on a non-object b) cannot delete from scalar Root cause: json.dumps(code_by_company) returns 'null' when code_by_company is None. This results in code_store being stored as JSON null instead of SQL null resulting in the errors men
Original PR description
Repro steps: 1) Create 2 accounts in 2 different companies, both with no code 2) Merge the 2 accounts 3) On the merged account, attempt to a) add a code b) unmerge the accounts Problems: a) psycopg2.errors.InvalidParameterValue: cannot call jsonb_each on a non-object b) cannot delete from scalar Root cause: json.dumps(code_by_company) returns 'null' when code_by_company is None. This results in code_store being stored as JSON null instead of SQL null resulting in the errors mentioned above because the field is expected to hold SQL NULL when empty instead of JSON null. task-6397515
### Issue before this commit: When generating an electronic invoice (e.g., ZUGFeRD/Factur-X) with a 0% tax from a non-EEA supplier (e.g., Switzerland) to an EEA customer (e.g., Germany), the XML tax <ram:CategoryCode> is incorrectly set to 'E' (Exempt) instead of 'G' (Export). ### Steps to reproduce the issue: 1. Download Accounting and l10n_ch 2. Set the VAT for the CH company 3. Create an invoice for a German customer with 0% tax setted (for which you have to set as electronic invoicing
Original PR description
### Issue before this commit: When generating an electronic invoice (e.g., ZUGFeRD/Factur-X) with a 0% tax from a non-EEA supplier (e.g., Switzerland) to an EEA customer (e.g., Germany), the XML tax…
### Issue before this commit: When generating an electronic invoice (e.g., ZUGFeRD/Factur-X) with a 0% tax from a non-EEA supplier (e.g., Switzerland) to an EEA customer (e.g., Germany), the XML tax <ram:CategoryCode> is incorrectly set to 'E' (Exempt) instead of 'G' (Export). ### Steps to reproduce the issue: 1. Download Accounting and l10n_ch 2. Set the VAT for the CH company 3. Create an invoice for a German customer with 0% tax setted (for which you have to set as electronic invoicing the ZUGFeRD template into the Accounting tab of his contact) 4. Send it and see that the tag <ram:CategoryCode> is setted as E instead of G ### Cause of the issue: The logic assigning the 'G' and 'K' tax category codes was only triggered if the supplier was located within the EEA. If the supplier was outside the EEA, the code bypassed this block entirely and fell back to the default 'E' code for 0% taxes. ### Reason to introduce the fix: Update the condition to trigger when either the supplier or the customer is in the EEA. This ensures that cross-border transactions involving at least one EEA party correctly evaluate and apply the 'G' (Export outside the EU) category code. Also the case supplier not in eea with VAT filled in + customer in eea + RC tax with amount != 0 is fixed now (letter G reported instead of S). ### Documentation: [eInvoicing technical guidance document_v1.pdf](https://github.com/user-attachments/files/30831749/eInvoicing.technical.guidance.document_v1.pdf) opw-6407399 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282497 Forward-Port-Of: odoo/odoo#281245
**Steps to reproduce:** 1. Install Sales and payment_authorize modules 2. Enable "Online Payment" in the settings and Configure the payment method to be Authorize.net 3. Create a sale order, confirm it and create the invoice 4. Pay the invoice with an eCheck (ACH) payment method through the Authorize.net provider 5. Wait for the payment to be settled by Authorize.net (_around 24 hours_) 6. Initiate a refund of the payment **Issue:** The refund fails with error `E00003: "The 'AnetApi/xm
Original PR description
**Steps to reproduce:** 1. Install Sales and payment_authorize modules 2. Enable "Online Payment" in the settings and Configure the payment method to be Authorize.net 3. Create a sale order, confirm…
**Steps to reproduce:** 1. Install Sales and payment_authorize modules 2. Enable "Online Payment" in the settings and Configure the payment method to be Authorize.net 3. Create a sale order, confirm it and create the invoice 4. Pay the invoice with an eCheck (ACH) payment method through the Authorize.net provider 5. Wait for the payment to be settled by Authorize.net (_around 24 hours_) 6. Initiate a refund of the payment **Issue:** The refund fails with error `E00003: "The 'AnetApi/xml/v1/schema/AnetApiSchema.xsd:cardNumber' element is invalid - The value XX is invalid according to its datatype 'String' - The actual length is less than the MinLength value` **Expected behavior:** The refund should be processed successfully regardless of whether the original payment was made by credit card or eCheck (ACH) **Why this happens:** - The `refund()` method in `AuthorizeAPI` builds the refund request using a `creditCard` payment payload - When the original transaction was an ACH/eCheck payment, the `creditCard` key is absent from the transaction details returned by Authorize.net - The resulting request is rejected by Authorize.net because it does not satisfy the minimum length constraint for `cardNumber` **Fix:** - Detects whether the original payment used `creditCard` or `bankAccount` from the transaction details and build the appropriate payload according to Authorize.net API documentation: https://developer.authorize.net/api/reference/index.html#payment-transactions-credit-a-bank-account opw-6359726 Forward-Port-Of: odoo/odoo#277742
Steps to reproduce: - Install employees and attendance app - Make sure there are 2 companies - Make user's employee record for Company B, but not A - Make company A the default company for user - Enable "attendances from backend" setting - Click on the attendance dot (systray) Current Behavior: The dot disappears and you can't check in Expected Behavior: You are able to check in Other bug scenario: If you have employee records in both Company A and Company B, you can check in.
Original PR description
Steps to reproduce: - Install employees and attendance app - Make sure there are 2 companies - Make user's employee record for Company B, but not A - Make company A the default company for user - Enable "attendances from backend" setting - Click on the attendance dot (systray) Current Behavior: The dot disappears and you can't check in Expected Behavior: You are able to check in Other bug scenario: If you have employee records in both Company A and Company B, you can check in. However, you can never check in for Company B as the default company is always selected in the server code opw-6392301 Forward-Port-Of: odoo/odoo#280566 Forward-Port-Of: odoo/odoo#278377
***Steps to reproduce*:** * Install the `l10n_din5008` module. * Set up a German Company and set the fiscal location to Germany. * Select the German company and create a new Purchase Journal. * Enable **Self Billing** for the journal. * Create a vendor bill and print the PDF. ***Observed behavior*:** * The printed document displays the regular vendor bill header instead of the self-billing header. ***Cause*:** * The condition required to display the self-billing header was mi
Original PR description
***Steps to reproduce*:** * Install the `l10n_din5008` module. * Set up a German Company and set the fiscal location to Germany. * Select the German company and create a new Purchase Journal. * Enable **Self Billing** for the journal. * Create a vendor bill and print the PDF. ***Observed behavior*:** * The printed document displays the regular vendor bill header instead of the self-billing header. ***Cause*:** * The condition required to display the self-billing header was missing from the report template if self-billing is enabled. ***Fix*:** * Add the missing condition so that the self-billing header is displayed when **Self Billing** is enabled on the journal. * For reference, here is the [Document](https://www.gesetze-im-internet.de/ustg_1980/__14.html) link. Ticket [link](https://www.odoo.com/odoo/project.task/6281066) opw-6281066 Forward-Port-Of: odoo/odoo#282664 Forward-Port-Of: odoo/odoo#272857
### 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 t
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
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: - The colon notation comes from Uruware's technical manual and is not how identity card numbers are written in Uruguay: users write `3.402.010-1` or plain digits (`34020101`). Real users facing the error keep reformatting a number whose check digit is simply wrong, believing the problem is the forma
Original PR description
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: -…
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: - The colon notation comes from Uruware's technical manual and is not how identity card numbers are written in Uruguay: users write `3.402.010-1` or plain digits (`34020101`). Real users facing the error keep reformatting a number whose check digit is simply wrong, believing the problem is the format. - The CI example shown is itself an **invalid** number: the check digit of `3.402.010` is `1`, not `2` — the module's own test suite (`l10n_uy/tests/test_check_vat.py`) uses `3:402.010-2` as an invalid sample. This was reported by an Uruguayan eCommerce user who lost sales because buyers interpreted the message as requiring an unusual format that does not exist in Uruguay. ## Change Replace the examples with valid numbers written in the notations actually used in Uruguay, and clarify that separators are optional — which is exactly what `_l10n_uy_ci_nie_is_valid()` already accepts (it strips `:. ,-` before checking the verification digit): ``` 3.402.010-1 or 34020101 (CI), 93.402.010-1 (NIE); dots and dash are optional ``` Only the `expected_format` string changes; the validation logic is untouched. Existing tests only assert the fixed part of the message (`The CI/NIE number.*does not seem to be valid`), so they are unaffected. Forward-Port-Of: odoo/odoo#282489
**Step to Reproduce:** 1. Create a database in 18.0 with `l10n_mt` installed. 2. Select the Malta chart template (COA). 3. Upgrade the database to 19.0. 4. Verify the tax grids. **Issue:** Tax grids remain unchanged after the upgrade and do not reflect the modifications introduced in [1]. The tax definitions are loaded from CSV data and don't happen during upgrade or module update it did through try_loading". Since the account tags already exist in upgraded databases, the changes
Original PR description
**Step to Reproduce:** 1. Create a database in 18.0 with `l10n_mt` installed. 2. Select the Malta chart template (COA). 3. Upgrade the database to 19.0. 4. Verify the tax grids. **Issue:** Tax grids…
**Step to Reproduce:** 1. Create a database in 18.0 with `l10n_mt` installed. 2. Select the Malta chart template (COA). 3. Upgrade the database to 19.0. 4. Verify the tax grids. **Issue:** Tax grids remain unchanged after the upgrade and do not reflect the modifications introduced in [1]. The tax definitions are loaded from CSV data and don't happen during upgrade or module update it did through try_loading". Since the account tags already exist in upgraded databases, the changes are not applied during module loading and the updated grid assignments are not assigned to taxes. **Fix:** Apply the grid update directly through SQL during the upgrade or module update. The change is limited to tax grid assignments and does not require a full tax reload using ``try_loading`` or ``load_data``. **Before fix:** <img width="1458" height="724" alt="image" src="https://github.com/user-attachments/assets/eaac5e3f-d551-4ec0-b282-bb39a50438f9" /> **After fix:** <img width="1240" height="583" alt="image" src="https://github.com/user-attachments/assets/74409c53-dce0-45b2-a6b5-e60f5c2d826d" /> Note: why this fix is needed because existing upgrade script do update move line grid but still tax have the old grid which is weird and will cause issue when journal entry will create. [1]: https://github.com/odoo/odoo/pull/254894/changes/8921186850e31c53072d49a5dc760192f3edb902 opw-6325845 upg-4391393 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272038
Problem: When using the many2many_avatar_user widget (such as in the Live Chat app), the badge displaying the number of hidden tags overflows if the count reaches double digits (e.g., "+10"). This causes the text to get cut off, making the exact number unreadable. Solution: This commit updates the badge container to properly accommodate larger numbers. The text now fits entirely within the badge without overflowing, ensuring the hidden tag count remains fully readable. Steps to reproduce (
Original PR description
Problem: When using the many2many_avatar_user widget (such as in the Live Chat app), the badge displaying the number of hidden tags overflows if the count reaches double digits (e.g., "+10"). This…
Problem: When using the many2many_avatar_user widget (such as in the Live Chat app), the badge displaying the number of hidden tags overflows if the count reaches double digits (e.g., "+10"). This causes the text to get cut off, making the exact number unreadable. Solution: This commit updates the badge container to properly accommodate larger numbers. The text now fits entirely within the badge without overflowing, ensuring the hidden tag count remains fully readable. Steps to reproduce (runbot v19.3): 1. Open the Live Chat app (or any view using the many2many_avatar_user widget). 2. Add enough agents to a session so the remaining count hits double digits (10 or more). 3. Observe that the badge showing the remaining agent count (e.g., "+10") overflows the badge container, cutting off the text and making it unreadable. opw-6453976 <img width="2655" height="1111" alt="avatar_tag_193_before" src="https://github.com/user-attachments/assets/9c31007d-5300-4bb7-a703-01a13b5bdd10" /> <img width="2655" height="1112" alt="avatar_tag_193_after" src="https://github.com/user-attachments/assets/7b2a4074-3738-4a6c-9649-f6652601f3f3" />
Previously, the live chat statistics section did not use the available width on mobile devices, leaving unnecessary empty space and resulting in an awkward layout. This PR makes the statistics cards take the full available width on mobile, providing a cleaner and more consistent interface. <table> <tr> <th>Before</th> <th>After</th> </tr> <tr> <td> <img width="372" height="805" alt="image" src="https://github.com/user-attachments/assets/6934927b-f744-4c93-b63e-1f49ecd33004" /> </td
Original PR description
Previously, the live chat statistics section did not use the available width on mobile devices, leaving unnecessary empty space and resulting in an awkward layout. This PR makes the statistics cards take the full available width on mobile, providing a cleaner and more consistent interface. <table> <tr> <th>Before</th> <th>After</th> </tr> <tr> <td> <img width="372" height="805" alt="image" src="https://github.com/user-attachments/assets/6934927b-f744-4c93-b63e-1f49ecd33004" /> </td> <td> <img width="382" height="734" alt="image" src="https://github.com/user-attachments/assets/24f8b7e4-9fe7-4fa0-bf64-5d7917623830" /> </td> </tr> </table> --- Task ID - 6372787
Fix the function signature of the _reset_peppol_configuration function Step to reproduce: - Install l10n_fr_pdp and register a company on Peppol - When the _peppol_out_of_sync_disconnect_this_database will be called, it will call the _reset_peppol_configuration(soft=True), and since l10n_fr_pdp overrides this function but don't declare the soft parameter, it will raise a TypeError. opw-5728596 Forward-Port-Of: odoo/odoo#282763
Original PR description
Fix the function signature of the _reset_peppol_configuration function Step to reproduce: - Install l10n_fr_pdp and register a company on Peppol - When the _peppol_out_of_sync_disconnect_this_database will be called, it will call the _reset_peppol_configuration(soft=True), and since l10n_fr_pdp overrides this function but don't declare the soft parameter, it will raise a TypeError. opw-5728596 Forward-Port-Of: odoo/odoo#282763
Behavior before: When uploading an animated GIF to fields utilizing image responsive sizing or cropping (such as employee avatars or product images), no downscaling or cropping occurs for sub-variants like 'image_128' or 'image_1024'. The responsive fields replicate the exact file size and data footprint of the original large image, leading to heavy storage overhead and unnecessary frontend asset loading. Behavior after: Animated GIF images scale down and crop correctly to match requested r
Original PR description
Behavior before: When uploading an animated GIF to fields utilizing image responsive sizing or cropping (such as employee avatars or product images), no downscaling or cropping occurs for…
Behavior before: When uploading an animated GIF to fields utilizing image responsive sizing or cropping (such as employee avatars or product images), no downscaling or cropping occurs for sub-variants like 'image_128' or 'image_1024'. The responsive fields replicate the exact file size and data footprint of the original large image, leading to heavy storage overhead and unnecessary frontend asset loading. Behavior after: Animated GIF images scale down and crop correctly to match requested responsive dimensions and aspect ratios. Sub-variants take up significantly less space in the filestore, matching proportional dimensions without dropping or stripping the underlying animation loop. Large images that are smaller than requested boxes are safely left un-upscaled to maximize database deduplication. Root Cause: Historically, a legacy safeguard bypassed GIF resizing and cropping because older versions of the Pillow library did not gracefully handle multi-frame sequential image buffers. As a result, standard 'image.crop()', 'image.thumbnail()', or 'image.resize()' implementations would flatten multi-frame animated sequences down into a single, static first frame or throw dimension/mode mismatches during save operations. Fix: Intercept the image processing pipeline when encountering an asset identified as a GIF where 'is_animated' evaluates to True. Implemented a unified, in-place multi-frame helper routine (`_apply_gif_operation`) using PIL's 'ImageSequence.Iterator' to cleanly step through, normalize to a uniform color mode (RGBA), duplicate, and modify each animation frame individually. This single helper handles sequential workflows for both 'crop' and 'thumbnail' operations while preserving individual frame duration arrays and native loop metadata. Both 'resize' and 'crop_resize' leverage this logic to achieve precise dimensions cleanly. Crucially, upscaling (expanding) is intentionally unsupported for animated GIFs. Forcing a low-resolution, 256-color indexed animation to stretch beyond its native dimensions forces heavy color dithering across every single frame. This breaks the sequential LZW pattern compression, causing the resulting file sizes to skyrocket catastrophically. The logic utilizes thumbnail boundaries to completely block this expansion, protecting the filestore from accidental bloat. Benchmark: -------------------------------------------------------------------------------------------- | GIF size | Variant | Size Before (KB) | Size After (KB) | |---------------|--------------------|--------------------------|-----------------------| | (2.5MB) | image_1024 | 2475.87 | 2475.87 | | | image_128 | 2475.87 | 257.93 | |---------------|--------------------|--------------------------|-----------------------| | (3.8MB) | image_1024 | 3724.93 | 3724.93 | | | image_128 | 3724.93 | 463.62 | |----------------|-------------------|--------------------------|-----------------------| | (442KB) | image_1024 | 432.49 | 432.49 | | | image_128 | 432.49 | 36.14 | |----------------|-------------------|--------------------------|-----------------------| | (3.6MB) | image_1024 | 3491.98 | 3491.98 | | | image_128 | 3491.98 | 1728.25 | |----------------|-------------------|--------------------------|-----------------------| opw-6232841 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#281883 Forward-Port-Of: odoo/odoo#273098