Daily updates from Odoo
Tuesday, July 7, 2026
42 changes · saas-19.3
New functionality added to Odoo
Odoo now supports PayU as an additional payment provider. This gives businesses another way to accept online payments from customers, helping expand checkout options in supported regions.
Original PR description
Add new payment provider PayU. See README.md for more details. task-6219530 Forward-Port-Of: odoo/odoo#267962
Enhancements to existing features
UK VAT returns now prompt users when their company belongs to a tax unit and can automatically switch the report to that tax unit. When filing to HMRC, the system uses the tax unit's VAT number where applicable, reducing filing errors for grouped companies.
Original PR description
BEFORE: - Before this commit, when the current company is a member of the tax unit, there is no blocking level error for the user to select the tax unit. - And the vat used while creating a connection to the HMRC or while sending a tax report to the HMRC is of the current company. AFTER: - After this commit, there is one blocking level error, which tells the user that the current company is part of a tax unit, and on confirmation, the tax unit will automatically be selected for the current report. - And if the return contains the data of a tax unit, then the vat set on the tax unit will be considered while establishing the connection and sending the tax report to HMRC. Task-5865605 Forward-Port-Of: odoo/enterprise#122965 Forward-Port-Of: odoo/enterprise#107253
This update improves the French reporting tools used for fiscal declarations. It helps make local compliance reporting more reliable and easier to maintain for businesses operating in France.
The product list in Point of Sale now stays in the full layout on medium-sized tablets instead of switching to the compact view too early. This gives staff more space to browse products and improves usability on devices in landscape or medium-width screens, including a fix for iPhone/iPad orientation changes.
Original PR description
Previously, the product list was rendered in "small display" mode for all screen sizes below the medium breakpoint (< 992px). However, some small tablets are able to fully display the product list at the medium breakpoint (≥ 768px and ≤ 991px). After this fix, "small display" mode is only applied when the screen width is below 768px. This commit also includes a fix for iOS devices where the screen breakpoint was not correctly recomputed on orientation change. Task.6251934 Enterprise: https://github.com/odoo/enterprise/pull/119534 Forward-Port-Of: odoo/odoo#270394 Forward-Port-Of: odoo/odoo#266704
Resolved issues and error corrections
Hong Kong payroll now calculates payment in lieu of notice based on the employee's actual contract start date instead of assuming a full prior year of work. This helps produce more accurate termination-related payments for employees with shorter service periods, with added tests covering special cases.
Original PR description
Currently, the calculation of the payment in lieu of notice is assuming the employee worked a whole 12 months prior to it being paid. This is of course not always going the be case, and when it happens our calculation is often incorrect. We update the salary rule to calculate a more accurate total days (which is no longer based on a fixed 12-month period but takes into account the contract's start date). We also now calculate the number of months more accurately by taking, once again, the contract's start date into account. Also adding a few test cases to test a bit more some special case we didn't yet test correctly. task-6348903 Forward-Port-Of: odoo/enterprise#122580
EC Sales List returns are now created separately for each company in a tax unit, using that company's own VAT number. This prevents member companies from being incorrectly combined into one declaration under the tax unit VAT number, improving tax reporting accuracy.
Original PR description
Issue: The EC Sales List return is currently generated under the tax unit VAT number, consolidating all member entities into a single declaration. Expected: The EC Sales List return must be generated individually per member entity, each under their own VAT number, even when those entities belong to a tax Unit. Fix: Apply tax unit only if report's multi company filter is `tax_units`. Ref: https://github.com/odoo/enterprise/blob/07e8aba8604319747a5925c83576095ce9a63f9e/account_reports/models/account_return.py#L316-L317 task-6069402 Forward-Port-Of: odoo/enterprise#115974
Task progress shading in the Gantt view now correctly reflects completed work, such as showing 10 hours out of 20 as 50% instead of almost invisible. This helps field service and project users quickly understand task progress from the schedule view.
Original PR description
Steps to reproduce: ------------------------------------------- 1. Install the Timesheet module with demo data 2. Go to any FSM task > set allocated hours to 20h 3. Fill the timesheet for the same…
Steps to reproduce: ------------------------------------------- 1. Install the Timesheet module with demo data 2. Go to any FSM task > set allocated hours to 20h 3. Fill the timesheet for the same task for 10h 4. Navigate to FSM > My Tasks > Gantt view Observation: ------------------------------------------- Look at the task on the Gantt chart. The shading is barely visible because it covers only 0.5% of the bar, not 50% Issue: ------------------------------------------- In the commit https://github.com/odoo/odoo/pull/137570/changes/4a93d9aee957dd3feb3db0cb69eb3b8f0f4a4683 The progress field computation was changed from storing percentage values (0-100) to storing decimal values (0-1). Specifically, the `_compute_progress_hours` method was modified. This change was made to standardize the progress field storage format, with the understanding that the UI layer would multiply by 100 when displaying the value. While most views (form, list, kanban, etc.) were updated to multiply the progress by 100 for display purposes, the Gantt view's pill progress bar was missed. Solution: ------------------------------------------- Overrides the `enrichPill` method to multiply the `_progress` value by 100 before it's passed to the template. This ensures the Gantt pill progress bars display correctly without modifying the core web_gantt module. Before --------------------------- <img width="268" height="368" alt="image" src="https://github.com/user-attachments/assets/6d35927f-bd3f-47fc-9101-e2e188d419b8" /> After: -------------------------- <img width="250" height="371" alt="image" src="https://github.com/user-attachments/assets/fe7133e0-26d1-4c5f-b903-48826fda9488" /> opw-6038983 Forward-Port-Of: odoo/enterprise#122899 Forward-Port-Of: odoo/enterprise#111270
Field service interventions now require both start and end dates before they can be marked complete, and setting one date makes the other required. Send and publish options are hidden when no date is set, helping teams avoid incomplete scheduling information.
Original PR description
After this PR: - Both dates are required to use the 'Complete' action button on an intervention - If the start date is set on an intervention, the end date should be required (and vice versa) - We hide the 'Send' and 'Publish' buttons if there is no date set task-6234939 Forward-Port-Of: odoo/enterprise#118411
Swiss payroll now automatically calculates the 2050 salary rule used in ELM transmission. This helps reduce manual corrections and improves accuracy when preparing Swiss payroll reporting.
Original PR description
task-5166226 Forward-Port-Of: odoo/enterprise#108047 Forward-Port-Of: odoo/enterprise#103453
The project Sales Orders tab now correctly shows standard sales orders instead of rental orders when both are linked to the same project. This prevents confusion for users reviewing project-related sales activity and ensures the embedded view reflects the expected business documents.
Original PR description
Steps to Reproduce --- 1. Install sale_renting_project. 2. Create a Project linked to 1 standard Sales Order and 1 Rental Order. 3. Open the "Sales Orders" embedded tab. Issue --- In saas-19.3, is_rental_order became a non-stored computed field. When the "Sales Orders" embedded tab filters on non-rental orders, _search_is_rental_order incorrectly translates the domain and matches rental orders instead of standard Sales Orders. Current Behavior --- The standard Sales Order is excluded and the embedded view displays rental orders. Expected Behavior --- The "Sales Orders" embedded tab displays standard Sales Orders linked to the project. Fix --- In _get_sale_orders_domain, replace the is_rental_order filter with explicit checks on rental_start_date and rental_return_date to avoid the faulty search translation. task-6321803
The scheduled process that checks Mexican electronic invoice status now keeps running when more documents remain after a batch is processed. This prevents invoices from being left pending when the process handles them in smaller batches.
Original PR description
Steps to reproduce ----------------- - Install l10n_mx_edi; - Switch to the mexican company; - Create 3 invoices for the mexican company (you will need to set an UNSPSC code on the products); - Send them to CFDI; - Go to the scheduled action "Automatic update of state on the SAT" and add "batch_size=2" to the method's parameters; - Manually run the cron; - Only two invoices will be updated, the cron is not retriggered to process the remianing one. Why is it hapening ------------------ We set a limit of batch_size + 1 in the search method, and the cron is retriggered if and only if the number of documents fetched is equal to the batch size, meaning there is no more documents to fetch. This should be triggered if we fetched more documents than the batch size. opw-6328118 Forward-Port-Of: odoo/enterprise#122659
Fixed an issue where the snippet selection window in the mailing editor could appear behind the fullscreen editor when the AI chat was open. This restores normal editing behavior and prevents save or discard actions from becoming blocked during email campaign creation.
Original PR description
When an AI chatbox is active, all non-error dialog overlays are set to be behind the chatbox through their z-index. This causes an issue where the dialog overlay that adds new snippets to a mailing is placed behind the fullscreen mailing editor, preventing its use and freezing the use of some commands (save & discard). This commit restores the snippet dialog's z-index to its original value. Steps to reproduce: - Create a new mailing - Select a builder-enabled theme (such as Events Promo) - Open a new AI chat by clicking the AI icon in the top right - Open the fullscreen editor - Click on the Headers block category task-6321624
Fixed an error that prevented the task Gantt view from loading when grouped by sale order item. This lets teams review planned work by customer order line without interruption.
Original PR description
### Steps to reproduce - Install `sale_timesheet_enterprise`. - Go to **Tasks → All Tasks**. - Switch to the **Gantt** view. - Group by **Sale Order Item** (`sale_line_id`). ### Issue A traceback is…
### Steps to reproduce - Install `sale_timesheet_enterprise`. - Go to **Tasks → All Tasks**. - Switch to the **Gantt** view. - Group by **Sale Order Item** (`sale_line_id`). ### Issue A traceback is raised when loading the Gantt view with group by `sale_line_id`: ```text ValueError: Invalid field 'planned_hours' on model 'project.task' for 'planned_hours:sum' ``` ### Root cause The Gantt progress bar computation for the `sale_line_id` grouping performs a `_read_group()` aggregation on the `planned_hours` field of `project.task`. However, `planned_hours` was renamed to `allocated_hours` during the saas-16.5 migration, so the former field no longer exists on `project.task`. As a result, the aggregation raises a `ValueError`. Migration reference: https://github.com/odoo/upgrade/blob/e638c6ce00d9d8936d034ad7130fef51565b9195/migrations/project/saas~16.5.1.2/pre-migrate.py#L10 Issued PR: https://github.com/odoo/enterprise/pull/49685 ### Fix Use `allocated_hours`, the renamed equivalent of `planned_hours`, when computing the Gantt progress bar. This restores the Gantt view when grouping tasks by **Sale Order Item** and prevents the traceback. Forward-Port-Of: odoo/enterprise#122994 Forward-Port-Of: odoo/enterprise#122297
The timesheet assistant now captures time spent in Odoo applications even when the page cannot be linked to a specific project, task, or ticket. This helps users see a more complete set of work suggestions and fixes cases where valid Odoo pages were previously missed.
Original PR description
This PR adds support for tracking time spent in the Odoo apps in the assistant, for when we can't trace URLs to a project/task/ticket. The activities detected this way are marked as key events, such that each appears as an individual line in the assistant suggestions. With this, most of the time users spend working in their Odoo database should be reflected in the assistant suggestions. Task-6250449 Forward-Port-Of: odoo/enterprise#119096
Point of Sale now shows the fuller product list on medium-sized tablet screens instead of switching too early to the compact layout. This gives staff using small tablets a more spacious and usable product selection view while keeping the compact mode for narrower phones.
Original PR description
Previously, the product list was rendered in "small display" mode for all screen sizes below the medium breakpoint (< 992px). However, some small tablets are able to fully display the product list at the medium breakpoint (≥ 768px and ≤ 991px). After this fix, "small display" mode is only applied when the screen width is below 768px. Task.6251934 Community: https://github.com/odoo/odoo/pull/266704 Forward-Port-Of: odoo/enterprise#120777 Forward-Port-Of: odoo/enterprise#119534
Users can now create purchase approval requests even when a product has vendor pricing records linked to vendors they are not allowed to access. This prevents unnecessary access errors in multi-company setups and keeps RFQ approval workflows running smoothly.
Original PR description
**Issue** Having supplier pricelists with at least one vendor inaccessible to the current user can trigger an access error when creating an RFQ approval request. **Steps to reproduce** - Have two…
**Issue** Having supplier pricelists with at least one vendor inaccessible to the current user can trigger an access error when creating an RFQ approval request. **Steps to reproduce** - Have two companies A and B and two users u1 and u2 - user u2 only have access to company A - With user u1: - Create two vendors v1 and v2 without any company assigned - Create vendor pricelists for a product for each vendor and assign the company A to the pricelist - Add the company B for the vendor v2 - With user u2: - Open approval application - Try to create an approval for an RFQ for that product (the vendor v1 will be automatically selected) - Save it -> An access error is thrown **Cause** Saving the approval request computes `has_no_seller`, which calls `_select_seller`: https://github.com/odoo/enterprise/blob/03c737685ff6dfc95a8bc72491646774fc426b1f/approvals_purchase/views/approval_product_line_views.xml#L9 https://github.com/odoo/enterprise/blob/03c737685ff6dfc95a8bc72491646774fc426b1f/approvals_purchase/models/approval_product_line.py#L32 https://github.com/odoo/enterprise/blob/03c737685ff6dfc95a8bc72491646774fc426b1f/approvals_purchase/models/approval_product_line.py#L62-L70 Which filtered the right seller https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_product.py#L759 By preparing the sellers: https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_product.py#L721 https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_product.py#L712 Please note that `self.seller_ids` contains both sellers (even v2) By filtering the suppliers: https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_supplierinfo.py#L104-L105 But at that point, one of the supplier in `self`, can be accessed, thus an access error is thrown while trying to access its associated `partner_id`. opw-6203910 Forward-Port-Of: odoo/enterprise#120251
Shopfloor users can no longer create new serial numbers for manufacturing components when that option is disabled on the manufacturing operation type. This keeps shopfloor behavior aligned with inventory controls and prevents unintended traceability records.
Original PR description
**Issue**: Even when creation of new Serial Numbers for components is disabled on the Manufacturing Operation Type, it is still possible to create them from the shopfloor application. **Steps to…
**Issue**: Even when creation of new Serial Numbers for components is disabled on the Manufacturing Operation Type, it is still possible to create them from the shopfloor application. **Steps to reproduce**: - Enable "Lots & Serial Numbers", on the global settings - Create two products, one tracked by unique serial number - Go to Inventory > Configuration > Warehouse Management > Operations Types - Select Manufacturing and disable "Create New Lots/Serial Numbers for Components" - Create and confirm a MO using the tracked product as component - Go to shopfloor - Click the "+" button next to the component, then "New" -> No error is raised when creating a serial number **Cause**: The `_check_create` constraint relies on `active_mo_id`: https://github.com/odoo/odoo/blob/494cdcfdf4ec166e0a643ee70a53c12c810d02b4/addons/mrp/models/stock_lot.py#L11-L19 However, the shopfloor does not pass this, in context: https://github.com/odoo/enterprise/blob/54c6252a0e13b11fc297b6828883923c0f89881a/mrp_workorder/static/src/mrp_display/mrp_record_line/stock_move.js#L193-L199 As a result, the check is bypassed. opw-6041241 Forward-Port-Of: odoo/enterprise#111408
This update ensures invoices still reduce the declaration-of-intent plafond even when the DoI tax is used together with another tax on the same line. It prevents incorrect remaining amounts and keeps the customer’s tax limit accurate.
Original PR description
- Create a declaration of intent in the customer's contact - Issue an invoice that includes both the 0% E (DoI tax) and any other tax - You will see how the plafond is not updated and the amount of this invoice is not deducted from it The method _compute_l10n_it_edi_doi_amount specifically exclude from the doi amount lines with the doi tax and another tax. However it should be possible to use both on a single line. We can use the amount subtotal because the doi is always 0%. opw-6253475 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267669
This fix ensures that hours already fully invoiced or refunded are not counted again when creating a new invoice from a sales order. It prevents customers from being charged twice for the same timesheeted work after a partial refund.
Original PR description
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h…
### Steps to reproduce: - Download 'Sales' and 'Timesheets' apps - Create a service product invoiced on delivered quantities with timesheet tracking - Create and confirm a SO for quantity 1 - Log 20h on timesheets - Invoice the SO - Create a credit note for 11 hours => only 9 hours are invoiced - Log 5h more on timesheets - Back to the SO > create invoice again > All the 25hrs are to invoiced, although 9 of them were invoiced before ### Cause of Issue: When generating the new invoice, `_recompute_qty_to_invoice` calls `_get_delivered_quantity_by_analytic` which retrieves the analytic values for the SO line. The values retrieved are later used to determine the delivered quantity, which is later assigned to be `line.qty_to_invoice` without taking into account the already invoiced hours. https://github.com/odoo/odoo/blob/7a6518e39d34575a3977e7c4a0053a45223e203c/addons/sale_timesheet/models/sale_order_line.py#L176-L186 ### Fix: Ensures that hours that have already been completely invoiced are deducted from the quantity to invoice. opw-6253650 Forward-Port-Of: odoo/odoo#274035 Forward-Port-Of: odoo/odoo#268025
Mass mailing unsubscribe links now use the recipient’s own website address instead of the global database address. This prevents recipients in multi-company setups from being sent to a login page when they try to opt out, and keeps the unsubscribe flow working as expected.
Original PR description
In a multi-company setup with a website per company, the unsubscribe link in mass mailing emails could send recipients to the login page instead of the unsubscribe confirmation page. ### Steps to…
In a multi-company setup with a website per company, the unsubscribe link in mass mailing emails could send recipients to the login page instead of the unsubscribe confirmation page.
### Steps to reproduce
1. Enable multi-company and create a second company `Company B`.
2. Create two websites with different domains, one per company:
- `Website A` on the main company, domain `http://website-a.test`
- `Website B` on `Company B`, domain `http://website-b.test`
3. Set the system parameter `web.base.url` to `http://website-a.test`. System parameters are global, so this value applies to the whole database regardless of the company you switch to.
4. Create a contact and set its `Company` field to `Company B`.
5. In Email Marketing, create a mailing with recipient model `Contact`, target the contact above, pick any template with an unsubscribe link, and send it.
6. Open the email in an incognito window and click the unsubscribe link: you land on the login page instead of the unsubscribe page.
### Cause
Mass mailing builds the unsubscribe link in two steps.
First, each email body is rendered for its recipient. While rendering, relative URLs like `/unsubscribe_from_list` are turned into absolute URLs by prepending a base URL. That base URL comes from the recipient record itself: `recipient.get_base_url()`. The `website` module overrides this so that, when the record has a company, it returns that company's website domain. For a contact in `Company B`, the body ends up with `http://website-b.test/unsubscribe_from_list`.
Second, right before sending, `mail_mail._prepare_outgoing_list` replaces that placeholder URL with a per-recipient signed URL pointing to `/confirm_unsubscribe`. It does this by plain string replacement: it looks for `{base_url}/unsubscribe_from_list` in the body and swaps it. The `base_url` used here came from `self.mailing_id.get_base_url()`. A mailing has no company, so its base URL falls back to the global `web.base.url`, which in our setup is `http://website-a.test`.
The two base URLs no longer match. The body contains the website B URL, but the replacement code searches for the website A URL. The search fails, the placeholder stays in the email, and the recipient clicks a link to `/unsubscribe_from_list`. That route only redirects to `/mailing/my`, which requires being logged in, so the user lands on the login page.
### Fix
Compute the base URL from the recipient record (the same record used when rendering the body) instead of the mailing. The two URLs then agree and the replacement works. Fall back to the mailing's base URL if there is no recipient model on the mail.
opw-4914203
Forward-Port-Of: odoo/odoo#274191
Forward-Port-Of: odoo/odoo#264055This fix restores missing payment method information in self-order order responses. As a result, customers can again download their receipts correctly after an order is paid in the backend.
Original PR description
Steps to reproduce: - Set up a kiosk with pay at counter - Order a product - Settle the order in backend - Go to my order on the self, try donwload the receipt - TB Issue: This commit https://github.com/odoo/odoo/pull/237553 removed the pos_payment_method from the _generate_return_values method. The fornt-end didn't had the necessary data to generate the receipt. Fix: Restore payment method in the _generate_return_values method. Task-6191379 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273831 Forward-Port-Of: odoo/odoo#263515
WebP images uploaded to Odoo now follow the same maximum size checks as other image formats. This prevents very large images from being accepted on upload, helping keep attachments consistent and avoiding oversized files in the system.
Original PR description
Since 17.0, `webp` images can be uploaded at any resolution, whereas every other format is refused above IMAGE_MAX_RESOLUTION (50 Mpx) when the attachment is created on the server. Root cause…
Since 17.0, `webp` images can be uploaded at any resolution, whereas every other format is refused above IMAGE_MAX_RESOLUTION (50 Mpx) when the attachment is created on the server. Root cause =========== `ImageProcess` grouped webp together with empty sources and SVG and set `self.image = False`, returning before the `verify_resolution` check. As a result the resolution limit enforced for `png/jpeg/...` was never applied to `webp`. Fix === Split `webp` out of the skip branch: it is still not processed as before, but its resolution is now read from the RIFF header with `get_webp_size()` and checked against `IMAGE_MAX_RESOLUTION`, so oversized webp images are refused on upload like any other format. Steps to reproduce =================== 1. Edit any page with the website editor 2. Upload a `webp` image larger than 50 Mpx (e.g. 8000x8000) => The image is accepted, while a `png/jpeg` of the same size is refused task-4134430 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273570 Forward-Port-Of: odoo/odoo#273010
The receipt deadline is now calculated without considering cancelled stock moves. This ensures that when a purchase line is reduced to zero and later dates are updated, the receipt shows the correct deadline instead of keeping an outdated one.
Original PR description
Steps to reproduce the bug:
- Create a Purchase Order with 2 products and confirm it
- Note the receipt's deadline (= date_planned of both lines)
- Set the quantity of one PO line to 0
- Update the scheduled date (date_planned) of the purchase order
Problem:
the receipt deadline does not update.
The receipt kept the old deadline from the cancelled move. When a PO line qty is set to 0, `_merge_moves` cancels the corresponding stock move via `_action_cancel`. Then `_update_move_date_deadline` correctly skips cancelled moves (filtered by `state not in ('done', 'cancel')`), so the cancelled move retains its original `date_deadline`. However, `_compute_date_deadline` on `stock.picking` used
`move_ids.filtered('date_deadline')`, which not checks move state, so the stale deadline of the cancelled move was included in the min/max computation.
opw-6292600
Forward-Port-Of: odoo/odoo#271890
Forward-Port-Of: odoo/odoo#270985This change prevents an error that could happen when searching paid POS orders after an employee has been archived. It ensures the POS can still load order data correctly, so users can keep working without interruption.
Original PR description
Steps to reproduce on runbot:
- Enable "Log in with Employees"
- Connect to the POS with an employee
- Process an order
- Go to the backend
- Archive the employee
- Connect to the POS with another employee
- Go to the "Order" tab and search for "Paid" orders
Error:
Odoo Server Error: {archived_employee_id}
[opw-6223243](https://www.odoo.com/odoo/project/49/tasks/6223243)
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#273889
Forward-Port-Of: odoo/odoo#266364This update prevents an error that could appear when opening the Time Off report by employee for French companies. It ensures the report opens correctly even when an employee has no working hours calendar set, improving reliability for HR users.
Original PR description
A singleton error appears when opening the "By Employee" report under Time Off. Steps to reproduce: 1)Install the l10n_fr_hr_holidays module. 2)Switch to the French company. 3)Open any employee record. 4)Set Working Hours (resource_calendar_id) to empty. 5)Set Hours Per Week. 6)Create a time off request for any past date. 7)Go to Reporting → By Employee. Task:-6043142 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#264585
This fixes a problem that prevented PDF generation for Polish e-invoices when QR codes were included. As a result, customers can again create and print the affected PDFs without errors.
Original PR description
Due to this (https://github.com/odoo/odoo/pull/244421/changes/546a4425884e61080a7a7e8d2b2a97c8a2e3f38e), PDF generation was broken. Removing the manual encoding of the data. Runbot [link](https://runbot.odoo.com/odoo/error/941292) runbot-941292
Customers paying with DPO were sometimes sent to an error page instead of the payment page because the transaction details were being sent in a way DPO did not preserve during a redirect. This update makes the payment details travel correctly so customers can complete their payment as expected.
Original PR description
When a customer paid through DPO, they landed on the DPO error page ("Not Valid - this transaction is no longer valid") instead of the payment page, even though the transaction token was created…
When a customer paid through DPO, they landed on the DPO error page ("Not Valid - this transaction is no longer valid") instead of the payment page, even though the transaction token was created correctly.
The customer is redirected to `https://secure.3gdirectpay.com/payv2.php` with the transaction token passed as the `ID` parameter. `payv2.php` answers with a 302 redirect to `payv3.php`. Per HTTP semantics, a 302 turns the request into a GET and drops the body, so when `ID` is sent in the body of a POST it never reaches `payv3.php`: the customer arrives at `payv3.php?` with no token and DPO rejects it as invalid.
This regressed with 97ec8a3e72d9d48a0a9620c53feea533421b9d67, which moved redirect providers to the generic redirect form. Before that commit, the token was part of the action URL itself (`payv2.php?ID=<token>`), so it stayed in the query string across the redirect. The refactor moved `ID` into `url_params` rendered as a hidden input, and since `http_method` was left unset the generic form defaults to POST, putting `ID` in the body.
Set `http_method` to "get" so the token is serialized back into the query string and survives the `payv2.php` -> `payv3.php` redirect, restoring the pre-refactor behavior. This also matches DPO's documented convention of passing the token in the URL query string.
opw-6312732
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prAll-day events imported from Google are now stored in a way that keeps them on the correct calendar day for users in any timezone. This prevents events from appearing one day earlier in list views for users west of UTC, while keeping calendar views unchanged.
Original PR description
When Google sends an all-day event, the payload only contains a date (no time), so the inbound sync stores start and stop at 00:00 UTC. The list view renders those Datetime fields in the viewing…
When Google sends an all-day event, the payload only contains a date (no time), so the inbound sync stores start and stop at 00:00 UTC. The list view renders those Datetime fields in the viewing user's timezone, so any user west of UTC sees the previous day. The calendar view stays correct because it reads the date part directly. The rest of Odoo stores all-day events at 08:00 and 18:00 UTC, which keeps the stored datetime inside the same calendar day: https://github.com/odoo/odoo/blob/327ad9b737b0f1d4c547c71a52c45d8433f2b3f4/addons/calendar/models/calendar_event.py#L408-L414 Apply the same 08:00 / 18:00 normalization when building values from a Google all-day payload, so events created on Google match events created in Odoo. Steps to reproduce: 1. Set your user timezone to America/Winnipeg. 2. Connect a Google account and run the calendar sync. 3. In Google Calendar, create an all-day event on January 31. 4. Sync, then open Calendar in list view in Odoo. => Start Date and End Date columns show January 30. Ticket [link](https://www.odoo.com/odoo/project.task/6145880) opw-6145880 Forward-Port-Of: odoo/odoo#271573 Forward-Port-Of: odoo/odoo#262282
The Point of Sale screen now shows the change due to the customer with the correct sign and value. This avoids confusion during payments and refunds, especially when the customer is owed money back.
Original PR description
Steps to Reproduce ------------------------ - Install point of sale. - Do a order and pay more than the amount. Issue ------ - The change amount is displayed as a positive value on the UI. - Typically, amounts going out of the shop (like change given to the customer) should be shown as negative. Cause ------- - The change amount was not correctly represented in the UI. - Since the change flows in the opposite direction of the payment, it should be displayed as the negation of the original amount. FIX ----- - Updated the frontend to display the change amount with the correct (negative) sign. - No backend changes were required, as the correct value was already being handled during order synchronization Enterprise PR: https://github.com/odoo/enterprise/pull/112560 task: 6074620 Forward-Port-Of: odoo/odoo#274534 Forward-Port-Of: odoo/odoo#256776
This change makes employee data refresh more reliably after updates, preventing outdated information from being shown because of cache issues. It helps ensure changes are reflected consistently when employee records are modified.
Original PR description
There was yet another issue with the cache but related to the contract_date_start field. To prevent any issues like this to arise again, the version_revision will have the write_date to invalidate cache everytime the model has been written to. task-6326052 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273516 Forward-Port-Of: odoo/odoo#272855
Paymob no longer operates in Pakistan, so payments routed through that country are no longer available in Odoo. This change removes the unsupported option to avoid failed payment setups and confusion for users.
Original PR description
Paymob stopped their operations in Pakistan unexpectedly. Domain was dropped so none of the APIs work for Pakistan. Therefore we are removing the support of Pakistan in the Paymob provider. See Also: https://github.com/odoo/documentation/pull/18768 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273935
This update fixes an issue where the meeting controls could be cut off on mobile browsers like Chrome and Safari. It adjusts the call overlay so it fits the visible screen area, making it easier to mute, turn on video, or hang up during a call.
Original PR description
Before this commit, joining a call in a mobile browser (Chrome, Safari, ...) would crop the bottom call controls (mic, camera, hang-up, ...) behind the browser's chrome (URL bar / bottom nav). On…
Before this commit, joining a call in a mobile browser (Chrome, Safari, ...) would crop the bottom call controls (mic, camera, hang-up, ...) behind the browser's chrome (URL bar / bottom nav). On mobile, entering the meeting uses the fullscreen overlay with `keepBrowserHeader: true`, so no native fullscreen is requested and the browser UI stays visible. The overlay was sized with the Bootstrap `vh-100` class (`height: 100vh`), and `100vh` resolves to the *large* viewport (as if the URL bar were hidden). Combined with `fixed-top`, the overlay extended past the visible area and pushed the control row off screen. This commit sizes the overlay with `100dvh` (dynamic viewport height) instead, which tracks the currently visible viewport and shrinks while the URL bar is shown, keeping the controls on screen. This is a no-op in native/desktop fullscreen where `dvh == vh`, and matches the `dvh` usage already present in the codebase (welcome page, bottom sheet). task-6353266 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273949
This change fixes a problem that could cause an error when printing an invoice PDF after sending it to KSeF. It ensures the QR code is handled correctly, so the invoice can be printed normally and its KSeF status is properly saved.
Original PR description
Issue: Traceback while printing a PDF with a KSEF QR Code. Steps to reproduce: - In a Company with KSEF configured - Create an invoice - Confirm it - Send to KSEF Current behavior: - Invoice is sent to KSEF - Invoice status is fetched from KSEF (but not saved) - raise a traceback while printing the PDF Expected behavior: - Invoice is sent to KSEF - Invoice status is fetched from KSEF - PDF is printed - Invoice status is saved opw-6211058
This change fixes a crash that could happen when signing Egyptian invoices using a certificate from the ETA USB tool. The system now reads the certificate in the correct format, so invoice signing completes successfully instead of failing.
Original PR description
Steps to reproduce: - Configure a thumb drive with a certificate read from the ETA USB tool, so l10n_eg_edi.thumb.drive.certificate is populated - Open a customer invoice, confirm it, then Sign…
Steps to reproduce:
- Configure a thumb drive with a certificate read from the ETA USB tool, so l10n_eg_edi.thumb.drive.certificate is populated
- Open a customer invoice, confirm it, then Sign invoice
Before this commit, signing crashed with:
`TypeError: encoded_data must be a byte string, not
odoo.orm.fields_binary.BinaryValueAttachment`
raised by `asn1crypto` in `x509.Certificate.load()`, called from `_generate_signed_attrs__` and identically from `_generate_signer_info__` and `_generate_cades_bes_signature`.
Reading an attachment-backed Binary field now returns a lazy `BinaryValueAttachment` wrapper rather than raw bytes, and `asn1crypto` rejects any value that is not a bytes instance. `set_certificate` and the `l10n_eg_eta_json_doc_file` reads were already moved to the new binary API but the three certificate loads were missed and still passed the wrapper straight to asn1crypto.
Load the certificate through `self.certificate.content`, which returns the stored DER bytes.
opw-6365281
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prIndian companies now each get their own "Half Up" cash rounding setup instead of sharing one record. This prevents access errors when viewing invoices after creating additional companies.
Original PR description
### Issue: When creating a new Indian company, the `Half Up` cash rounding is reassigned to the new company instead of being duplicated Previous Indian companies lose access to it, causing errors…
### Issue: When creating a new Indian company, the `Half Up` cash rounding is reassigned to the new company instead of being duplicated Previous Indian companies lose access to it, causing errors when opening invoices that reference the cash rounding if the user doesn't have access to that company ### Cause: `cash_rounding_in_half_up` was defined as a `data` record with a fixed XML ID (`l10n_in.cash_rounding_in_half_up`) `_get_in_account_cash_rounding` referenced that XML ID directly and set `company_id` to the current company on each chart of accounts installation This reassigned the single shared record to the new company instead of creating a new one Moving the definition to the `@template` decorator without a module-prefixed XML ID lets the chart of accounts system create one record per company, as intended ### Steps to reproduce: - Install `l10n_in` and switch to `IN Company` - Check the Cash Rounding records grouped by company - Create a new Indian company - Enable both `IN Company` and the new company - Check the Cash Rounding records grouped by company again Before the fix, only the last created Indian company has the Cash Rounding record opw-6318857 Forward-Port-Of: odoo/odoo#272866
When a campaign email bounces, the contact’s bounce count is now updated reliably. This improves the accuracy of mailing records and helps teams better identify problematic email addresses.
Original PR description
Previously, when a mailing campaign sent out an email to a mailing.contact, and that email bounced, the bounce would not increment the contact's bounce count. This commit makes it so that the bounce count is correctly updated when a mailing campaign sends an email that bounces. task-4893615 Forward-Port-Of: odoo/odoo#272648 Forward-Port-Of: odoo/odoo#226362
This change prevents approval requests from failing when a product has vendor records that the current user cannot access. It makes the vendor selection process safely ignore inaccessible vendors, so users can save approval requests without unexpected permission errors.
Original PR description
**Issue** Having supplier pricelists with at least one vendor inaccessible to the current user can trigger an access error when creating an RFQ approval request. **Steps to reproduce** - Have two…
**Issue** Having supplier pricelists with at least one vendor inaccessible to the current user can trigger an access error when creating an RFQ approval request. **Steps to reproduce** - Have two companies A and B and two users u1 and u2 - user u2 only have access to company A - With user u1: - Create two vendors v1 and v2 without any company assigned - Create vendor pricelists for a product for each vendor and assign the company A to the pricelist - Add the company B for the vendor v2 - With user u2: - Open approval application - Try to create an approval for an RFQ for that product (the vendor v1 will be automatically selected) - Save it -> An access error is thrown **Cause** Saving the approval request computes `has_no_seller`, which calls `_select_seller`: https://github.com/odoo/enterprise/blob/03c737685ff6dfc95a8bc72491646774fc426b1f/approvals_purchase/views/approval_product_line_views.xml#L9 https://github.com/odoo/enterprise/blob/03c737685ff6dfc95a8bc72491646774fc426b1f/approvals_purchase/models/approval_product_line.py#L32 https://github.com/odoo/enterprise/blob/03c737685ff6dfc95a8bc72491646774fc426b1f/approvals_purchase/models/approval_product_line.py#L62-L70 Which filtered the right seller https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_product.py#L759 By preparing the sellers: https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_product.py#L721 https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_product.py#L712 Please note that `self.seller_ids` contains both sellers (even v2) By filtering the suppliers: https://github.com/odoo/odoo/blob/c37e76850d3ff790b76493bd1003d80e170bd4bf/addons/product/models/product_supplierinfo.py#L104-L105 But at that point, one of the supplier in `self`, can be accessed, thus an access error is thrown while trying to access its associated `partner_id`. opw-6203910 Forward-Port-Of: odoo/odoo#269552
This update corrects how loyalty discounts are applied in Point of Sale when a promotion targets the cheapest or specific items. Fixed taxes will now stay on the original product line instead of being moved to the discount line, which prevents totals from being understated and keeps pricing accurate.
Original PR description
sale_loyalty filters amount_type == 'fixed' taxes when building discountable_per_tax in all three helpers: _discountable_order , _discountable_cheapest, _discountable_specific, so fixed taxes stay…
sale_loyalty filters amount_type == 'fixed' taxes when building discountable_per_tax in all three helpers: _discountable_order , _discountable_cheapest, _discountable_specific, so fixed taxes stay only on the original product line and are not transferred onto the discount reward line.
pos_loyalty has the equivalent filter on _getDiscountableOnOrder (pos_order.js:962, added in commit 68d35232dd5) but not on the two sibling methods. As a result, when a promotion program uses discount_applicability='cheapest' or 'specific', the fixed tax is copied onto the reward line's tax_ids and because the reward line carries a negative price its fixed-tax contribution cancels the same tax on the original product line, understating the order total.
This change ports the filter expression from _getDiscountableOnOrder to _getDiscountableOnCheapest and _getDiscountableOnSpecific, preserving the e-wallet / gift-card carve-out so those programs can still consume the full amount.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#261370This change stops the system from creating a manufacturing order when a product has no Bill of Materials. Instead, replenishment rules will handle the case as intended, avoiding confusing empty draft manufacturing orders for users.
Original PR description
Steps to reproduce: - unarchive the MTO route - Create a storable product "P1" with the MTO + Manufacture routes but set no Bill of Materials on it - Create a sales order with one unit of P1 and confirm it Problem: An empty draft MO is created even though no Bill of Materials exists. When no BoM is available, manufacturing orders should not be created, only replenishment rules are expected to handle this case. Fix: Added an early `continue` in `_run_manufacture` to skip MO creation when no BoM is found. opw-6174886 Forward-Port-Of: odoo/odoo#272064 Forward-Port-Of: odoo/odoo#263108
Manufacturing orders can now be validated even when a branch uses a component product owned by another company. This prevents access errors during validation and makes branch manufacturing flows work smoothly in multi-company setups.
Original PR description
### Steps to reproduce: - Have a company with a branch say "company1" and "branch" - Create two products: Final product (FP), Component (Comp) - Set the company_id of FP to "branch" and of Comp to…
### Steps to reproduce: - Have a company with a branch say "company1" and "branch" - Create two products: Final product (FP), Component (Comp) - Set the company_id of FP to "branch" and of Comp to "company1" - Associate both products with a product category set to avco in company1 (the field is company dependant) - Create a bom for FP with company_id set to "branch": 1 X Comp - Impersonate a user whose only allowed and default is "branch" - Create and confirm an MO for 1 unit of FP - Set the qty_producing to 1 unit and validate #### > Access Error: Access to unauthorized or invalid companies. ### Cause of the issue: Validating the MO will, validate the component move and set its value: https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/stock_move.py#L168-L173 But, in order to determine this value, it is necessary to determine its `property_cost_method`: https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L60-L69 Now, the issue is that the `product_template` of the component belongs to "company1" so that the user is unauthorized to read the valuation method of the product category for "company1". opw-6216141 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274108 Forward-Port-Of: odoo/odoo#272429
This fix prevents an error when changing a product bill of materials from a kit to a manufacturing setup in a multi-company environment. It ensures the validation only blocks changes when the affected sales order belongs to the same company, avoiding unnecessary interruptions for other companies.
Original PR description
### Steps to reproduce: - Have two companies: company1 and company2 - Create a producct P available in both company1 and company2 - with company1, create a kit bom for a product P - with company2,…
### Steps to reproduce: - Have two companies: company1 and company2 - Create a producct P available in both company1 and company2 - with company1, create a kit bom for a product P - with company2, create and confirm a sale order for 1 unit of P - with company1, change the bom type of P from kit to manufature #### > UserError: As long as there are some sale order lines that must be delivered/invoiced and are related to these bills of materials, you can not remove them. ### Cause of the issue: Changing the bom type from a kit (phantom type) to a non kit will launch a call of the `_ensure_bom_is_free` in order to ensure data integrity if the kit bom was used by a relevant sale order line: https://github.com/odoo/odoo/blob/f4c76be062bec47b68ee42505d7d42fed31ac0f2/addons/sale_mrp/models/mrp_bom.py#L15-L18 https://github.com/odoo/odoo/blob/f4c76be062bec47b68ee42505d7d42fed31ac0f2/addons/sale_mrp/models/mrp_bom.py#L24-L42 However, this check does not take the company of the bom into account and in the present flow, the company of the bom is different from the company of the supposedly problematic sol. opw-6290304 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273839 Forward-Port-Of: odoo/odoo#271119
This update fixes an issue where project templates weren't correctly associated with sale orders, and allows users to only select project templates relevant to the sale order's company. This ensures projects are created with the appropriate billable status and simplifies project setup for sales teams.
Original PR description
Fix 1 : project, sale_timesheet: remove default_allow_billable context in Create a Project --------------------- **Issue:** When a project is created from the sale app, it is not billable by default,…
Fix 1 : project, sale_timesheet: remove default_allow_billable context in Create a Project --------------------- **Issue:** When a project is created from the sale app, it is not billable by default, and the sale order / sale order line are not set. **Fix:** Remove default_allow_billable = False in the Create a Project **Note:** default_allow_billable = True already exists in action_view_project_ids, but that default context is replaced when opening the project directly from the view. This happens because default_allow_billable = False is set in the Create a Project action. Fix 2: sale_project: show only relevant project templates per company ---------------- **Steps:** - Install sale_project - Create two companies (A, B) - Create three project templates: - Template A (company A) - Template B (company B) - Template C (no company → visible to all) - Create a sale order for company A with a service product - Confirm the sale order - Create a project and try to select a template **Issue:** All project templates were visible even if the sale order had a company set. **Fix:** Added a filter (domain) on the project template field so only templates for the sale order’s company or templates with no company are shown. Users cannot select templates from other companies. task-5074893 Forward-Port-Of: odoo/odoo#274194 Forward-Port-Of: odoo/odoo#229309