Friday, August 8, 2025
18 changes · saas-18.3
Enhancements to existing features
Event staff can now better manage attendance across multi-day events. Ticket scans and manual attendance updates are logged, and repeat scans on a different day show when the ticket was previously scanned.
Original PR description
**Purpose:** If there is a multi-day event, there's no way manage attendance for each day. **Specifications:** - When ticket is scanned show log in chatter. - In the Registration Desk, when scanning or manually selecting an attendee, if the attendee is already marked as `done` but today's date is different from the current `date_closed`, update the warning message to 'Ticket was last scanned on `<DATE>`' Task-4864066
Product attribute loading in the online shop has been optimized so search and filtering pages respond much faster, especially on stores with large catalogs. This improves customer browsing speed and reduces delays when many products are involved.
Original PR description
### Description: The default query to fetch attributes for searched products needs to join on the `product_tmpl_ids` Many2Many field. This can be slow, as it requires a join on an intermediate table. This change avoids that by directly querying the attribute lines and grouping by `attribute_id` to remove duplicates. ### Benchmark (in 18.0): | N° of products | Before | After | |----------------|--------|-------| | 3708 | 400ms | 200ms | | 128539 | 4 min | 800ms | | 484798 | 6 min | 3s | ### Reference: opw-4937361 Forward-Port-Of: odoo/odoo#222064
Belgian payroll departure notices now exclude PFI contracts when calculating an employee’s seniority. This helps ensure notice periods for leaving employees are calculated according to Belgian rules and avoids overstating seniority.
Original PR description
When you generate the notice period for leaving employees, you must compute their seniority. In Belgium, PFI contracts should not be taken into account. This commit filters the contracts to ignore PFI ones. task-4788455 Forward-Port-Of: odoo/enterprise#90902
Resolved issues and error corrections
Refund payments with withholding taxes now use the correct refund tax allocation, so tax reports reflect amounts in the right direction. The change also prevents payment editing problems when withholding tax lines use the company's default transfer account.
Original PR description
Currently, there is an issue with how the repartition line is selected when registering payment for a refund for withholding taxes. It would select the invoice repartition line, causing the tax amounts to affect the tax report in the wrong way. - It is not setting the is_refund param on the base line, leading to incorrect lines on the payment's entry. - Detecting if we are a refund is tricky with the current system, which is based on the wizard payment_type. It requires some changes in order to be able to have withholding lines with a type_tax_use which doesn't match the payment wizard. Also fixes an issue where a payment could not be edited after creation if the withholding tax line is using the company's default transfer account. In this case, the lines would be counted as counterparts and cause issues. As the account on the base line does not matter that much, we decided to use the simple solution of blocking its usage. Forward-Port-Of: odoo/odoo#220509
The Spanish VeriFactu electronic invoicing module can now be installed even if certain optional tax records have been removed. This prevents installation failures for affected Spanish accounting setups and helps businesses continue enabling compliance features without manual data repair.
Original PR description
In case some taxes for which we specify Applicability info (field `l10n_es_applicability`) do not exist the module can not be installed. I.e. the `_l10n_es_edi_verifactu_post_init_hook` raises. Reproduce 1. Install `l10n_es` without installing `l10n_es_edi_verifactu` 2. Delete tax with xmlid `account_tax_template_s_iva_e` (sales tax with description "VAT 0% export (services)") 3. Install `l10n_es_edi_verifactu` 4. A "Validation Error" appears ``` The operation cannot be completed: - Create/update: a mandatory field is not set. - Delete: another model requires the record being deleted. If possible, archive it instead. Model: Tax (account.tax) Field: Tax Name (name) ``` opw-5003231 opw-4996685 opw-4999922 Forward-Port-Of: odoo/odoo#222313 Forward-Port-Of: odoo/odoo#222225
Manufacturing orders created manually in a 3-step warehouse setup now correctly increase the stock forecast for the final warehouse location. This helps planners see expected inventory accurately and avoid unnecessary replenishment orders.
Original PR description
### Steps to reproduce: - In the settings enable Multi-Steps Routes - Put your warehouse in manufacture in 3 steps - Create a storable product P - Create and confirm an MO for 1 unit of P - Go to…
### Steps to reproduce: - In the settings enable Multi-Steps Routes - Put your warehouse in manufacture in 3 steps - Create a storable product P - Create and confirm an MO for 1 unit of P - Go to Inventory > Operations > Procurement > Replenishment - Create a new one for P in WH/stock #### > The forecasted quantity in stock is still 0 but should be at 1, just as if the the MO had been generated using the replenishment for 1 unit ### Cause of the issue: When an MO is created using a procurement, a `location_final_id` is set to WH/Stock on the MO and propagated on the move for the finished product: https://github.com/odoo/odoo/blob/8d4e6df0c0ac5ebc6362f3578ae8cc8edf4a76f7/addons/mrp/models/stock_rule.py#L165 https://github.com/odoo/odoo/blob/8d4e6df0c0ac5ebc6362f3578ae8cc8edf4a76f7/addons/mrp/models/mrp_production.py#L1169-L1175 As such, even if the manufacturing is now handled in push, this move will contribute positively to the forecast in the `virtual_available` quantity of the product in WH/Stock bevause of the `location_final_id`. By contrast, if hte MO is created by hand, it does not bear a `location_final_id` so that the MO can only contribute to the stock forecast has been processed and the related internal transfer `WH/post-prod -> WH/Stock` is created. opw-4882390 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#221462 Forward-Port-Of: odoo/odoo#221003
Fixed an issue where products tracked by lot could be counted twice when a sales order with multiple lots was paid through Point of Sale. The order is now split correctly by lot, keeping delivered quantities accurate and preventing inventory discrepancies.
Original PR description
When creating a sale order for a product, confirming it, and then modifying the stock move to use 2 different lots. Then paying the order in the POS, the picking created for the pos order will have double qty Steps to reproduce: ------------------- * Create a product tracked by lot * Create 2 lots for that product * Create a sale order for that 3 quantity of that product * Confirm the sale order * Modify the stock move to use 2 different lots, for example: - Lot 1001: 2 units - Lot 1002: 1 unit * Pay the order in the POS * Check the picking created for the pos order > Observation: The picking created for the pos order has double the quantity for the lots. Why the fix: ------------ A PoS line can only hold one lot, so when we import a sale order with multiple lots, we need to split the line into multiple lines, each with a single lot opw-4804704 Forward-Port-Of: odoo/odoo#221625 Forward-Port-Of: odoo/odoo#217537
Fixes several self-ordering issues that could block tables or devices when payments failed, direct sales were synced, or orders were cancelled. It also makes ticket printing more reliable by printing preparation and customer tickets one after another.
Original PR description
*: pos_restaurant, pos_online_payment_self_order, pos_self_order When self-ordering is enabled and in pay after each mode, the table selection was handled via table_id fields, which caused issues…
*: pos_restaurant, pos_online_payment_self_order, pos_self_order When self-ordering is enabled and in pay after each mode, the table selection was handled via table_id fields, which caused issues when a customer had an issue with its payment and wanted to pay later. It was blocking the table with its order, so if another customer wanted to scan the same QR code, it would not be able to add anything to the cart. Now in pay after each mode, the table selection is handled via floating_order_name, which allows the order to be processed without blocking the table. This way, the table can be reused for another customer, and the order can be paid later. --- Regarding direct sale in `point_of_sale`, when a customer wanted to pay by card the order was synced with the server when the waiter clicked on the payment method, if the direct sale had no table and no floating order name, when synching on other devices the order was blocking them. Now if no table and no floating order name is set, when synching the order, the pos_reference is set to the floating_order_name, which prevents the order from blocking other devices. --- Regarding self order cancellation from a PoS, when the waiter was cancelling an order it was not synced with the self-order app, which caused the self-order app to keep the order in the cart, preventing the customer from starting a new order. --- Regarding self order ticket printing, when an order arrives on the PoS to be printed, we were trying to print the preparation ticket and the customer ticket at the same time, which caused issues with the printing. Now we printing them one after the other.
This update corrects how Malaysian e-Invoicing data is filled for foreign customers and consolidated invoices. It helps ensure invoices meet Malaysian reporting requirements and reduces the risk of submission errors.
Original PR description
## Before this commit: The `CountrySubentityCode` was computed by simply using the `state code` of the partner, without handling the following cases: - Non-Malaysian Partners: For partners located outside Malaysia, the format requires the `state name` to be passed instead of the `state code`. - Consolidation Entries: For consolidation invoices, a fixed value `17` should be passed as the `CountrySubentityCode`, regardless of the partner’s state. ## After this commit: The computation of `CountrySubentityCode` has been updated to handle both cases correctly: - For non-Malaysian partners, the `state name` is used. - For consolidation entries, the fixed value `17` is used. - For regular Malaysian partners, the `state code` continues to be used as before. This ensures compliance with Malaysian e-Invoicing specifications and prevents errors. > Task-4938198 Forward-Port-Of: odoo/odoo#222153 Forward-Port-Of: odoo/odoo#219201
This fix stops users from creating website menu structures that go deeper than the supported two levels, which previously caused some menu items to disappear from the site. It also enforces clearer rules around mega menus so website navigation remains predictable and easier to manage.
Original PR description
Steps to Reproduce: 1. Go to the website. 2. Create a new menu (e.g., 'Test 1') using the 'Edit Menu' option and add it under another menu (e.g., 'Contact Us'), creating a sub-menu. 3. Turn on the…
Steps to Reproduce:
1. Go to the website.
2. Create a new menu (e.g., 'Test 1') using the 'Edit Menu' option and add it under another menu (e.g., 'Contact Us'), creating a sub-menu.
3. Turn on the developer mode.
4. Go to Configuration -> Menus and add two menus (e.g., 'menu 1' and 'menu 2') under the new sub-menu (e.g., 'Test 1').
5. Notice that the two menus ('menu 1' and 'menu 2') are not visible on the website.
Description:
As per functional specifications, creation of multi-level sub-menus should not be allowed.
Key Changes:
raises a `UserError` if a menu exceeds the two-level hierarchy by checking the parent and grandparent levels.
- Implements mega menu restrictions:
- A mega menu cannot have a parent menu.
- A mega menu cannot have child menus.
- Any menu cannot be a child of a mega menu.
- Prevents menus with child menus from being added as submenus to existing menus.
This ensures that the website menu structure adheres to the defined functional specifications, providing a consistent and predictable user experience.
task-3901371
Forward-Port-Of: odoo/odoo#222254
Forward-Port-Of: odoo/odoo#168801Changing an item quantity with the Point of Sale numpad is now saved immediately in the local browser database. This prevents checkout carts from reverting to the original quantity if the page is refreshed, reducing order mistakes and cashier frustration.
Original PR description
If you modified the quantity of a pos order line with the numpad, then refreshed the page the new quantity would be lost. Steps to reproduce: ------------------- * Open PoS * Add any product to the cart * Modify the quantity using the numpad * Direclty refresh the page > Observation: The quantity of the product is back to 1 Why the fix: ------------ When modifying a pos order line it was not synched with the indexed local database. We now make sure that when a pos order line is modified it is synched correctly. opw-4801613
Archiving or restoring a company now refreshes the company information shown to users correctly. This prevents outdated company lists and avoids errors in related apps such as Timesheets and Italian point of sale when a company is restored.
Original PR description
Currently below error occurs while archive/unarchive company: - Company list is not being updated. - In `hr_timesheet` KeyError will be encountered on unarchiving a company. - In `l10n_it_pos` module…
Currently below error occurs while archive/unarchive company: - Company list is not being updated. - In `hr_timesheet` KeyError will be encountered on unarchiving a company. - In `l10n_it_pos` module an AccessError will be encountered on unarchiving a company. **Root Cause:** Since https://github.com/odoo/odoo/commit/22e9d822e8cab08114c006eb8c4054c4de0c40f2, the `session_info` method relies on `user_companies` at [1], which fetches only active company IDs via `_get_company_ids()`. However, `_get_company_ids` uses caching as shown at [2]. When a company is archived or unarchived, 'write' method is called. Now, here the cache is invalidated before writing to the company as shown at [3]. Due to which '_get_company_ids' method doesn't receive the updated list of companies, causing this issue. **Solution:** This commit prevents error by ensuring companies are stored before invalidating the cache. [1]- https://github.com/odoo/odoo/blob/d3482e32f7dbe273125de84f1bbc434ac3c9ffc3/addons/web/models/ir_http.py#L146 [2]- https://github.com/odoo/odoo/blob/7db62d6d65bd57f6ed43ce52a820cf03df19efdd/odoo/addons/base/models/res_users.py#L927-L931 [3]- https://github.com/odoo/odoo/blob/3173a5f993a3ffa3d3a14d29713142e5b6b314d9/odoo/addons/base/models/res_company.py#L343-L367 Sentry-6251348500 Forward-Port-Of: odoo/odoo#219044
Winbooks data imports now continue even when the source chart of accounts differs from Odoo's default setup. This prevents import failures for draft accounting data, allowing users to review and adjust records inside Odoo afterward.
Original PR description
When importing Winbooks data, if the CoA configuration in Winbooks is different from Odoo's default, users will experience a failure with a message similar to ``` Validation Error Tax Receivable Account (411000 VAT Recoverable) should be an account of type "Receivable" or "Payable" with both options "Allow Reconciliation" and "Non Trade" enabled. ``` This occurs because the account 411000, meant to store the tax provisions, in Winbooks was used as tax receivable. In similar cases we should not raise any error and should let the import go through: the data is imported in draft and the user has the possibility to bring the modifications in Odoo opw-4850314 Forward-Port-Of: odoo/enterprise#91828
The Indian GSTR document summary now excludes cancelled invoices that were never officially posted, preventing inaccurate tax reporting totals. It also improves serial continuity checks by return period and keeps the document summary view consistently visible in the GSTR-1 section.
Original PR description
Before: The document summary included all cancelled invoices, even those that were never posted. After: Only invoices that were posted and subsequently cancelled are now considered in the summary. Additional Changes: - Updated check_serials to validate serial continuity within current company. - Made the document summary view always visible in the GSTR-1 section. opw-4940053 Forward-Port-Of: odoo/enterprise#91971 Forward-Port-Of: odoo/enterprise#90160
Scheduled bank statement reconciliation now uses the correct company context, preventing incorrect currency rates from being applied. The related code was also cleaned up to avoid duplicated logic, making future maintenance safer.
Original PR description
In this commit: https://github.com/odoo/enterprise/pull/89244/commits/788d79cea3c2d45582e16b25847c7821a6521da1 we fixed a bug where the cron would be trigger with the wrong company which impacted the currency rate. We decided to put the change in the try_auto_reconcile, but it was a bit of code duplication so this commit will refactor that. Task-4933083
Odoo can now handle large files sent through WhatsApp without running into memory errors. This helps ensure customer messages with bigger attachments are received reliably instead of failing silently or causing errors.
Original PR description
Currently a below occurs or content is not receiving to odoo WhatsApp when the user uploads a large file (tried with > 35 MB). Stack Trace: ``` MemoryError: null File "odoo/http.py", line 2383, in…
Currently a below occurs or content is not receiving to odoo WhatsApp when the user uploads a large file (tried with > 35 MB).
Stack Trace:
```
MemoryError: null
File "odoo/http.py", line 2383, in __call__
response = request._serve_db()
File "odoo/http.py", line 1913, in _serve_db
return self._transactioning(
File "odoo/http.py", line 1976, in _transactioning
return service_model.retrying(func, env=self.env)
File "odoo/service/model.py", line 134, in retrying
result = func()
File "odoo/http.py", line 1943, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "odoo/http.py", line 2187, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "odoo/addons/base/models/ir_http.py", line 227, in _dispatch
result = endpoint(**request.params)
File "odoo/http.py", line 757, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "home/odoo/src/enterprise/saas-17.4/whatsapp/controller/main.py", line 42, in webhookpost
wa_account_id._process_messages(value)
File "home/odoo/src/enterprise/saas-17.4/whatsapp/models/whatsapp_account.py", line 206, in _process_messages
datas = wa_api._get_whatsapp_document(messages[message_type]['id'])
File "home/odoo/src/enterprise/saas-17.4/whatsapp/tools/whatsapp_api.py", line 236, in _get_whatsapp_document
file_response = self.__api_requests("GET", file_url, auth_type="bearer", endpoint_include=True)
File "home/odoo/src/enterprise/saas-17.4/whatsapp/tools/whatsapp_api.py", line 46, in __api_requests
if 'error' in res.json():
File "requests/models.py", line 971, in json
return complexjson.loads(self.text, **kwargs)
File "requests/models.py", line 928, in text
encoding = self.apparent_encoding
File "requests/models.py", line 793, in apparent_encoding
return chardet.detect(self.content)["encoding"]
File "__init__.py", line 49, in detect
detector.feed(byte_str)
File "chardet/universaldetector.py", line 274, in feed
if prober.feed(byte_str) == ProbingState.FOUND_IT:
File "chardet/charsetgroupprober.py", line 70, in feed
state = prober.feed(byte_str)
File "chardet/hebrewprober.py", line 240, in feed
byte_str = self.filter_high_byte_only(byte_str)
File "chardet/charsetprober.py", line 73, in filter_high_byte_only
buf = re.sub(b"([\x00-\x7F])+", b" ", buf)
File "__init__.py", line 186, in sub
return _compile(pattern, flags).sub(repl, string, count)
```
At line [1], the code `'error' in res.json()` is used, which reads all the json content of `res`, but at times users upload large files, it will cause the above error because the `json()` tries to read all bytes from res, which is a very large amount to handle in memory.
This commit will fix the above issue by returning a response if the response
contains content_length more than 10 MB.
[1] - https://github.com/odoo/enterprise/blob/aca7ae2a7cf4aad5427a60d5cad60d08774357d0/whatsapp/tools/whatsapp_api.py#L46
sentry-5810101850
Forward-Port-Of: odoo/enterprise#70001Luxembourg VAT report XML exports now include certain special-scheme fields only when the reporting period includes dates in 2025 or later. The annual VAT report also fills required fields when related mandatory fields are present, helping businesses submit legally compliant tax files.
Original PR description
As per legal requirements, the 491, 492 and 493 fields of the xml export of the tax return only need to be included if at least one day of the period includes dates in 2025 or later. As per legal requirements too, the 192 and 193 fields are mandatory in the annual report if some other fields are present. Among those are 361 and 362 which are mandatory even if null so if 192 and 193 are not there yet, we set them to 362 and 363 values (0.0 in most cases). --- from feedback on task-4587067 opw-4757770 Forward-Port-Of: odoo/enterprise#91859 Forward-Port-Of: odoo/enterprise#89437
Fixed an issue where invoices or bills linked to payments in a validated batch could remain marked as in payment after matching the batch with a bank statement line. This ensures payment statuses update correctly when bank journals do not use outstanding payment or receipt accounts.
Original PR description
Setting a batch payment having payments without journal entry with a statement line does not reconcile the invoices/bills linked to the payments. Steps: - Make sure to not have outstanding receipts/payments accounts set on bank journal - Create two invoices, confirm and register payment for both - Create a batch payment with tho payments, validate - Create a statement line with amount = sum of the 2 invoices - Add the batch to the statement line - Go back to invoice tree view -> Both invoices are still 'in_payment' instead of 'paid' opw-4980004