Wednesday, July 30, 2025
20 changes · saas-18.3
Enhancements to existing features
Users can now resend modified vendor bills to the Bizkaia Batuz reporting system without cancelling and recreating the accounting entry. This reduces administrative effort for customers and auditors when correcting supplier bill information that has not been cancelled with the vendor.
Original PR description
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#205946
A new warning banner alerts users when key totals in the French tax report do not match expected corresponding totals. This helps businesses spot reporting inconsistencies earlier and reduce the risk of submitting incorrect tax information.
Original PR description
This commit will add a warning banner when the sum of field 08+09+9B+10+11+T1->T7 is not equal to sum of field A1+A2+A3+B2+B3+B4 Task-4933787 Forward-Port-Of: odoo/enterprise#91157 Forward-Port-Of: odoo/enterprise#89956
Resolved issues and error corrections
This update brings the spreadsheet component to the latest version and fixes several user-facing issues. Users should see safer handling of cancelled formatting changes, better pivot calculations for date/time fields, and more accurate data validation suggestions.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/2a7178a06 [REL] 18.3.14 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/2a7178a06 [REL] 18.3.14 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/34fdd1eb3 [FIX] cf: do not override changes on cancel [Task: 4609695](https://www.odoo.com/odoo/2328/tasks/4609695) https://github.com/odoo/o-spreadsheet/commit/4a1547b56 [FIX] pivot: support SUM and AVG aggregators for datetime fields [Task: 4945217](https://www.odoo.com/odoo/2328/tasks/4945217) https://github.com/odoo/o-spreadsheet/commit/f23bfa0ca [FIX] data validation: handle cell format in auto-complete [Task: 4865661](https://www.odoo.com/odoo/2328/tasks/4865661) Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya <rmbh@odoo.com> Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
This fix prevents customers from completing checkout with products that have been archived or unpublished after being added to their cart. When a product is archived, it is removed from existing website carts so orders cannot be placed for unavailable items.
Original PR description
Fix issue where customers could complete purchases of products that where archived or unpublished after being added to cart but before checkout completion. => Step to reproduce bug : - Install website_sale. - Add a product to the cart. - (In an other window) archive the product. - finish the buy from the cart. (don't reload the cart or the product will be gone) => Cause: The bug originate from: https://github.com/odoo/odoo/blob/2d7bb960b00bfeae3e6ab8c0367237f3b08271cb/addons/website_sale/controllers/main.py#L1781-L1793 There is no check up during the last part of the transaction to verify if the product is still available. => Fix: Erase from the product from all cart when it's archived opw-4829872 Forward-Port-Of: odoo/odoo#220162 Forward-Port-Of: odoo/odoo#213656
This fix prevents Odoo Inventory from creating duplicate stock quantity records when validating transfers that move entire packages. It helps keep package and product quantities accurate, reducing the risk of incorrect inventory availability or fulfillment issues.
Original PR description
### Steps to reproduce: - In the settings enable "packages" and Multi-Step Routes - Create a 3 storable products: P1, P2, P3 - Put on hand quantities for each of them: - 1 x P1 in PACK001 in stock -…
### Steps to reproduce:
- In the settings enable "packages" and Multi-Step Routes
- Create a 3 storable products: P1, P2, P3
- Put on hand quantities for each of them:
- 1 x P1 in PACK001 in stock
- 1 x P2 in PACK001 in stock
- 1 x P3 in PACK002 in stock
- Create and confirm a delivery with 3 moves:
- 1 x P1
- 1 x P2
- 1 x P3
- Go to Inventory > Configuration > Warehouse Management > Operation type
- Modify internal transfers to "Move entire packages"
- Create and confirm and internal transfer for PACK001 from stock to the sublocation stock/shelf1
- Mark the package as done and validate the transfer
#### > You end up with 2 quants for PACK002 in stock
### Cause of the issue:
When validating the internal transfer for PACK001, we launch an `_action_done` on the moves lines of the internal transfer. In particular, during this action done, a `quant_cache` will be set for to fetch and use the quants for P1 and P2:
https://github.com/odoo/odoo/blob/cb1c761777e84d96f82c4f754586795509ce1b3d/addons/stock/models/stock_move_line.py#L679-L681 https://github.com/odoo/odoo/blob/cb1c761777e84d96f82c4f754586795509ce1b3d/addons/stock/models/stock_quant.py#L933-L949 This cache is correctly use in order to update our move lines during the `synchronize_quant` that will follow:
https://github.com/odoo/odoo/blob/cb1c761777e84d96f82c4f754586795509ce1b3d/addons/stock/models/stock_move_line.py#L684-L692 However, they are not in the `_free_reservation` because this call will reassign the move line and then `_check_entire_pack` for the entire picking:
https://github.com/odoo/odoo/blob/cb1c761777e84d96f82c4f754586795509ce1b3d/addons/stock/models/stock_move.py#L1980-L1981 In particular, it will update the reservation for the move related to P3. But, since the `quant_cache` is taken from the context and did not change it can not find the quants related to P3 and a new quant will be created in addition to the already existing one: https://github.com/odoo/odoo/blob/82639728f2bcb4e7786f120de2aeba3f5fbab209/addons/stock/models/stock_quant.py#L1055 https://github.com/odoo/odoo/blob/82639728f2bcb4e7786f120de2aeba3f5fbab209/addons/stock/models/stock_quant.py#L1101
opw-4922032
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#219049The Philippine BIR 2307 spreadsheet export now includes the partner ZIP code and uses the correct tax description for the nature of payment. It also formats company and individual taxpayer names more accurately, helping businesses produce cleaner and more compliant withholding tax reports.
Original PR description
The BIR 2307 XLS export was missing the `ZIP_code` and incorrectly showing the `nature` of payment from the invoice line instead of the tax description. In this commit: --- - Added a new column `zip_code` to show the ZIP from the `partner`. - Changed the `nature` column to use the `tax-description` instead of `product-name`. - Displaying `companyName` only when the commercial partner is a company. - Displaying `surName`, `firstName`, and `middleName` only when the commercial partner is an individual. enterprise-PR- odoo/enterprise#91120 task-4880921 Forward-Port-Of: odoo/odoo#220946 Forward-Port-Of: odoo/odoo#214940
Automatic invoice emails for website purchases now use the proper system context, so they are sent from the assigned salesperson instead of appearing to come from the customer or admin. This prevents confusing duplicate notifications to the administrator and improves customer-facing invoice communication.
Original PR description
**Steps to reproduce**: 1. Enable automatic invoicing `Settings -> Sales -> Invoicing -> Automatic Invoice` 2. Configure a payment provider like `Stripe` (not demo) 3. Open the website in an…
**Steps to reproduce**: 1. Enable automatic invoicing `Settings -> Sales -> Invoicing -> Automatic Invoice` 2. Configure a payment provider like `Stripe` (not demo) 3. Open the website in an incognito browser and log in as a portal user 4. Add a product to cart and checkout with the portal user's delivery address 5. Complete payment using test card credentials 6. Navigate to the created invoice in Sales **Observed behavior:** The invoice email is sent to both the portal user (customer) and the system admin, appearing as if the email is sent "from admin to admin" instead of from the assigned salesperson. **Root cause:** When automatic invoicing is enabled and a portal user completes a website purchase, the `_send_invoice()` method uses `self.env['account.move.send']` which runs in the portal user context. The portal user is selected as author, and the email template uses `partner_to` so it is also selected as partner. While sending mail, this triggers the `mail_notify_author` context. Additionally, due to the portal user not having proper email sending permissions, the system adds admin as fallback. As a result, emails are sent by admin, and because of `mail_notify_author`, mail is also sent to admin. **Solution:** Changed `self.env['account.move.send']` to `tx.env['account.move.send']` in the `_send_invoice()` method. Since `tx` is created with `SUPERUSER_ID` context, this ensures the invoice sending runs with proper system permissions and uses the transaction's context instead of the portal user's context. This ensures emails are authored by the correct salesperson, not the portal user. opw-4760568 Forward-Port-Of: odoo/odoo#221064 Forward-Port-Of: odoo/odoo#220504
This fix restores missing product identification information in electronic invoices and corrects a bank-branch field to match Peppol BIS3 rules. It helps ensure generated UBL invoices validate correctly and reduces the risk of rejected electronic invoices.
Original PR description
### [FIX] account_edi_ubl_cii: Add SellerItemIdentification and add tests Before the UBL refactor, the `SellerItemIdentification` node was populated with the product code. We restore this behaviour. In addition, the refactor added the `StandardItemIdentification` node without specifying the `schemeID` (which is required by the schematron), then commit f46c10f03e59e8 added `schemeID="0160"` This commit adds tests to enforce this behaviour. task-none ### [FIX] account_edi_ubl_cii: BIS3 rm FinancialInstitutionBranch/schemeID In BIS3, the `FinancialInstitutionBranch` node should not specify the `schemeID` property. https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/UBL-CR-655/ This was correct before the UBL refactor but was messed up by the refactor. task-none Forward-Port-Of: odoo/odoo#219924
Improves how Odoo recognizes Microsoft Office documents uploaded through the Documents flow, especially older Word, Excel, and PowerPoint files and newer Excel files. This prevents valid Office files from being renamed incorrectly, such as changing an Excel spreadsheet into a ZIP file, reducing confusion for portal users and document managers.
Original PR description
## [FIX] core: python3-magic vs .doc/.xls/.ppt [python3-magic](https://packages.ubuntu.com/noble/python3-magic)/[python-magic](pypi.org/project/python-magic) (apt/pip) is a frontend for…
## [FIX] core: python3-magic vs .doc/.xls/.ppt [python3-magic](https://packages.ubuntu.com/noble/python3-magic)/[python-magic](pypi.org/project/python-magic) (apt/pip) is a frontend for [libmagic](https://manned.org/man/ubuntu-noble/magic) the library that can introspect files to determine their types. The Documents app makes heavy usage of our mimetypes utilities to scan and fix the extensions of files uploaded by portal users. Everytime a portal user uploads a `.doc`/`.xls`/`.ppt` file, python3-magic is gonna guess the mimetypes `application/x-ole-storage` or `application/CDFV2` which are the mimetypes of the generic file format that Microsoft Office was using until 2006. The problem is that there is no specific extension for those two mimetypes as Microsoft was using the same file format for many of its office applications. In this work we enrich python3-magic's detection with our own, which is able to tell different `application/x-ole-storage` and `application/CDFV2` files apart. **Please note**: Excel files are detected only when the entire file is present. Excel files uploaded via the Documents app are not detected because Documents only `guess_mimetype` on the first 1kiB of the document. We also added a condition to keep the .doc/.xls/.ppt extension in case the generic `application/x-ole-storage` or `application/CDFV2` mimetype is guessed. Before it was emitting a warning due to the unknown extension. ## [FIX] core: python3-magic vs new (2025) .xlsx files [python3-magic](https://packages.ubuntu.com/noble/python3-magic)/[python-magic](pypi.org/project/python-magic) (apt/pip) is a frontend for [libmagic](https://manned.org/man/ubuntu-noble/magic) the library that can introspect files to determine their types. The Documents app makes heavy usage of our mimetypes utilities to scan and fix the extensions of files uploaded by portal users. Sometypes when portal user uploads a `.xlsx` file, python3-magic fails to detect the Microsoft Excel 2007+ (OOXML) mimetype and instead guesses a generic `application/zip`. Technically this is not wrong, OOXML files (like Java JAR and Python Weels) are using the zip format. This is quite strange because python3-magic is able to work with `.xlsx` files. I'm guessing that Microsoft deployed a new version of Excel and that magic doesn't correctly guess the new (2025) `.xlsx` files. Using a hex editor, the old (from our unittests) and new (from a 2025 support ticket) seem similar: OOXML files, deflate compression, same files present. They are a bit different, in the old the `[Content_Types.xml]` file comes last, in the new it comes first. The zip headers are different too, the old uses zip Data Descriptors, the new doesn't. The problem is that the Documents app uses the guessed mimetype to "fix" the extension of the uploaded file. So the portal-user's `file.xlsx` gets wrongly rewritten to `file.zip`. We first used an approach similar to the previous commit[^1], to use our own detection of OOXML files. It works great in base where we run the detection on whole files. However it doesn't work for the Documents app because it attempts to guess the mimetype only reading the first 1kiB of the uploaded file. A first PR odoo#213647 suggested to change Documents to load the whole file first, and then run `guess_mimetype`, but was rejected. In this work, we made so we don't fix the extension of zip-like files should the guessed mimetype be application/zip. [^1]: [FIX] core: python3-magic vs .doc/.xls/.ppt opw-4607156 opw-4753670 Forward-Port-Of: odoo/odoo#220644
Editing existing website links with relative addresses now keeps those addresses unchanged instead of converting them into full website URLs. This prevents unintended link changes when users open and apply the link editor in the website builder.
Original PR description
This PR is backport of https://github.com/odoo/odoo/pull/216832 and https://github.com/odoo/odoo/pull/218587 Steps to reproduce: - Open website builder - Click on a link with a relative URL (there is /contactus in footer) - Bug: the url shown is an absolute url (https://.../contactus) - Click "Edit Link" (one of the icons) - Click "Apply" - Bug: the url in the dom has changed (it saved the absolute url) The link popover used HTMLAnchorElement.href to get the url. This getter returns an absolute url even if the href attribute is a relative url. task-4890293 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Spanish Modelo 390 VAT report now avoids counting vendor refunds twice and includes all relevant manual adjustment lines in the total. This improves the accuracy of annual VAT reporting for Spanish localization users.
Original PR description
This commit addresses two issues in the Mod 390 report:
---
1. Vendor refunds were being reported twice:
- Once correctly via the tax grid.
- And again incorrectly through the cross-formula on lines 639 and 62, which are meant for special manual adjustments only.
➤ Fix: Lines 639 and 62 are now treated as external values, making them
editable and excluding them from automatic computation.
2. Line 64 was missing part of the total:
- It did not include balances from lines 661 and 62, resulting in an incomplete total.
➤ Fix: Updated the computation of line 64 to sum all relevant manual lines.
---
task-4972473
Forward-Port-Of: odoo/odoo#220730This fix reduces memory usage when changing account codes in the Chart of Accounts. It prevents large accounting databases from running out of memory during these updates, making the process more reliable for finance teams.
Original PR description
Description ----------- Writing a new code for an account in the mappings of a COA will retrigger expensive recomputations for all moves linked to the accounts via its lines. This can lead to quick…
Description ----------- Writing a new code for an account in the mappings of a COA will retrigger expensive recomputations for all moves linked to the accounts via its lines. This can lead to quick exhaustion of the memory budget for the processing of the request (2 GiB by default). The commit odoo/odoo@8c5bfff4667ac2f8ec349278dda77d980242aae1 was supposed to address this issue by disabling the fields prefetcher when either 'code' or 'account_type' are being written to, but there is a logical oversight in the condition. ```py prefetch_fields=any(field in vals for field in ['code', 'account_type']) ``` Means "activate prefetch if it exists a field 'code' or 'account_type' in the vals". This is the opposite of what was intended, if the fields *do not* exist, only then we prefetch. So we can just negate the condition. Benchmark --------- For a database where updating the code of an account in the COA mapping, which impacts ~370K account.move and their related ~1.56M account.move.line, saving the new code memory took: | | Before | After | |-------------------|----------|---------| | Peak Memory Usage | 2.78 GiB | 879 MiB | Reference --------- opw-4951670 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#220849
Employees can now place or update lunch orders when the cost is within their allowed overdraft limit. This prevents valid orders from being blocked and keeps the Lunch wallet rules aligned with the company’s configured settings.
Original PR description
**Current Behavior:** The overdraft amount (`lunch_minimum_threshold`) is configured in Lunch settings is ignored when employees create or update lunch orders. As a result, even if the total order…
**Current Behavior:** The overdraft amount (`lunch_minimum_threshold`) is configured in Lunch settings is ignored when employees create or update lunch orders. As a result, even if the total order amount is within the allowed overdraft limit, the system blocks the action. **Steps to Reproduce:** 1) Install the Lunch module. 2) Set an overdraft amount in the Lunch settings. 3) Ensure an employee's wallet balance is less than a desired order total. 4) Attempt to create a lunch order or increase product quantity such that the total is more than the wallet balance but within the wallet + overdraft amount. **Issue:** - In both the product view (`_compute_display_add_button`) and the dashboard (`canAdd` logic), the wallet balance is calculated using `get_wallet_balance(include_config=False)`. - This call excludes the overdraft threshold, causing incorrect warnings and hiding of the `Add to Cart` or `+` buttons. **Solution:** - Remove the explicit `include_config=False` argument so the default True is used, ensuring the overdraft is included. - In the dashboard logic, enhance _make_info() to return a wallet_with_config key using get_wallet_balance(include_config=True) and update the canAdd check to use this value. opw-4782564 Forward-Port-Of: odoo/odoo#217532
Products with GST rates other than 5% now include the required packaged-good tag when syncing to Swiggy and Zomato. This helps ensure product information is classified correctly on delivery platforms and reduces sync inconsistencies.
Original PR description
*: pos_urban_piper_swiggy, pos_urban_piper_zomato Before this commit: --- - Products with GST != 5% were not receiving the `packaged-good` tag in the sync payload for Swiggy and Zomato. After this commit: --- - Now, products with GST != 5% correctly include the `packaged-good` tag in their respective provider tag list (`swiggy` or `zomato`). task-4954571 Forward-Port-Of: odoo/enterprise#90991 Forward-Port-Of: odoo/enterprise#90595
Appointments in some time zones were wrongly shown as unavailable when they overlapped the UTC date boundary before a next-day all-day calendar event. The fix checks all-day event timing using the user’s local time zone, so valid evening appointment slots remain bookable.
Original PR description
All-day calendar events were incorrectly causing unavailability for appointment slots scheduled on the previous evening, due to mismatches between local time and UTC. Previously, all-day events…
All-day calendar events were incorrectly causing unavailability for appointment slots scheduled on the previous evening, due to mismatches between local time and UTC. Previously, all-day events scheduled for Tuesday were stored with a start time of 00:00 UTC. In timezones like America/Chicago, this corresponds to 7:00 PM Monday. As a result, a valid appointment booked from 6:30 PM to 8:00 PM local time on Monday would partially fall into Tuesday in UTC (ending at 01:00 UTC), causing a false conflict with Tuesday’s all-day event. The `calendar_verify_availability` method now calls the newly added `_get_date_boundaries` which than computes naive datetime boundaries based on the user's local timezone by taking the end of the previous local day and the start of the next local day. This change resolves those edge cases where evening slots crossing the UTC boundary were falsely marked as unavailable due to timezone misalignment. opw-4614544 Forward-Port-Of: odoo/enterprise#85654
Fixed an issue where partially processing a lot-tracked receipt in the Barcode app could incorrectly increase the receipt demand after returning to it. This keeps inventory operations accurate when users complete remaining quantities across multiple barcode sessions, including batch picking flows.
Original PR description
### Steps to reproduce: - Create a storable product tracked by lot - Create and confirm a receipt for 10 units of that product - Go to the barcode app and regiter 1 unit - Exit the picking - Go back…
### Steps to reproduce: - Create a storable product tracked by lot - Create and confirm a receipt for 10 units of that product - Go to the barcode app and regiter 1 unit - Exit the picking - Go back to the picking register the 9 remaining units - Exit the picking and comeback #### > The demand of the receipt has been updated from 10 to 19. ### Cause of the issue: Exiting the picking will launch a call of the `post_barcode_process` in order to keep track of the changes you made without changing the initial demand: https://github.com/odoo/enterprise/blob/c26c21cec14a234021bb13bbc2684334bc64b70b/stock_barcode/models/stock_move.py#L50-L53 THe first time you enter the picking you have a single move with a quantity of 10. The first time you exit, since you have set a qty_done of 1 (and hence have updated the quantity of the associated move to 1), the `split_uncompleted_moves` will then create a move for a quantity of 9 in order to keep 10 units assigned. The second time you enter the picking both moves are grouped in a single line since the product is tracked by lot, however, when you update the qty_done of that grouped line you will actually only update the quantity of the first move line and its related move. Since the `_truncate_overreserved_moves` was not designed to handle these grouped lines, it does not notice that the combined reservation overcomes the actual demand. opw-4731803 Forward-Port-Of: odoo/enterprise#90497 Forward-Port-Of: odoo/enterprise#89967
Barcode scans for warehouse locations now only match locations belonging to the active company. This prevents users in multi-company setups from being sent to the wrong internal location when companies share the same barcode.
Original PR description
Description of the issue/feature this PR addresses: When scanning a location by barcode, the system may return the wrong location if multiple companies have internal locations with the same barcode. This is because the search does not currently filter by company. Current behavior before PR: The system searches for a location using only the barcode and usage='internal', without restricting by company. If multiple companies use the same barcode for different locations, the first match (regardless of company) is returned. Desired behavior after PR is merged: The location search is now restricted to the active company. Forward-Port-Of: odoo/enterprise#89286
Users with access to a specific field service task can now start and stop timers even when the related project is private. This prevents access errors and ensures their timesheets are created correctly without needing broader project access.
Original PR description
Before this commit, when the project is private and the user can only access to some tasks inside that project, he can create timesheets but cannot stop timer in one of those tasks when…
Before this commit, when the project is private and the user can only access to some tasks inside that project, he can create timesheets but cannot stop timer in one of those tasks when `industry_fsm` module is installed. This commit makes sure the user can start/stop a timer on task for which he has access to even if the project is private and he does not have access to it. Steps to reproduce the issue: ---------------------------- 0. install industry_fsm module 1. Create a project A with "followers" as privacy visibility 2. Create a task A and assign it to a project user with lowest timesheet access right. 3. log in as that user 4. Start a timer on Task A 5. Stop the timer and confirm the wizard Expected Behavior: ----------------- The timesheet should be created without any issue Current Behavior: ---------------- An Access error is raised because the user has no access to the project. task-4680412 Forward-Port-Of: odoo/enterprise#91015 Forward-Port-Of: odoo/enterprise#84807
Kitchen ticket printing now works reliably when using IoT-connected printers, including cases where the system needs to fall back to websocket communication. The point of sale also loads the necessary IoT device information more consistently, preventing frontend errors when configured devices have not changed recently.
Original PR description
Due to a missing `iot_id` field when the `DeviceController` for kitchen printers is instantiated, the websocket fallback does not work. This results in kitchen tickets failing to print even when regular receipts are working correctly. The fix is to provide the full IoT device model to the `DeviceController` constructor, as is done elsewhere. To do so we now pass the `device_id` field for the preparation printer model from the backend. We also ensure IoT devices/boxes are loaded even with a 'limited' load. This is because of an issue where an IoT device is added to the PoS config, but because the device itself has not be written to recently, the PoS doesn't load it and you get a traceback in the frontend.
This fixes an issue where a payment line could get stuck if a Worldline payment terminal disconnected during payment processing. Cashiers can now remove that payment line and complete the sale with another payment method, reducing checkout disruption.
Original PR description
If the terminal disconnects for any reason while processing a payment, we need to be able to remove the payment line the pos, to allow paying using another method.