Tuesday, July 7, 2026
29 changes · saas-19.4
Enhancements to existing features
The timesheet assistant now gives clearer guidance when setup issues prevent activity tracking, including distinguishing connection problems from CORS configuration issues and warning when the browser extension is missing or inactive. It also improves suggestion quality by showing real record names, preserving deleted Odoo events properly, and selecting more relevant projects for calendar-based timesheets.
Original PR description
Forward-Port-Of: odoo/enterprise#121939 Forward-Port-Of: odoo/enterprise#115859
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
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.
Resolved issues and error corrections
Users can now audit values in the Balance Sheet when it is grouped by analytic account without hitting an error. This improves reliability for accounting teams reviewing report details and investigating balances.
Original PR description
Currently an error occurs when user tries to audit a cell when Balance Sheet is grouped by an analytic acccount. Steps to replicate: - Install accountant with demo data and turn on Analytic…
Currently an error occurs when user tries to audit a cell when Balance Sheet is grouped by an analytic acccount.
Steps to replicate:
- Install accountant with demo data and turn on Analytic Accounting.
- Open Balance Sheet Report > Group By an Analytic Account > Click on Any Value under an Analytic Account Column.
Error:
```
File '/home/odoo/src/enterprise/saas-19.3/account_reports/models/account_report.py', line 2876, in dispatch_report_action
return report_method(model, *args)
File '/home/odoo/src/enterprise/saas-19.3/account_reports/models/balance_sheet.py', line 28, in action_audit_cell
action['context'].update({
AttributeError: 'str' object has no attribute 'update'
```
Cause:
- When clicking on a report cell, `dispatch_report_action()` calls `action_audit_cell()` of the corresponding report (Balance Sheet in this case) which in turn calls `action_audit_cell()` of `account.analytic.report`.
- When the flow reaches [1], the window action for analytic items is fetched, where its [context] is returned as a string instead of a dictionary.
- This action is then received [here] with `context` as a string, and attempting to update it results in an error.
Solution:
- Converted the string to dict using `literal_eval()`.
[1]: https://github.com/odoo/enterprise/blob/a26981667361839ad38f45da5a6f23ae8e6478f1/account_reports/models/account_analytic_report.py#L208
[context]: https://github.com/odoo/odoo/blob/824446b65cbe3850f88f56090f0473f0e94bf4f3/addons/account/views/account_analytic_line_views.xml#L88-L91
[here]: https://github.com/odoo/enterprise/blob/b5f884a49344aa097c20fc128e9d290b97970f1a/account_reports/models/balance_sheet.py#L28
opw-6311673
sentry-7513784149
Forward-Port-Of: odoo/enterprise#120734Fixed an issue where the dialog for adding snippets to a mailing could appear behind the full-screen editor when the AI chatbox was active. This restores normal editing behavior and prevents save or discard actions from becoming blocked.
Original PR description
When an AI chatbox is active, all non-error dialog modals are set to be behind the chatbox through their z-index. This causes an issue where the dialog modal to add new snippets to a mailing is set behind the fullscreen edit window, preventing its use and freezing the use of some commands (save & discard). This commit restores the snippet dialog's z-index to its original value. task-6321624
EC Sales List returns are now generated separately for each company in a tax unit using that company's own VAT number. This prevents multiple entities from being incorrectly combined into one declaration under the tax unit VAT number, improving compliance accuracy.
Original PR description
Issue: The EC Sales List return is currently generated under the tax unit VAT number, consolidating all member entities into a single declaration. Expected: The EC Sales List return must be generated individually per member entity, each under their own VAT number, even when those entities belong to a tax Unit. Fix: Apply tax unit only if report's multi company filter is `tax_units`. Ref: https://github.com/odoo/enterprise/blob/07e8aba8604319747a5925c83576095ce9a63f9e/account_reports/models/account_return.py#L316-L317 task-6069402 Forward-Port-Of: odoo/enterprise#115974
Cancelled UrbanPiper delivery orders are now ignored when calculating active delivery counts. This prevents affected point-of-sale sessions from failing to reopen after a delivery provider cancels an order, helping staff continue service without interruption.
Original PR description
### Steps to reproduce 1. Configure UrbanPiper and start a POS session. 2. Receive an order from the delivery provider. 3. Accept the order and mark it as **Ready**. 4. Cancel the order from the…
### Steps to reproduce 1. Configure UrbanPiper and start a POS session. 2. Receive an order from the delivery provider. 3. Accept the order and mark it as **Ready**. 4. Cancel the order from the delivery provider. 5. Reopen the running POS session. ### Current behavior When a delivery provider cancels an order, the `delivery_status` is updated to `cancelled`, while the POS order state remains (`draft`, `paid`, or `done`). As a result: * Cancelled deliveries are still included in the active delivery order count. * `_get_urbanpiper_order_count()` attempts to map the `cancelled` status, which is not present in `status_map`, raising a `KeyError`. * The POS UI fails to load, preventing users from reopening the running session. ### Expected behavior Cancelled delivery orders should not be considered active delivery orders and should not be included in the delivery status count, allowing the POS session to open normally. ### Solution Exclude orders with `delivery_status = 'cancelled'` from the active delivery order count computation. This prevents the `KeyError` and ensures cancelled delivery orders are ignored when computing active delivery statistics. [Video reproducing the issue](https://drive.google.com/file/d/1XdiylekWV-q6LTbvhCgbyd_KDKlG2imz/view?usp=sharing) --- **opw-6353861** Forward-Port-Of: odoo/enterprise#122798
Shipment insurance configured on Envia delivery methods is now sent in the format expected by Envia. This helps ensure insured shipments, such as those using Mexican FedEx ground services, correctly generate the related insurance documents.
Original PR description
Issue ----- Insurance set on the delivery method is not correctly being communicated to Envia. Steps to reproduce ----- - Create a MX company - Set up Envia - Fedex Nacional Economico (ground) - 10% insurance - Create a MX client - Create a product (with some weight) - Create a SO using the delivery method & confirm - Validate the picking > No insurance pdf is being printed Cause ----- We are passing the insurance value as a `insurance` field on the shipment, which is not what the API expects. We should instead pass it in `additionalServices` as shown in the example of https://docs.envia.com/docs/additional-services#how-to-add-services-to-a-shipment ----- Ticket: opw-5254952 Forward-Port-Of: odoo/enterprise#121691 Forward-Port-Of: odoo/enterprise#118966
Audit reports exported to PDF now include images inserted with the file command. This prevents missing visual evidence or supporting materials in generated reports, making exported documents more complete and reliable.
Original PR description
Currently, when a user uses the `/file` command to insert an image into an audit report and exports the report to PDF, the image is omitted from the generated PDF. To improve the support of those blocks, we will pre-process the document and replace the embedded files that correspond to images with standard image elements before PDF generation. This will ensure that images are correctly rendered and displayed within the document's text flow in the exported PDF. Task [link](https://www.odoo.com/odoo/project.task/5115280) task-5115280 Forward-Port-Of: odoo/enterprise#122699 Forward-Port-Of: odoo/enterprise#121673
Payroll work entries now avoid counting the same public holiday twice when attendance-based employees also have worked-time leave from sandwich rules. This helps keep generated payroll hours accurate and prevents inflated work entry totals on affected holidays.
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/enterprise#121940 Forward-Port-Of: odoo/enterprise#119530
Users without payroll access can now open employee working schedules without seeing an error linked to Belgian payroll data. The change prevents restricted payroll-related information from being read when the user does not have the required permissions, improving reliability for HR configuration screens.
Original PR description
**Steps to Reproduce:** 1)Create a database in v19.2. 2)Install the l10n_be_hr_payroll module. 3)Open the login user's profile and set the Payroll access rights to No (null). 4)Navigate to Employees…
**Steps to Reproduce:** 1)Create a database in v19.2. 2)Install the l10n_be_hr_payroll module. 3)Open the login user's profile and set the Payroll access rights to No (null). 4)Navigate to Employees → Configuration → Working Schedules. **Actual Result:** A traceback is triggered after opening the record. ```python Failed to read field resource.calendar.l10n_be_reorganisation_measure_ids You are not allowed to access 'BE: Reorganisation Measure.' (l10n.be.reorganisation.measure) records. This operation is allowed for the following groups: - Payroll/Assistant Contact your administrator to request access if necessary. ``` **Issue:-** The traceback is caused by the following commit introduced in v19.2 [here](https://github.com/odoo/enterprise/commit/e1092393ff99e9dad84ea8b9d6066e0bc61d6312) In this commit, a new computed field `l10n_be_reorganisation_measure_ids` was added on `resource.calendar`. The field is computed and store=true when the read function is called, and reads the data from the database at that time; The payroll doesn't have any access rights due to the error **Solution:** To fix this issue, a group access check is added inside the field Ticket:- 6245936 Forward-Port-Of: odoo/enterprise#119518
Stripe expense authorizations are now matched correctly when merchant category codes fall within configured ranges, reducing incorrect authorization errors. Declined Stripe expenses also avoid duplicate refusal messages, keeping expense records clearer for users.
Original PR description
# [FIX] hr_expense_stripe: Fix MCC ranges Context: Since 3e52d875 when receiving an authorization whose MCC fits in a range we would not find it in the search. This is logical yet we return an error before checking properly mcc codes with range included After this commit: This will also check that the authorization MCC exist if we don't directly find the range. We move the "not found" error after that check too The forgotten tests have been added into the overrides opw-6185961 opw-6288399 # [FIX] hr_expense_stripe: Fix double refusal of expenses Context: When an expense is created through a declined stripe authorization, the expense is refused twice, resulting in a duplicated refusal message After this commit: Do not refuse already refused expenses Forward-Port-Of: odoo/enterprise#122923 Forward-Port-Of: odoo/enterprise#121474
The Planning Analysis report no longer crashes when the Field Service planning module is installed. The Priority filter is now placed correctly, keeping the report accessible for users reviewing planning data.
Original PR description
Steps to reproduce: - 1. Install `planning_field_service`. 2. Open Planning > Reporting > Planning Analysis. Issue: - The view crashes with `UncaughtPromiseError > Error: Attribute "domain"` is missing, and the Planning Analysis report cannot be opened. Cause: - The xpath adding the "Priority" filter anchors on `//filter[@name='unpublished_shifts']`, which is a child of the "Status" filter. As a result, "Priority" is inserted inside "Status", and it requires a `domain` on any filter nested inside another filter. "Priority" has none. Fix: - Anchor the xpath on `//filter[@name='status']` instead. task-6358804
This change prevents a server error that could occur while WebSockets are being opened, improving stability for users. It also ensures cleanup happens correctly even if something goes wrong after the connection has been created.
Original PR description
Currently, when a `PoolError` from `psycopg2.pool` is raised (code ref [1]) during the execution of `retrying()` inside `acquire_cursor()`, Python's contextlib generates the following server error: ``` RuntimeError: generator didn't stop after throw() ``` This occurs because the exception is suppressed within the generator-based context manager, causing the generator to continue execution after throw(). This commit fixes the issue by limiting the `suppression/retry` logic to the cursor acquisition step. It also uses `ExitStack()` to ensure proper management of the exit callback stack across the `yield`, guaranteeing that resources are cleaned up correctly even if an exception is raised after the cursor is yielded. [1]: https://github.com/odoo/odoo/blob/1956c5e3e14086f699e4582a7332577bc2fbf557/odoo/sql_db.py#L648 Sentry-7567409750,7401155298 Forward-Port-Of: odoo/odoo#271527
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 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
Invoices 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
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#266364