Thursday, September 10, 2026
25 changes · 19.0
Resolved issues and error corrections
Empty or interrupted file uploads could previously cause the image picker and chatter areas to crash when loading media. This fix lets those screens handle incomplete attachments gracefully, keeping users working without an error message.
Original PR description
Problem: Interrupted uploads or malformed email payloads can create 0-byte binary `ir.attachment` records where the `checksum` is `False`. Accessing the computed `image_src` field on these records…
Problem:
Interrupted uploads or malformed email payloads can create 0-byte binary `ir.attachment` records where the `checksum` is `False`. Accessing the computed `image_src` field on these records triggers a `TypeError: 'bool' object is not subscriptable` when attempting to slice `attachment.checksum[:8]`. This crashes the Media Dialog and Chatter.
Purpose:
Add a fallback boolean guard to `attachment.checksum` inside `_compute_image_src` so that empty attachments evaluate safely to a string ('0') instead of raising a traceback, allowing the UI to render gracefully.
Steps to Reproduce on Runbot:
1. Go to Settings > Technical > Database Structure > Attachments.
2. Create a new record: Name: `test.png`, Type: `File` (Binary), File Content: [leave empty], Is public document: Checked.
3. Open any record with a Chatter (e.g., Contact or CRM Lead) and click "Insert Image" to open the Media Dialog.
4. The system attempts to evaluate `image_src` and throws the `TypeError`.
Notes:
A test (`test_compute_image_src_empty_checksum`) was added to `test_ir_attachment.py`
opw-6482674
Forward-Port-Of: odoo/odoo#287444Downloaded signed PDF documents now correctly align multi-line Arabic text entered in right-to-left text areas. This improves the accuracy and readability of generated signature documents for Arabic-language users.
Original PR description
Issue: ---------------------------------------- Arabic text in a rigth-to-left text area is not aligned in the downloaded PDF documents. Steps to reproduce: ---------------------------------------- -…
Issue: ---------------------------------------- Arabic text in a rigth-to-left text area is not aligned in the downloaded PDF documents. Steps to reproduce: ---------------------------------------- - Create a template with a rigth-to-left textarea., Make it fillable by the user. - Click "Sign Now" - Enter multiline arabic text - Validate and download PDF - The lines aren't aligned in the PDF Cause: ---------------------------------------- We compute the empty space before the line with `stringWidth()` on the line but this method doesn't do text-shaping on arabic text. We then do the text-shaping using `reshape_text()` and draw the line. the text shpaing "merged" some caracters making `stringWidth()` return a higher number than the actual drawned line. Solution: ---------------------------------------- Call `reshape_text()` before `stringWidth()`. Before: <img width="937" height="247" alt="image" src="https://github.com/user-attachments/assets/8a43a677-fd29-4d8e-a1cd-419b358667b9" /> After: <img width="896" height="213" alt="image" src="https://github.com/user-attachments/assets/cf8d0ebe-e363-4e07-9eb5-e4963e5ca137" /> opw-6438643x Forward-Port-Of: odoo/enterprise#130672
This fixes Malaysia payroll calculations so SOCSO and Employment Insurance contributions are applied correctly and not counted twice. Payslip net pay and employer contribution displays now better match official rates and external payroll references, reducing payroll discrepancies.
Original PR description
There was a difference between the actual and expected NET. **Causes** - rules existed for both SOCSO Act 800 and EIS, but Act 800 seems to be the official name of the Employment Insurance System…
There was a difference between the actual and expected NET. **Causes** - rules existed for both SOCSO Act 800 and EIS, but Act 800 seems to be the official name of the Employment Insurance System (source: https://www.perkeso.gov.my/images/dokumen/Rate_of_Contribution_ACT_800.pdf). Keep the ACT 800 one as its matches the expected amount. - there was a double counting of the SOCSO employee contributions (4 and 800) , as `l10n_my_rule_socso_employee` (the sum of both) was added to the total deductions. **Change** Before: 3500/month wage results in a 3108 NET. <img width="1181" height="533" alt="before" src="https://github.com/user-attachments/assets/cdf4420f-e2cf-4dc7-9488-6bde3f1f9961" /> After: 3500/month wage results in a 3090.85 NET: - 6.90 SOCSO Act 800 Employee - 6.90 SOCSO Act 800 Employer - 17.25 SOCSO Act 4 Employee - 60.35 SOCSO Act 4 Employer Which seems consistent with online sources (https://payroll.my/) <img width="1181" height="425" alt="after" src="https://github.com/user-attachments/assets/7fb41186-dbb4-4d7a-ac9a-14fb56efc4bc" /> Other fix: while not affecting the calculation, 'SOCSO Employer Share' appeared as incorrect, the two rules SOCSO_800_EMPLR and SOCSO_4_EMPLR should have the same sign. opw-5976362 Forward-Port-Of: odoo/enterprise#129688 Forward-Port-Of: odoo/enterprise#118138
The Chilean electronic invoicing process now keeps reporting progress while sending large invoice backlogs to the tax authority. This prevents the scheduled job from being disabled after timeouts, so high-volume customers can continue clearing pending invoices over multiple runs.
Original PR description
Steps to reproduce: - Have a large number of invoices (thousands) with l10n_cl_dte_status 'not_sent' - Let the "Cron Job - Send document to SII" cron run and time out before it can send them all -…
Steps to reproduce: - Have a large number of invoices (thousands) with l10n_cl_dte_status 'not_sent' - Let the "Cron Job - Send document to SII" cron run and time out before it can send them all - After a few consecutive timeouts, the cron gets deactivated by Odoo, leaving the backlog stuck and growing Cause of the issue: cron_send_dte_to_sii() searches for every 'not_sent' move and sends them one by one in a single unbounded loop, committing after each send but never reporting progress to the cron framework Odoo cron worker treats a job that times out without ever calling _notify_progress as a full failed run, even though most of the batch was actually sent and committed. After enough consecutive failures within a short time span, the cron is auto-deactivated, which is exactly what happens once daily invoice volume outpaces what a single cron run can send before the worker times out Solution: Report progress via ir.cron._notify_progress() after each invoice is sent. A timeout mid-run is then treated as partially done instead of failed, the cron gets rescheduled immediately instead of waiting for its daily interval, and it no longer counts towards deactivation, This lets the cron drain an arbitrarily large backlog safely over several runs instead of dying after a handful of timeouts opw-6487236 opw-6511624 Forward-Port-Of: odoo/enterprise#129789
SEPA direct debit mandates are now considered used as soon as a collection is initiated, even if the bank reconciliation has not happened yet. This prevents active mandates from being closed too early for companies that reconcile bank statements later.
Original PR description
_get_expiry_date_per_mandate() only looked at account.payment records in state 'paid' to find a mandate's last usage. Per SEPA, the 36-month clock resets on the last *initiated* collection, not on…
_get_expiry_date_per_mandate() only looked at account.payment records in state 'paid' to find a mandate's last usage. Per SEPA, the 36-month clock resets on the last *initiated* collection, not on its later bank reconciliation. Any company that doesn't reconcile bank statements promptly saw its payments stuck in 'in_process' forever, so Odoo saw no usage at all and auto-closed still-active mandates. Now also count payments in the 'in_process' state, not just 'paid'. A SEPA direct debit that bounces at the bank by insufficient funds or closed account or any cause doesn't automatically flip the payment to rejected in Odoo. The only way Odoo learns about it is by reconciliation. In that case the fix odoo would treat the failed collection as valid usage and keep extending the mandate's life which isn't strictly correct since the mandate is dead and this isn't caused by the fix, Odoo had zero visibility into that failure either way, before the fix it just wrongly closed the mandate regardless opw-6509401
This update refreshes Odoo’s spreadsheet engine with several fixes that make spreadsheet work more reliable. It improves search and replace handling, chart display and export behavior, copy and paste with merged cells, and small-screen grid behavior, helping users avoid errors and inconsistent results.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/e499258724 [REL] 19.0.50 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/e499258724 [REL] 19.0.50 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/cde861fc7a [FIX] search and replace: manage invalid range [Task: 6483222](https://www.odoo.com/odoo/2328/tasks/6483222) https://github.com/odoo/o-spreadsheet/commit/c11573c495 [FIX] xlsx: fix geo chart xlsx export [Task: 4632983](https://www.odoo.com/odoo/2328/tasks/4632983) https://github.com/odoo/o-spreadsheet/commit/23ec0effd9 [FIX] grid: hide AddRowFooter when the mainViewport is too small [Task: 6103620](https://www.odoo.com/odoo/2328/tasks/6103620) https://github.com/odoo/o-spreadsheet/commit/2a7dd7b0ce [FIX] clipboard: typo on copy/paste on a merge [Task: 6515850](https://www.odoo.com/odoo/2328/tasks/6515850) https://github.com/odoo/o-spreadsheet/commit/8ebd955a5b [FIX] charts: show value do not work for combo chart [Task: 6528059](https://www.odoo.com/odoo/2328/tasks/6528059) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
This fixes a copy-and-paste issue in the HTML editor where list items with nested lists could paste with missing outer list structure or extra unselected nested content. Users can now copy selected list content more accurately, preserving the intended formatting.
Original PR description
Problem: Copying content from a list item containing a nested list can either include the unselected nested list or lose part of the copied content. Solution: - Detect whether the whole `<li>` was selected before copying the full list item with its nested lists. - When only part of the list item is selected, copy only the selected content and rebuild the required `<li>` wrapper. - Apply this logic only to list items with multiple top-level children. Steps to reproduce: - Add bullet list with nested list. - CTRL+A - Press Enter twice to create new paragraph. - Paste. - Observe the outer list is lost. task-6438347 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Point of Sale closing reports now correctly include cash in/out movements for POS administrators who do not have accounting access. This prevents sessions from showing a false cash difference after the register was closed with the correct amount.
Original PR description
Steps to reproduce: - Give a user PoS Administrator rights and no Accounting rights. - As that user, open a session with cash control, sell something paid in cash and do a Cash Out. - Close the…
Steps to reproduce: - Give a user PoS Administrator rights and no Accounting rights. - As that user, open a session with cash control, sell something paid in cash and do a Cash Out. - Close the register. The closing popup shows the expected cash (opening + cash payments - cash out); enter exactly that amount, the popup shows no difference. - Open the closed session: its chatter says "Closing difference: -X", X being the cash out amount, and "Closing expected" is the amount before the cash out. Issue: The closing control data agrees with the user, but the closing message records a cash difference equal to the cash out. Cause: `_compute_cash_balance` sums `statement_line_ids` with the rights of the current user. Cash moves are `account.bank.statement.line` records, which `_inherits` `account.move`, so the account.move record rules apply to them too. For a PoS user the only such rule is `rule_invoice_pos_user` (`pos_order_ids != False`), and a cash move has no PoS order: unless the user is in `account.group_account_invoice`, their own cash moves are filtered out and `cash_register_balance_end` ignores them, while `get_closing_control_data` sums the lines in sudo. Since da8be203ec32 PoS managers can create cash moves without any accounting group, which made this visible: in 18.0 cash in/out required `account.group_account_invoice`, whose `account_move_see_all` rule makes every move readable. The values stored at closing are right by accident: `_validate_session` reads the lines in sudo just before, and the one2many cache is shared between the sudo and non-sudo environments of the same transaction. The message posted by `update_closing_control_state_session` runs in its own transaction and gets the filtered sum. Fix: Read the cash lines in sudo in `_compute_cash_balance`, as `get_closing_control_data`, `get_cash_in_out_list` and `_validate_session` already do. Users with accounting rights see all lines already, so nothing changes for them. opw-6521591 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix keeps Mexican electronic payment documents aligned with the official stored exchange rate when the recalculated rate is within an acceptable rounding range. It prevents invoices and related payments issued on the same date from showing slightly different currency rates, reducing CFDI discrepancies and potential validation issues.
Original PR description
Issue: Currency rate for payment is recomputed up to 6 digit. However, it may differ from the official one stored in db up to 4 digits. Which creates differencies between invoice and payment rate…
Issue: Currency rate for payment is recomputed up to 6 digit. However, it may differ from the official one stored in db up to 4 digits. Which creates differencies between invoice and payment rate made at the same date. Steps to reproduce: - In MX company, - Enable USD, - Set currency rate for today to 1 USD = 17.4455 MXN - Create a PPD invoice (due date > 40 days) in USD - Add a line with - qty: 1, - unit_price: 3.488 - tax: 16% (default tax) - Send it to CFDI - Create payment - On the invoice Form click on "Update Payment" Current behavior: - In the CFDI sheet, Payment and Invoice XML files will have different currency rates Expected behavior: - In the CFDI sheet, Payment and Invoice XML files will have the same currency rate Cause: PACs require having the payment `amount` to be equal to `currency_amount * currency_rate`. For huge amout it may happen that using the 6 digits rounding of currency rate to compute the amount won't fall exactly on the two digit precision for the amount and payment would be refused. Therefore, for all payment, we recompute a 6 digits precision `currency_rate` from `amount` and `currency_amount` then using it to compute the final amount. However, Banxico (Mexican central Bank) publish rates with a 4 digit precision. Recomputing the currency rate up to 6 digits may slightly change it from the 4 digit precision official currency rate. opw-6411530 Forward-Port-Of: odoo/enterprise#129700
This fix prevents products from appearing twice in the Point of Sale product list after viewing paid orders. It also ensures product names stay consistent across devices, so receipts and invoices no longer include unwanted internal reference codes.
Original PR description
Since 4d4c1ee9293f, `get_ticket_screen_order_data()` sends the orderlines' `product.product` records along with the paid orders. Two side effects of reloading products that are already in the client…
Since 4d4c1ee9293f, `get_ticket_screen_order_data()` sends the orderlines' `product.product` records along with the paid orders. Two side effects of reloading products that are already in the client store show up on the product screen and on the orderlines. 1. Duplicate cards for templates with variants Steps to reproduce: - A product template with a variant attribute (e.g. "Side": Bread/Rice) - Sell one of its variants and pay the order - In a new session, open Orders, select the "Paid" filter and go back to the product screen Issue: The template is displayed twice in the product list: one card per variant loaded from the paid order. Every template with variants that appears in the fetched orders is duplicated the same way. Cause: The product screen shows one card per template because `processProductAttributesByProducts()` marks every variant but one as `available_in_pos = false` on the client-side records. `loadData()` rebuilds an already-loaded record from the raw payload, so the server's `available_in_pos = true` overwrites the client-side flag and the variant reappears as its own card. The ticket screen was the only product-loading path not re-running the grouping afterwards; the barcode scan and "Search more" paths already do. Fix: Re-run `processProductAttributesByProducts()` on the products returned by `get_ticket_screen_order_data()` in `_fetchSyncedOrders()`, as the other loading paths do, so the reloaded variants are collapsed again. 2. Internal reference in the product name on the other devices Steps to reproduce: - Two devices (or two cashiers) on the same PoS session - A product with an internal reference, sold in a paid order - Device A: open Orders, select the "Paid" filter - Device B: add that product to a new order Issue: On device B the orderline is named "[REF] Product" instead of "Product", and that name is stored in `full_product_name`, so it also ends up on the receipt and on the invoice line. The name flips back to "Product" as soon as the record is reloaded through a regular loader. Cause: `callRelated()` dispatches every static-model record of a payload to `pos.config.notify_synchronisation()`, which reads those records with a plain `read()` and broadcasts them to the other devices of the session, where `loadData()` overwrites the existing records. Unlike every PoS product loader (`_load_product_with_domain()`, `find_product_by_barcode()`, the data service `read`/`search_read`), this read is done without `display_default_code=False`, so a product reaches the other devices with its reference in `display_name`. The gap was latent since the device synchronisation (0c8f0d730465): before 4d4c1ee9293f no product record went through that path. Fix: Read the synchronised records with `display_default_code=False` in `notify_synchronisation()`, as the PoS loaders do. opw-6543906 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287219
This fixes an issue where shortened, already-approved time off could be treated as worked time in payroll. The system now updates the linked calendar entry instead of deleting it, helping ensure final payslips and attendance calculations remain accurate.
Original PR description
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the…
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the leave), the linked resource.calendar.leaves record was unconditionally unlinked. To reproduce: 1. Create and validate a time off request covering a whole month. 2. Register the employee's departure with a departure date in the middle of that time off. 3. Generate the employee's last payslip. The leave is correctly cut at the departure date, but since it never leaves the `validate` state, it never goes through `_validate_leave_request()` again, so its resource.calendar.leaves record is never recreated. The days that were covered by the deleted entry are no longer blocked in the employee's resource calendar, so the payslip's worked day lines (computed from resource.calendar.leaves) count them as attendance instead of time off. Cause ----- `hr.leave.write()` removed the resource.calendar.leaves record any time either the state changed away from `validate` or the leave's dates changed, regardless of whether the leave remained validated. Date-only changes on an already-validated leave never re-trigger validation, so the entry was not recreated. Solution -------- Only remove the resource.calendar.leaves record when the leave actually loses its validated state. When a validated leave's dates change but it stays validated, amend the existing resource.calendar.leaves record in place instead, falling back to creating one if none exists. Related PR: odoo#249527 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286465
The WhatsApp Disconnect option is now only shown when it can safely be used on accounts set up through onboarding. This prevents manually configured accounts from appearing connected while incoming messages have actually stopped, and avoids access errors for regular internal users opening the account form.
Original PR description
`show_disconnect` was true for any account holding a token, so the Disconnect button showed on manually configured accounts as well. Pressing it unsubscribes the webhook on Meta first, then clears the token, and that write is rejected on a manual account since the credentials are required there. Odoo rolls back, Meta does not, so the account keeps looking configured while incoming messages stop. The compute also read `token`, restricted to WhatsApp administrators, while every internal user has read access on `whatsapp.account`, so opening the account form as a regular user raised an AccessError. The button is now shown only for administrators on onboarded accounts, and `button_disconnect` checks write access, since hiding a button does not stop an RPC call and the request to Meta happens before the write that would have been refused. Task-6544628
When an external attendee replies to an appointment invitation, their response is now shared with all followers added to the appointment. This helps teams stay informed and prevents important customer replies from only reaching the main organizer.
Original PR description
Steps to Reproduce - Install the website_appointment module. - Configure both incoming and outgoing mail servers. - Go to the Appointments application and create an appointment. - Add a partner as an…
Steps to Reproduce - Install the website_appointment module. - Configure both incoming and outgoing mail servers. - Go to the Appointments application and create an appointment. - Add a partner as an extra follower to the appointment. - Publish the appointment. - Open the published appointment from the website. - As an external user, book an appointment and provide an email address. - Confirm the appointment. - An invitation email is sent to the external user. - Open the external user's mailbox and locate the invitation email. - Reply to the invitation email. Observed Behavior - The reply email is not sent to the partner who was added as an extra follower of the appointment Cause: - The invitation mail's subtype is set as 'note'. So the reply will only be received to main user Solution: - Pass 'mail.mt_comment' as the message subtype via context when notifying attendees for the booking confirmation, so replies are treated as followers' comments and routed to all followers instead of being restricted to a note. opw- 6457299 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
Signed document creation could fail on Ubuntu 22.04 because of a compatibility issue with the PDF library version available there. This update uses a compatible PDF operation so signed document overlays generate reliably across supported environments.
Original PR description
On Ubuntu Jammy (22.04 LTS), PyPDF2 v1.26 does not support the snake_case `add_transformation` method, causing an `AttributeError` when generating signed document overlays. But this issue was shadowed by the patching of `PageObject` in tests, creating the method even if not present. Switch to `addTransformation`, which is supported natively in PyPDF2 1.x and shimmed via `odoo.tools.pdf._pypdf` for newer `pypdf` versions. runbot-946875
This fixes an issue where completing packaged stock transfers could assign the right total quantity to the wrong delivery lines. Businesses using packages and multi-step deliveries should see shipments close correctly without unnecessary backorder prompts or stuck reserved transfers.
Original PR description
### Steps to Reproduce: 1. Make sure that the Sale and Inventory modules are installed 2. Enable Multi-Step Routes and Packages in Inventory Configurations 3. Warehouse configuration > Outgoing…
### Steps to Reproduce: 1. Make sure that the Sale and Inventory modules are installed 2. Enable Multi-Step Routes and Packages in Inventory Configurations 3. Warehouse configuration > Outgoing shipments > Select Pick then Deliver (2 steps) 4. Routes > Deliver in 2 steps (pick + ship) > Pull From > Destination Location > Select WH/Output 5. Routes > Deliver in 2 steps (pick + ship) > Push To > Action > Change to Pull From 6. Operation Types > Delivery Orders > Packages > Enable Move Entire Packages 7. Go to any product, ex. Drawer > On Hand > Set original on hand qty to 16 and new lot to 50 8. Create a new SO and make 2 lines, with the same product, and change the second line's price to something else, ex. 80.0 9. Deliveries > WH/PICK/00001 > Set quantity to 4 > Put in Pack > Validate and Create Backorder 10. WH/PICK/00002 > Put in Pack > Validate 11. WH/OUT/00012 > Mark PACK0000001 Done > Save. Observe how the first line quantity is changed from 3 to 4 12. Mark PACK0000002 Done > Save > Validate > Observe how it's asking for a backorder even though we already packed all 5 items. ### Description of the issue/feature this PR addresses: Instead of using the `product_qty` from the stock move, use the quantity of the move line to correctly allocate the quantities in StockPackageLevel ### Current behavior before PR: In the Shop Floor when loading packages, marking the package level as done causes issues on the quantity processed on the corresponding move lines. On stock transfers, we currently allocate the Quantity Done to the wrong product line. The total quantity is correct, but the distribution across lines does not match the Demand values. This causes the transfer to remain stuck in Reserved, even though the shipment was already processed operationally. ### Desired behavior after PR is merged: The correct quantity from the move line is used and this resolves the issue with quantity distribution not matching move line quantities when using packages. opw-6040640 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278992 Forward-Port-Of: odoo/odoo#242487
Spreadsheet dashboards now wait for all map chart data to finish loading before they are considered ready. This prevents dashboards from appearing incomplete or showing incorrect behavior when geographic charts are involved.
Original PR description
### [FIX] spreadsheet: wait for spreadsheet data We introduced a new helper `waitForSpreadsheetDataLoaded` that waits for the geo chart's jsons to be loaded. We should call it in the `waitForDataLoaded` helper. Task: [4632983](https://www.odoo.com/web#id=4632983&cids=1&menu_id=4720&action=333&active_id=2328&model=project.task&view_type=form) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Point of Sale now avoids charging a product variant's extra price twice when the item is selected through barcode search or similar flows. This ensures customers are charged the correct price for configurable products and reduces cashier pricing errors.
Original PR description
Steps to reproduce: - Create an attribute "Size" with values M and L, variants creation mode "Dynamically", and an extra price of 10 on L - Create a product at 30 using that attribute, and give the L…
Steps to reproduce:
- Create an attribute "Size" with values M and L, variants creation mode "Dynamically", and an extra price of 10 on L
- Create a product at 30 using that attribute, and give the L variant a barcode
- In the PoS, type that barcode in the search bar and click the card
Issue:
The line is added at 50 instead of 40. Scanning the barcode with a barcode reader was fixed by c1ae3261e895, but resolving the variant from the search bar still charges the extra price twice.
Cause:
The lst_price of a variant already contains the extra price of every attribute value that creates a variant ('always' and 'dynamic'); only 'no_variant' extras are missing from it and have to be carried by the order line as price_extra. This is what ProductConfiguratorPopup does, hence the correct price when the configurator opens.
Both openConfigurator(), when the resolved variant leaves a single value per attribute line and no popup is needed, and handleConfigurableProduct(), when configure is false, filter those values with create_variant !== 'always', so a 'dynamic' extra is added on top of a lst_price that already includes it. The same fix was made in 18.0 by d4fabfa08d1c but was lost when pos_store.js was refactored in saas-18.1.
Fix:
Filter on create_variant === 'no_variant' in both places, like the configurator popup. This supersedes the !opts.code condition of c1ae3261e895: product_template_variant_value_ids never holds 'no_variant' values, so the extra is now ignored however the line was added - scan, barcode search, sale order import or optional product.
opw-6531114
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThe Point of Sale customer list now avoids loading each row's action menu until it is actually needed. This reduces freezes when stores have hundreds of customers, especially on slower devices, while keeping the same look and behavior for users.
Original PR description
Opening the customer list with a few hundred partners freezes the UI for several seconds on a slow device (~300ms on a desktop, 5.6s with a 6x CPU throttle in the Chrome profiler). Each `PartnerLine`…
Opening the customer list with a few hundred partners freezes the UI for several seconds on a slow device (~300ms on a desktop, 5.6s with a 6x CPU throttle in the Chrome profiler). Each `PartnerLine` instantiates a full `Dropdown` for its "≡" menu. Setting up a `Dropdown` registers about ten lifecycle hooks (`useDropdownNesting`, `useNavigation`, `useDropdownGroup`, `usePopover`, `useEffect`, ...), and each hook registration eagerly allocates two `OwlError` objects to keep a stack trace. Multiplied by hundreds of rows, this dominates the render: ~60% of the click task is spent constructing errors for menus that will never be opened. Render a plain button per row instead and only mount the `Dropdown`, driven by a `useDropdownState`, once that button is clicked. The `Dropdown` opens its popover on mount when its state is already open and is unmounted again when it closes. The placeholder button keeps the classes the `Dropdown` would add to its toggler so the DOM, the styling and the tour selectors (`button.dropdown`) are unchanged. opw-6453848 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
This fixes the Orders screen in Point of Sale on small screens so users can see and choose all available search options, such as date or customer. It prevents mobile cashiers from being limited to the default search field and adds coverage to catch this visual issue in the future.
Original PR description
Steps to reproduce: - Open a PoS session on a small screen (mobile app or mobile browser) - Register at least one order, so the order list is not empty - Go to the Orders screen and type a term in…
Steps to reproduce: - Open a PoS session on a small screen (mobile app or mobile browser) - Register at least one order, so the order list is not empty - Go to the Orders screen and type a term in the search bar Issue: On a desktop the search bar drops down the list of fields to search on (Reference, Receipt Number, Invoice Number, Date, Customer). On a small screen that list never shows up, so the search silently falls back to the first field and there is no way to search by Date or by Customer. Cause: The list is rendered, but painted behind the order list. Under the media-breakpoint-down(sm) block of ticket_screen.scss the order list becomes `position: sticky; z-index: 1`, so a sibling rule raised `.search .fields` to `z-index: 2` to keep the dropdown on top. The `z-1` utility class put on that dropdown in 07f743843830 compiles to `z-index: 1 !important` and overrides the rule. Both elements end up at `z-index: 1` in the same stacking context, and the order list wins the paint order because it comes later in the DOM. Fix: Drop the `z-1` utility and declare `z-index: 2` on `.fields` in the search bar's own stylesheet, which makes the small-screen override in ticket_screen.scss redundant. The stacking of the dropdown now lives in a single place, next to the rest of its styling, so a utility class added to that element cannot silently disable it again. A tour clicks its target element directly and so cannot see a purely visual overlap, which is why the existing MobileTestUi runs of TicketScreen.search() never caught this. The added assertion checks that a suggestion is the topmost element at its own center. opw-6540466 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Customers using an embedded Odoo Livechat widget on an external website can now download files shared in chat without browser blocking errors. This improves the reliability of file sharing in livechat conversations while avoiding broader changes to file access permissions.
Original PR description
**Steps to reproduce:** - Install `im_livechat` module - Copy the code from the livechat channel's widget tab - Paste it in an external website `<head>` (e.g., local python webserver on `0.0.0.0`) -…
**Steps to reproduce:**
- Install `im_livechat` module
- Copy the code from the livechat channel's widget tab
- Paste it in an external website `<head>` (e.g., local python webserver on `0.0.0.0`)
- Start a conversation and send a file from odoo
- Try to download it from the external website
- POST request is sent for the download
- Download fails: `CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.`
- Also when the file is a PDF the preview won't open: `404 (File not found)`
**Issue:**
Mix of multiple issues:
- The download is triggered using a POST request instead of a GET request
(see similar issue for the file viewer [1])
- The file route does not provide the required CORS headers, so requests
originating from the external website are blocked by the browser
- PDF files are rooted to the related path of the `pdfjs` fileviewer
(e.g. `http://0.0.0.0:8000/web/static/lib/pdfjs/web/viewer.html?file=...`)
```xml
<!--
Template rendering all the scripts required to execute the Livechat from an external page (which not contain Odoo)
-->
<template id="external_loader" name="Livechat : external_script field of livechat channel">
<!-- the loader -->
<script defer="defer" t-attf-src="{{url}}/im_livechat/loader/{{channel_id}}" type="text/javascript"/>
<!-- js of all the required lib (internal and external) -->
<script defer="defer" t-attf-src="{{url}}/im_livechat/assets_embed.js" type="text/javascript" />
</template>
```
**Fix:**
Make the `downloadFile` helper handle cross-origin URLs by falling back to a native `<a download>` click (like before) when the target route has the same origin as the embedded script. Could use `session.origin` or `new URL(document.currentScript.src).origin` for this. This is done to avoid allowing CORS on the file content route.
The file viewer issue is handled separately by [1].
For the PDF issue we could manually add the origin to the full url everywhere (but we get some `SecurityError` error from the library due to the cross-origin iframe), add the libjs library in the `assets_embed` (not sure it's possible in `im_livechat.assets_embed_external`), or block external PDF preview for now.
[1] https://github.com/odoo/odoo/pull/281330
opw-6444167This fixes an issue where Time Off requests could be saved with a zero-day duration when an automation rule created an activity. Businesses using automated activity notifications can now rely on accurate leave durations and avoid incorrect absence records.
Original PR description
Problem: A time off request is saved with a duration of 0 days instead of 1 day as soon as an automation rule on Time Off has an action that creates an activity. Cause: `_compute_field_value` runs…
Problem: A time off request is saved with a duration of 0 days instead of 1 day as soon as an automation rule on Time Off has an action that creates an activity. Cause: `_compute_field_value` runs the actions while the fields of the computation it wraps are still protected and not written yet. On `hr.leave` the actions run from a computation nested in `_compute_date_from_to`, so `date_from` is empty when the activity notification reads `display_name`, and `_compute_duration` stores 0. Setting `date_from` afterwards does not mark `number_of_days` to compute again since it is protected. Solution: Extract the snapshot `_filter_pre` already does into `_keep_to_compute` and wrap the post filter and the actions with it, so the fields depending on the running computation stay to compute. Same treatment as 488419a5ca49 (odoo/odoo#243611) on the pre filter. Steps to reproduce: - Enable the developer mode. - Go to Settings > Technical > Automation Rules and create a rule on the Time Off model with the trigger On create and edit. - Add an action of type Create Activity, set its Responsible to another user, and save. - Go to Time Off > New, pick an employee and a time off type, and request one working day. - Observe that the request shows a duration of 0 days. Ticket [link](https://www.odoo.com/odoo/project.task/6498783) opw-6498783
Fixed an issue where invoice forms using document extraction could crash when a user clicked into certain code editor fields. This improves reliability for accounting workflows, especially in localizations that include code fields on invoices.
Original PR description
Follow-up of odoo/odoo#287085, as suggested by @aab-odoo: on a stable branch the fix belongs here rather than in the ace templates. Fixes odoo/odoo#286871. opw-6543997 ### The problem The `focusin`…
Follow-up of odoo/odoo#287085, as suggested by @aab-odoo: on a stable branch
the fix belongs here rather than in the ace templates.
Fixes odoo/odoo#286871.
opw-6543997
### The problem
The `focusin` listener of the extract mixin walks up from the focused node with
`closest(".o_field_widget,.o_field_cell")` and assumes whatever it finds
identifies a field. Not every `.o_field_widget` node does: a widget template
may render its own `.o_field_widget` inside the one the `Field` wrapper already
provides, and that inner div has no `name`. `web.AceField` (`widget="code"`)
does exactly that:
```html
<div name="my_field" class="o_field_widget o_field_code ...">
<div class="o_field_widget oe_form_field o_ace_view_editor oe_ace_open">
<div class="ace_editor">... <textarea class="ace_text-input">
```
The listener stops on the inner div, `getFullFieldName` finds no name and
builds `"<parent_field>.null"`. Since the name now contains a dot, `getBoxType`
takes the x2many branch:
```js
modelFieldType = this.props.record.data[parentField]?._config.fields[fieldName]?.type;
```
On a code field `record.data[parentField]` is a plain string. It is truthy, so
the optional chaining does not short-circuit, but it has no `_config` →
`undefined.fields` → `TypeError: Cannot read properties of undefined (reading
'fields')`, and the whole form breaks.
`account_invoice_extract` installs the renderer for `account_move_form` with
`force: true`, so this runs on every invoice form.
### Steps to reproduce
1. Install `account_invoice_extract`.
2. Put a `widget="code"` field on the `account.move` form view (with
`l10n_ar_edi` installed there are already two on the ARCA tab).
3. Give the field a value — with an empty value the error does not happen,
`record.data[parentField]` is `false` and the optional chaining cuts.
4. Enable developer mode and click inside the code editor.
Reproduced on a plain 19.0 runbot build.
### The fix
Restrict the selector to `.o_field_widget[name]`, so nodes that cannot identify
a field are skipped. The `focusout` listener a few lines below also matches
`.o_field_widget`, but it only calls `onBlurFieldWidget()` and never resolves a
name, so it is left as is.
The duplicated class in the ace templates looks like the actual root cause and
is handled separately in odoo/odoo#287085, retargeted to `master`.The POS customer list now calculates each customer's outstanding due amount more efficiently, avoiding repeated work for every row. This improves loading speed when many customers are available and also helps ensure dues are matched to the correct customer record.
Original PR description
Opening the customer list with a few hundred partners is slow; part of the time is spent in `getPartnerCredit`. The `PartnerLine` template reads `this.partnerInfos` eight times per row, and each call runs `getPartnerCredit` again. For a contact, that method resolves the partner carrying the due by scanning every loaded partner for one named like `parent_name`, so the cost grows with the square of the number of partners. Cache the getter in a template variable so it is evaluated once per row, and resolve the due partner through the loaded `commercial_partner_id` instead of the name scan. This is also what the server sums the dues by, and it no longer picks a wrong homonym. `refreshTotalDueOfPartner` used the same lookup and now shares it. opw-6453848
Argentina export invoices now send the correct recipient identification information in the QR validation data. This allows invoices to be verified successfully on the ARCA website and shown as valid legal documents.
Original PR description
Before this change we were sending id type code 0 and this generate two problems * ARCA verification page it was wrongly taking "CI Policia Federal" as the identification type of the receptor * We were not able to validate the expo invoice, we get always an error With this change the expo invoice can be checked as a real legal document in the ARCA page https://servicioscf.afip.gob.ar/publico/comprobantes/cae.aspx Forward-Port-Of: odoo/enterprise#126704
This change prevents users from being blocked by an access error when saving records that include restricted calculated fields. It ensures Odoo can complete the background calculation safely during record creation, improving reliability for affected workflows such as sales orders with margin-related customizations.
Original PR description
**Description of the issue/feature this PR addresses:** If a field is `computed`, `precomputed`, and has `groups` defined, an `AccessError` is raised when a user who does not have the required group…
**Description of the issue/feature this PR addresses:** If a field is `computed`, `precomputed`, and has `groups` defined, an `AccessError` is raised when a user who does not have the required group creates a record because the field is precomputed. After this commit https://github.com/odoo/odoo/pull/201565/changes/48521a311a6dc857c3808db70ed359c7866aa125, Odoo checks field access in `__get__`, so an `AccessError` is now raised. If the field is not precomputed, everything works fine. Therefore, this commit prevents the error by using `sudo()` to recompute the value. I have attached a module to demonstrate the issue. **Steps to reproduce the issue:** 1. Install the attached module. [sale_margin_security_test.zip](https://github.com/user-attachments/files/31967357/sale_margin_security_test.zip) 2. Create a sales order and add a product. 3. Try to save the sales order. The AccessError is raised. https://github.com/user-attachments/assets/54c7e4e2-005f-4ced-bb62-22d0e00049d9 For more context, this module is a simple example extracted from the OCA `sale_margin_security` module, which inherits from a mixin and adds groups to the fields: https://github.com/OCA/margin-analysis/pull/285 You can see the error in this PR: https://github.com/OCA/margin-analysis/actions/runs/34254749207/job/102157600233?pr=285#step:8:126 @Tecnativa @pedrobaeza @kmagusiak @rco-odoo @Feyensv, could you please review this? --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr