Saturday, September 12, 2026
13 changes · master
Enhancements to existing features
Users can now start online payment initiation directly from the payment record instead of navigating batch-payment details. This makes the payment workflow clearer, supports more eligible payment methods, and keeps payment status better aligned with the provider status.
Original PR description
*: account_batch_payment, account_iso20022, l10n_au_aba The previous flow exposed batch-payment internals to end users, which made routine payment actions harder to understand and increased friction in the payment workflow. Users should act from the payment itself, while technical batching concerns stay transparent and backgrounded. This change aligns the UX with that goal by keeping user actions on account.payment and using batch payments only as an implementation mechanism when needed. It also removes an unnecessary functional limitation where only sepa_ct could follow the direct initiation flow; all non-manual methods can now do so when their journal/method setup supports it. task-6326470
Customers can now pay a single order using multiple payment methods by entering a custom amount during checkout or in the sales portal. This gives buyers more flexibility while keeping safeguards so sales orders are only confirmed when the required prepayment amount is actually received.
Original PR description
An amount input field is added to the payment form of payment methods to allow split payments. This allows the user to pay an order through multiple payment methods on: - eCommerce (checkout flow) - Sales' portal (except for downpayment and payment links) task-6359070 See also: - https://github.com/odoo/enterprise/pull/125129
Appointment and POS Appointment screens now show more consistent information and controls, making it easier for teams to view, report on, and update booking statuses. The update adds Kanban, graph, and pivot views for appointments, improves popovers, and aligns the quick creation form for touchscreen POS use.
Original PR description
This PR alignes appointment and pos_appointment as the applications must display the same views and data as they have the same purpose: - The kanban view has been added in appointment to see the…
This PR alignes appointment and pos_appointment as the applications must display the same views and data as they have the same purpose: - The kanban view has been added in appointment to see the appointments per status and to easily update that attribute. A popover is displayed when clicking on kanban's records in order to show quickly essential information as in the gantt view. - The graph and privot views have been added in appointment to allow a better reporting. - The design of the popovers of the gantt and kanban views in appointment and pos_appointment have been updated. In appointment, the update of the appointment status is no longer done from a field but from buttons as they are more visible. As the popovers needs to display the same data, design and use the same logic, a unique popover as been created and used by those views. - The design of the form for the quick creation in pos_appointment has been aligned with the one in appoitment. To give more visibility to the appointment status, it can be updated using buttons in the footer. The form in pos_appointment is bigger than in appointment as it is used by touchscreens. Community PR: https://github.com/odoo/odoo/pull/287457 Upgrade PR: https://github.com/odoo/upgrade/pull/11154 Task-6185312
Employees and finance teams can now dispute fraudulent Stripe expense card transactions directly in Odoo. The update adds dispute handling, evidence file upload and download, notifications, and testing support so companies can manage card disputes more efficiently.
Original PR description
If a transaction made is fraudulent, ... It can now be disputed. More details about dispute workflow with stripe can be found at https://docs.stripe.com/issuing/purchases/disputes. Add a widget to upload file to and download file from stripe such as dispute evidence, which will be able to be used for the other purpose. Buttons to win/lose a dispute are present in debug mode on the submit confirmation prompt to try those cases. task-4938982
Payment completion now happens immediately after each payment is processed instead of waiting for separate background jobs or customer page actions. This should speed up backend workflows such as captures, refunds, invoices, subscriptions, and POS confirmations, while reducing customer wait times and keeping a fallback retry process for failures.
Original PR description
As of commit 4588e939, payment data records are queued and processed serially by a dedicated cron. This allowed payment requests to be isolated from the processing step and, by design, eliminated…
As of commit 4588e939, payment data records are queued and processed serially by a dedicated cron. This allowed payment requests to be isolated from the processing step and, by design, eliminated race-condition rollbacks on payment transactions. However, the post-processing remained a separate asynchronous step, still run on demand when customers reached the `/payment/status` page and from cron triggers scattered across provider and app modules for backend flows. This had several undesired side effects: - Backend business logic (captures, refunds, subscription invoicing, POS order confirmation, etc.) was forced to wait for a free cron worker or for the next scheduled run. - Customers had to wait out the 10-second timeout when the notification that should have triggered the redirect was missed (sent before the page had subscribed to its channel) or premature (sent before the transaction had been post-processed). - The post-processing cron had to run at a relatively high frequency, with only 10-minute intervals by default. - Transactions that had several state updates before they could be post-processed would only be post-processed for their last state change. - The extra step added overhead to the overall transaction processing cycle. This design was intentional, as it allowed payment requests and the processing step to be isolated from the much more failure-prone post-processing step. Indeed, it executed fragile business logic (e.g., posting invoices according to the journal configuration, sending emails) whose failures could have rolled back the transaction and replayed payment requests. Since this is no longer a risk, the post-processing can be unified with the processing step. This commit now synchronously post-processes transactions right after they are processed. If the post-processing fails, only that step is rolled back, and the transaction is left to the post-processing cron, which serves as a fallback and retry mechanism. Since the cron is now triggered whenever the synchronous post-processing fails, its interval is increased from 10 minutes to 24 hours. Satellite flows that confirm transactions without going through the processing (e.g., manual confirmation, refunds already executed on the provider side) now post-process immediately through the new `_try_post_process` helper method, and the asynchronous post-processing triggers scattered across provider and app modules are removed. The bus notification signaling that a transaction is processed is now sent only after post-processing is completed (succeeded or failed), and carries all the status values needed to redirect the customer. This, in turn, removes the synchronous trigger of the post-processing through a JSONRPC call to the `/payment/post_process` route, further reducing overhead in the payment flow. To avoid missing any notifications, they are now resent upon subscribing to their channel, allowing the redirect to rely on the websocket alone, with the timeout redirect kept only as a fallback. In addition, the retry window of the post-processing cron is reduced from 4 days to 24 hours. The longer window was meant to give slower providers time to send payment status updates, but processing such updates resets the transaction's `last_state_change` field anyway, so it was never necessary to keep retrying the post-processing for that long. If a transaction fails to be post-processed throughout the retry window, it is left for manual intervention. The cron is also modernized to use the new cron API to report its progress and to stop early when its time budget is exhausted. task-6240311 See also: - https://github.com/odoo/enterprise/pull/124474 - https://github.com/odoo/upgrade/pull/10846
VoIP calling is now more reliable across browsers, desk apps, and native SIP phones, with better matching of call records and fewer duplicate or missing calls. The update also adds phone provisioning by QR code, number request handling, improved user routing, and a recovery payload so phone service tenants can be rebuilt if accidentally deleted.
Original PR description
## voip: call correlation, number requests, user routing and tenant recovery Twenty commits on top of master. This is the client side of the phone_service work in odoo/iap-apps#1854 — the two land…
## voip: call correlation, number requests, user routing and tenant recovery Twenty commits on top of master. This is the client side of the phone_service work in odoo/iap-apps#1854 — the two land together, since the routing call and the user sync now carry new fields the service expects. ### Call records and events - **correlate on the conversation, not on a dual-purpose handle** — `control_handle` held the PBX conversation identifier for the Odoo provider and the SIP Call-ID for every other one, the browser collapsing the two into one value whose nature the server could no longer tell. The INVITE now sends `conversation_identifier` apart from `sip_call_id`; `get_or_create` matches the conversation first and the Call-ID through the legs after. The column stays — it still names a call for the softphone and the push payload — but nothing searches it. - **one live call per user and conversation** — a call is one user's point of view on one conversation, so a second ringing device joins it instead of opening a second record. Every event is processed in its own transaction and under REPEATABLE READ neither sees the other's insert, so find-or-create could not hold that on its own. - **give a call placed from a native SIP phone its own record** — a call dialled from Linphone left no trace: only the browser ever created a `voip.call`, and there is no browser. - **never settle a caller number from a reclassified leg** — Wazo swaps `caller_id_number` and `peer_caller_id_number` when it reclassifies a callee leg as internal on hangup, so the reversal that reads a ringing leg correctly reads a reclassified one backwards. - **log the whole webhook payload in debug**, behind an `isEnabledFor` check so `json.dumps` never runs on a normal server, **accept open-ended WAV data chunks** (both `0x7fffffff` and `0xffffffff`, which streamed audio uses and the decoder rejected), and **use UTC in astimezone**. ### User routing and preferences - **let users choose between being forwarded and being woken** — an explicit "On Disconnected" setting filling Wazo's chanunavail slot, taken the moment a dial finds no reachable SIP contact. It is the same decision as the wake-up token: with nowhere to forward, the PBX wakes the browser with a push; with a destination, it hands the call over at once. Settling that token is now entirely phone_service's business (odoo/iap-apps#1854), so voip stops generating, storing and sending a PBX credential — `voip_pbx_auth_password`, its reconcile cron and the `auth` object all go. What stays here is the user's own choice. - **forward disconnected calls to the phone by default** — mirroring the "no answer" fallback: the "On Disconnected" slot is seeded from the user's own number when it is valid, never replacing an explicit choice. The three forward slots also merge into a single "Forward" section. - **improve phone number flows** — regular users can request a number from the VoIP status menu, through a wizard and a mail template; purchase feedback, DID flags and destination selection are polished; and a SIP failure raised when a user cancels an outgoing call before its INVITE completes is no longer surfaced as an error, while genuine INVITE errors still are. - **use a busy hangup as the default busy fallback** — Hangup with the Busy cause and a 5s timeout when nothing else is configured. - **allow users to limit concurrent calls** — a "Ring Concurrent Calls" preference synchronized with the PBX, for queue and call group members who would otherwise ring several times at once. - **synchronize personal mailboxes with Wazo** — mailboxes created during user provisioning inherited the context that disables PBX sync and never reached Wazo. - **restrict voip.extension range** to 3 or 4 digits (100-9999), resolving a disagreement with the range phone_service configures on the tenant. ### Registration and provisioning - **one registration binding per softphone window** — Asterisk identifies a binding by its Contact URI alone (`pjsip_uri_cmp`, object named after the URI's MD5); neither `+sip.instance` nor `reg-id` is consulted, so every window of a user advertised the same binding. - **provision Linphone from a QR code** — a `linphone-config:` URI pointing at a signed URL serving a remote provisioning file. Linphone scans a provisioning URI, never credentials. - **let the Odoo provider's settings be edited** (the lock was view-only and cost nothing but keystrokes), and **drop the icon from the phone numbers Refresh button**. ### Number requests - **introduce number requests** — a search that returns no inventory left an administrator with no way to obtain numbers that need manual sourcing. A persistent DID number request, backed by the advanced order API of odoo/iap-apps#1854, carries a UUID for idempotent cross-service correlation, reuses the regulatory requirement groups, and syncs status, proposed numbers and comments into a provider-neutral form. Fulfilled numbers join the existing DID assignment flow and stay linked to their request. Creating or reviewing a request charges nothing; charges apply once the carrier fulfils it. ### Tenant recovery - **expose a tenant recovery payload** — a versioned description of the complete desired tenant configuration, served over a public route that only accepts a short-lived token signed with the client secret and scoped to recovery, so a Wazo tenant deleted by accident can be rebuilt.
The website AI can now create and configure web forms using the same guided setup as the standard website builder instead of hand-written form code. This helps prevent lost submissions, broken email routing, and form setup errors, making AI-generated website forms more reliable for business users.
Original PR description
The website builder agent could only touch forms by writing raw HTML, which produces forms that look right but lose submissions: field names that are not whitelisted degrade to plain text in the…
The website builder agent could only touch forms by writing raw HTML, which produces forms that look right but lose submissions: field names that are not whitelisted degrade to plain text in the record, a `send_mail` form missing its hidden `email_to` field gets no `website_form_signature` at render so every submission raises AccessDenied, and missing model-required fields fail at submit time. Teach the agent webforms as structured operations instead of markup: - "Get Webform Info" lists the available form actions and, per action, the model fields a form field can be bound to (from `get_compatible_form_models` / `get_authorized_fields`). - "Edit Webform" applies a validated list of operations (set_action, add_field, update_field, remove_field, set_success, set_recipient_email) through a new `website_edit_form` client tool. Every operation delegates to the community form option plugin (its builder actions and shared methods), so the resulting markup is exactly what the builder sidebar would produce and the save-time machinery (field whitelisting, default `email_to`, label/name sync) keeps working. The tool returns a per-operation report and the resulting form HTML, following the HTML apply report pattern. - "Apply HTML to Page" now refuses hand-written webform markup (form fields outside a form, webforms without a form-enabled `data-model_name`, unnamed inputs) and points the agent to the webform tool. Canonical form snippets and existing forms carried along in a zone replace pass untouched. - The Website Builder skill documents the workflow: insert a form snippet unchanged, then configure it with the webform tools. task-6251785
The accounting dashboard now guides users more directly through e-invoicing setup and company data completion for sales journals. Companies can also mark themselves as not subject to VAT, with Belgian setups automatically using the right no-VAT tax behavior to reduce manual configuration and mistakes.
Original PR description
This PR introduces following changes: - Improves Account onboarding dashboard for Sales type Journals. It adds two actions for onboarding: "Activate Peppol" and "Set Company Data". - Introduces a new boolean "Not Subject to VAT" on Company. Which means that the company doesn't intend to apply VAT on sales / invoices. - For BE: If this boolean is checked, it disables all the active Sales Taxes and sets a new "0% NA" sale tax as a default tax for company. It also hides the tax column on move line for invoices and adds the default 0% NA tax. task-6264468 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The sales order screen is now easier to use on phones, with a cleaner product search drawer, compact action menus, and mobile-friendly order lines. Customers can configure combo products, discounts, shipping, coupons, and rewards more reliably from small screens, reducing friction for mobile sales workflows.
Original PR description
Mobile UX pass on the Sales quotation/order screen, including a fix for combo products being effectively unconfigurable on mobile: the product catalog search panel and the order line actions (combo…
Mobile UX pass on the Sales quotation/order screen, including a fix for combo products being
effectively unconfigurable on mobile: the product catalog search panel and the order line actions
(combo configurator, manual discount, shipping, loyalty coupon/reward) were designed desktop-first and
didn't adapt well to small screens.
Current behavior before PR:
- Configuring a combo product was not possible at all from mobile: tapping a combo line opened the
generic line form (product_id as a plain many2one_barcode field) instead of the combo configurator, so
there was no way to pick combo items on a small screen.
- The product catalog search panel used the generic responsive web.SearchPanel layout on mobile, which
is cramped and hard to use on a small screen.
- Order lines (mobile kanban) exposed raw inline controls ("Add Line", "Add Section", "Add Note",
"Catalog") directly in the list footer.
- Shipping / discount / coupon / reward were desktop-sized buttons with no mobile-specific layout,
competing for space on small screens.
Desired behavior after PR is merged:
- Tapping a combo line on mobile now opens the combo configurator dialog to pick items, same as the
desktop flow.
- The product catalog search panel opens as a dedicated mobile drawer (slide-in panel with backdrop)
instead of the generic responsive layout.
- Order lines mobile kanban drops the inline create controls in favor of a single "Add" button that
opens the product catalog.
- Combo selection and manual discount are exposed as compact dropdown widgets (combo_menu /
discount_menu) on mobile.
- Shipping / discount / coupon / reward actions are split into a compact mobile variant and the
existing desktop variant (d-none d-md-block).
- Combo lines can be deleted directly from the combo configurator dialog on mobile.
task-5270010
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prExpense users now approve expenses instead of posting them, and approval creates draft accounting entries for accountants to review and post. This clarifies responsibilities between HR expense management and accounting, while adding easier access to expense-related journal entries and attachment import options.
Original PR description
modules: `hr_expense, sale_expense, project_sale_expense` Removing `accounting` functional flow from the `expense` module. - The `hr_expense` user used to `post` expenses, now they can only `approve`…
modules: `hr_expense, sale_expense, project_sale_expense`
Removing `accounting` functional flow from the `expense` module.
- The `hr_expense` user used to `post` expenses, now they can only `approve` them.
- Approving an expense will create `draft` moves.
- `accountant` users will post the moves instead.
- An `employee-paid` expense linked to a `draft` move will be considered as `approved`
- When the move gets `posted`, the expense will be set to `posted` too.
- A `company-paid` expense linked to a move, it will be considered `paid`, regardless of the `move.state`
- Removing the `hr.expense.post.wizard` for `employee-paid` expenses, the accounting date default is now `today`
- the `sale_expense` option to import attachments is now on the `sale.order` form view, as a button that opens a wizard to select what attachments the user wants to import from linked expenses
- `journal_id` on `hr.expense` is now computed, as we lost information for `employee-paid` expenses by removing the wizard
- Add a filter in the `account.move` list view to display entries linked to expense
- Add a link from journal dashboard to all the draft moves linked to expenses
Enterprise PR: https://github.com/odoo/enterprise/pull/126246
Upgrade PR: https://github.com/odoo/upgrade/pull/10950
Task [link](https://www.odoo.com/odoo/project.task/6327097)
task-6327097French companies using the normal fiscal regime are now directed to an ASPOne-hosted portal to submit their tax package. Companies on the simplified regime can choose between the existing Odoo submission flow and the ASPOne portal, with a guided setup shown when an ASPOne account is needed.
Original PR description
As part of the management of the "liasse fiscale" in France, we handled the submission of the simplified "liasse fiscale", which is directly integrated into Odoo. This commit makes that for companies with normal (as not simplified) fiscal regime, users are redirected to a white-label portal hosted by ASPOne. And it allows users from simplified fiscal regime companies to choose between both options. If there is no account for this db/company combo a wizard is displayed to get the needed info to create an account on ASPOne. Task: 6460125 Task: 6060727 IAP PR: https://github.com/odoo/iap-apps/pull/1887
Resolved issues and error corrections
This fix prevents Saudi electronic invoices from being reset to draft while they are being submitted to ZATCA. It helps avoid accidental duplicate submissions in production, reducing the risk of taxpayer compliance issues while still allowing test workflows in sandbox and simulation modes.
Original PR description
Bulk sending is queued: the batch wizard stamps sending_data on the moves and lets the send cron do the work. Before this commit: Reset to Draft stayed available while the moves were being sent, so…
Bulk sending is queued: the batch wizard stamps sending_data on the moves and lets the send cron do the work. Before this commit: Reset to Draft stayed available while the moves were being sent, so the user could reset an invoice mid-submission. The invoice then ended up draft with an accepted submission and could be sent to ZATCA a second time, which ZATCA treats as a taxpayer compliance issue. After this commit: The records are locked on both sides, the way l10n_jo_edi does. Reading the state instead would not help, as a transaction only sees the snapshot taken when it started, in which the move is still posted. 1. When submitting, the moves that cannot be locked are skipped, as another transaction is already working on them. 2. When resetting to draft, the reset is refused if the records cannot be locked, otherwise it applies once the submission it races with committed. Reset to Draft stays available for invoices accepted in Sandbox and Simulation, as deleting or resubmitting test invoices is the point of those modes. Production remains covered by the l10n_sa_edi_is_production check. Steps to reproduce: 1. Install l10n_sa_edi and onboard a sales journal in Sandbox mode. 2. Create and post two invoices. 3. Select them, Action > Send & Print. 4. Select them again, Action > Reset to Draft. task-6267293 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284107
Code cleanup and technical improvements
Marketing Automation has been redesigned to make campaign creation easier for non-technical users, with a visual workflow editor, clearer activity types, drag-and-drop organization, and more ways to enroll participants. The update also adds AI-assisted campaign creation so users can describe a campaign in plain language and get help building it.
Original PR description
As Odoo grows, we're always looking for ways to make our apps even more accessible and user-friendly. Over time, we've noticed that our current Marketing Automation app requires a bit of a technical…
As Odoo grows, we're always looking for ways to make our apps even more accessible and user-friendly. Over time, we've noticed that our current Marketing Automation app requires a bit of a technical background to get the most out of it. While the engine under the hood is super powerful, the sheer number of options can feel a bit overwhelming for everyday users who just want to get a campaign running quickly. To bridge this gap and open up the app to everyone, we're giving it a fresh, modern makeover! The goal of this task is to shift toward a fully visual experience. This PR introduces a brand-new custom graph view that makes campaign building much more intuitive. Instead of managing complex configurations through standard forms, users can now see their entire marketing flow mapped out clearly. Building a campaign becomes super straightforward: you can add, remove nodes on the fly to design your workflow visually. Instead of throwing all options into a single, heavy form, we've split the configuration into specific, easy-to-understand node types (like Delay, Filter, and Interaction). This keeps the interface clean, clutter-free, and way easier to use. ENT: https://github.com/odoo/odoo/pull/258112 Task-3866422