Tuesday, September 1, 2026
36 changes · 19.0
Security fixes and vulnerability patches
Portal access checks now require access tokens to match exactly, preventing partial or unintended matches. This helps ensure shared portal links and protected records are only accessed with the correct token value.
Original PR description
Access tokens can only be matched by exact value. Accept `in` and `not in` operators. Task-6481193 Forward-Port-Of: odoo/odoo#285388 Forward-Port-Of: odoo/odoo#285218
Enhancements to existing features
Indian POS orders now decide whether to create an invoice based on the customer's GST treatment instead of whether the contact is marked as a company. This helps ensure GST-registered business customers receive mandatory tax invoices, while consumer and unregistered customers are treated as retail sales.
Original PR description
### **PURPOSE** - Currently, we enable Invoice checked based on 'is_company' (i.e Contact Type) - However, in India, for businesses registered under GST, It is mandatory to issue a Tax Invoice. ### **SPECIFICATION** - Invoice generation in POS must be determined based on GST Treatment Type - If the contact’s GST Treatment Type is not equal to “Consumer,” not equal to “Unregistered,” and not empty. Treat it as B2B and make the invoice boolean True. - For all other cases than this, treat it as B2C and make invoice boolean False task-4894111
Resolved issues and error corrections
Users who can view but not edit a sales order can now register invoice payments without hitting an access error. The system still logs the payment note on the order, while preserving restrictions that prevent those users from changing sales order details.
Original PR description
Description of the issue/feature this PR addresses: Registering a payment for a confirmed sale order must not require write access on the order. Today the automatic "Invoice %s paid" chatter…
Description of the issue/feature this PR addresses:
Registering a payment for a confirmed sale order must not require write access on the order. Today the automatic "Invoice %s paid" chatter notification is posted through message_post, which checks the caller's write permission via the model's _mail_post_access attribute [[1](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/mail/models/models.py#L66-L83)]. A user with read but not write access on the order (e.g. through fine-grained record rules) therefore gets an AccessError while registering the payment, even though the payment is actually registered.
sale.order is the only affected model that never defines _mail_post_access, while account.move [[2](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/account/models/account_move.py#L78)], hr.employee [[4](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/hr/models/hr_employee.py#L41)], hr.leave, project.task [[5](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/project/models/project_task.py#L102)], discuss.channel and several website models all allow posting chatter with read access only. Reported upstream in odoo/odoo#54081 (2020, closed "Need information", never fixed).
Current behavior before PR:
Registering a payment on an invoice linked to a read-but-not-writable sale order raises an AccessError. It is a side effect of _invoice_paid_hook() calling order.message_post(body=_("Invoice %s paid", name)) without sudo: message creation re-checks access on the linked document, and sale.order's missing _mail_post_access falls back to the mail.thread default 'write'. The error surfaces as "Message / create" on mail.message, masking the real cause.
Desired behavior after PR is merged:
Define _mail_post_access = 'read' on SaleOrder. The paid-invoice audit message is then logged with read access only, and registering the payment succeeds. Read-only users still cannot modify sale order fields — they can only add chatter messages and followers, the same trade-off Odoo already accepts for every other affected model. The change is minimal (one attribute) and appropriate for a stable series: no data model, method signature or XML changes.
Tests:
Added test_invoice_payment_on_read_only_sale_order to addons/sale/tests/test_access_rights.py. It blocks write access on the sale order via a record rule with perm_write, then asserts has_access('read') is True and has_access('write') is False, registers a payment as the restricted user without raising AccessError, and checks the "Invoice ... paid" message is present in the order's chatter.
Related:
- Closed odoo/odoo#54081.
- The _mail_post_access attribute was introduced by commit [[3](https://github.com/odoo/odoo/commit/e81708ba35046f272f6a48c7d88edbd59fbf0f8c)]; the read-access pattern is already used by account.move [[2](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/account/models/account_move.py#L78)], hr.employee [[4](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/hr/models/hr_employee.py#L41)] and project.task [[5](https://github.com/odoo/odoo/blob/620c1e6510cee407b9962823a622b8405f7420ea/addons/project/models/project_task.py#L102)].
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis change reuses an existing rental product rule instead of defining a separate one in the rental stock area. It makes the behavior easier for other Odoo extensions to customize while keeping the current rental stock calculations unchanged.
Original PR description
There is already an existing hook on product.product ([sale_renting.models.product_product.ProductProduct._get_qty_in_rent_domain](https://github.com/acsone/enterprise/blob/2cd105c93bb7199a5ecea67a919a7ab5d9251cbf/sale_renting/models/product_product.py#L24)), use it to get the domain, so it can be inherited by other modules.
While the upstream method uses `('product_id', 'in', self.ids)` instead of this one's `('product_id', '=', self.id)`, this isn't an issue since there's a `self.ensure_one()` just above.Invoice journal item lines now hide zero debit and credit amounts, making the accounting details easier to read. This reduces visual clutter for users reviewing invoice entries without changing the underlying accounting data.
Original PR description
Mutes the 0.0 credit and debit amounts for journal items page in the invoices form view
The invoices list now includes an "In Payment" filter so users can quickly find posted invoices where payment has been started but not fully completed. This helps finance teams distinguish completed payments from invoices still awaiting final payment confirmation.
Original PR description
Created a new payment filter "In Payment" in invoices list view, to filter posted and in_payment invoices.
Cancelled invoices now hide the due date because no payment is expected after cancellation. This reduces confusion for users reviewing invoice details and keeps the invoice view focused on relevant information.
Original PR description
Make due date invisible for cancelled invoices task from the onboarding.
When appointment staff update only the capacity, the system now automatically chooses suitable resources using the same logic as online bookings. Manual resource choices are still respected when users update resources directly, helping reduce scheduling effort without removing control.
Original PR description
If the user only selects a new capacity, and not new resources it is best to set the resources with logic similar to what is used when an attendee would want to book from the website front-end. If both resources and capacity are updated (or only resources) then the behavior remains the same as before, so users are still able to manually assign resources. task-6185278
This fix prevents slow simulated mobile taps from being mistaken for long presses during automated tests. It improves reliability for mobile web test runs, reducing random failures when saving rich HTML content under heavy load.
Original PR description
Before this commit, MobileWebSuite.test_unit_mobile failed at random on the mail test "HtmlMail add icon and save inline html", which taps the save button of an html_mail field and waits for the…
Before this commit, MobileWebSuite.test_unit_mobile failed at random on the mail test "HtmlMail add icon and save inline html", which taps the save button of an html_mail field and waits for the write:
[verifySteps] expected the following steps
> Expected: [
"web_save",
]
> Received: []
This happens because a tap held longer than `LONG_TAP_DELAY` counts as a long press, and `_pointerUp` then dispatches no click at all. That delay spans the synthetic pointerdown and pointerup of a single `click()`, so the work a listener does during the press counts towards that delay: tapping save blurs the html field, which converts the whole editor content to inline styles, and under the load of a runbot build that conversion alone takes longer than the delay.
One solution could have been to raise `LONG_TAP_DELAY`, but a listener slower than the new value suppresses the click again.
This commit counts a tap as long only when the test releases the pointer itself, with `pointerUp`, so the work a listener does during a `click()` no longer suppresses the click.
https://runbot.odoo.com/odoo/error/946581
Forward-Port-Of: odoo/odoo#285640
Forward-Port-Of: odoo/odoo#284971This fix updates a website test tour step to prevent occasional random failures during automated checks. It helps keep validation runs stable without changing the website experience for users.
Original PR description
This is just a backport of the step from 19.3 where the tour is never failing randomly. https://github.com/odoo/odoo/commit/c5d7a6a90be4730e18070826a9394ab10dfc6be8#diff-c7720501ec33f5f92c907d8bb41de50edd832a4564317073e801a6915796a6bdR260-R261 runbot-237566
The link popover no longer shows an inactive wand icon when editing a linked image. This removes a confusing control that did not work, making the image link editing experience clearer for users.
Original PR description
**Current behavior before PR:** Steps to reproduce the issue: - Add an image - Add a link to the image - Put cursor just right after the image link so that link popover is opened - Notice that there is a wand icon in link popover to replace title, clicking on it does nothing **Desired behavior after PR:** There should be no replace title icon in popover for image-link. task-6420902 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix stops Odoo live chat embeds from blocking common browser keyboard shortcuts when no Odoo shortcut hints are available. Users on websites with embedded live chat can keep using shortcuts like focusing the address bar, while Odoo shortcut overlays still work where they exist.
Original PR description
Description of the issue/feature this PR addresses: When `im_livechat` (or any page) boots the OWL web stack with no `[data-hotkey]` / `withOverlay` targets, pressing Alt still called…
Description of the issue/feature this PR addresses: When `im_livechat` (or any page) boots the OWL web stack with no `[data-hotkey]` / `withOverlay` targets, pressing Alt still called `preventDefault()` in `hotkey_service`. That blocked browser shortcuts such as Alt+D (focus address bar) on websites that embed the livechat scripts. See https://github.com/odoo/odoo/issues/267928 Current behavior before PR: - Pressing the overlay modifier (Alt, or Ctrl on macOS mapped to `"alt"`) always set `overlaysVisible = true` and called `preventDefault()`, even when there were no hotkeys to overlay. - A follow-up key (e.g. D while Alt is held) then also hit `preventDefault()` because overlays were considered visible. - Loading `/im_livechat/loader/...` + `assets_embed.js` on an external site therefore broke browser Alt shortcuts. Desired behavior after PR is merged: - `addHotkeyOverlays` returns whether overlays were actually displayed. - `preventDefault` on the overlay modifier runs only when there is at least one overlay target. - Pages with no hotkeys (typical livechat embed) leave browser shortcuts alone. - When hotkeys exist, Alt still shows overlays and cancels the default, same as before. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Closing an AI live chat conversation no longer triggers an unexpected chat window to appear. This improves the website visitor experience by making the chat close action behave as expected and avoiding confusing follow-up messages.
Original PR description
Closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. -…
Closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. - Click on edit and choose `Contact & Forms`. - Add the AI Livechat website snippet. - From the snippet options, add an AI Agent and choose a livechat team. Make sure that Mitchell Admin is configured as an operator for that livechat team (livechat channel). - Click on save. - Open an incognito tab. Log in as Marc Demo. - Go to Website. - Type a message inside the `ASK AI` text area and press enter. - Wait until you receive a response and then click close. - A chat window will popup with the messages of the conversation with the AI along with a message saying `Visitor has left the channel`. This happens because `close` button will call `closeConversation` => `livechatService.leave()` => `visitor_leave_session` => `_close_livechat_session` that posts a message that the visitor has left the channel. This commit solves the issue by setting the user who left the channel as the author of the `visitor left chat` message instead of OdooBot.
The point of sale now prevents users from selecting a quotation that has already been settled. This reduces the risk of duplicate settlement attempts and helps keep sales records accurate.
Original PR description
Once a quotation is settled, opening the quotation list should not allow selecting it again. This commits updates the domain to prevent it. task-6479356
This fixes internal website builder tests so they continue to pass when newer Chrome versions report background image sizing slightly differently. It helps keep automated quality checks reliable without changing how users experience the website builder.
Original PR description
In Chrome 152, single-value `background-size` properties may be serialized or expanded to include implicit dimensions (e.g., appending `auto` like `100px auto`), causing strict exact-string test assertions to fail. This commit updates `website` builder test expectations to use regex prefix matching or substring inclusion so tests remain reliable across different Chrome versions. runbot-946570
The Expenses app now uses the correct “approved” state name in its interface. This prevents confusion for employees and managers reviewing expense reports and keeps wording consistent with the actual approval workflow.
Original PR description
Use the correct state name (approved) @Tecnativa --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Opening the WhatsApp Business account page could fail when users switched Odoo to another language, such as French. The fix makes the page rely on a language-neutral identifier so it opens reliably regardless of translation.
Original PR description
Currently, an error occurs when a user tries to view the WhatsApp Business account. **Steps to Reproduce:** - Install the `whatsapp_oauth` module. - Go to `Settings` > `Languages` and add `French…
Currently, an error occurs when a user tries to view the WhatsApp Business account. **Steps to Reproduce:** - Install the `whatsapp_oauth` module. - Go to `Settings` > `Languages` and add `French (BE)`, then `switch to it`. - Go to `WhatsApp` > `Configuration` > `WhatsApp Business Accounts (Comptes Whatsapp Business)`. `ValueError: L'élément '<xpath expr="//div[contains(normalize-space(.), 'Receiving Messages')]">' ne peut être localisé dans la vue parente` When the user changes the language, the text in the view is translated [1]. Since the WhatsApp account view tries to locate the div using the plain text Receiving Messages [2]. Since the text has been translated in the parent view, the XPath can no longer locate the element and raise the error. This commit ensures that the XPath uses the name attribute to identify the element, which is language-independent. We cannot use the class attribute because the same class is used by other div elements. [1]- https://github.com/odoo/enterprise/blob/a3db72899cb97612505aebbd5450aeb65205db73/whatsapp/views/whatsapp_account_views.xml#L81-L84 [2]- https://github.com/odoo/enterprise/blob/a3db72899cb97612505aebbd5450aeb65205db73/whatsapp_oauth/views/whatsapp_account_views.xml#L61-L63 7534862409
This fix prevents one timed-out mail test from causing many unrelated tests to fail afterward. It makes test results clearer and more reliable for developers, reducing noise during quality checks without changing customer-facing behavior.
Original PR description
Before this commit, one test contains() reaching its 10 seconds budget made the 129 tests that ran after it fail with its own assertion, over 14 suites:
[HOOT] Test "@mail/message/link_preview/Delete all link previews at
once" failed:
Failed assertion:
3. [toBe] expected values to be strictly equal (Failed to find 1 of ".o-mail-Message-body:text(...)" (Timeout of 87 seconds). Found 0 instead.)
This happens because the tick re-schedules itself on the result of runOnce(), which is undefined on a failure, the crashing one included. The check keeps selecting every 500ms and logs its assertion against whichever test is running, until a tick lands between two suites and getFixture() throws.
This commit re-schedules a tick only while the check is not done, so the crashing one is the last.
https://runbot.odoo.com/odoo/error/946650
Forward-Port-Of: odoo/odoo#285585The web test runner now records its final memory information before reporting the test result. This avoids misleading extra failure alerts in failed builds, helping teams focus on the real test issue.
Original PR description
Before this commit, a build whose unit tests suite fails collects a second runbot error on top of the real one: odoo.addons.web.tests.test_js.WebSuite.test_unit_desktop.browser: Error received after…
Before this commit, a build whose unit tests suite fails collects a second runbot error on top of the real one:
odoo.addons.web.tests.test_js.WebSuite.test_unit_desktop.browser:
Error received after termination: [MEMINFO] tests done (after GC)
- used: 220535896 - total: 341990868 - limit: 4395630592
Note that the figures themselves are fine (220MB used of a 4.4GB limit): the error is not about memory, only about when the line is logged.
This happens because the runner logs that line after `stop()`, and `stop()` is what reports the suite result: the server settles the test on that report and kills the browser. The final major GC outlasts the report. A passing build escapes because the browser dies within milliseconds; a failing one stays up for the screenshot, long enough for the GC to finish and the line to land after termination.
This commit fixes the issue by moving `stop()` after the final cleanups and their memory log: the [MEMINFO] line is still logged, but before the result report, while the server still waits on the browser.
https://runbot.odoo.com/odoo/error/229655
Forward-Port-Of: odoo/odoo#284957The web test suite now stops promptly when required test dependencies cannot be fetched. This avoids long, silent build timeouts and helps teams identify infrastructure or loading problems faster.
Original PR description
Before this commit, a unit test suite could go silent between two test files, and `browser_js` then waited out its whole timeout before failing with:
[ERROR] TypeError: Failed to fetch
at orm (web.assets_unit_tests.min.js)
FAIL: WebSuite.test_unit_desktop
AssertionError: Script timeout exceeded
This happens because `fetchDependencies` gives every addon a `Deferred` that only the success handler of the batched `all_dependencies` call resolves, and a loaded runbot host answers that call with `RuntimeError: can't start new thread`. Nothing then settles those deferreds, so the `Promise.all` waiting on them never returns, `stop()` is never called, and the browser holds the build until the timeout.
This commit rejects the deferreds of a failed batch and reports an error escaping `runTests` on the console, so the build fails on the fetch error instead of running out its timeout.
https://runbot.odoo.com/odoo/error/243504
Forward-Port-Of: odoo/odoo#284980This fix prevents restaurant point-of-sale orders from being finalized without their product lines when multiple devices are working on the same table. It ensures reloaded order items are kept correctly, preserving accurate receipts, totals, and sales records.
Original PR description
Steps to reproduce: - Restaurant config with two POS devices on the same session - Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids) -…
Steps to reproduce:
- Restaurant config with two POS devices on the same session
- Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids)
- Device A: remove both lines, without syncing
- Device B: touch the same order, so device A re-reads it from the server through the synchronisation websocket
- Device A: pay and validate the order
Issue:
The order is saved as paid, with its total and its payment, but without any orderline. The lines are deleted on the server: odoo.models.unlink: deleted pos.order.line records with IDs: [...]
Cause:
Removing an orderline queues an unlink command in
models.commands['pos.order'].unlink['lines_<order id>'] (delete_ in related_models.js). That command is only discarded by clearCommands(), which syncAllOrders() calls after a successful sync, so it stays pending in between.
Any read of the order in that window puts the line back: a deleted record counts as missing in missingRecursive(), so it is fetched again and loadData() re-creates it with its server id.
serialize() then emits both the update of the live line and the still pending unlink, and sync_from_ui writes
lines: [[1, id, {...}], [3, id]]. The ORM applies commands in order, and pos.order.line.order_id is ondelete='cascade', so Command.UNLINK deletes the line right after writing it.
Fix:
Skip a removal command when the record is still linked to the parent at serialization time. A record cannot be both linked and removed in the same payload, so the pending command is stale and dropping it keeps the local state. Genuine removals, where the record is no longer linked, are still sent.
opw-6401146
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285427
Forward-Port-Of: odoo/odoo#284695This change corrects how HR contract salary and payroll version information is updated in the employee HR context. It helps prevent incorrect version data from being saved, reducing the risk of payroll or contract salary inconsistencies for Belgian HR processes.
Original PR description
task-6521357
Users can now correct sales order line descriptions while an order remains open, even after the line has been delivered or invoiced. This keeps product changes protected on processed lines while removing confusing behavior where editability depended on the visible columns.
Original PR description
Descriptions on order lines become impossible to change after the line is delivered or invoiced, even though the order is still open. Interestingly, hiding the product column makes the description editable again. This shows that editing descriptions is already technically allowed, but currently depends on the column layout, which is confusing for users. Keep descriptions editable until the order is locked or cancelled. This lets users correct text without allowing changes to products on processed lines. Desired behavior after PR is merged: After a sale order is confirmed, users can modify descriptions while the order remains unlocked, regardless of the column layout. Products on processed lines remain protected. @moduon MT-15454 opw-6432243 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixed an issue where reconciliation models linked to bank journals in another currency could be hidden when users opened Manage Models from a bank statement line. This helps accounting users find and manage the right matching rules without confusion or manual workarounds.
Original PR description
### Issue before this commit: When clicking "Manage Models" from a bank statement line, the associated reconciliation models are not displayed when belonging to a foreign currency journal. ### Steps…
### Issue before this commit: When clicking "Manage Models" from a bank statement line, the associated reconciliation models are not displayed when belonging to a foreign currency journal. ### Steps to reproduce the issue: 1. Download Accounting 2. Go to Currencies and activate another currency like EUR 3. Go to Journals and create a new one with bank type and curency EUR 4. Go to dashboard > new test bank journal created > 3 dots in the upper-right corner > Models > create a new one (ex Tester) setting the new test bank created as journal 5. Go to test bank journal and create a new bank matching 6. After the line is created click the 3 dots and go to Manage Models 7. See that the new model Tester created does not appear ### Cause of the issue: Foreign currency journals append their currency to the display_name (e.g., "Bank (EUR)"). The JS search framework passes this full decorated string into the search domain. The backend then attempts to match "Bank (EUR)" exactly in the database name and code fields, which fails because the database name is "Bank" without the currency added. https://github.com/odoo/odoo/blob/9c67949be529eb86886b3d5bde08e81e048ecfe7/addons/account/models/account_journal.py#L1095-L1100 ### Reason to introduce the fix: Perform an additional search using both the display_name and the clean name will fix the search functionality for foreign currency journals. opw-6481970 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes Polish electronic invoice reporting so certain 0% EU reverse charge taxes are identified correctly in KSeF XML. Businesses using Polish EDI invoices will now send the expected reverse charge indicator and taxable base, reducing reporting errors and compliance risk.
Original PR description
**PROBLEM** K_12 taxes needs to be reported as reverse charge tax. **STEP TO REPRODUCE** 1. Create an invoice with the tax 0% EU U. 2. Send the invoice the ksef. 3. Open the xml, an notice P_18 value is 2 (meaning no reverse charge), and there is no tag P_13_10. expected behavior: P_18 = 1, P_13_10 = base for the tax. opw-6460338 Forward-Port-Of: odoo/odoo#281934
Sales of kit products now correctly exclude deliveries completed after the selected accrual entry date when calculating quantities delivered by that date. This prevents orders from incorrectly remaining eligible for invoicing when the delivery happened outside the reporting period.
Original PR description
When selling a kit, the qty_delivered_at_date was not ignoring moves that were done after the accrual_entry_date. Steps to reproduce: ------------------- * Create a kit with any component and make it's invoice policy "Delivered quantities" * Create a sale order for this kit and confirm it, change the order date to any date in the past * Validate the picking * Go check the "Invoiced to be issued" * Change the accrual_entry_date to a date before the picking was validated > Observation: The order still appears opw-6290222
This fix makes horizontally scrolling event agendas smoother in Firefox and Safari, especially when there are many agenda items. It reduces visible stuttering so visitors can browse event schedules more comfortably.
Original PR description
On Firefox and Safari, horizontal scrolling on an agenda with many items stuttered. This was likely caused by triggering too many scroll events, without throttling them. Steps to reproduce: - On Firefox or Safari, run with demo data - Go to the "Design Fair Los Angeles" agenda (click on the event > Talks > Agenda. Alternatively, enter the URL directly: `/event/ID/agenda`, with the right event ID) - Resize the page (or open the dev tools) so that there is a horizontal scrollbar. - Scroll horizontally with a trackpad or with shift + mouse wheel. => The agenda stutters/shakes: as you go to the right, it sometimes slightly goes back left, and vice-versa. You might have to scroll from left to right and right to left a few times to see the issue. Forward-Port-Of: odoo/odoo#285237
Product variant prices now keep the same minimum decimal display settings as their product templates. This prevents prices such as 19.50 from appearing with too few decimals in reports, improving consistency and reducing confusion.
Original PR description
Issue: --- Record's `min_display_digits` is not inherited in case of a model inheritance. e.g. `list_price` is inherited to `product.product` from `product.template`, however it renders with fewer decimals than `product.template.list_price` in reports. Steps to reproduce: 1- Set a product's Sales Price to 19.5. 2- Add `product.product.list_price` to a report using studio. The variant's field renders with 1 decimal while `decimal.precision` is 2. Cause: --- This is missed in ee32a17495a10af18f0b5de0a295283c5059d3c2 which introduces minimum precision. opw-6465159 Forward-Port-Of: odoo/odoo#285336 Forward-Port-Of: odoo/odoo#285064
Bank reconciliation now shows the matched bill or invoice number even when currency exchange differences are involved. This helps accounting users verify reconciliations more easily and avoids confusion when matching foreign-currency payments.
Original PR description
### Issue before this commit: In the bank reconciliation widget, the matched bill or invoice name is missing from the UI when a currency exchange difference occurs (e.g., when the payment date…
### Issue before this commit: In the bank reconciliation widget, the matched bill or invoice name is missing from the UI when a currency exchange difference occurs (e.g., when the payment date differs from the bill's exchange rate date). ### Steps to reproduce the issue: 1. Download Accounting and hr_expense_stripe 2. Go to currencies > euro > activate it and create a new rate (ex. 1.3 USD) with a previous date (ex. yesterday) 3. Go to Vendor Bills and create a new bill with: 1. Bill date posterior to the currency rate creted 2. EUR as currency 4. Go to Dashboard > Stripe Issuing 5. Crete a new bank matching with date previous than the date of the exchange rate created and the amount of the bill just created with minus sign 6. Reconcile it selecting the corresponding bill 7. The number of the reconciliated bill is not displayed ### Cause of the issue: The field count_reconciled_lines_excluding_exchange_diff on account.move.line was incorrectly defined as a fields.Boolean instead of fields.Integer. As a result, the Python backend casts the line count to a boolean (true) before sending it to the frontend. The JavaScript widget performs a strict equality check (=== 1) on this value. Since true === 1 evaluates to false, the UI fails to fetch the correct move data and hides the origin bill's name. ### Reason to introduce the fix: Changing the field type to fields.Integer is not stable so it has been decided to change the js file that was using it. opw-6425896
The Kenya OSCU invoicing form now hides the validation message area when there is no message to show. This removes an unnecessary blank gap, making the form layout cleaner and easier to read.
Original PR description
When there is no validation message, an empty div causes a whitespace gap between the header and the sheet in the form view. This commit adds `invisible="not l10n_ke_validation_message"` to the div to prevent this issue. Forward-Port-Of: odoo/enterprise#129364 Forward-Port-Of: odoo/enterprise#129318
The Frontdesk app now explicitly includes the required planning view component so it can install correctly in single-app setups. This prevents installation failures in specific deployment modes and improves reliability for customers enabling Frontdesk on its own.
Original PR description
Installation of this module fails in single-app skip auto-install. Gantt is not a valid root element in views when web_gantt isn't installed. There is no error on other builds because of the auto-install dependency using the following chain; [frontdesk] ──[depends]──> [hr] ──[depends]──> [web] ──⚡[AUTOLOAD]──> [web_gantt] This change needs forward porting to v19. saas-19.1 already has a fix. REF Runbot; https://runbot.odoo.com/odoo/error/237890 Forward-Port-Of: odoo/enterprise#128592
Fixes an issue where Argentine online stores could show a lower tax-excluded price on the product page than on the shop listing when a discounted pricelist was used. Customers now see consistent pricing across the catalog and product detail pages, reducing confusion during shopping.
Original PR description
Steps to produce: --- - Create a company with `Argentina` as the country. - Switch to the newly created company. - Create a new website for the `Argentina company`. - Create a pricelist with a `23%`…
Steps to produce: --- - Create a company with `Argentina` as the country. - Switch to the newly created company. - Create a new website for the `Argentina company`. - Create a pricelist with a `23%` discount on the sales price for all products. - Create a new product, set the sale price to 100, remove all tax, and publish. - Go to the website and switch to the newly created Argentina company website. - Go to the Shop page and open the product. Issue: --- - The tax-excluded price (`Precio s/Imp. Nac.`) displayed on the shop catalog card differs from the tax-excluded price displayed on the product detail page. Root cause: --- - In `_get_additionnal_combination_info`, [1] returns the unit price with the pricelist discount already applied. However, when `combination_info['has_discounted_price']` [2] is `True`, the method applies the discount again manually, resulting in the pricelist discount being applied twice on the product detail page. Solution: --- - Remove the redundant discount calculation block. This ensures that the tax-excluded price displayed on the product detail page matches the price shown on the shop catalog card. [1]https://github.com/odoo/odoo/blob/7f9560bd0ff66882459593a2d043c0197ceec0bb/addons/l10n_ar_website_sale/models/product_template.py#L61-L62 [2]https://github.com/odoo/odoo/blob/7f9560bd0ff66882459593a2d043c0197ceec0bb/addons/l10n_ar_website_sale/models/product_template.py#L71-L74 opw-6480531 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283761
Accountants without company access-rights permissions can now generate BOE files for Spanish Modelo 115 tax reports without an access error. The change prevents the export wizard from unnecessarily trying to update company data, keeping the process aligned with normal accounting permissions.
Original PR description
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax…
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax reports, even though exporting a BOE file has nothing to do with editing company configuration and should be available to any accountant. Steps to reproduce: ------------------- * Log in as a user with accounting rights only (not part of the Companies > Access Rights group) * Go to Accounting > Reporting > Tax Return, open the Mod 115 report * Click the gear icon > BOE > Generate BOE > Observation: An AccessError is raised: "You are not allowed to modify 'Companies' (res.company) ... This operation is allowed for the following groups: Access Rights", even though the user is not trying to edit the company. Why the fix: ------------ The BOE wizard shared by Mod 111/115/303 declares a `company_partner_id` field, related to `company_id.partner_id`, with `readonly=False`. That field is invisible in every view and only exists to compute the domain of `partner_bank_id`; it was never meant to be edited by the user. Because it is declared writable, the ORM attaches an inverse to the related field, so saving the wizard (which happens when generating the BOE, since the field, though invisible, is still part of the view and thus of the saved values) writes `company.partner_id` back onto `res.company`, even though the value never actually changes. That implicit write requires write access on res.company, which is only granted to the Access Rights group, causing the error for regular accountants. Dropping `readonly=False` keeps the field as a plain readonly related field, still usable for the bank account domain, without ever triggering that spurious write. opw-6388869 Forward-Port-Of: odoo/enterprise#129841 Forward-Port-Of: odoo/enterprise#126661
This fix keeps expected quantities aligned between reordering rules and the forecast report when manufactured products move between warehouses. It prevents misleading negative forecasts that could cause businesses to reorder items unnecessarily or make poor stock planning decisions.
Original PR description
Currently, users encounter discrepancies between the reordering rule and forecast quantities when products are in inter-warehouse transit. ## Steps to produce: - Install Manufacturing and Sales -…
Currently, users encounter discrepancies between the reordering rule and forecast quantities when products are in inter-warehouse transit.
## Steps to produce:
- Install Manufacturing and Sales
- Enable Multi-Step Routes and MTO from settings.
- Create a warehouse with 3 steps manufacturing and Short Name: WH2
- Go to warehouse 1 and Enable `Resupply From: My Company - warehouse # 2`
- Routes > Open the resupply route > click WH2/Stock -> Inter-warehouse transit
- Set supply method to 'Take From Stock, if unavailable, Trigger Another Rule' and save.
- Create a tracked product 'Generic Chair' with both MTO and warehouse resupply route enabled.
- Create an empty Manufacturing BoM for the `Generic Chair`
- Create a manual reordering rule for chair with
- location: WH2/Stock
- Min:5 and Max: 10
- Create and confirm a sales order for chair with quantity 1.
- Open the related manufacturing change the quantity to 5 and confirm it.
- Open the forecast report for chair in warehouse 2 the forecasted quantity is 4
- Open the reordering rule and observe the forecasted quantity.
## Observed behavior:
The forecast report for the chair in the reordering rule shows -1, even though 5 units are expected from the manufacturing order. This is inconsistent with the forecast report and could cause errors when reordering the generic item.
## Root cause:
When the user changes the quantity in a Manufacturing Order (MO), the write method is called with values such as:
`[[2, 9], [0, 'virtual_14', {...}]]`
As a result, the existing `move_finished_ids` records are unlinked and recreated using only the field values that are present in the MO form view.
Since the Final Location field in `move_finished_ids` is not present in the MO form view, its value is not included when `move_finished_ids` is recreated.
Consequently, the create method of the stock.move model applies the default value at [1] and sets the final destination location to:
`WH2/Stock → Post Production`
This changes the `location_final_id` of the finished move to Post Production.
When the user opens the Forecast Report for the warehouse, `_get_report_data` searches for incoming and outgoing quantities across all child locations of the warehouse at [2]. Since Post Production is included in the warehouse's child locations, its quantities are correctly included in the forecast report.
However, reordering rules work differently. They calculate quantities only for the specific location configured on the orderpoint, which in this case is WH2/Stock
At [3], because `location_final_id` of `move_finished_ids` is set to Post Production, and Post Production is not a child location of WH2/Stock, the incoming quantity from this move is not included in the reordering rule calculation.
As a result, the forecasted quantity is calculated as -1 (outgoing) instead of 4, which leads to the reported issue.
[1]-
https://github.com/odoo/odoo/blob/5e84fdd99e34836a15cadc4fdf4b6bc449727e58/addons/mrp/models/stock_move.py#L278-L279
[2]-
https://github.com/odoo/odoo/blob/5e84fdd99e34836a15cadc4fdf4b6bc449727e58/addons/stock/report/stock_forecasted.py#L156-L168
[3]-
https://github.com/odoo/odoo/blob/c4de5361fb207916175332dcf6815c0814ecf09c/addons/stock/models/product.py#L437-L441
## Solution:
Keep the final location ID set during the initial quantity change if it already exists on the manufacturing order, since the order will ultimately end up in WH2/Stock as its final location in the chain. This ensures consistent forecast quantities between the reordering rule and the forecast report, while also keeping the final location consistent across the manufacturing order and its finished stock moves.
opw-6432985This update keeps Odoo compatible with newer operating system versions of its certificate library by passing certificates in the newer supported format. It prevents unnecessary warning messages in logs while preserving support for older library versions.
Original PR description
pyOpenSSL 24.3.0 deprecated passing its own X509/PKey objects to Context.use_certificate()/use_privatekey(), and started accepting cryptography objects instead. Odoo pins pyopenssl 24.1.0, but the distro builds run the version shipped by the OS: since the test added by f0fb287c6502 covers that path, they now add a warning in the logs. Load the certificate and the key as cryptography objects when the installed pyOpenSSL supports them, keep the previous loaders otherwise. Reference: https://github.com/pyca/pyopenssl/commit/b0cb4b4 This fix is based on https://github.com/odoo/odoo/blob/a2b4f618328f3ce3f654fd2c1ee4410365706a7e/odoo/addons/base/models/ir_mail_server.py#L34-L46 runbot-944176 Forward-Port-Of: odoo/odoo#284802 Forward-Port-Of: odoo/odoo#277449
This fix prevents a Helpdesk ticket list from crashing when users open it after navigating through an email alias. It ensures the system uses the correct Helpdesk Team information, so support teams can access tickets normally from the team page.
Original PR description
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe…
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe error ### Description of the issue/feature this PR addresses: **Issue:** When navigating from an email alias to its parent document (e.g., a Helpdesk Team), the web client incorrectly retains the `active_id` and `active_model` of the alias in the context. **Solution:** We updated the `action_view_ticket` method in `helpdesk.team` to explicitly inject the correct `active_model` and `active_id` into the context before calling `_for_xml_id`. This overwrites the polluted Alias data before the window action is evaluated. ### Current behavior before PR: Clicking the "Tickets" smart button passes the old alias context into the action. This bad data flows into the ticket view, which attempts to look up a Helpdesk Team using the Alias's ID to generate the empty list help message, resulting in a `MissingError`. ### Desired behavior after PR: The Python action sanitizes the context at the source, ensuring that the XML action and subsequent view evaluations receive the correct Helpdesk Team ID. Ultimately, the view will load normally without crashing. opw-6395638 Forward-Port-Of: odoo/enterprise#129669 Forward-Port-Of: odoo/enterprise#125232