Daily updates from Odoo
Tuesday, July 7, 2026
305 changes
27 changes
Enhancements to existing features
The Courses menu is no longer hidden when Course Allocation is enabled in a restaurant point of sale. This makes course setup and management accessible to the people who need it, and the added help text guides users when creating courses.
Original PR description
Before this commit: ======================= The Courses menu was always hidden because it was restricted to `base.group_no_one`, making course management inaccessible even when Course Allocation was enabled in a restaurant PoS. After this commit ====================== The Courses menu now always visible. A help message is also added to the Courses action to guide users when creating courses. Task-6317914 Forward-Port-Of: odoo/odoo#274300 Forward-Port-Of: odoo/odoo#271308
Desktop notifications will no longer be forced onto a single line, so longer messages can display more of their content. This makes important alerts, such as style compilation errors, easier to read and understand at a glance.
Original PR description
Before this commit, on screens wider than the medium breakpoint, notification messages were force-clamped to a single line via a `--lines-clamp: 1` override, hiding the rest of the text for longer messages, such as the "Style error" notification shown when SCSS compilation fails. After this commit, the single-line override is removed so notifications fall back to the default 2-line clamp. task-6037796
PDF reports can now be converted into images before printing, which makes them compatible with ePOS printers. This helps businesses print documents like labels on devices such as the Epson TM-L100.
Original PR description
We now use wkhtmltoimage to render pdf reports as images in order to print them using an ePOS printer. This is useful to print labels using an Epson TM-L100 for example.
The mailing form now places the Exclusion List option inline with the Recipients and Dynamic Lists fields for a more consistent layout. It also hides the Dynamic Lists section when no dynamic list has been set, reducing visual clutter and making the form easier to use.
Original PR description
This commit moves the Exclusion List checkbox to be inline with the Recipients and Dynamic Lists fields. This commit also hides the Dynamic Lists div if the mailing is sent while no dynamic list has been set. task-6321624
Odoo now shows cleaner VAT/Tax ID placeholders and a clearer tooltip when editing customer details. When the expected VAT format is known, users will see an example format; otherwise the field stays empty, reducing confusion and making the guidance easier to understand.
Original PR description
In this commit: - Remove 'or not applicable' and '/ if not applicable' from VAT placeholders. - Display the expected VAT format when available (e.g. BE0477472701). - Leave the placeholder empty when no format is known. - Simplify the tooltip to: 'You can use / to indicate that the customer has no Tax ID.' task-[5005896](https://www.odoo.com/odoo/project/967/tasks/5005896) Forward-Port-Of: odoo/odoo#274578 Forward-Port-Of: odoo/odoo#273344
Resolved issues and error corrections
This change fixes an unreliable test in the dropshipping stock flow that could pass or fail for the wrong reason. It makes the validation timing consistent so product cost updates are checked against the intended values, reducing false build failures and improving confidence in stock valuation behavior.
Original PR description
The below test sometimes fail for an incorrect reason and leads to a false positive:…
The below test sometimes fail for an incorrect reason and leads to a false
positive:
https://github.com/odoo/odoo/blob/6dbeac3a42f46b42c638c05aea8285452c944c3f/addons/stock_dropshipping/tests/test_purchase_order.py#L21
Here is another way to reproduce the issue with a higher probability of
false positive (and it is actually easier to read and understand what the
test is doing and what's wrong). It needs to edit the following test:
https://github.com/odoo/odoo/blob/ec58c5e12987401659ea0d75d3be2905ad1d807d/addons/purchase_stock/tests/test_create_picking.py#L953
With the below diff:
```diff
--- a/addons/purchase_stock/tests/test_create_picking.py
+++ b/addons/purchase_stock/tests/test_create_picking.py
@@ -965,6 +965,7 @@ class TestCreatePicking(ProductVariantsCommon):
'price': 500.0,
'discount': 10,
})]
+ self.product_id_1.standard_price = 1.0
po = self.env['purchase.order'].create(self.po_vals) # create a PO for 5 units
po.button_confirm()
with Form(po) as po_form:
```
It will lead to:
```
Traceback (most recent call last):
File ".../test_create_picking.py", line 976, in test_average_cost_updated_after_po_with_discount
self.assertEqual(self.product_id_1.standard_price, 450.0)
AssertionError: 1.0 != 450.0
```
Here are the explanations: when receiving an AVCO product, at some point, we
recompute its standard price. To do so, among several operations, we take
the last manual update, and we ignore all previous SM:
https://github.com/odoo/odoo/blob/2dbd88657395da965125c8f085da93e04c9c8f0a/addons/stock_account/models/product.py#L463-L465
This is an issue when things are done too quickly. See the pattern:
```py
self.product_a.standard_price = 5.0 # -> define valuation_from_date
po.confirm() # with another cost
receipt.button_validate() # -> define move.date
```
In case of a fast execution, both dates will be equal. We therefore ignore
the SM and rely on the manual update to define the standard price, which is
not expected. This explains the above `AssertionError`.
Fixing the codebase is quite tricky since the opposite use case could also
happen, aka first processing a receipt and only then modifiying the standard
price.
Tests side, a more important solution should probably be implemented to ease
their redaction and avoid this basic pattern. Yet, a WIP task is changing
the valo for Odoo 20, so the whole logic may change. Second, the current
issue is impacting a lot of builds, so we need to move forward. For both
reason, the commit only "fixes" the current test.
runbot-939955
Forward-Port-Of: odoo/odoo#273820
Forward-Port-Of: odoo/odoo#273078This change fixes an intermittent test failure in the messaging bus by making the subscription wait logic more reliable. It ensures the test only proceeds once the correct server-side response has been processed, avoiding false matches with unrelated background notifications.
Original PR description
`test_subscribe_to_new_channel_with_higher_id` sometimes fails. This happens because we only have one test cursor, with no lock preventing two threads from fighting over it: - `trigger_notification_dispatching` calls `precommit.run()`, which creates `bus.bus` records. - Processing an incoming `subscribe` message server-side also acquires a cursor. This means the test thread must always wait for those operations to complete before proceeding. `subscribe` already has a guard meant to wait until its own request has been processed. However, it's too naive: any dispatch that happens after we call `subscribe` is treated as the response to our request, while in practice it can be caused by an unrelated `NOTIFY` that was already in flight. runbot-243463 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 change prevents an unexpected error when a sales order quantity is reduced to zero after part of the order has already been delivered. Instead of a technical crash, the system now follows the normal validation path and shows the expected message if the quantity is not allowed.
Original PR description
*: sale_stock_margin When reducing the ordered quantity to 0 after a partial delivery, the margin onchange is triggered before the sale order line quantity is validated. As a result, the margin computation divides by `product_uom_qty`, which is already 0 at that point, raising a `ZeroDivisionError`. This prevents the normal validation flow from reaching `_update_quantity`, which is responsible for rejecting quantities lower than the delivered quantity with a `UserError`. Skip the margin onchange when the ordered quantity is 0 so the existing quantity validation can execute and raise the expected `UserError` instead of an unexpected traceback. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change fixes a form behavior issue where some values could be recalculated too early, leading to incorrect results shown to users. It also corrects a related accounting test that was relying on the old behavior, helping ensure invoices and forms behave consistently.
Original PR description
Consider two models like this: - model A has fields `base`, `total` and `line_ids` (to model B); - model B has fields `price` and `subtotal`. Field `subtotal` depends on `base`, and field `total`…
Consider two models like this: - model A has fields `base`, `total` and `line_ids` (to model B); - model B has fields `price` and `subtotal`. Field `subtotal` depends on `base`, and field `total` depends on `line_ids.subtotal`, but does not use it in its compute method. The second time `base` is modified in a form view, method `onchange()` is sent the values of `base`, `total` and `line_ids` (with the former update of `subtotal`). When putting field `line_ids` in cache, field `subtotal` invalidates field `total` on the main record. This causes field `total` to be recomputed too soon, which eventually prevents `onchange()` from detecting that `total` has changed. The form view ends up with an incorrect value of `total`. The fix consists in setting field `subtotal` in cache without triggering recomputation of dependent fields. The error actually lies in method `convert_to_cache()` of x2many fields, specifically in the way to handle UPDATE commands. It should update the corresponding record's cache instead of assigning its fields.
When shoppers change product filters on the website, the selected category is now kept during the page refresh. This prevents the product grid from showing results that ignore the category the customer was browsing, making navigation more accurate and consistent.
Original PR description
Since commit 49059469309b73a5a450ef85780adb539fb5d47c when we reload the product grid we don't consider the category since its extracted from the path only relevant filters from the search params were handled. As a fix, we pass the category id into the search params given to the reload route. opw-6360717 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix corrects how the web profiler reads component data, preventing an error when the profiler is enabled. As a result, users can turn on profiling without the page failing due to missing values.
Original PR description
Enabling the profiler results in an error because the template attempts to access component values without `this`, which resolves to undefined. This commit fixes the issue by updating the problematic cases to use `this`. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The work entries export wizard now closes correctly when users click Cancel or Discard. This makes the export flow behave as expected and avoids confusion when backing out of the action.
Original PR description
Version: - saas-19.4 Issue: - The Cancel (Discard) button in the work entries export wizard does not close the wizard as expected. Fix: - Added special="cancel" to ensure the wizard closes correctly when the Cancel button is clicked. Task-6365691
This change prevents an error that could occur when opening Talent Pools from an application if duplicate talent records existed with the same information. It improves reliability for recruiters by ensuring the Talent Pools view opens normally instead of showing a traceback.
Original PR description
When multiple talent records share the same information, opening the Talent Pools smart button from a matching application will trigger a traceback. Steps to reproduce the error: - Install…
When multiple talent records share the same information, opening the Talent Pools smart button from a matching application will trigger a traceback. Steps to reproduce the error: - Install ``hr_recruitment`` module with demo data - Go to Recruitment > Applications > Talent Pools > Create a new pool - Go to Recruitment > Applications > All Applications > Create a new application with valid email > Click Add to Pool > Select the Talent Pool > Add to Pool - Duplicate the created talent record - Create another application with the same email > save > click Talent Pools Traceback: ```py ValueError: Expected singleton: hr.applicant(2, 1) ``` https://github.com/odoo/odoo/blob/d4e76a5663223a2a2c6e50d1701fabbdcaf32405/addons/hr_recruitment/models/hr_applicant.py#L857-L859 Here, the talent is searched using matching applicant information. When a matching talent has been duplicated, the search returns multiple records. Assigning a multi-records to the many2one field ``pool_applicant_id`` then raises a singleton error. Solution: Restrict the duplication of talent. sentry-7556261128 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273565 Forward-Port-Of: odoo/odoo#270827
This update fixes a website logout problem that could trigger a session error when users clicked the logout button in the preview. It also adjusts an automated website signup test so it works reliably without requiring manual configuration, improving test stability.
Original PR description
### Commit 1: [FIX] website: prevent CSRF error by blocking duplicate form submission Before this commit: Clicking the logout button from the website preview triggered two simultaneous logout…
### Commit 1:
[FIX] website: prevent CSRF error by blocking duplicate form submission
Before this commit: Clicking the logout button from the website
preview triggered two simultaneous logout requests:
1. The browser performed the default form submission with a valid
`csrf_token`, destroying the session afterward.
2. During the same click event, `setupClickListener()` intercepted
the click using `closest('[action]')`, found the parent
`/web/session/logout` form, and triggered a second POST request
using `odoo.csrf_token`.
Since the session was already destroyed by the first request, the
second request resulted in a "CSRF validation failed" error.
This commit prevents the default form submission before triggering
the manual POST request, ensuring that only one request is sent.
Runbot-940403
--------------------------------------------------------------------------------------------------------------------------------
### Commit 2:
[FIX] website: enable free sign up setting in test_auth_forms_warning
Steps to reproduce:
1. Install any website related module (e.g. `website`, `website_event`).
2. Keep the default configuration and do not manually enable
'Free sign up' in Settings.
3. Run `test_auth_forms_warning`.
Before this commit: The test did not programmatically enable the
'Free sign up' setting. As a result, it failed unless a developer
manually navigated to the setting and enabled it beforehand.
After this commit: This commit explicitly enables the "Free sign up"
configuration during test execution, allowing public access to the
`/web/signup` page and ensuring the test passes without any manual
setup.
runbot-940394
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274115
Forward-Port-Of: odoo/odoo#272020Invoices now correctly reduce the declaration-of-intent plafond even when the DoI tax is used together with another tax on the same line. This fixes cases where the invoice amount was previously ignored, helping keep the 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#273553 Forward-Port-Of: odoo/odoo#267669
This update fixes a layout issue where text columns could shift slightly when resize indicators were shown. It also prevents Odoo’s built-in column resizing from interfering with Website and Mass Mailing editors, so their own resize controls work as expected.
Original PR description
*: [html_builder, mass_mailing] ### Description of the issue/feature this PR addresses: - The resize handle SCSS used `border-left/right` on `.col-*` elements which adds real pixels to the box model,…
*: [html_builder, mass_mailing] ### Description of the issue/feature this PR addresses: - The resize handle SCSS used `border-left/right` on `.col-*` elements which adds real pixels to the box model, shifting column content to the right even when the border was transparent. - The column resize mechanism (hover detection + drag) was active in website and mass_mailing, conflicting with their own resize handle indicators. - Website and mass_mailing live inside an outer `.container` which provides Bootstrap gutter padding (`--gutter-x`). The border on top of that made the shift layout. ### Desired behavior after PR is merged: - Replace `border` with `box-shadow: inset` for the resize handle indicator. `box-shadow` is visual and takes zero space in the box model, so content alignment is preserved. - Style scope to body:not(.editor_enable) .odoo-editor-editable .o_text_columns `body:not(.editor_enable)` excludes the builder editor context. Website and mass_mailing rely on Bootstrap classes for their column layout. - Add `allowTextColumnResize` config flag (default: `true`) in `ColumnPlugin`. Website and mass_mailing set it to `false` to disable the `ResizePlugin` column parameters, preventing conflicts with their own resize handles. task-6233558 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix stops a public holiday and a worked-time leave from being counted twice on the same day. It ensures the holiday is applied first, so payroll work entries stay accurate and employees do not end up with duplicate time recorded for the same period.
Original PR description
Issue: When work entries are generated from Attendances, a worked-time time off created by the Indian sandwich rule can overlap a public holiday and generate duplicate work entries for the same day.…
Issue: When work entries are generated from Attendances, a worked-time time off created by the Indian sandwich rule can overlap a public holiday and generate duplicate work entries for the same day. Steps to reproduce: - Create an employee with Work Entry Source set to Attendances - Use a flexible working schedule on the employee - Configure a public holiday on a scheduled day with work entry type (Paid time off) - Create a time off type with Count as set to Worked Time - Generate time off for the period so the public holiday entry exists (maybe a day before and a the public holiday and the day after) - Open Payroll > Work Entries (Observe the date of the public holiday will have more than 8h entry) Cause: In `_get_version_work_entries_values()`, calendar leaves are split by `hr_holidays` `time_type` into: - leaves: absences and public holidays - worked_leaves: worked-time time off For attendance-based contracts, both sets were turned into work entries without removing overlap between a public holiday and a worked-time leave on the same period. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L222-L226 For non-flexible calendar: Public holidays and worked-time leaves are both clipped to the static working schedule (e.g. 8h per working day). overlap was kept in both result sets. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L260 For flexible calendar: The one-day intervals are kept as the actual interval (often 00:00-23:59 for a public holiday). The worked-time on that day is schedule-shaped (e.g. 8h). Subtracting intervals on a full-day public holiday left a 16h fragment instead of removing the public holiday entry. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L242-L249 Solution: We need to make regular leaves take priority over worked-time leaves, compute the real regular leave intervals first, then remove those intervals from the worked-time leave intervals before work entries are created: - for fully flexible employees, subtract regular leaves from worked leaves; - for flexible calendars, keep one-day regular leaves as is and subtract them from worked-time leaves - for non-flexible attendance-based calendars, clip regular leaves on the static schedule, then subtract them from worked-time leaves clipped on the same schedule. This means that when a sandwich worked-time leave overlaps a public holiday, the public holiday consumes that period first. The overlapping part is then removed from `real_worked_leaves`, so no second worked-time entry is generated for the same public holiday period. opw-6237163 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272487 Forward-Port-Of: odoo/odoo#268527
This update corrects an automated test for the QFpay payment terminal so it matches the current payment flow. It helps keep the test suite reliable and prevents false failures after recent changes to terminal behavior.
Original PR description
In odoo/odoo#270240 we removed the automated "send" call on payment terminals, but the test for pos_qfpay wasn't updated in the fw port. This commit fixes by adding a call to "send" in the test. Forward-Port-Of: odoo/odoo#274316
The webhook sample payload preview now safely handles fields that return complex mapping-like values. This prevents the preview from failing when users include certain accounting data, so server actions can be configured and tested without interruption.
Original PR description
**Steps to Reproduce:** - Create a Server Action of type 'Webhook Notification'. - Select a model containing a field that returns a `frozendict`-based structure (e.g. `account.move` →…
**Steps to Reproduce:** - Create a Server Action of type 'Webhook Notification'. - Select a model containing a field that returns a `frozendict`-based structure (e.g. `account.move` → `needed_terms`). - Add the field to the webhook fields. - Open the webhook sample payload preview. **Issue:** - During sample payload generation: - The selected fields are read from a sample record. - A selected field returns a structure containing `frozendict` objects. - The payload is serialized using `json.dumps()`. - JSON serialization fails with: ```text TypeError: keys must be str, int, float, bool or None, not frozendict ``` - The webhook sample payload computation crashes and the preview cannot be displayed. **Root Cause:** - The webhook sample payload may contain `frozendict` objects returned by selected fields. - The serializer used for payload generation does not handle such mapping-like objects, causing `json.dumps()` to fail. **Solution:** - Use a serializer that converts mapping-like objects into JSON-compatible structures before serializing the webhook sample payload. **OPW-6295777** 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#273119 Forward-Port-Of: odoo/odoo#271864
Cashiers with minimal POS rights can no longer use the keyboard minus key to create a negative quantity line. This keeps keyboard behavior aligned with the disabled minus button and prevents unauthorized quantity changes at checkout.
Original PR description
Currently minimal rights employee cannot select the "+/-" button to have a negative quantity line. However if they have a keyboard and press the "-" key they can modify the quantity to negative. Steps to reproduce: ------------------- * Modify the shop settings, give some employee minimal rights * Open shop and use the minimal employee as cashier * Add a product to the order * Press the "-" key on the keyboard > The line quantity becomes -1 Why the fix: ------------ The button on the product screen is disabled for the employee with minimal rights https://github.com/odoo/odoo/blob/4a2aa33ded628200935b22c501a5f94c21dffb1f/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js#L154 We extend that to the input key "-". opw-6248098 Forward-Port-Of: odoo/odoo#273580 Forward-Port-Of: odoo/odoo#267748
Email unsubscribe links now point to the correct company website for each recipient, instead of sometimes sending people to a login page. This improves the experience for customers receiving mass mailings in multi-company setups and makes unsubscribe actions work reliably.
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 change prevents an error when searching paid POS orders after the employee who created them has been archived. It ensures the system can still load and display those orders normally, improving reliability for users managing employee accounts.
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 fix prevents hours that were already fully invoiced from being billed again after a partial refund. It ensures new invoices only include the remaining unpaid time, which protects customers from duplicate charges and keeps billing accurate.
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
Fixed an issue that could cause an error when opening the forecast margin view. This ensures users can access the view normally and review margin forecasts without interruption.
Original PR description
- The forecast margin view could raise a traceback when opened due to an unexpected condition. This commit fixes the issue to ensure the forecast margin can be opened correctly. task-6345397
WebP images uploaded through Odoo now follow the same size limits as other image formats. This prevents very large images from being accepted, which helps avoid performance and storage issues caused by oversized uploads.
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
All-day events imported from Google were being saved at midnight, which could make them appear on the previous day for users in some time zones. This change stores them in a safer time range so they display on the correct day in Odoo’s list and calendar views.
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
This update prevents link suggestion boxes from overflowing the screen when users type a URL on mobile devices. It improves the link editing experience by keeping the suggestions within the visible area, making the interface easier to use on smaller screens.
Original PR description
Step to reproduce: - Open Notes - Open the link popover - Type a URL in the URL input field Description of the issue/: - On mobile devices, URL autocomplete suggestions overflow the viewport. Cause: - The autocomplete suggestions container has a max-width of 600px. - On smaller screens, the container does not shrink to fit the available width, causing it to overflow the viewport. Solution: - Add width: 100% to the autocomplete suggestions container so it adapts to the available screen width on smaller devices while still respecting the existing max-width on larger screens. task-6201175 Forward-Port-Of: odoo/odoo#271925 Forward-Port-Of: odoo/odoo#269493
17 changes
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
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
This update simplifies the VAT input field in the accounting system, making it easier for users to enter tax ID information. It removes confusing phrases and displays the expected VAT format when available, ensuring accurate data entry. The tooltip has also been streamlined for clarity.
Original PR description
In this commit: - Remove 'or not applicable' and '/ if not applicable' from VAT placeholders. - Display the expected VAT format when available (e.g. BE0477472701). - Leave the placeholder empty when no format is known. - Simplify the tooltip to: 'You can use / to indicate that the customer has no Tax ID.' task-[5005896](https://www.odoo.com/odoo/project/967/tasks/5005896) Forward-Port-Of: odoo/odoo#273344
Resolved issues and error corrections
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
Odoo now recognizes links that point to local addresses such as http://localhost:8069. This makes it easier for users and developers to share or click local environment URLs in messages instead of having them ignored.
Original PR description
Before this commit, url like `http://localhost:8069` were ignored by the `urlRegexp` because it requires a Top-Level Domain. This commit makes the Top-Level Domain optional. Forward-Port-Of: odoo/odoo#274379
The link editor now keeps URL autocomplete suggestions within the screen on smaller devices. This prevents the suggestion box from spilling off the viewport, making it easier to use on mobile and improving the overall editing experience.
Original PR description
Step to reproduce: - Open Notes - Open the link popover - Type a URL in the URL input field Description of the issue/: - On mobile devices, URL autocomplete suggestions overflow the viewport. Cause: - The autocomplete suggestions container has a max-width of 600px. - On smaller screens, the container does not shrink to fit the available width, causing it to overflow the viewport. Solution: - Add width: 100% to the autocomplete suggestions container so it adapts to the available screen width on smaller devices while still respecting the existing max-width on larger screens. task-6201175 Forward-Port-Of: odoo/odoo#271925 Forward-Port-Of: odoo/odoo#269493
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
A technical issue prevented users from previewing the '2Fa New Login' template. This was caused by an assertion error triggered when the preview environment wasn't running with elevated privileges. The fix replaces the assertion with a conditional check that returns placeholder data, allowing the preview to function correctly.
Original PR description
Issue: ---------------------------------------- Clicking "Preview" on the template "2Fa New Login" causes an error. Steps to reproduce: ---------------------------------------- - Search for the mail template "Settings: 2Fa New Login" - Click "Preview" - Error in terminal - From 17.0+ the error shows in UI - From saas-18.3+, a traceback occurs whe resetting the template Cause: ---------------------------------------- There is an assert the method `_get_totp_mail_code()` to ensure it's used during 2FA. But when passing by rendering this template in preview we aren't in sudo and the assert in `_get_totp_mail_code()` raises. Solution: ---------------------------------------- We replace the `assert` by a `if` which will return fake results. opw-6333887 Forward-Port-Of: odoo/odoo#273914 Forward-Port-Of: odoo/odoo#273125
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
21 changes
Enhancements to existing features
Creating employees will now process future public holiday time entries much faster. This prevents long waits or timeouts when many holidays have been set up far into the future, improving reliability for HR and planning teams.
Original PR description
**Problem:** When creating a new employee, the future timesheets due to public holidays are computed. If the number of public holidays is large (i.e. if the user creates them for each year, several…
**Problem:** When creating a new employee, the future timesheets due to public holidays are computed. If the number of public holidays is large (i.e. if the user creates them for each year, several years in the future), then it takes excessively long and the action may not complete. **Cause:** The pytz method `localize` and comparing times with non-static timezones is done repeatedly and unnecessarily which becomes costly with more records. **Solution:** Only localize the time when absolutely necessary (determining the date of the leave in the calendar timezone). **Performance Stats:** |Record count|Time before|Queries before|Time after|Queries after| |------------|-----------|--------------|----------|-------------| |100 |3.1s |393 |0.8s |117 | |1,000 |22.3s |2,090 |1.5s |183 | |10,000 |Timeout |N/A |6.7s |541 | opw-6087422 Forward-Port-Of: odoo/odoo#270684 Forward-Port-Of: odoo/odoo#263953
This update simplifies the VAT input field in the accounting module, making it easier for users to enter tax ID information. The system now displays the expected VAT format when available and provides a clearer tooltip explaining how to indicate a customer without a tax ID. This improves data accuracy and user experience.
Original PR description
In this commit: - Remove 'or not applicable' and '/ if not applicable' from VAT placeholders. - Display the expected VAT format when available (e.g. BE0477472701). - Leave the placeholder empty when no format is known. - Simplify the tooltip to: 'You can use / to indicate that the customer has no Tax ID.' task-[5005896](https://www.odoo.com/odoo/project/967/tasks/5005896) Forward-Port-Of: odoo/odoo#273344
Resolved issues and error corrections
This update fixes a display issue where URL autocomplete suggestions could extend beyond the screen on mobile devices. The suggestions now fit the available width, making it easier to use the link editor without layout problems on smaller screens.
Original PR description
Step to reproduce: - Open Notes - Open the link popover - Type a URL in the URL input field Description of the issue/: - On mobile devices, URL autocomplete suggestions overflow the viewport. Cause: - The autocomplete suggestions container has a max-width of 600px. - On smaller screens, the container does not shrink to fit the available width, causing it to overflow the viewport. Solution: - Add width: 100% to the autocomplete suggestions container so it adapts to the available screen width on smaller devices while still respecting the existing max-width on larger screens. task-6201175 Forward-Port-Of: odoo/odoo#271925 Forward-Port-Of: odoo/odoo#269493
This change prevents a validation error that could happen when users edit subcontracting raw materials by removing one line and adding another in the same step. It helps production recording complete reliably without losing required product information during the save process.
Original PR description
**Issue** In subcontracting, deleting a raw move line and adding a new one in the same editing flow can lead to a validation error during production recording. **Steps to reproduce** - Create a…
**Issue** In subcontracting, deleting a raw move line and adding a new one in the same editing flow can lead to a validation error during production recording. **Steps to reproduce** - Create a subcontracting product with a comp A - Create and confirm a purchase order of that product (with the subcontracting partner) - Open the associated delivery - Open the move details (hamburger button) - Delete the move line linked to the comp A - Create a new move line for a comp B with a quantity of 1 - Record the production -> A validation error occurs: the mandatory field `product_uom_id` is not set. **Cause** The regression comes from this commit: https://github.com/odoo/odoo/commit/54f10b56f577ad9ed5575bd396dba7d20d22fc2e While assigning `move_raw_ids`, the inverse method is triggered: https://github.com/odoo/odoo/blob/9267b2d1a9b2d2d6a33eceab07d572406c68c723/addons/mrp_subcontracting/models/mrp_production.py#L34 At this stage, newly added lines are still virtual records (`line`): https://github.com/odoo/odoo/blob/9267b2d1a9b2d2d6a33eceab07d572406c68c723/addons/mrp_subcontracting/models/mrp_production.py#L38 The previous implementation directly unlinked removed move lines (see commit https://github.com/odoo/odoo/commit/54f10b56f577ad9ed5575bd396dba7d20d22fc2e): https://github.com/odoo/odoo/blob/9267b2d1a9b2d2d6a33eceab07d572406c68c723/addons/mrp_subcontracting/models/mrp_production.py#L40-L43 Which will eventually flush and invalidate all the cache: https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/odoo/models.py#L4666 And since `line` is a virtual record (not in db), its associated values will be reset, among those, `product_uom_id`. Later, when the move line is reassigned: https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/addons/mrp_subcontracting/models/mrp_production.py#L49 https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/odoo/models.py#L5223-L5228 the validation fails because the virtual line no longer contains the required values. **Additional note** An alternative could have been using Command but since this line: https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/addons/mrp_subcontracting/models/mrp_production.py#L42 can not be converted to: `Command.set([line.id for line in lines])` because `lines` may also contain virtual records. This causes an invalid quantity for the move. Indeed, even if the command operator would update the quantity on the `move_line` correctly, it won't for the quantity of the `move` because of its associated compute method: https://github.com/odoo/odoo/blob/26ba95ac1c5bbb24975efb1a6f53c1ab47b61532/addons/stock/models/stock_move.py#L399-L400 that relies on `.ids`, which is `[]` on virtual records. Therefore, keep the change minimal. opw-6133281 Forward-Port-Of: odoo/odoo#267279 Forward-Port-Of: odoo/odoo#263058
All-day events synced from Google Calendar will now be stored in a way that keeps them on the correct day for users in every time zone. This prevents dates from appearing one day earlier in Odoo’s list view, while keeping the calendar display consistent.
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
Signing up with the same email address in different letter cases will now be treated as the same account. This prevents customers from accidentally creating duplicate logins for one mailbox and makes account creation messages clearer.
Original PR description
Login uniqueness is enforced by a `UNIQUE (login)` constraint that Postgres compares byte for byte, so signing up with foo@example.com and then Foo@example.com produces two separate accounts pointing…
Login uniqueness is enforced by a `UNIQUE (login)` constraint that Postgres compares byte for byte, so signing up with foo@example.com and then Foo@example.com produces two separate accounts pointing at the same real mailbox. https://github.com/odoo/odoo/blob/66a6c16551041543b5addfe846f15e769b4e9afe/odoo/addons/base/models/res_users.py#L274 Even if the DB constraint did catch an exact-case duplicate and `_signup_create_user` re-raised it as a `SignupError`, the controller's friendly "already registered" branch only triggers when the duplicate lookup finds a row, and that lookup goes through `_get_login_domain` with an exact `=` operator. Case variants would fall into the generic "Could not create a new account" branch instead. https://github.com/odoo/odoo/blob/66a6c16551041543b5addfe846f15e769b4e9afe/addons/auth_signup/controllers/main.py#L68-L75 https://github.com/odoo/odoo/blob/66a6c16551041543b5addfe846f15e769b4e9afe/odoo/addons/base/models/res_users.py#L749-L750 `_signup_create_user` now refuses creation when a user with the same email already exists, applying to both b2c free signup and token-based invitations. It raises `UserError` directly so the controller's `except UserError` surfaces the message without a redundant lookup. The check uses `_get_email_domain`, whose base implementation is switched from `=` to `=ilike` over a value escaped via `tools.escape_psql` so `%` and `_` are matched literally rather than as wildcards. Its only existing caller is `reset_password`, which already wants case-insensitive matching. Steps to reproduce: 1. In Settings, set Customer Account to "Free sign up" and save. 2. Log out, then on the login page click "Don't have an account?". 3. Register with foo@example.com. 4. Log out again and click "Don't have an account?". 5. Register with Foo@example.com. => Two distinct user accounts are created for the same mailbox. opw-6199441 Forward-Port-Of: odoo/odoo#263864
This update prevents a validation error when confirming a manufacturing order that uses a component owned by another company branch. It helps users complete production without being blocked by access restrictions on shared product settings.
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 update fixes how the system recognizes URLs that point to local services, such as "http://localhost:8069". As a result, these links will now be handled consistently instead of being skipped, which improves testing and local setup workflows.
Original PR description
Before this commit, url like `http://localhost:8069` were ignored by the `urlRegexp` because it requires a Top-Level Domain. This commit makes the Top-Level Domain optional. Forward-Port-Of: odoo/odoo#274379
This fix prevents an error when changing a product’s Bill of Materials type in one company while related sales exist in another company. It ensures Odoo only blocks changes when the affected records belong to the same company, avoiding unnecessary interruptions for users working across 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
When a new Indian company is created, the shared Half Up cash rounding is now duplicated for that company instead of being moved from the previous one. This prevents access errors on invoices and ensures each company keeps its own rounding setup.
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
This fix ensures that when a cashier logs into Point of Sale using an employee account, the receipt sent by email shows the same cashier name as the in-store receipt. It prevents customers from seeing the connected user’s name instead of the actual employee who handled the sale.
Original PR description
Currently, when using the "login with employee" feature and sending the order receipt by mail, the cashier name is not the name of the employee using the pos. Steps to reproduce: ------------------- * Use the login with employee feature * Open the pos * Connect with an employee not linked to the current user * Make an order * Send the receipt to the customer by mail. > The receipt from the shop shows the cashier's name, the receipt sent by mail shows the connected user as the cashier opw-6291485
The Unit Cost History report now shows quantities using the product’s own unit of measure instead of the move’s unit. This fixes inflated quantities and added value amounts when materials are consumed in a different unit, giving users accurate costing information.
Original PR description
**Issue** The quantity displayed in the Unit Cost History report can be incorrect when the move UoM differs from the product UoM. **Steps to reproduce** - Create an AVCO product Comp tracked in tons…
**Issue** The quantity displayed in the Unit Cost History report can be incorrect when the move UoM differs from the product UoM. **Steps to reproduce** - Create an AVCO product Comp tracked in tons with a unit price of 100. - Create another product with a BoM consuming 100kg of Comp - Create, confirm and produce a MO for that product - Open the Unit Cost History for Comp -> Quantity is incorrect (100 instead of 0.1). As a consequence, the computed added value displayed in the report is also incorrect (10000 instead of 10). **Cause** The quantity and added value fields come from the `stock.avco.report` model, which is defined by this SQL view: https://github.com/odoo/odoo/blob/aef190dbff365f4fe5d92a2c41c35f92b66ce5fd/addons/stock_account/report/stock_avco_audit_report.py#L36-L39 The view uses the `quantity` field of `stock.move`: https://github.com/odoo/odoo/blob/aef190dbff365f4fe5d92a2c41c35f92b66ce5fd/addons/stock_account/report/stock_avco_audit_report.py#L48 without converting it in the right uom opw-6271229 Forward-Port-Of: odoo/odoo#269846
This update improves the speed of finding tasks in timesheets when the holidays extension is installed. It helps users get search results more quickly, making time entry and task lookup smoother.
Original PR description
This commit adds an index to speed up the task name_search in timesheets when project_timesheet_holidays is installed.
This change fixes the “Parallax to Bottom” intensity slider so users can choose custom values below the previous limit. It prevents the slider from snapping back to the minimum, making the website builder behave as expected.
Original PR description
`BuilderRange` supported inverted ranges (`props.min > props.max`) to keep the slider direction consistent across options, which was the case for the `Parallax to Bottom` (`min="-0.15" / max="-3"`) with `get min()`/`get max()` normalizing the bounds and `o_we_inverted_range` flipping the direction whenever `props.min > props.max` was detected. For the Bottom case, any value smaller than `-0.15` (e.g. `-1.5`) satisfied `value < props.min` and was clamped back to `-0.15`, making custom intensities impossible. Since this is the only inverted `BuilderRange` in the codebase, we restored the right order for the min/max, dropped the getters and replaced them with a prop to apply the `o_we_inverted_range` class in this scenario only. task-6058500 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255252
A technical issue prevented users from previewing the '2Fa New Login' template. This was caused by an assertion that triggered when the preview environment lacked the necessary permissions. The fix replaces the assertion with a conditional check that returns placeholder data, allowing the preview to function correctly.
Original PR description
Issue: ---------------------------------------- Clicking "Preview" on the template "2Fa New Login" causes an error. Steps to reproduce: ---------------------------------------- - Search for the mail template "Settings: 2Fa New Login" - Click "Preview" - Error in terminal - From 17.0+ the error shows in UI - From saas-18.3+, a traceback occurs whe resetting the template Cause: ---------------------------------------- There is an assert the method `_get_totp_mail_code()` to ensure it's used during 2FA. But when passing by rendering this template in preview we aren't in sudo and the assert in `_get_totp_mail_code()` raises. Solution: ---------------------------------------- We replace the `assert` by a `if` which will return fake results. opw-6333887 Forward-Port-Of: odoo/odoo#273914 Forward-Port-Of: odoo/odoo#273125
This update ensures that webp images, like other image formats, are now subject to the 50MPx resolution limit when uploaded through the website editor. Previously, webp images could be uploaded regardless of size, leading to potential issues with website performance and design. This change corrects a bug in the image processing 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
This update fixes a UI issue where refunds weren't accurately displaying the change amount. Previously, the system incorrectly showed change as a positive value. Now, refunds and overpayments are displayed correctly as negative amounts, ensuring accurate financial reporting and a better customer experience.
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#263606 Forward-Port-Of: odoo/odoo#256776
This commit addresses a minor issue identified in a previously merged test related to the Italian VAT withholding functionality. The change ensures the test accurately reflects the current state of the code, preventing potential discrepancies in reporting. This is a routine maintenance update to maintain the quality and reliability of our Italian tax integration.
Original PR description
This commit just want to correct a test of a PR already merged. Original commit: 78ffb5a2e63401123e4506056493e52cf3e69953 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274262 Forward-Port-Of: odoo/odoo#274062
This update fixes an issue where purchase order receipt deadlines weren't updating correctly after quantities were set to zero. The fix ensures that cancelled stock moves no longer incorrectly influence the calculated deadline, providing accurate and reliable receipt scheduling for purchase orders. This improves the accuracy of delivery timelines.
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 update fixes a testing issue within the account_edi_ubl_cii module by using a realistic partial XML file for partner bank account retrieval tests. Previously, a generated XML was used, which wasn't representative of real data. This change ensures more accurate and reliable testing of the bank account retrieval process.
Original PR description
Move the partner retrieval bank account number test to the `test_ubl_import_bis3_invoice_be_retrieve_partner.py` file and use a partial XML instead of a generated XML. Forward-Port-Of: odoo/odoo#274158 Forward-Port-Of: odoo/odoo#269995
Features or functions removed from Odoo
Due to Paymob unexpectedly ceasing operations in Pakistan, we've removed support for this payment provider within Odoo. This change ensures continued functionality and avoids disruptions for users who no longer rely on Paymob in that region.
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
15 changes
Resolved issues and error corrections
Previewing the “2Fa New Login” email template now works without triggering an error. This prevents interruptions for administrators who want to review or reset the template, while keeping the 2-factor login flow unchanged.
Original PR description
Issue: ---------------------------------------- Clicking "Preview" on the template "2Fa New Login" causes an error. Steps to reproduce: ---------------------------------------- - Search for the mail template "Settings: 2Fa New Login" - Click "Preview" - Error in terminal - From 17.0+ the error shows in UI - From saas-18.3+, a traceback occurs whe resetting the template Cause: ---------------------------------------- There is an assert the method `_get_totp_mail_code()` to ensure it's used during 2FA. But when passing by rendering this template in preview we aren't in sudo and the assert in `_get_totp_mail_code()` raises. Solution: ---------------------------------------- We replace the `assert` by a `if` which will return fake results. opw-6333887 Forward-Port-Of: odoo/odoo#273914 Forward-Port-Of: odoo/odoo#273125
This fixes an issue where selling and dropshipping a kit could leave a component’s cost incorrectly set to zero after delivery. The correction ensures the supplier cost is properly carried through so inventory valuation stays accurate for AVCO/FIFO products.
Original PR description
**Issue** Selling a kit with dropshipping in AVCO/FIFO could wrongly set the standard_price of the component product to 0 after validating the dropship transfer. **Steps to reproduce** - Create a kit…
**Issue** Selling a kit with dropshipping in AVCO/FIFO could wrongly set the standard_price of the component product to 0 after validating the dropship transfer. **Steps to reproduce** - Create a kit and component product - Activate MTO and dropship route for the kit - Activate dropship route for the component - Add a vendor for the component (ex: 100 dollars) - Set component to AVCO valuation - Create and confirm a sale order - Confirm the associate purchase order and the dropship transfer - Go to the product -> The standard price is still 0 instead of being updated from the supplier price. **Cause** While confirming the PO, a picking is created: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_stock/models/purchase_order.py#L371 With its associated moves: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_stock/models/purchase_order.py#L383 During the move preparation, the `cost_share` is not propagated on the generated move values, so it remains equal to 0. The `cost_share` is computed while exploding the kit BOM: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_mrp/models/purchase.py#L94 https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/mrp/models/mrp_bom.py#L463 https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_mrp/models/mrp_bom.py#L62 https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_mrp/models/mrp_bom.py#L70 However, only the `bom_line_id` is propagated on the move values: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_mrp/models/purchase.py#L94-L99 Later, while validating the dropship transfer: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/stock_account/models/stock_move.py#L177 the move value is used to recompute the standard price: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/stock_account/models/product.py#L651 While `move.value` is not zero, `move._get_value` is: https://github.com/odoo/odoo/blob/cac987867b083355d3366228d0c551b34f366d92/addons/stock_account/models/product.py#L485-L487 since it depends on the move `cost_share`: https://github.com/odoo/odoo/blob/43f5ceadbc1f7df9898c327bf65bffdbe9860c1c/addons/purchase_mrp/models/stock_move.py#L25 Since the move `cost_share` is 0, the AVCO/FIFO recomputation uses an incorrect value and the component standard price is not updated. **Solution** No need to use `cost_share` to compute the value if the price_unit is already given for the component opw-6176571 Forward-Port-Of: odoo/odoo#264341
When the app loses connection, users can now keep navigating Discuss channels and access some channel details instead of having all actions blocked. This makes it easier to review locally available messages, members, pinned posts, attachments, and threads while temporarily offline.
Original PR description
Before this commit, when losing connection to the server, the "Offline UI" introduced in [1] would disable all buttons in Discuss. This prevents navigating the Discuss channels, even if we potentially have local knowledge of the messages in those channels. It also prevents using Thread actions like: - Channel Members - Pinned Messages - Attachments - Threads Which may also only need data that is available locally. This commit fixes the issue by marking the appropriate buttons as available offline (`data-available-offline`), which prevents the Offline UI service from disabling them. [1] https://github.com/odoo/odoo/pull/229492 task-6185454
The parallax-to-bottom intensity slider now accepts custom values correctly instead of snapping back to the default minimum. This fixes a usability issue that prevented users from fine-tuning the effect as intended.
Original PR description
`BuilderRange` supported inverted ranges (`props.min > props.max`) to keep the slider direction consistent across options, which was the case for the `Parallax to Bottom` (`min="-0.15" / max="-3"`) with `get min()`/`get max()` normalizing the bounds and `o_we_inverted_range` flipping the direction whenever `props.min > props.max` was detected. For the Bottom case, any value smaller than `-0.15` (e.g. `-1.5`) satisfied `value < props.min` and was clamped back to `-0.15`, making custom intensities impossible. Since this is the only inverted `BuilderRange` in the codebase, we restored the right order for the min/max, dropped the getters and replaced them with a prop to apply the `o_we_inverted_range` class in this scenario only. task-6058500 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Paymob has unexpectedly stopped operating in Pakistan, and its payment APIs no longer work there. This update removes Pakistan as a supported country for the Paymob payment provider so customers are not offered a broken payment option.
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 change corrects an automated test related to Italian e-invoicing withholding. It does not introduce new business behavior, but helps ensure the existing feature is verified correctly and stays reliable after related updates.
Original PR description
This commit just want to correct a test of a PR already merged. Original commit: 78ffb5a2e63401123e4506056493e52cf3e69953 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274062
This fix makes the system correctly detect links that point to local addresses such as localhost. As a result, these links will be handled like other web links instead of being ignored, which improves testing and local development workflows.
Original PR description
Before this commit, url like `http://localhost:8069` were ignored by the `urlRegexp` because it requires a Top-Level Domain. This commit makes the Top-Level Domain optional. Forward-Port-Of: odoo/odoo#274379
Fixed an issue where recurring calendar events synced from Google could create one extra event on the final allowed day for users in timezones behind UTC. This keeps Odoo aligned with Google Calendar and avoids duplicate meetings appearing after a series is shortened.
Original PR description
When Google sends a recurrence with UNTIL in UTC (UNTIL=...Z), users in timezones behind UTC can get one extra occurrence on the boundary day. Google's UNTIL represents the last allowed start in UTC,…
When Google sends a recurrence with UNTIL in UTC (UNTIL=...Z), users in timezones behind UTC can get one extra occurrence on the boundary day. Google's UNTIL represents the last allowed start in UTC, but that UTC date fell into the previous local day. Because Odoo was comparing event start times as naive local datetimes against a cutoff derived from the wrong date, the boundary occurrence passed the check and was created. Steps to reproduce: 1. Set the user's timezone to a UTC-negative offset (e.g. America/Argentina/Buenos_Aires, UTC-3). 2. In Google Calendar, create a weekly recurring event (e.g. every Thursday at 12:00 local). 3. Edit the series with "This and following events" so the old series ends with UNTIL set to 02:59:59 UTC of the next day (= 23:59:59 local of the last valid occurrence day). 4. Sync with Odoo -> an extra event is created on the day after the last valid Thursday, which does not exist in Google Calendar. opw-6024835 Forward-Port-Of: odoo/odoo#273915 Forward-Port-Of: odoo/odoo#265297
Fixed an issue where a chatbot could crash if it tried to hand a visitor over to an operator, but no operators were configured for the live chat channel. Instead of showing an error, the chat now handles this case cleanly so the conversation can continue without interruption.
Original PR description
When a live chat channel has no operators configured and the chatbot script ends with a Forward to operator step, triggering that step causes a traceback. Steps to reproduce the error: - Install…
When a live chat channel has no operators configured and the chatbot script ends with a Forward to operator step, triggering that step causes a traceback. Steps to reproduce the error: - Install ``im_livechat`` module with demo data - Go to Live Chat > Configuration > Chatbots > Create a new chatbot > Add script > Step Type: Question > Set answers > Save > Add script > Step Type: Forward to operator > Only If: Set one of the above answers > Save - Go to Live chat > Channel > Click the configure channel on YourWebsite.com > Remove the operators > Save - Go to the chatbot > test > select the configured answer Traceback: ```py StopIteration ``` https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/addons/im_livechat/controllers/chatbot.py#L65-L70 When the chatbot script reaches a Forward to operator step while no operator is configured in the live chat channel, no chatbot message is created. As a result, the generator iterates over an empty iterator, and the ``next()`` call raises a ``StopIteration`` exception, causing a traceback during the conversation. sentry-7435424405 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274384 Forward-Port-Of: odoo/odoo#261726
The attendance-based timesheet report now uses fully specified column names in its database query. This prevents the report from failing in customer databases that add their own fields with the same names, helping ensure the report opens reliably.
Original PR description
In `hr_timesheet_attendance_report`, the SQL query was using unqualified columns (e.g. `date` instead of `ts.date`)
It was not an issue in standard, but if a customer adds a `date` or `check_in` column to `hr_employee`, the query becomes ambiguous and fails.
To solve the issue, we explicitly qualify `ts.date` and `hr_attendance.check_in`.
upg-4445460
```python
File "/home/odoo/src/odoo/19.0/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py", line 24, in init
self.env.cr.execute("""CREATE OR REPLACE VIEW %s AS (
File "/home/odoo/src/odoo/19.0/odoo/sql_db.py", line 440, in execute
self._obj.execute(query, params)
psycopg2.errors.AmbiguousColumn: column reference "date" is ambiguous
LINE 44: AND date <= CURRENT_DATE
```
Forward-Port-Of: odoo/odoo#274341This update prevents URL autocomplete suggestions from spilling outside the screen on smaller devices. It improves the mobile editing experience by making the suggestion box fit the available width while still behaving normally on larger screens.
Original PR description
Step to reproduce: - Open Notes - Open the link popover - Type a URL in the URL input field Description of the issue/: - On mobile devices, URL autocomplete suggestions overflow the viewport. Cause: - The autocomplete suggestions container has a max-width of 600px. - On smaller screens, the container does not shrink to fit the available width, causing it to overflow the viewport. Solution: - Add width: 100% to the autocomplete suggestions container so it adapts to the available screen width on smaller devices while still respecting the existing max-width on larger screens. task-6201175 Forward-Port-Of: odoo/odoo#271925 Forward-Port-Of: odoo/odoo#269493
This change prevents a crash that could happen when users try to split a transfer that is already completed. In that situation, there is nothing left to split, so the system now safely does nothing instead of showing an error.
Original PR description
Issue before this commit: ========================= When splitting a done picking that contains at least one stock move whose done quantity is less than the demanded quantity (product_uom_qty), an…
Issue before this commit: ========================= When splitting a done picking that contains at least one stock move whose done quantity is less than the demanded quantity (product_uom_qty), an expected singleton traceback occurs. Steps to Reproduce: ========================= - Install the stock module with demo data. - Create a delivery picking for any product with a demand of 5. - Set the done quantity to 2. - Validate the picking without creating a backorder. - Try to split the validated/done picking. - An expected singleton traceback is raised. Cause of the issue: ========================= Previously, attempting to split a done picking simply returned because there was nothing left to split. After this [PR](https://github.com/odoo/odoo/pull/224952), the split action calls **message_post()** to post a note on the original picking of the generated backorder. However, no backorder is created when splitting a done picking since there is no remaining quantity to split. As a result, message_post() is called on an empty recordset, leading to an expected singleton traceback. With This Commit: ========================= Splitting a done picking has no functional purpose, as there is nothing left to split. In this case, simply return without performing any action. This preserves the previous behaviour and prevents the traceback.
This update ensures that certain Odoo modules are correctly licensed as LGPL-3, aligning with their community module status. Previously, these modules were incorrectly marked with an enterprise license. This change clarifies licensing and avoids potential legal issues.
Original PR description
Before this commit, the license set on manifest of some modules uses the enterprise license instead of `LGPL-3` license since it is a community module. This commit changes the license to set `LGPL-3`. Fixes #205134 Forward-Port-Of: odoo/odoo#274123 Forward-Port-Of: odoo/odoo#273597
This update fixes an issue where call controls were hidden behind the browser's URL bar on mobile devices. The change adjusts the overlay size to dynamically adapt to the visible viewport, ensuring call controls remain visible regardless of browser chrome. This improves the user experience when joining calls on mobile browsers.
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
This update resolves an issue where inventory loss operations incorrectly opened a new scrap form instead of the associated picking. The fix ensures that when creating an inventory adjustment, the system now correctly directs users to the relevant picking, improving usability and accurate tracking of inventory loss. This change was made to align with the standard behavior of the system.
Original PR description
Currently, when the user manually creates an operation for inventory adjustment and then creates a picking for that operation, opening the move line for a product does not navigate to the…
Currently, when the user manually creates an operation for inventory adjustment and then creates a picking for that operation, opening the move line for a product does not navigate to the corresponding picking. Instead, it opens a new scrap form. ## Steps to replicate: - Install Inventory - Go to settings and Enable 'Storage Locations' - Create a product named 'Pen' with an on-hand quantity of 10. - Navigate to Inventory > Configurations > Locations - Create a new location: - Name: Office Supplies Consumption' - Location Type: Inventory Loss - Go to Configurations > Operation types > Create new operation type: - Name: Transfer to Office Consumption - Type of operation: Delivery - Sequence prefix: TOC - Source Location: 'WH/Stock' - Destination Location: Office Supplies Consumption - Create and validate a picking for 1 Pen using the operation type 'Transfer to Office Consumption'. - Open the 'Pen' product form. > In/Out smartbutton - Open the move line corresponding to the transfer created. ## Observed behavior: Instead of redirecting the user to the picking created for the office supply consumption operation, the system opens a new stock scrap form view. ## Root cause: This issue occurs when a user clicks the move line reference associated with a picking. In that case, `action_open_reference` is invoked on the stock move line, which subsequently calls the method with the same name on the related stock move at [1]. As a result, the scrap view is returned at [2]. This happens because the move is not marked as is_inventory, since the operation neither relocates quants nor manually adjusts on-hand quantities and the destination location still uses the Inventory Loss / Inventory usage type. Since the move is not linked to any scrap record, its `scrap_id` is empty. Consequently, the method returns an empty scrap form, which causes the issue. **Why did this not occur in earlier versions?** Prior to this [commit](https://github.com/odoo/odoo/commit/53181c7ac4d940d889370da61d39150560cdb0d8 ), the `scrap_location` field existed. After its removal, the logic was changed to rely on locations of type Inventory Loss, effectively causing all inventory loss locations to be treated as scrap locations. Before this change, the condition depended on the scrapped boolean [3], which was only set for actual scrap locations. Because not all inventory loss locations were considered scrap locations, the system would fall back to opening the picking form as the reference, avoiding this issue. **Why does the issue not occur in 19.2+?** The issue no longer occurs in 19.2+ because the `stock.scrap` model was removed and the scrap-related logic was moved to stock.move. As part of this [commit](https://github.com/odoo/odoo/commit/1c7d80a10b5d7db1c4163166bf52b3f3c77044ba ), the code path that returned the stock scrap view was removed. The system now falls back to the picking view instead, as shown at [4]. [1]- https://github.com/odoo/odoo/blob/65937b5cdc6c638a9e9ff7085cbab1567ca24142/addons/stock/models/stock_move_line.py#L1030 [2]- https://github.com/odoo/odoo/blob/65937b5cdc6c638a9e9ff7085cbab1567ca24142/addons/stock/models/stock_move.py#L2584-L2594 [3]- https://github.com/odoo/odoo/blob/24f829cd8de3d97d6ed414e179f9903e3f8e7b70/addons/stock/models/stock_move.py#L2542-L2552 [4]- https://github.com/odoo/odoo/blob/90769e7f74b4d8e86e5bbc2e4403ed4cd0c09ae6/addons/stock/models/stock_move.py#L2609-L2627 ## Solution: Tighten the condition for inventory loss operations so that the scrap view is only returned when the move is actually linked to a scrap record (`scrap_id`). Otherwise, the method should fall back to opening the source picking, matching the behavior of both earlier and later versions. This distinction is important because an inventory loss location is not necessarily used for scrapping. As demonstrated in the reproduction steps, such locations can also be used to track other forms of inventory loss, such as distributing office supplies to employees. Creating dedicated inventory loss locations allows users to categorize and account for different types of losses more accurately. In these cases, opening the picking that generated the inventory loss provides a more meaningful reference to the user than displaying a new, empty scrap form. opw-6270679 Forward-Port-Of: odoo/odoo#270563
10 changes
Enhancements to existing features
The out-of-office banner now keeps the "Back on" status on a single line instead of letting it wrap. This makes the header look cleaner and more consistent for users.
Original PR description
Previously, the 'Back on' status in the out-of-office banner could wrap onto multiple lines, making the header appear misaligned. This PR keeps the status on a single line for a cleaner and more consistent layout. <table> <tr> <th>Before</th> <th>After</th> </tr> <tr> <td> <img width="378" height="630" alt="image" src="https://github.com/user-attachments/assets/e4c9f278-9a65-48ad-8841-6ed059bd766d" /> </td> <td> <img width="372" height="631" alt="image" src="https://github.com/user-attachments/assets/f3dd860b-eba5-4233-a4c0-f286eec95c63" /> </td> </tr> </table> --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Resolved issues and error corrections
This update corrects how Nilvera PDF responses are saved so the system stores the actual PDF file instead of base64 text. As a result, users can preview and download Turkish e-invoice PDFs again without errors.
Original PR description
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session…
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes.
The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` from Python 3.14's stricter base64 validation in the `datas` auto-decode path. That switch silently changed what ends up on disk (`datas` decodes its input, `raw` does not)
Storing that string in the binary `raw` field encodes it as UTF-8, so the file on disk ends up as the literal ASCII of the base64 text. The attachment is served as `application/pdf` but the browser receives base64 ASCII and cannot preview or download the PDF.
Call `b64decode(response)` before storing so the attachment contains the actual PDF bytes.
OPW-6302803
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#273791
Forward-Port-Of: odoo/odoo#270759This update replaces an outdated Belgian VAT number in a test with a valid one. It helps ensure the invoice import checks behave consistently across different environments and library versions.
Original PR description
The previous Belgian VAT is rejected by newer versions of `python-stdnum`. Replace it with a valid VAT so the test behaves consistently across environments. Forward-Port-Of: odoo/odoo#274402
This fix ensures imported credit note lines keep the right sign for quantities and prices, so taxes are calculated correctly. As a result, Belgian credit notes imported from XML now show the expected total instead of being reduced by a negative tax amount.
Original PR description
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as…
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as a negative value which is corrected with a rounding line. - The 6% tax rate is applied to the negative invoice line, resulting in a negative tax amount being deducted from the total (e.g., 449.32 + (-26.96) = 422.36) instead of being added (449.32 + 26.96 = 476.28) Expected behavior: price_unit, quantity and the related tax amounts should all be positive, matching a normal in_refund/out_refund line. Why this happens: - In `_import_ubl_invoice_line_add_price_unit_quantity_discount`, `BaseQuantity` was multiplied by file_document_sign, unlike `PriceAmount` from the same node which is left untouched. This flips price_quantity to -1, which later flips price_unit to negative when `price_unit = price_subtotal / price_quantity`. opw-6310442 Forward-Port-Of: odoo/odoo#271148
This change reduces the number of server calls needed when the Point of Sale loads orders. As a result, order retrieval is faster and users should experience less waiting during POS operations.
Original PR description
Issue: pos_self_order overrode getServerOrders() to add a separate loadServerOrders() call for it's own orders before delegating to super, resulting in up to an additional sequential RPCs on every order fetch. Fix: Extract the base query domain into a new overridable getServerOrdersDomain() method. Each module overrides it to OR in its own domain via Domain.or([super.getServerOrdersDomain(), extraDomain]), so all orders are fetched in a single RPC call instead of three. Task-6284860 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#274148 Forward-Port-Of: odoo/odoo#269260
This update prevents errors when opening payments that include withholding taxes in Argentina, especially when a 0% withholding line is present. It also ensures these withholding lines are not removed when a payment is reset to draft, so users can keep managing the payment correctly without having to recreate lines.
Original PR description
**Steps to reproduce:** - Install Accounting and l10n_ar_withholding - Switch to an Argentinian company (e.g. (AR) Responsable Inscripto) - Create a 0% Payment Withholding tax: * Tax Type: Customer…
**Steps to reproduce:**
- Install Accounting and l10n_ar_withholding
- Switch to an Argentinian company (e.g. (AR) Responsable Inscripto)
- Create a 0% Payment Withholding tax:
* Tax Type: Customer Payment Withholding
* Amount: 0.00 %
* Add an account for the tax distribution lines
- Create an invoice with a tax
- Confirm the invoice
- Pay the invoice:
* Withholdings:
- Add a line with the created 0% Payment Withholding tax
- Add a line with another Payment Withholding tax
- Create Payment
- Go to the payment
**Issue 1:**
When clicking on the first withholding line, a JS error is raised due to a missing index (i.e. currency_id).
**Cause 1:**
One of the fields has an aggregate sum function applied on it (i.e. amount_currency).
As it is a monetary field, the corresponding currency field is required in the view.
**Issue 2:**
When resetting the payment to draft, the withholding line with the 0% tax is deleted.
As the withholding table is not editable, it is not possible to add the line again.
**Cause 2:**
When the payment is reset to draft, the state of the associated journal entry is also set to draft and a "_sync_dynamic_lines" is triggered, which remove tax lines having a zero amount during the process.
**Solution 2:**
Keep all the lines with a Customer Payment Withholding tax as it is not possible to add a withholding line in the payment afterwards.
opw-6298058
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#271555This update corrects a display issue in journal entry previews when currency information is missing. Previously, the credit column could incorrectly repeat the debit amount, which could confuse users reviewing entries.
Original PR description
In _move_dict_to_preview_vals(), when no currency is provided, the credit column falls back to the line's debit value, so any caller omitting currency_id would show the debit amount in both columns of the journal entry preview. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269335
This fix removes a blocker that prevented users from deleting an expense when it had an attachment. It helps keep expense records manageable without requiring users to manually work around attached files first.
Original PR description
To reproduce: - Create an expense - Add an attachment - Try to delete the expense --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix ensures employee leave start and end times are calculated from the correct work schedule when a calendar has effective date ranges. As a result, time off is assigned more accurately in cases where different schedules apply on different dates, preventing incorrect leave hours from being shown or used.
Original PR description
Define the correct hours in the leaves if the calendar has defined dates (`date_from` and `date_to`) Use case example: - Create a calendar and define on Friday (Morning: from 08:00 to 13.00,…
Define the correct hours in the leaves if the calendar has defined dates (`date_from` and `date_to`) Use case example: - Create a calendar and define on Friday (Morning: from 08:00 to 13.00, Afternoon: from 19:00 to 21:00) with date_to=2025-01-01. - Define another specific Friday (Morning: from 09:00 to 14.00, Afternoon: from 17:00 to 20:00) in the same calendar with date_from=2025-01-01. - Create an employee and define the calendar created for him/her. - Create a leaves for the employee and select a Friday (2025-05-02). - The start hour of the leave must be 2025-05-02 09:00:00 - The end hour of the leave must be 2025-05-02 20:00:00   Please @pedrobaeza can you review it? @Tecnativa TT56218 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255398 Forward-Port-Of: odoo/odoo#208378
This update resolves a test failure related to an outdated cryptography library used in Odoo. The change skips a specific test that previously failed when using older versions of the library, ensuring consistent test results. This improves the stability and reliability of Odoo's certificate handling.
Original PR description
CertificateBuilder.public_key() rejects X25519 keys before cryptography 36.0.0, and Odoo pins 3.4.8 for python < 3.12, so test_is_issued_by errored on runbot. Skip the X25519 case when the lib can't build it. https://cryptography.io/en/latest/changelog/#v36-0-0 Runbot Error: https://runbot.odoo.com/odoo/runbot.build.error/941306 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#274371
8 changes
New functionality added to Odoo
This update introduces new accounting accounts within the Odoo Vietnam (l10n_vn) module to better support financial reporting. Specifically, it allows for distinguishing between short-term and long-term accounts, aligning with Vietnamese accounting standards and reporting requirements. This enhancement improves the accuracy and completeness of financial data for Vietnamese businesses using Odoo.
Original PR description
With the addition of financial reports where the distinction between short and long term is done, we add new default accounts to cover these needs. task-2492680 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Enhancements to existing features
Users can now manually import bills from KSeF whenever they need them, instead of waiting for the scheduled background sync. The new option appears in the list and kanban view menus and refreshes the screen after the import completes, making it easier to get up-to-date documents on demand.
Original PR description
Previously, bills could only be retrieved from the KSeF platform via a scheduled cron job, leaving users with no option to manually sync documents on demand. An "Import from KSeF" action has been added to the gear (cog) menu within both the list and Kanban views. Clicking this option triggers the synchronization process immediately and reloads the active view. Task [link](https://www.odoo.com/odoo/project.task/6306892) task-6306892
Resolved issues and error corrections
Fixed a display issue in journal entry previews where the credit amount could incorrectly repeat the debit amount when no currency was set. This makes the preview more accurate for users and avoids confusion when reviewing entries.
Original PR description
In _move_dict_to_preview_vals(), when no currency is provided, the credit column falls back to the line's debit value, so any caller omitting currency_id would show the debit amount in both columns of the journal entry preview. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269335
This update prevents Point of Sale from showing an access error for users who can use POS but do not have admin settings rights. It improves the login and launch experience by avoiding an unnecessary dependency on a module that may not be installed.
Original PR description
Fixes https://github.com/odoo/odoo/issues/238503 Avoid access error if `base_install_request` is not installed Example use case: - Install point_of_sale (without having `base_install_request` installed) - Give user Marc Demo Point of Point of Sale permission but NOT Administration > Settings - Go to Point of Sale ``` Access Error You are not allowed to access 'Module' (ir.module.module) records. This operation is allowed for the following groups: - Administration/Settings Contact your administrator to request access if necessary ``` Please @pedrobaeza and @christian-ramos-tecnativa can you review it? @Tecnativa TT57146 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Registration Desk now updates immediately after the Registration Summary dialog is closed, no matter how it is dismissed. This keeps attendee status accurate in both Kanban and List views without requiring a manual page refresh.
Original PR description
**Current behavior before PR:** Closing the Registration Summary dialog by pressing **Escape** or clicking outside the dialog does not refresh the Registration Desk view. As a result, the attendee state is not reflected until the view is manually reloaded. **Desired behavior after PR is merged:** The Registration Desk view is refreshed whenever the Registration Summary dialog is closed, regardless of whether it is closed using the **Close** button, by pressing **Escape**, or by clicking outside the dialog. This ensures the attendee information is always updated in both the Kanban and List views. Task - [#6333829](https://www.odoo.com/odoo/project.task/6333829) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
When a message is scheduled from an email template, all attachments are now correctly linked to the scheduled message. This prevents access errors for other users when they later view the scheduled email or its attachments.
Original PR description
**Problem:** When scheduling a message using an email template with custom attachments, those attachments will not have their `res_model` and `res_id` updated to relate to the scheduled message…
**Problem:** When scheduling a message using an email template with custom attachments, those attachments will not have their `res_model` and `res_id` updated to relate to the scheduled message record. This can lead to access errors. **Cause:** When composing a message using an email template with attachments, those attachments are created with their `res_model` and `res_id` values corresponding to the mail composer record. However, when scheduling a message, only attachments with no `res_id` value (or a value of 0) are updated to correspond to the scheduled message record. https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/mail/wizard/mail_compose_message.py#L1198-L1201 https://github.com/odoo/odoo/blob/30ca89b9e0d3c43d019167ec2de816c263f4bb92/addons/mail/models/mail_scheduled_message.py#L86 **Purpose:** Modify the `mail.scheduled.message` override of `create` to not require an attachment have no `res_id` value to be properly updated. **Steps to Reproduce in Runbot:** 1. Add an attachment to an email template. 2. Open a mail composer using that email template, then schedule the message for later. 3. Attempt to view the scheduled message with a different user. More specific example flow: 1. Add an attachment to the Sales: Send Quotation email template. 2. Create a Quotation and send it with the Send by Email button, selecting Send Later instead of Send. 3. Attempt to view the Quotation with a different user. opw-6293587
This update fixes an issue where some numbers could show an extra incorrect digit when displayed with very high decimal precision. It makes quantity and similar values appear cleaner and more accurate in the interface, which helps avoid confusion in day-to-day operations.
Original PR description
**Description of the issue/feature this PR addresses:** When rendering floating-point numbers with high decimal accuracy (e.g., UoM quantities set to 10 decimals), the UI can occasionally display a…
**Description of the issue/feature this PR addresses:** When rendering floating-point numbers with high decimal accuracy (e.g., UoM quantities set to 10 decimals), the UI can occasionally display a trailing parasitic digit (such as 53000.0000000002 instead of 53000.0000000000). This commit resolves the issue by backporting the formatting logic from master. The `maxDecDigits` calculation is moved outside the conditionals so it unconditionally caps precision for all numbers. Furthermore, the global significant digit ceiling is reduced from 15 to 14. This 14-digit ceiling reserves a 1-digit buffer, allowing the newly introduced `formatFixedDecimals` utility to safely run `roundDecimals` on the float. This mathematically sanitizes the trailing corrupted digit before it is ever converted to a string. opw-6313540 **Current behavior before PR:** - With Product UoM set to 10 Decimal Accuracy, floats such as 53000 are displayed with a corrupted digit (e.g. 53000.0000000002) **Desired behavior after PR is merged:** - With Product UoM set to 10 Decimal Accuracy, floats such as 53000 are displayed without corrupted digits (e.g. 53000.000000000) This PR is essentially a backport of https://github.com/odoo/odoo/commit/07da917f6e3319b4acde1029e77f69f1aba314b8 and https://github.com/odoo/odoo/commit/c4e7ba8d8fdfd7b0c442cf834f562ef8cedf019b for numbers.js
This update ensures that invoices sent to Nilvera are consistently checked for their final status, even if initially reported as 'Unknown'. Previously, the system wouldn't re-poll these invoices, leading to delays in accurate reporting. This fix resolves a technical issue impacting the reliable transmission of invoice data to Nilvera.
Original PR description
## Short fix summary:
Nilvera reports `Unknown` as a normal, transient `StatusCode` value (their own e-Archive API docs
list the enum as `unknown`/`waiting`/`succeed`/`error`) right after a document is sent, before their
daily batch resolves the final status. But `_cron_nilvera_get_invoice_status`'s search domain only
matches `l10n_tr_nilvera_send_status in ('waiting', 'sent')`, so once an invoice lands on `unknown` it
is never polled again — even after Nilvera later resolves the real status on their side. This adds
`unknown` to that domain so these invoices keep getting polled until Nilvera reports a final status.
task-6328589
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#2743112 changes
Resolved issues and error corrections
This change prevents invoices from showing amounts like “-0.00” when totals are effectively zero after rounding. It improves the appearance and clarity of PDF invoices so customers see a clean zero instead of a confusing negative value.
Original PR description
### Steps to Reproduce 1. Create SO with one line. Unit price $100 + 15% tax 2. Create a down payment for $100 and confirm 3. Create a down payment for the remaining $15 and confirm 4. Create an…
### Steps to Reproduce 1. Create SO with one line. Unit price $100 + 15% tax 2. Create a down payment for $100 and confirm 3. Create a down payment for the remaining $15 and confirm 4. Create an invoice for the original SO, the total is 0.00 5. Download the PDF and notice that the total is -0.00 ### Description of the issue/feature this PR addresses: **Issue:** There should not be a negative sign on the invoice PDF. Because of rounding to accommodate the limitations of binary memory, there is a negligible negative remainder sometimes, which gets shown as -0.00. **Solution:** Update the _compute_tax_totals method to check if the calculated totals and subtotals evaluate to zero using the currency's precision (is_zero()). If a value evaluates to zero, we explicitly force the amount to 0.0 and reformat it using formatLang, ensuring the PDF displays a clean 0.00 ### Current behavior before PR: Some cases cause there to be a negative zero on the invoice PDF. ### Desired behavior after PR: No negative zeroes on the invoice PDF. opw-6298712 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change fixes an issue where messages in a conversation could fail to appear after a reload. It improves reliability so users can consistently see the latest thread messages without the screen getting stuck in a blank state.
Original PR description
The Thread component mirrors `thread.isLoaded` into the `state.mountedAndLoaded` flag that gates, in the template, whether the real messages are rendered. That mirroring effect both read…
The Thread component mirrors `thread.isLoaded` into the `state.mountedAndLoaded` flag that gates, in the template, whether the real messages are rendered. That mirroring effect both read `mountedAndLoaded` as one of its dependencies and wrote it. `useEffect` records its dependency array before running the body, so right after the effect sets `mountedAndLoaded` to true the recorded dependencies still hold the pre-write `[isLoaded=true, mountedAndLoaded=false]` pair; that update only settles on a later, microtask-deferred patch. When a second reload runs `reset()` in that window it drives `mountedAndLoaded` back to false while `isLoaded` stays true, and the settling patch then computes the very `[true, false]` pair already recorded. The effect never re-runs, so `mountedAndLoaded` is stranded at false and no message is rendered. Depend on a monotonic `resetCount` instead. Reading it in the effect dependency array subscribes the render to it (OWL subscribes a `useState` proxy's render callback on every read, wherever it happens), so a `reset()` bump re-renders and re-runs the mirror to re-sync `mountedAndLoaded` with `isLoaded`. Bump it only when `isLoaded`: while loading, `applyScroll` resets on every patch, so an unconditional bump would spin the render loop; the guard re-arms only in the case that heals. `reset()` still clears `mountedAndLoaded` (the false dip is needed for the reload scroll handshake), so behaviour is otherwise unchanged. The race is not deterministically reproducible with this version's test tooling, which cannot advance the render loop a single frame, so no test is added here; the fix is covered by tests on later versions. https://runbot.odoo.com/odoo/error/940032