Daily updates from Odoo
Thursday, November 13, 2025
19 changes
3 changes
Enhancements to existing features
If a payment gets stuck while waiting for a terminal response, the cashiers now see a "force done" option. This prevents the point of sale from being blocked and lets staff continue serving customers without waiting on the device.
Original PR description
If a payment line is stuck in a waiting state (e.g. payment terminal not responding to a request), we now display the "force done" button, to avoid blocking the user. Forward-Port-Of: odoo/odoo#235244
The work order completion process now avoids repeating the same checks and updates for every item, which reduces unnecessary database work. This makes finishing large batches of manufacturing work orders much faster and more reliable.
Original PR description
- The `button_finish` method contained a variable intended to filter out moves whose `operation_id` matched the `operation_id` of the entire recordset of work orders passed to the function. However,…
- The `button_finish` method contained a variable intended to filter out moves whose `operation_id` matched the `operation_id` of the entire recordset of work orders passed to the function. However, this filtering was performed inside a loop iterating over all work orders, even though the result of the filtration did not depend on any single work order. Before this commit: - The filtration was executed repeatedly for each work order, despite being deterministic. - This unnecessary repetition caused performance degradation and multiple redundant updates to the `picked` field of the same moves, resulting in fake or redundant database writes. After this commit: - The filtration logic has been moved outside the iteration, ensuring that the update to the moves is performed only once, improving overall performance and preventing redundant updates. - The `end_all` method is now executed on the entire recordset of work orders at once, instead of being called individually for each iteration. The benchmark below is done on a recordset of workorders of size **500** and the number of moves returned from the filter were **100**. It set the picked field to be **True** for every workorder in the recordset, potentially triggering recomputation of some of the fields and doing more redundant SQL queries. opw-5092636 ### Benchmark Results | Scenario | Execution Time | | :--- | :--- | | **Before this Commit** | **Memory Error** | **After this Commit** | **22 seconds** --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#233102
This update clears the payment/blackbox processing queue whenever point of sale data is reloaded. It helps avoid rare situations where repeated queued calls could loop unexpectedly and impact the checkout flow.
Original PR description
This commit adds a clear of the blackbox queue when reloading data. This could avoid potential unexpected deadloop of calls in the queue. Forward-Port-Of: odoo/enterprise#99252
3 changes
Enhancements to existing features
When a card or terminal payment gets stuck waiting for a response, the payment screen now shows a "force done" option. This helps cashiers continue checkout instead of being blocked by an unresponsive payment terminal.
Original PR description
If a payment line is stuck in a waiting state (e.g. payment terminal not responding to a request), we now display the "force done" button, to avoid blocking the user. Forward-Port-Of: odoo/odoo#235244
The work order finishing process has been streamlined so repeated checks and updates are no longer done over and over for each item. This reduces unnecessary database activity and makes large manufacturing batches complete much faster and more reliably.
Original PR description
- The `button_finish` method contained a variable intended to filter out moves whose `operation_id` matched the `operation_id` of the entire recordset of work orders passed to the function. However,…
- The `button_finish` method contained a variable intended to filter out moves whose `operation_id` matched the `operation_id` of the entire recordset of work orders passed to the function. However, this filtering was performed inside a loop iterating over all work orders, even though the result of the filtration did not depend on any single work order. Before this commit: - The filtration was executed repeatedly for each work order, despite being deterministic. - This unnecessary repetition caused performance degradation and multiple redundant updates to the `picked` field of the same moves, resulting in fake or redundant database writes. After this commit: - The filtration logic has been moved outside the iteration, ensuring that the update to the moves is performed only once, improving overall performance and preventing redundant updates. - The `end_all` method is now executed on the entire recordset of work orders at once, instead of being called individually for each iteration. The benchmark below is done on a recordset of workorders of size **500** and the number of moves returned from the filter were **100**. It set the picked field to be **True** for every workorder in the recordset, potentially triggering recomputation of some of the fields and doing more redundant SQL queries. opw-5092636 ### Benchmark Results | Scenario | Execution Time | | :--- | :--- | | **Before this Commit** | **Memory Error** | **After this Commit** | **22 seconds** --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#233102
Stock availability information is now calculated much faster for pickings with many related incoming and outgoing moves. This reduces waiting time when users open or review large warehouse operations, while keeping the result the same.
Original PR description
Before this commit, computing `product_availability` and `product_availability_state` required calling the `get_report_lines` method from the `stock.forecasted_product_product` model. In pickings…
Before this commit, computing `product_availability` and `product_availability_state` required calling the `get_report_lines` method from the `stock.forecasted_product_product` model. In pickings containing moves linked to many incoming and outgoing moves, the `reconcile_out_with_ins` function caused performance issues. The reconciliation logic worked as follows: 1. For each `out_move`, attempt to match it with an `in_move` if the `in_move` references the `out_move` in its `move_dests`. 2. If the demand of the `out_move` is not fully satisfied, add it to `unreconciled_outs`. 3. Loop over `unreconciled_outs` (after attempting to reconcile them using the initial prodcedure) to reconcile against the remaining `in_moves`. The performance bottleneck was that even when an `in_move` directly referenced an `out_move`, the code would unnecessarily loop over **all** `in_moves` to filter out the `in_moves` that has the `out_move` in its `move_dest`. --- To improve performance, an **inverse mapping** from `out_move` IDs to their corresponding `in_moves` is introduced. - Reconciliation now starts by iterating only over the relevant `in_moves`. - If the demand is still unmet, the algorithm attempts reconciliation against the remaining `in_moves`. - This reduces the time complexity to **O(N + M)**, since `in_moves` with zero quantities are removed and never revisited. **Implementation details:** - An `OrderedSet` is used for the inverse mapping to preserve the original query order. - Benefits of `OrderedSet`: - **O(1)** removal (assuming no collisions) - Maintains insertion order, ensuring the same order as the query result. --- | Metric | Before PR | After PR | |---------------|-----------|----------| | Execution Time| ~90 sec | ~10 sec | The benchmark above is done on a `stock.picking` record that queried in the `_get_report_lines` method **5331** `out_moves` and **8922** `in_moves`. opw-4951469 Forward-Port-Of: odoo/odoo#224002
8 changes
Enhancements to existing features
Belgian point-of-sale blackbox messages are now queued so the register can continue more quickly when an immediate response is not required. This improves reliability and reduces delays for workflows such as pro forma sales while preserving communication with the fiscal device.
Original PR description
In this commit, we introduce a queuing mechanism for blackbox messages to make all interactions with the blackbox faster and more reliable. This is particularly useful for messages that do not require the response from the blackbox to continue the workflow such as pro forma sales messages. Forward-Port-Of: odoo/enterprise#98040 Forward-Port-Of: odoo/enterprise#90747
Tax return generation is now more efficient by grouping repeated checks and updates, reducing delays when saving fiscal year settings or refreshing returns. The change also prevents manually created draft returns from being removed incorrectly, helping preserve user-entered accounting work.
Original PR description
- batched _compute_company_ids on returns - batched return to unlink in _generate_or_refresh_all_returns - removed redondant call to _generate_or_refresh_all_returns in…
- batched _compute_company_ids on returns - batched return to unlink in _generate_or_refresh_all_returns - removed redondant call to _generate_or_refresh_all_returns in action_save_onboarding_fiscal_year since we want to call it only when a value is changed and that's already handled in the write on the company. - batched _is_available_for on reports - make only one write on the company (and one call to _generate_or_refresh_all_returns) when writing or create the fiscal year wizard. - dont delete return created manually with a date before the date of the account_opening_date and not yet posted. Detected from runbot error: https://runbot.odoo.com/odoo/runbot.build.error/231463 A small workaround is needed on the company when writing the changes from the fiscal year wizard. This is because related fields are writen one by one, which then trigger multiple times _generate_or_refresh_all_returns. To prevent this, we are batching all the write on the company from the fiscal year wizard in one write. The opening_date need to be handled separatly since it's not a related field but still need to be writen on the company, if removed from the vals during the create it's then not possible to save the wizard anymore. Also fixed a bug where the returns manually created before the account_opening_date would be deleted if they were not posted before the next call to _try_create_returns_for_fiscal_year. **Detailed Explanation** We can see the tour being broken when trying to save on the wizard: <img width="1366" height="768" alt="image" src="https://github.com/user-attachments/assets/9443251c-aee1-4fbe-be2f-27c11bcbb77d" /> So we can do a flamegraph to take a look at what's happening on that database during that time and we can see 2 majors time uses, a call to `_generate_or_refresh_all_returns` triggered by a write on the wizard and a call to `action_save_onboarding_fiscal_year` <img width="1396" height="855" alt="image" src="https://github.com/user-attachments/assets/a9d310da-2787-4806-951d-e50b8c1da2a8" /> The first call to `_generate_or_refresh_all_returns` took about 7.8s, the second one took about 1.1s First, we can see a lot of `_compute_company_ids` calls, this is because we have `precompute=True` on this field due to it being needed in the different access rules. Since all the returns that are created have similar main company, tax unit and return type, we can easily batch them to only call `_get_company_ids` once for all the returns that will be created. Resulting in the following flamegraph: <img width="1422" height="866" alt="image" src="https://github.com/user-attachments/assets/c52c88c7-0263-44fb-a16c-52456673dc92" /> The first call to `_generate_or_refresh_all_returns` took about 0.8s, the second one took about 1.2s Another little change we can do is to remove the call to `_generate_or_refresh_all_returns` during `action_save_onboarding_fiscal_year` and adding the forced_date from the first one to the second one. <img width="1294" height="844" alt="image" src="https://github.com/user-attachments/assets/7ad55d06-ecd4-4957-b514-71fffa6de00c" /> We now end up with one call to `_generate_or_refresh_all_returns` that takes 1.4s BUT, we can still do better. We can see that all we did previously was only do to one thing less. The "real" problem was the many search calls done in `_is_available_for` during the `_init_options_variants`. <img width="1268" height="693" alt="image" src="https://github.com/user-attachments/assets/ebae398d-dfdd-4c1e-99ac-44f25e5875e7" /> After the final change, we are down to 0.9s ! One thing to keep in mind, all those flamegraph were done on a dump of a database from a nightly test, which had many companies created as well as **every** modules installed, which means every return types, reports, ... This means those performance improvements wont be as useful on small databases. Forward-Port-Of: odoo/enterprise#95760
A new setting lets users choose to disable barcode lookup instead of being forced to keep it active when an API key is automatically provided. This gives SaaS customers clearer control over the feature and can guide them through uninstalling the module when they no longer want to use it.
Original PR description
### Issue: Currently, the `product_barcodelookup` feature is disabled only when no API key is configured: https://github.com/odoo/enterprise/blob/86937d9c974125d60f6e2e9dc6bf0b048e20e489/product_barcodelookup/models/product_template.py#L199-L203 https://github.com/odoo/enterprise/blob/86937d9c974125d60f6e2e9dc6bf0b048e20e489/product_barcodelookup/tools/barcode_lookup_service.py#L7-L11 FHowever, for SaaS users, the API key is hardcoded, which makes it impossible to disable the feature without uninstalling the module. ### Improvement: The purpose of this commit is to add a boolean setting that allows users to explicitly disable the barcode lookup feature by proposing to uninstall the module for them. Community: https://github.com/odoo/odoo/pull/232794 opw-5096468
The report editor now automatically places the cursor in the first editable area when a report opens. This makes it clearer where users can begin editing, especially for new or blank reports, and shows the relevant placeholder guidance immediately.
Original PR description
Before this commit it was a bit hard to see where to click to start editing a report especially a blank or new one. After this commit, we focus the first hintable node that we can discover. This will: 1. Put the selection and cursor in that element 2. Trigger the hint plugin that will display the relevant placeholder task-4936527
The spreadsheet interface now supports dark mode using shared styling variables, giving users a more comfortable viewing option in low-light environments. This also simplifies future visual maintenance by avoiding separate dark-mode stylesheets.
Original PR description
This commits implements dark mode for the user interface in o-spreadsheet. We can drop the drak mode-specific stylesheets and use CSS variables using `light-dark()` instead. Task: 5082659
Record creation controls in kanban and list views now use proper buttons, so they are automatically disabled while another action such as saving is in progress. This prevents users from accidentally triggering conflicting actions on slow connections, reducing crashes and improving reliability across affected workflows.
Original PR description
Before this commit, the `Add a record` in x2many kanban was a `<div>`, and the `Add a line` (and other "creates") in lists were `<a>`. Using buttons is semantically more correct. Morevoer, buttons are disabled when an action is ongoing in the webclient. For instance, when the form view is being saved, buttons are disabled. This allows to avoid concurrent and unwanted behaviors. Before this commit, such a behavior could happen with x2manys: in a form view with some changes (and on a slow network), click on the save icon, and directly click to add a record in the x2many. That "add" request was done on the current version of the static list, but that static list was replaced by a new one when the record is reloaded (post save), so a crash occured. By using `<button>`, those actions are automatically disabled when saving, thus removing the race condition.
User-facing wording has been updated from “Shipping Methods” or sales-level “Carrier” labels to “Delivery Methods” across supported delivery and marketplace integrations. This makes sales and delivery settings more consistent and easier to understand, while keeping carrier wording where it still applies to warehouse or provider-specific operations.
Original PR description
delivery_* = bpost, dhl, easypost, envia, fedex, sendcloud, shiprocket, starshipit, ups, usps, ups_rest, delivery_easypost, delivery_shiprocket, l10n_br_edi_website_sale, sale_amazon, sale_shopee…
delivery_* = bpost, dhl, easypost, envia, fedex, sendcloud, shiprocket,
starshipit, ups, usps, ups_rest, delivery_easypost, delivery_shiprocket,
l10n_br_edi_website_sale, sale_amazon, sale_shopee
With this PR:
---
1. Renamed 'Shipping Methods' to 'Delivery Methods'
* Renamed all user-facing labels of "Shipping Method(s)" to "Delivery Method(s)"
for consistent terminology across apps and improved user clarity, without
altering terminology used by specific delivery providers or third-party
integrations.
2. Renamed 'Delivery Carrier'/ 'Carrier' to 'Delivery Methods'
* A `Carrier` label is used at the stock level, while `Delivery Method` is used at
the sales level.
* A Delivery Method represents the complete carrier process along with the
required parameters defined at the sales level, whereas a Delivery Carrier
(or Carrier) is used at the transfer/picking level, where the focus is on
which carrier actually ships the goods rather than the full delivery
configuration.
* Therefore, all relevant occurrences of Delivery Carrier have been updated to
Delivery Method.
* The remaining occurrences of Carrier/Delivery Carrier refer either to
stock-level usage, delivery provider's internal labels, or
localization-specific terminology.
Impact:
-------
- This avoids confusion between "Shipping Method" and "Delivery Method", as
"Delivery Method" better reflects how an order is handed over to the customer,
making the terminology clearer and more contextual.
- Clarifies when "Delivery Method" vs. "Carrier" should be used by keeping "Carrier"
terminology where it correctly reflects stock-level operations, while ensuring sales-level
terminology remains consistent.
task-4720174Currency rates will now use only values from before the requested date, reflecting that rates apply to the following day and should remain stable during the day. This improves consistency in accounting, reporting, payments, and electronic invoicing where exchange rates affect financial amounts.
Original PR description
Currency rates are valid for the next day and should not change during the day. Use only rates strictly earlier than the requested date task-5173684
4 changes
Enhancements to existing features
The website generator now verifies a website URL before sending a request to create a site. This helps block invalid or blocked addresses early, reducing unnecessary processing and avoiding failed scraper attempts.
Original PR description
This PR adds the client side verification of an url for the request we make to generate a website using the website scraper. **The goal is to filter all the unwanted requests (invalid urls, banned urls) before launching the scraper process.** The check is done on the IAP server, and retrieved on the DB. The reason is that we don't want to send a request directly from the db [as this was already discussed](https://github.com/odoo/enterprise/pull/92724). Since the IAP server is also the one that will eventually do the scraping request, it also makes more sense that it is the one to check (to avoid the case where odooDB has access to an URL and IAP server does not).
We removed the warning that compared HSN codes with product type, and now the product type is inferred directly from the HSN. This reduces unnecessary alerts during invoicing while still stopping invoices when the HSN is missing or invalid, helping users complete entries more reliably.
Original PR description
With this **PR**, we remove the warning about mismatches between HSN code and product type. From now on, the product type will be derived directly from the HSN code: if the HSN starts with '99', it will be considered a 'service'; otherwise, it will be treated as 'goods'. This simplifies the user experience by reducing unnecessary warnings. This will also cover cases where the product is service but the HSN is of goods for e.g. Job Work, Discount Line etc. Additionally, invalid/missing HSN on invoice line will now raise a blocking warning. **task**-5061450 Community PR - https://github.com/odoo/odoo/pull/227879
This update makes Indian invoicing easier by removing confusing HSN mismatch warnings and automatically determining whether an item is goods or a service from its HSN code. It also enforces required HSN entries and automatically shortens overly long product descriptions for e-invoicing and e-waybill submissions, helping avoid file rejection while alerting users when changes are made.
Original PR description
**[IMP] l10n_in_*: streamline HSN warnings** - With this PR, we remove the warning about mismatches between HSN code and product type. From now on, the product type will be derived directly from the…
**[IMP] l10n_in_*: streamline HSN warnings** - With this PR, we remove the warning about mismatches between HSN code and product type. From now on, the product type will be derived directly from the HSN code: if the HSN starts with '99', it will be considered a 'service'; otherwise, it will be treated as 'goods'. This simplifies the user experience by reducing unnecessary warnings. This will also cover cases where the product is service but the HSN is of goods for e.g. Job Work, Discount Line etc. - Additionally, invalid/missing HSN on invoice line will now raise a blocking warning. **[IMP] l10n_in_*: truncate product descriptions for e-invoicing** - Product descriptions in invoice lines must comply with character limits for e-invoicing and e-waybill: - E-invoicing: maximum 300 characters - E-waybill: maximum 100 characters for both product names and descriptions - Descriptions exceeding these limits are automatically truncated in the generated JSON. A user alert is displayed during sending to notify about any truncation. **task**-5061450 Enterprise PR - https://github.com/odoo/enterprise/pull/95108
The Point of Sale screen now shows a "force done" option when a payment line gets stuck waiting for a terminal response. This helps cashiers keep serving customers instead of being blocked by an unresponsive payment device.
Original PR description
If a payment line is stuck in a waiting state (e.g. payment terminal not responding to a request), we now display the "force done" button, to avoid blocking the user. Forward-Port-Of: odoo/odoo#235244
1 change
Enhancements to existing features
Stock availability checks are now much faster when a picking contains many linked incoming and outgoing moves. The update reduces unnecessary looping, which lowers waiting time for users and improves responsiveness on large operations.
Original PR description
Before this commit, computing `product_availability` and `product_availability_state` required calling the `get_report_lines` method from the `stock.forecasted_product_product` model. In pickings…
Before this commit, computing `product_availability` and `product_availability_state` required calling the `get_report_lines` method from the `stock.forecasted_product_product` model. In pickings containing moves linked to many incoming and outgoing moves, the `reconcile_out_with_ins` function caused performance issues. The reconciliation logic worked as follows: 1. For each `out_move`, attempt to match it with an `in_move` if the `in_move` references the `out_move` in its `move_dests`. 2. If the demand of the `out_move` is not fully satisfied, add it to `unreconciled_outs`. 3. Loop over `unreconciled_outs` (after attempting to reconcile them using the initial prodcedure) to reconcile against the remaining `in_moves`. The performance bottleneck was that even when an `in_move` directly referenced an `out_move`, the code would unnecessarily loop over **all** `in_moves` to filter out the `in_moves` that has the `out_move` in its `move_dest`. --- To improve performance, an **inverse mapping** from `out_move` IDs to their corresponding `in_moves` is introduced. - Reconciliation now starts by iterating only over the relevant `in_moves`. - If the demand is still unmet, the algorithm attempts reconciliation against the remaining `in_moves`. - This reduces the time complexity to **O(N + M)**, since `in_moves` with zero quantities are removed and never revisited. **Implementation details:** - An `OrderedSet` is used for the inverse mapping to preserve the original query order. - Benefits of `OrderedSet`: - **O(1)** removal (assuming no collisions) - Maintains insertion order, ensuring the same order as the query result. --- | Metric | Before PR | After PR | |---------------|-----------|----------| | Execution Time| ~90 sec | ~10 sec | The benchmark above is done on a `stock.picking` record that queried in the `_get_report_lines` method **5331** `out_moves` and **8922** `in_moves`. opw-4951469 Forward-Port-Of: odoo/odoo#224002