Thursday, July 9, 2026
19 changes · saas-19.3
Enhancements to existing features
Self-order kiosk orders are now only sent to the Belgian fiscal blackbox once they are fully paid. This supports flows where customers choose to pay at the counter or use other payment methods, avoiding premature fiscal registration.
Original PR description
This commits adapts the code in confirmation_page.js to not send the order to the blackbox from the kiosk if the order is not in paid state. task-id: 5960666 Forward-Port-Of: odoo/enterprise#119724 Forward-Port-Of: odoo/enterprise#117585
The bank reconciliation screen now sends less unnecessary data to the browser and moves some heavy calculations to the server. This should make opening the widget faster on very large databases, helping accounting teams work with bank statements more smoothly.
Original PR description
When opening the bank rec widget on huge DB's, it takes
a lot of time to load everything.
This commit aims to improve the loading performances by
removing some JS fields:
1 - reconciled_lines_ids: We only use the first element of
this recordset in JS, so we add a new computed field
to only send 1 record to the JS
2 - hasAttachment: replace the long JS computation of
`get hasAttachment` with a python computed field.
3 - Replace matched_credit_ids and matched_debit_ids
with exchange_diff_partial_ids.
Linked:https://github.com/odoo/odoo/pull/269119
task-6275945
Forward-Port-Of: odoo/enterprise#119557Xendit payments now cover the Singapore market with PayNow (SGQR) support, and merchants can also accept SGD and USD. The update also adds support for additional card brands, including JCB and AMEX in supported markets, helping expand payment options for customers.
Original PR description
This commit expands Xendit support to include the Singaporean market and additional card brands. The following changes were made: - Added support for the PayNow (SGQR) payment method. - Added SGD and USD to the list of supported currencies. - Added JCB and AMEX to the supported card brands (available for some markets). - Updated the base payment provider data for Xendit to include PayNow. Task-5964309 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274769 Forward-Port-Of: odoo/odoo#253542
Customers can now choose to pay at the counter even when a payment method has already been set up in self-order. This gives businesses more flexibility at checkout and avoids blocking mixed payment flows.
Original PR description
pos*: point_of_sale, pos_self_order This commit allows the user to allow his customer to pay at the counter even if they already have payment method set in the self order. task-id: 5960666 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#268893 Forward-Port-Of: odoo/odoo#250364
Resolved issues and error corrections
This fix prevents Australian payroll users from seeing an error when recalculating a payslip after an employee's Income Stream Type has changed. Existing payslips now refresh that value before calculation, helping payroll processing continue smoothly and reducing manual disruption.
Original PR description
When an employee's Income Stream Type is changed after a payslip has been created, computing the sheet for payslip will raise a traceback. Steps to reproduce the error: - Install…
When an employee's Income Stream Type is changed after a payslip has been created, computing the sheet for payslip will raise a traceback. Steps to reproduce the error: - Install ``l10n_au_hr_payroll_account`` module with demo data - Switch to ``My Australian Company`` company - Create a new payslip for ``Dennis Cactus`` Employee > Save - Go to Employees > Open the ``Dennis Cactus`` employee > In Payroll tab, Income Stream Type: Other specified payments > Save - Go back to payslip > click the compute sheet button Traceback: ```py KeyError: 'OSP' ``` https://github.com/odoo/enterprise/blob/13e64e0cb7f566cbbb46109fe1c218eb7a14eead/l10n_au_hr_payroll/models/hr_payslip.py#L175-L178 The ``l10n_au_income_stream_type`` field on the payslip is a computed field that only depends on ``employee_id``. As a result, changing the employee's Income Stream Type does not trigger a recomputation of the corresponding field on existing payslip. So, when the ``payslip_ytd_totals`` field is computed, it uses the old value of ``l10n_au_income_stream_type`` field at [1], The resulting ``payslip_ytd_totals`` is then used to build the ``totals`` dictionary, and eventually, when the employee's current ``income_stream_type`` is used to access ``totals``, the mismatch key leads to the above traceback. https://github.com/odoo/enterprise/blob/13e64e0cb7f566cbbb46109fe1c218eb7a14eead/l10n_au_hr_payroll_account/models/hr_payslip.py#L75-L88 [1]: https://github.com/odoo/enterprise/blob/13e64e0cb7f566cbbb46109fe1c218eb7a14eead/l10n_au_hr_payroll/models/hr_payslip.py#L269-L272 solution: I added ``l10n_au_income_stream_type`` to ``add_to_compute()`` in ``compute_sheet()``. This ensures that stale values of ``l10n_au_income_stream_type`` on existing payslips are recomputed when the payslip sheet is computed. sentry-7536819310 Forward-Port-Of: odoo/enterprise#123288 Forward-Port-Of: odoo/enterprise#120143
Vendor bills imported from Chilean electronic invoice XML files now use the correct foreign-currency amounts instead of incorrectly taking peso values. This prevents overstated or understated bills when companies transact in currencies such as UF, improving accounting accuracy.
Original PR description
**STEP TO REPRODUCE** 1. Create a invoice to a chilian company, using another currency (for example UF, don't forget setup up a currency rate). 2. Confirm. 3. Download the xml in the chatter, and import it as a vendor bill. 4. Notice the imported bill amount are wrong (Pesos amount are used, with the currency being UF). opw-6269662 Forward-Port-Of: odoo/enterprise#123179 Forward-Port-Of: odoo/enterprise#119664
Belgian payroll no longer applies a special public holiday eligibility rule for time credit contracts because it lacked a legal basis. This helps ensure payroll calculations follow the correct legal interpretation and avoids unsupported holiday entitlement handling.
Original PR description
The specific code related to the eligibility to public holiday for time credit contracts has no legal base. This commit removes it. task-6370653 Forward-Port-Of: odoo/enterprise#123303
Deliveries using DHL from a company different than the main company now send a valid commercial invoice number. This prevents DHL validation errors and allows affected international shipments to be processed correctly.
Original PR description
Issue ----- When validating a delivery in a different company from the main one, the commercial invoice's number field is incorrect, which raises an error on DHL end.…
Issue ----- When validating a delivery in a different company from the main one, the commercial invoice's number field is incorrect, which raises an error on DHL end. `content/exportDeclaration/invoice/number: expected type: String, found: Boolean` Steps to reproduce ----- - Create a Belgian company - Setup DHL - DHL Product D - Express Worldwide - Dutiable Material enabled - Create an amrican customer - Deliver a product to the american customer > Validation Error Cause ----- The field is populated in https://github.com/odoo/enterprise/blob/cf3c2fce8a6b7b2d7547d44a0e4423f887986d52/delivery_dhl_rest/models/dhl_request.py#L204 The problem is that `next_by_code` uses the company found in the env, whereas the sequence's company is the main one, so it is not found when doing https://github.com/odoo/odoo/blob/e3b0ca11d99b2ef819cdad68b169112cd73668b6/odoo/addons/base/models/ir_sequence.py#L287 ----- Ticket: opw-6171886 Forward-Port-Of: odoo/enterprise#123170 Forward-Port-Of: odoo/enterprise#118379
This change prevents WhatsApp message lists from failing when a message is linked to a business document the user cannot access directly. Existing WhatsApp message visibility rules still determine who can see messages, so regular users only see their own messages while WhatsApp administrators can see all.
Original PR description
The `body` field on `whatsapp.message` was defined with `related_sudo=False` with the intent of restricting access to messages from restricted records. However, this was never actually providing any…
The `body` field on `whatsapp.message` was defined with `related_sudo=False` with the intent of restricting access to messages from restricted records. However, this was never actually providing any security value because [`mail.message.fetch()`] was overriding it with `self.sudo()` till `v19.1`, meaning the body was always fetched as superuser regardless:
```py
web_search_read() -> search_fetch()
-> fields.py _compute_related()
-> record[self.related_field.name] # triggers fetch of mail.message.body
-> models.py _fetch_field()
-> mail_message.py fetch()
-> self = self.sudo() # sudo hack overrides related_sudo=False silently
```
In `v19.2`, the `fetch()` sudo hack was intentionally removed (see commit odoo/odoo@4727f12d274a0b2d7c455363d189565bd8fb2e7a) as access rights are now cached and can be checked without a performance penalty. This exposed the broken `related_sudo=False` which now causes an `AccessError` when trying to read the body of a `whatsapp.message` whose linked `mail.message` points to a document the current user cannot access (e.g. `purchase.order`).
Access control on `whatsapp.message` is already correctly enforced at the `ir.rule` level:
- Regular users can only see messages they created (`create_uid = user.id`)
- WA Admins can see all messages
We have upgrade requests failing on this issue: TBG-[2765]
[`mail.message.fetch()`]: https://github.com/odoo/odoo/blob/saas-19.1/addons/mail/models/mail_message.py#L812-L819
[2765]: https://upgrade.odoo.com/odoo/tbg/2765?debug=1
Forward-Port-Of: odoo/enterprise#119867Point of Sale now fetches Urban Piper and platform orders together instead of making extra separate requests. This reduces waiting time and unnecessary server calls when retrieving orders, improving reliability and responsiveness for restaurant and delivery workflows.
Original PR description
Issue: pos_urban_piper overrode getServerOrders() to add a separate loadServerOrders() call for it's own orders before delegating to super, resulting in up to an additional sequential RPCs on every order fetch. Fix: Extract the base query domain into a new overridable getServerOrdersDomain() method. Each module overrides it to OR in its own domain via Domain.or([super.getServerOrdersDomain(), extraDomain]), so all orders are fetched in a single RPC call instead of three. Task-6284860 Forward-Port-Of: odoo/enterprise#123160 Forward-Port-Of: odoo/enterprise#120001
The timesheet grid now uses each employee's own working schedule to show public holidays, weekends, and approved time off as unavailable. This prevents employees from seeing incorrect availability based on the company default schedule and keeps Timesheets aligned with Time Off.
Original PR description
Steps to Reproduce --- - Create two different working schedules with different public holidays - Assign employees to specific working schedules - Set company default working schedule to a different…
Steps to Reproduce --- - Create two different working schedules with different public holidays - Assign employees to specific working schedules - Set company default working schedule to a different schedule - Login as employee with specific working schedule - Navigate to Timesheets app -> My Timesheets - Observe public holidays and personal time-off displayed in the timesheet grid Issue --- - The timesheet grid displays unavailable dates (public holidays, weekends) from the company's default working schedule instead of the employee's assigned working schedule. - Personal time-off requests are not reflected as unavailable dates in the timesheet grid. Current Behaviour --- - Public holidays shown are always from the company's default working schedule, ignoring employee-specific working schedule assignments. - Employee's approved time-off requests don't appear as unavailable in the timesheet. Expected Behaviour --- - Public holidays should display based on the employee's assigned working schedule, with company schedule as fallback only when no specific schedule is assigned. - Employee's personal time-off requests should appear as unavailable dates. - This should align with Time Off app behavior. Fix --- - Included employee-specific work interval calculation with personal time-off requests. - Added support for contract-based calendar changes and calendar validity periods. - Implemented proper fallback when valid intervals are not found. task-4997080 Forward-Port-Of: odoo/enterprise#123331 Forward-Port-Of: odoo/enterprise#95458
This change prevents an access error that could appear during subcontracting operations when handling serial numbers. It helps users complete the process smoothly without running into permission issues.
Original PR description
opw-6316136 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#274830 Forward-Port-Of: odoo/odoo#271739
This change prevents the website editor from crashing when a user opens the Documents tab after selecting an icon. It improves the media picker so it correctly distinguishes icons from document attachments, making the replace flow more reliable.
Original PR description
### Steps to reproduce: - Open the website editor and insert a snippet. - Inside the snippet, add an image and a document via /media. - Select the image, click Replace, pick an icon. - Click the icon, then click Replace from the sidebar. - In the dialog, click the Documents tab. - Traceback occurs. ### Root cause: - Both icon and document box elements are `<span>` tags. `DocumentSelector` inherits `selectInitialMedia()` from `FileSelector` which only checks the tag name, so it incorrectly returns true for icons. This causes `fetchAttachments` to call `querySelector(a)` on the icon span, which returns null and crashes. ### Solution: - Override `selectInitialMedia()` in `DocumentSelector` to also check for the `o_file_box` class. Add optional chaining on `querySelector(a)` as a safety net. task-6310147 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270548
This update improves the Point of Sale experience by making product cards larger so long names display correctly. It also automatically selects single-option product variants and ensures saved interface settings are restored correctly, so product details appear properly in the cart and receipt.
Original PR description
This commit fixes multiple issues: 1. Product visibility: Product card are too small, we increase their size so that big product name can be displayed properly. 2. Variant selection: When a product has attributes with only one choice the choice is not selected automatically. We select it in this commit such that the information is displayed properly in the cart and receipt. 3. uiState not updated: When we restore the uiState of a record, we do not take into account that the uiState architecture might have changed. We now init the uiState before restoring it so new fields are properly initialized even when not present in the saved uiState. task-id: 6344288 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272843
When a task is moved to another project, followers now inherit the notification settings of the new project. This ensures people continue receiving the right updates, such as stage changes, instead of missing important task activity.
Original PR description
Steps to reproduce: - 1. Create projects A and B. 2. Add a user as a follower of project B and select specific notification subtypes (e.g., 'Stage Changed'). 3. Create a task in project A and add the same user as a follower(defaulting to 'Discussions'). 4. Move the task from project A to project B. Issue: - The follower's subscription preferences on the task do not reflect their project-level settings after the move. In the example above, the user remains subscribed only to 'Discussions' and misses 'Stage Changed' updates. Cause: - The default auto-subscription logic skips existing followers. When moving a task, this prevents the system from adding the new project's notification preferences to users who were already following the task. Fix: - Override `_message_auto_subscribe` in project.task to the `update` policy when the `project_id` is changed. task-5877507 Forward-Port-Of: odoo/odoo#248224
This change ensures e-invoices sent through the Turkish Nilvera integration always include the exchange rate to TRY, even when the company’s main currency is not TRY. It prevents invoice rejections by making the XML meet Nilvera’s required currency rules.
Original PR description
Description of the issue/feature this PR addresses: This PR resolves an integration failure with the Nilvera e-invoicing provider when a company’s primary currency is configured to a foreign currency…
Description of the issue/feature this PR addresses: This PR resolves an integration failure with the Nilvera e-invoicing provider when a company’s primary currency is configured to a foreign currency (e.g., USD) rather than the local currency (TRY). Nilvera strictly requires a valid exchange rate relative to Turkish Lira (TRY) to be included inside the XML nodes of every posted invoice utilizing a foreign currency. Functions affected: def _add_invoice_exchange_rate_nodes(self, document_node, vals): def _l10n_tr_get_currency_conversion_rate(self, invoice): Current behavior before PR: When generating an invoice where both the company's main currency and the invoice currency are foreign (e.g., USD), the system does not calculate or embed a TRY conversion/exchange rate into the invoice payload. Because this mandatory local currency reference mapping is missing, Nilvera rejects the invoice submission. Desired behavior after PR is merged: For every invoice processed via the Nilvera localization, the system will explicitly calculate and inject the exchange rate between the active invoice currency and TRY into the posted document nodes, regardless of what the underlying company's primary currency is set to. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271129 Forward-Port-Of: odoo/odoo#270531
This change prevents paid point-of-sale orders from creating duplicate payment lines when they are synced again. It also avoids a crash that could happen if the system tried to process a payment that had already been removed, making payment updates more reliable.
Original PR description
A paid order can reach `sync_from_ui` more than once. In that case the order falls into the else branch of `sync_from_ui` and its payments are re-processed through `process_saved_payments`, which was…
A paid order can reach `sync_from_ui` more than once. In that case the order falls into the else branch of `sync_from_ui` and its payments are re-processed through `process_saved_payments`, which was not idempotent and led to two issues: - The change/return cash payment is generated server-side in `_process_payment_lines` and has no uuid, so `_update_lines` cannot deduplicate it. Each extra sync therefore created an additional return payment. It is now removed before being recomputed, which also keeps it correct when the payments are edited after payment (new return amount, or no change at all). - `_update_lines` replays the client commands as-is. On a second sync, a delete command (`[2, id]`) targets a payment that the first sync already removed, and `_create_pm_change_log` crashed with a MissingError while reading the deleted record. Update/delete/unlink commands referencing records that no longer exist are now skipped. Note that delete/unlink commands only carry 2 elements, so the check runs before the `len(line) < 3` guard. Steps to reproduce: - Pay an order, then re-sync it (or edit its payments and sync again). => the return payment was duplicated, or a MissingError was raised. opw-6327912 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272539
This update prevents manufacturing byproducts from being reset to zero when a manufacturing order is unreserved and then re-checked. As a result, businesses can safely unreserve and replan production without losing expected byproduct output.
Original PR description
On a mo that has byproducts, if you unreserve, it will also set the quantity of byproducts to 0 Steps to reproduce: ------------------- * Create a Products main, component and byproduct * Create a…
On a mo that has byproducts, if you unreserve, it will also set the quantity of byproducts to 0 Steps to reproduce: ------------------- * Create a Products main, component and byproduct * Create a bom for main with component as component and byproduct as byproduct * Create and confirm a mo for main * Set qty_producing to quantity ot produce * click on "Unreserve" (do_unreserve) * click on "Check availability" (action_assign) * Produce All -> the byproducts will not be produced. Observation: ------------- When updating the qty_producing value it will also update the quantity of the byproducts moves: https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/mrp/models/mrp_production.py#L892-L893 https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/mrp/models/mrp_production.py#L1350 https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/stock/models/stock_move.py#L2382 The quantity on the byproducts move has been updated. When clicking on Unreserve it will call do_unreserve, https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/mrp/models/mrp_production.py#L2297-L2298 It will filters the moves that do not need to be unreserved and select the others: https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/stock/models/stock_move.py#L900 and it will unlink all the sml from the moves: https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/stock/models/stock_move.py#L919 Which will set the quantity on the byproduct moves to 0. When Producing all (button_mark_done) since the qty_producing has already been set, it will simply mark the byproduct move has picked. https://github.com/odoo/odoo/blob/aca2e226143487b421f79194d677bb48d5da1358/addons/mrp/models/mrp_production.py#L1323-L1324 In our case, this means that the no byproducts will be created since, the quantity was previously set to 0 opw-6296562 Forward-Port-Of: odoo/odoo#273739 Forward-Port-Of: odoo/odoo#272216
This update resolves a problem where printed receipts sometimes resulted in blank or prematurely cut paper. The fix introduces a short delay after sending the receipt image to allow the printer to fully process the data. Additionally, the printing process is now more precisely controlled for optimal hardware performance.
Original PR description
Previously, printing a receipt could sometimes result in blank paper being dispensed or the paper being cut prematurely. This occurred because the sequence of line feeds and cut commands was dispatched immediately after sending the image payload, before the printer hardware had sufficient time to process and spool the bitmap. To resolve this, a 200ms delay is introduced after the bitmap is sent. Additionally, the arbitrary `printAndLineFeed` calls are replaced with a precise `printAndFeedPaper` and explicit `partialCut` command. This ensures the hardware has fully rendered the receipt before advancing the paper and engaging the blade. Finally, the internal imin SDK (`lib/imin-printer/imin-printer.js`) is updated to handle websocket connection timeouts gracefully and to expose new hardware APIs for future tracking. owp-6242801 Forward-Port-Of: odoo/odoo#274146 Forward-Port-Of: odoo/odoo#270765