Wednesday, January 14, 2026
20 changes · saas-18.4
New functionality added to Odoo
This update adds support for payment channels in Thailand (TH), Malaysia (MY), and Vietnam (VN) through Xendit. This expansion allows Odoo to process payments from these key markets, broadening our customer base and improving payment flexibility.
Original PR description
Xendit has expanded to TH, MY and VN supporting the local payment channels. This commit is to add the supported pamyent channels according to what they have added. task-4334511 Forward-Port-Of: odoo/odoo#189527
This update allows businesses to seamlessly handle self-billed invoices, a key requirement for Peppol integration. It enables the export of vendor bills as compliant self-billed invoices and the import of Peppol-provided invoices into Odoo, streamlining accounting processes and facilitating international trade.
Original PR description
Self-billed invoices are invoices which are created by the customer on behalf of the vendor. These can be used in certain business flows, typically when the customer needs to create an invoice…
Self-billed invoices are invoices which are created by the customer on behalf of the vendor. These can be used in certain business flows, typically when the customer needs to create an invoice internally for accounting purposes. Peppol provides the ability for customers to send self-billed invoices to the vendor over the Peppol network. This way the vendor can verify whether the invoice is correct. See the Peppol documentation at https://docs.peppol.eu/poacc/self-billing/3.0/bis-sb/ #### This PR provides (1) the ability to export vendor bills as self-billed invoices conformant to the BIS3 standard (2) the ability to import BIS3 self-billed invoices as customer invoices (3) the ability to send vendor bills as self-billed invoices over Peppol (4) the ability to receive self-billed invoices from Peppol in a specified Sales journal. #### Configuration Purchase journals now have a `Is Self Billing` boolean field. Self-billing journals have a per-partner sequence, and can send vendor bills as self-billing invoices via Peppol if the option is activated in the Peppol configuration. We also add the self-billed invoice Peppol document type IDs in the list of documents which the user can select or deselect to allow/prevent reception of those document types. Backport of https://github.com/odoo/odoo/pull/222658 Enterprise PR: https://github.com/odoo/enterprise/pull/97803 Upgrade PR: https://github.com/odoo/upgrade/pull/8886 task-5153851 Forward-Port-Of: odoo/odoo#232548
Enhancements to existing features
This update allows system administrators to customize the main Odoo Enterprise home menu with a targeted message. Administrators can set a message via a database configuration, such as a maintenance notice, which will then be displayed to all users. This provides a flexible way to communicate important information directly to users within the system.
Original PR description
Display a message on home menu based on an ir.config_parameter that can be added directly in the database by the system administrator.
The ir.config_parameter is sysadmin.message and should be a json loadable. The format shoud be something like this:
{
"type": "warning",
"replace": false,
"warning_type": "user",
"message": "`<span>A maintenance operation is planned on your server on <strong>2026-01-15</strong> between 14h and 15h</span>`"
}
Forward-Port-Of: odoo/enterprise#103831
Forward-Port-Of: odoo/enterprise#102239Resolved issues and error corrections
This update resolves an issue preventing receipt printing when multiple payment methods were used in the Italian POS (l10n_it_pos) module. The fix backports a change from another Odoo project, ensuring accurate receipt generation for all payment types. This improves the user experience for Italian POS users.
Original PR description
Backports of https://github.com/odoo/enterprise/pull/96353. References: See page 36 in the [official docs](https://download4.epson.biz/sec_pubs/bs/pdf/ePOS%20Fiscal%20Print%20Solution%20Development%20Guide%20Rev%20T.pdf). Ticket [link](https://www.odoo.com/odoo/project.task/5376242) opw-5376242 Forward-Port-Of: odoo/enterprise#103710
This update resolves an issue where Odoo incorrectly searched for proxy users when a branch company had the same VAT/Codice Fiscale as its parent. This prevented users from correctly configuring Electronic Invoicing (IT EDI) for branch offices. The fix ensures the correct company is identified during proxy user searches, improving EDI processing reliability.
Original PR description
Fix issue when saving a branch company sharing the same VAT and Codice Fiscale as its parent. The proxy user search fails because `account_edi_proxy_client.user` is looked up in the branch company instead of the parent one. The same applies when searching the demo user to remove. Steps to reproduce: - Install `account` and `l10n_it_edi` - Set up the company's VAT and Codice Fiscale - Create a branch company with the same VAT and Codice Fiscale - Enable the Electronic Invoicing processing through the SDI in the settings - Select only the branch company and try to save the settings - Observe error since we will try to create a proxy user on the IAP server for an already existing company (the parent one). Ticket [link](https://www.odoo.com/odoo/project.task/5391668) opw-5391668 Forward-Port-Of: odoo/odoo#241443
This update fixes a limitation in the portal's canned response feature, allowing internal users to properly access and utilize available responses. The previous fix was removed and replaced with a more appropriate solution, preparing for future support of the `::` delimiter within the portal. This enhancement ensures a smoother experience for users interacting through the portal.
Original PR description
*: im_livechat, portal, project, test_mail_full PR #192953 introduces a composer action for canned responses. The feature is available in portal for internal users but since `suggestion` is disabled in portal, this feature doesn't work properly. In preparation for supporting `::` delimiter in portal, the incorrect fix in PR #231360 has been reverted. `inFrontendPortalChatter` is specific to portal frontend and should not be set to `true` in the project sharing environment. Instead of the mentioned fix, a similar fix from PR #231441 has been backported. task-5262349 Forward-Port-Of: odoo/odoo#235551
This update ensures that the correct warehouse location is linked when manufacturing merged production orders. Previously, the system incorrectly defaulted to the warehouse's default location, causing issues with multi-location workflows. This fix guarantees accurate tracking of materials throughout the manufacturing process.
Original PR description
Situation ----- When applying a push rule after manufacturing a merged MO, there is an odd case where the link between the merged MO's transfer and the demand move breaks in…
Situation ----- When applying a push rule after manufacturing a merged MO, there is an odd case where the link between the merged MO's transfer and the demand move breaks in https://github.com/odoo/odoo/blob/182a387d0ec6ad28d7d052d7100b2184372514be/addons/stock/models/stock_move.py#L1054 because of the `m.location_id == move.location_final_id` part being false in https://github.com/odoo/odoo/blob/182a387d0ec6ad28d7d052d7100b2184372514be/addons/stock/models/stock_move.py#L1090-L1097 This is because, during the merge, `location_final_id` is not propagated to the new MO https://github.com/odoo/odoo/blob/5f8336c7d8ab891103a3035a9ebb5242cfa46ce6/addons/mrp/models/mrp_production.py#L2416-L2424 so when the new MO's `move_finished_id` gets computed https://github.com/odoo/odoo/blob/5f8336c7d8ab891103a3035a9ebb5242cfa46ce6/addons/mrp/models/mrp_production.py#L822 it gets the MO's `location_final_id` https://github.com/odoo/odoo/blob/5f8336c7d8ab891103a3035a9ebb5242cfa46ce6/addons/mrp/models/mrp_production.py#L1202 which is false. This leads to to the move getting the warehouse's default stock location thanks to https://github.com/odoo/odoo/blob/182a387d0ec6ad28d7d052d7100b2184372514be/addons/mrp/models/stock_move.py#L456-L457 This is problematic for complex use cases with multi-locations and custom routes. It should be safe to propagate the `location_final_id` of the merged MOs if they all share the same one. Use case example ----- <details> <summary>Full use case</summary> - Enable multi-step routes - Create location "WH/Stock/L1" - Create location "WH/Stock/L2" - Create Operation Type "MO child" - Type of Operation: Manufacturing - Sequence Prefix: MOCHILD - Source Location: L1 - Destination Location: L2 - Create Operation Type "Push Transfer" - Type of Operation: Internal Transfer - Sequence Prefix: L2L1 - Source Location: L2 - Destination Location: L1 - Create Route "MO child" - Create Rule "Manufacture" - Action: Manufacture - Operation Type: MO child - Source Location: False - Destination Location: Stock - Create Route "2-step" - Warehouse: Main WH - Create Rule "L1 -> Virtual/Production" - Action: Pull from - Operation Type: MO child - Source Location: L1 - Destination Location: Virtual/Production - Create Rule "Push: L2 -> L1" - Action: Push To - Operation Type: Push Transfer - Source Location: L2 - Destination Location: L1 - Unarchive MTO - Edit MTO route - Create Rule "L1 -> Virtual/production (MTO)" - Action: Pull - Operation Type: "My Company: Manufacturing" - Source Location: L1 - Destination Location: Virtual/Production - Supply Method: Trigger another rule - Create product "Main product" - Create product "Child product" - Routes: "MO child" & MTO - Create product "Material" (consumable) - Create BOM - Product: "Main product" - Component: "Child product" - Create BOM - Product: "Child product" - Component: "Material" - Create MO for "Main product" - Misc/Component Location set to L1 - Duplicate the MO - Merge child MOs & produce - Validate merged MO transfer to L1 - Go back to one of the "Main product" MO > Component quantity is 0 </details> ----- Ticket: opw-5144196 Forward-Port-Of: odoo/odoo#242373 Forward-Port-Of: odoo/odoo#240695
This update resolves a bug that occurred when switching fiscal localizations (like Jordan) within the Hair Salon industry module. The system was incorrectly attempting to delete and recreate journals, leading to a database error. This fix ensures a proper cascade delete process, preventing the error and maintaining data integrity.
Original PR description
Steps to reproduce: - Install industry Hair Salon - Settings > Invoicing > Fiscal Localization - Switch to Jordan fiscal localization Issue: Action will fail with error ``` ERROR: update or delete on table "account_journal" violates foreign key constraint "pos_payment_method_journal_id_fkey" on table "pos_payment_method" DETAIL: Key (id)=(6) is still referenced from table "pos_payment_method". ``` Analysis: It occurs because, when switching CoA, the system attempt to delete and re-create journals. However, the hair salon industry initialize a PoS configuration that will create a default payment method based on one of those journal, thus the system will raise a constraint error on delete. A solution is to manually enforce cascade delete when we are switching CoA. opw-5145235 Forward-Port-Of: odoo/odoo#243381 Forward-Port-Of: odoo/odoo#239433
This update corrects a bug where delivery fees weren't accurately calculated when sales orders and company currencies differed. The fix ensures that delivery fees are correctly priced based on the sales order's currency, preventing discrepancies in pricing displayed to customers. This improves financial accuracy and reduces potential billing errors.
Original PR description
Issue ----- When the SO and the company use different currencies, the picking currency is correctly set to the SO's but the amount is still computed using the company's currency. Example: Sale in…
Issue ----- When the SO and the company use different currencies, the picking currency is correctly set to the SO's but the amount is still computed using the company's currency. Example: Sale in EUR, Company in USD and 1.5 EUR = 1 USD rate. Sell for 15 EUR of products => the delivery picking shows 10 EUR Steps to reproduce ----- - Activate EUR currency at 1.5 EUR = 1 USD rate - Setup company in USD - Setup INTL FEDEX delivery method - Create a dummy product with a 10 USD sale price - Create a pricelist using the EUR currency - Create a sale for some INTL client - set pricelist to EUR - add dummy product - add INTL FEDEX shipping - confirm the sale - Confirm the linked delivery > Message in chatter shows a price of 10 EUR instead of 15 EUR Cause ----- The problem is with the `carrier_price` field of `stock.picking`. https://github.com/odoo/odoo/blob/7c443175f563b9b12a7b8f638524f7f625962dc2/addons/stock_delivery/models/stock_picking.py#L21 The value is set by https://github.com/odoo/odoo/blob/7c443175f563b9b12a7b8f638524f7f625962dc2/addons/stock_delivery/models/stock_picking.py#L155 which gets its' value from the response of https://github.com/odoo/enterprise/blob/0aea72c8db3067073afe1f89dfddf2b43d9392e9/delivery_fedex_rest/models/delivery_fedex.py#L157 We then go through https://github.com/odoo/enterprise/blob/0aea72c8db3067073afe1f89dfddf2b43d9392e9/delivery_fedex_rest/models/fedex_request.py#L382 where we call https://github.com/odoo/enterprise/blob/0aea72c8db3067073afe1f89dfddf2b43d9392e9/delivery_fedex_rest/models/fedex_request.py#L484 The problem is that in `_decode_pricing` we take the first line matching the `rateType` with no regard to the currency of the rate https://github.com/odoo/enterprise/blob/0aea72c8db3067073afe1f89dfddf2b43d9392e9/delivery_fedex_rest/models/fedex_request.py#L594-L598 we should also filter to ensure the rate matches the order's specified currency. ----- Ticket: opw-5419724 Forward-Port-Of: odoo/enterprise#103737 Forward-Port-Of: odoo/enterprise#103232
This update fixes an issue where components added to manufacturing orders through the product catalog weren't correctly transferred to the pre-production warehouse. The fix adds a warehouse ID to these moves, ensuring proper inventory tracking and fulfilling multi-step manufacturing processes. This improves the accuracy of stock levels and streamlines production workflows.
Original PR description
Issue
-----
In multi step manufacturing, components added to MO through the catalog don't get transfered to the pre-prod location.
Steps to reproduce
-----
- 2 step manufacturing
- Create 2 products
- Create a MO for the first product
- Open the product catalog
- Add some qty of the second product
- Go back to the MO & confirm it
> No procurement transfer for the second product from stock to pre-prod
Cause
-----
The move created by the catalog has no `warehouse_id` so in `adjust_procure_method` we don't find any rule which means it gets set to MTS
https://github.com/odoo/odoo/blob/6ecd271ff34313d900a0ad14b1c20679808ba9b8/addons/stock/models/stock_move.py#L2366-L2368
-----
Ticket:
opw-5221418
Forward-Port-Of: odoo/odoo#243036
Forward-Port-Of: odoo/odoo#239265This update fixes an issue where GS1 barcodes weren't accurately reflecting the quantity of products in manufacturing orders. Previously, the system only recorded a single unit regardless of the barcode's specified quantity. Now, the system correctly uses the barcode quantity to update the product's completed quantity, ensuring consistency and accurate tracking of manufactured goods.
Original PR description
Description of the issue/feature this PR addresses: The quantity of GS1 barcodes was not taken into account when scanning the final product of a manufacturing order. More details of this issue can be found in https://www.odoo.com/odoo/project.task/4817418 Current behavior before PR: When scanning a GS1 barcode with a quantity defined (e.g. 0120250524135700310210000010LOT887766 ) as the final product of a manufacturing order, the quantity is not taken into account in the call to produceQty(), so the line will have a qty_done of 0 regardless of the quantity specified in the barcode Desired behavior after PR is merged: The qty_done of the final product line should be the one specified in the barcode, in order to make the behaviour consistent with other usages of GS1 barcodes. Forward-Port-Of: odoo/enterprise#104024 Forward-Port-Of: odoo/enterprise#95174
This update fixes an issue where loyalty programs with pricelist restrictions weren't consistently applied in the POS. Previously, if a POS session's pricelist didn't match a loyalty program's restrictions, the program wouldn't be applied. Now, loyalty programs with restrictions will correctly filter applicable discounts in the POS, ensuring accurate pricing.
Original PR description
Before this commit, if a loyalty program had pricelist restrictions, the POS would not consider them when loading the applicable loyalty programs. This could lead to scenarios where a loyalty program was applied in a POS session even if the session's pricelist was not allowed by the program. This happened when the pricelist was also not available in the POS configuration and program.pricelist_ids was empty. opw-5467990 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#242279
This update resolves a problem where clients were incorrectly using outdated number ranges when syncing data with DIAN. The fix ensures that the latest, valid number range provided by DIAN is always used, preventing invoice errors and improving data accuracy. This impacts invoice processing with Colombian DIAN regulations.
Original PR description
**PROBLEM** When a client has exhausted a number range for a prefix, if he request a new one, when trying to sync the DIAN data the old range will be use instead of the new one. **STEP TO REPRODUCE**…
**PROBLEM** When a client has exhausted a number range for a prefix, if he request a new one, when trying to sync the DIAN data the old range will be use instead of the new one. **STEP TO REPRODUCE** According to the DIAN documentation, the GetNumberRange service is only available in the production environment. So i'm not sure if we can safely test this. The repro steps would be something like: 1. Request a range. 2. Exhaust all number from this range by sending invoices to DIAN. 3. Request a new range. 4. sync with DIAN. (notice the range selected is still the old one). 5. Try sending a new invoice to DIAN and notice there is an error. **CAUSE** In `l10n_co_dian/models/account_journal.py` the function `_l10n_co_dian_get_journal_values()` loops on all the xml `NumberRangeResponse` node and store the last range values encountered for each prefix. We don't check if the this last range is still valid, if it's the newest created (could be checked with the xml field `ResolutionDate`, but the date could be the same if the range were created the same day), if it's the latest in term of number range (DIAN start with range 1-100, then 101-something etc.). Forward-Port-Of: odoo/enterprise#103943
This update resolves an issue where users could incorrectly modify attendance records for employees they didn't manage. The change ensures that unauthorized updates are blocked, preventing data inconsistencies and maintaining accurate attendance tracking. Test coverage has been added to guarantee this fix.
Original PR description
Closes [odoo/odoo#226007](https://github.com/odoo/odoo/issues/226007). Description of the issue/feature this PR addresses: Prevents a user from updating their attendance record by changing the employee to the one whose attendance is not managed by the current user. Current behavior before PR: - Assign the Officer Group of Attendance group to a user. - Assign the user as the attendance manager of itself. - Login with that user. - Create an attendance record for the employee and save it. - Try to change the employee and save; an error will be thrown as expected. - Go to the Attendance menu; the record will still be saved. Desired behavior after PR is merged: This commit ensures that un-allowed write does not take place + test coverage added. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#243573 Forward-Port-Of: odoo/odoo#226335
This update corrects a potential issue with how payroll data is locked for tax reporting in Switzerland (ELM transmission). By using a reference date, the system now accurately reflects the period for which data is secured, ensuring compliance and reliable reporting. This change enhances the accuracy of Swiss tax filings.
Original PR description
Forward-Port-Of: odoo/enterprise#104269
This update corrects a bug where manually changed currency rates on invoices weren't updating correctly, leading to lost data. Now, the system only recalculates rates if the user hasn't modified them, preserving user input. This ensures invoices with different dates accurately reflect the intended currency rates.
Original PR description
in case the user would enter manually a different rate than the default one, but does not fill the invoice date; odoo was setting today as the invoice date, which was changing the rate and recomputing all the lines... Effectively losing everything the user just encoded. So now, we only recompute the rate and the lines if the user didn't change it. Fix: https://github.com/odoo/odoo/pull/226124/changes/1b48d141d7260a262075555c4ab9cedc691d3551 Issue with Fix: Invoices posted on dates different from their creation date do not update their currency rates, even though they should. Comparing `invoice_currency_rate` to the expected rate at creation is a better guess. task-5477481 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#243126 Forward-Port-Of: odoo/odoo#242800
This update ensures salary configuration personal information is automatically populated using the employee's most recent version, regardless of whether it's a standard offer or a contract template. This eliminates manual data entry and ensures accurate salary settings for all employees, streamlining the offer creation process.
Original PR description
The personal informations in the salary config is prefilled using the version selected in the offer. When making a new offert for an already employed person, the default version is the last active version, the address and other personal info are already set on that version and the salary has the last up-to-date data. But when selecting a contract template in an offer, the version does not have the personal info from the employee (as it's a template). In this commit, we force to use the employee itself (from the active version of the employee, or the employee linked to the contract template copy - created during the offer creation). may it be an applicant or an existing employee, when an offer is generated, an employee is created (or re-used) and set on the contract template. So it works in every case. Taks-5162703
This update resolves an issue where payment reports were inconsistently using different export formats (NACHA or last installed module). The fix ensures that payment reports now automatically use the correct format based on the company's localization, improving report accuracy and usability for users. The change is being backported to version 18.0.
Original PR description
\* = l10n_{ae, au, ch, in, sa, us}_hr_payroll + hr_payroll_account_iso20022
Issue:
The current behavior looks deterministic: when clicking on "Create Payment Report" it -sometimes- shows the current company's export format by default, other times it shows the "NACHA" type. Or it could be the last installed module's export format value for the other companies.
Solution:
I fixed it in this PR: https://github.com/odoo/enterprise/pull/93683 and now backporting the changes to version 18.0
task-5189295
Forward-Port-Of: odoo/enterprise#103864
Forward-Port-Of: odoo/enterprise#100126This update fixes an issue where customer statements incorrectly showed an outstanding balance after a payment was made on an invoice. The fix ensures the system accurately considers partially reconciled amounts when determining follow-up actions for invoices and payments. This improves the accuracy of customer statements and reporting.
Original PR description
**Steps to Reproduce:** 1. Create an invoice with a due date 20 days prior and an amount of $100 2. Create a payment of 120$ 3. Create an invoice of 100$ 4. Reconcile the second invoice with the…
**Steps to Reproduce:** 1. Create an invoice with a due date 20 days prior and an amount of $100 2. Create a payment of 120$ 3. Create an invoice of 100$ 4. Reconcile the second invoice with the payment 5. Go to the customer record. 6. The Customer Statement smart button shows an amount due, but the followup status in the Accounting tab shows "No action needed". [Video (with different values, same result)](https://drive.google.com/file/d/1MFg-tUos-oGbk7SKn92OE8w0-PnObae7/view?usp=sharing) **Cause:** - The query in `_get_followup_data_query` checks an account.move.line's `balance`, ignoring amounts partially reconciled. [1](https://github.com/odoo/enterprise/blob/da8a0fb49861a5cfb366c85da459876ad1556924/account_followup/models/res_partner.py#L404) - In the example above, the sum of unreconciled balances is 100 - 120 = -20 due, where the amount_residual shows 100 -20 = 80 due. **Solution:** Use `amount_residual` instead of `balance` in `_get_followup_data_query`. This fix was applied last year to 17.0, but was never forward-ported to master. [2](https://github.com/odoo/enterprise/pull/77679) [opw-5216007](https://www.odoo.com/odoo/project.task/5216007) Forward-Port-Of: odoo/enterprise#101874
This update resolves an issue where the height of image gallery snippets would unexpectedly reset when images were reordered. The fix ensures users can set and maintain custom heights for gallery snippets, providing consistent control over their appearance. Additionally, minor template issues have been addressed.
Original PR description
Steps to reproduce Scenario A 1. Go to Website → drop an Image Gallery snippet → A default height value appears in the `"Height"` input. 2. Select an image → change its order in the carousel → The…
Steps to reproduce Scenario A 1. Go to Website → drop an Image Gallery snippet → A default height value appears in the `"Height"` input. 2. Select an image → change its order in the carousel → The snippet height is automatically reset to `70%` of the screen height. Scenario B 1. Change the height value of the snippet from the `"Height"` option. 2. Select an image → change its order in the carousel → The snippet height is again reset (and the option value is overridden). Issue The original height behavior was introduced in [1] to make the slideshow mode auto-adapt to `70%` of the viewport height. This diff also removed height CSS for other modes where the height should depend on the content [2] Subsequent adaptations: [3] added a default height (`500px`) in XML, [4] removed it during a design refactoring, [5] restored the possibility to control the height of the image gallery snippet using the `"Height"` option. Keeping the same JS logic that forces the snippet height, led to the behavior explained above: even when the user manually sets a height, any action triggering `slideshow()` (e.g., image reorder) forces the height back to 70% of `window.innerHeight`. Fix 1. Remove the outdated JS code that automatically updates the height. 2. Keep the slideshow behavior consistent with [2] by excluding it from the height CSS removal logic. The snippet now starts with a default height and only changes when edited through the `"Height"` input. Additional fixes This commit also fixes a few minor issues in the new carousel items template introduced in [4]: items having an `"undefined"` class, and a missing margin style in the main snippet template. [1]: https://github.com/odoo/odoo/commit/239b6bc0b5a2a644486737f2b0b71e7e6c0a2edf [3]: https://github.com/odoo/odoo/commit/9069d0127c176317436b67b23ae5677dd9d53de7 [4]: https://github.com/odoo/odoo/commit/9042b1cae7b630b20e0670788b7a4ed9e4c97609 [5]: https://github.com/odoo/odoo/commit/d5d138e833344e857a420d865d4b12f1acdb0e7c task-3414281 Forward-Port-Of: odoo/odoo#242385 Forward-Port-Of: odoo/odoo#126766