Thursday, April 30, 2026
54 changes · saas-19.3
Resolved issues and error corrections
This update strengthens the security of Xendit payments by requiring a valid access token linked to the specific transaction being processed. Previously, payments could be executed without this verification, creating a potential risk. Now, payments are restricted to the correct transaction, improving data integrity and security.
Original PR description
**Description of the issue/feature this PR addresses:** The `/payment/xendit/payment` endpoint did not enforce validation of an access token tied to the transaction when processing direct payment requests. **Current behavior before PR:** The endpoint accepted public requests using only the transaction reference, allowing payment execution without verifying that the request was linked to the intended transaction. **Desired behavior after PR is merged:** The endpoint now requires a valid access_token associated with the transaction (reference) before processing. This ensures that payment execution is restricted to the correct transaction. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261912 Forward-Port-Of: odoo/odoo#260258
This update resolves an error preventing users from accessing the Spain VAT Books report. The issue stemmed from an outdated reference in a template, which has now been corrected. This ensures all users can generate the required VAT reports.
Original PR description
**Steps to reproduce:** - Install the `l10n_es_reports` and `accountant` modules. - Switch to an ES Company. - Navigate to Accounting > Reporting > Spain > `VAT Books`. **Error:** `Element '<xpath…
**Steps to reproduce:** - Install the `l10n_es_reports` and `accountant` modules. - Switch to an ES Company. - Navigate to Accounting > Reporting > Spain > `VAT Books`. **Error:** `Element '<xpath expr="//button[@t-if='this.props.line.chatter and !this.ui.isSmall']" position="replace"/>' cannot be located in element tree` **Root Cause:** After commit [1], the condition `this.ui.isSmall` was removed from the template `account_reports.AccountReportLineNameCustomizable` at [2]. However, the inherited template `l10n_es_reports.VatBooksLineName` was not updated accordingly and still references the old condition, which leads to the error. **Fix:** This commit prevents errors and ensures that users can open the `VAT Books` report by applying a fix similar to [2]. [1]: https://github.com/odoo-dev/enterprise/commit/fd0afa474600586e8703ec377f962c8d7d94307a [2]: https://github.com/odoo/enterprise/blob/7362f1c5be7f496bdab660ed8fad37a6dd283616/account_reports/static/src/components/account_report/line_name/line_name.xml#L81 opw-6169697 opw-6170173 Forward-Port-Of: odoo/enterprise#115412
This update ensures the IRN (Invoice Reference Number) generated during E-Invoicing is correctly saved and displayed on the invoice itself, both in the PDF and the invoice form. Previously, the IRN was only present in the PDF but not on the invoice record, now it's consistently saved for easier tracking and reporting.
Original PR description
**Steps to reproduce:** * Install module *Indian - GSTR with E-invoice (l10n_in_edi_gstr)*. * Configure *Indian integration* with required credentials (E-Invoicing, E-Way bill, etc.). * Save the…
**Steps to reproduce:** * Install module *Indian - GSTR with E-invoice (l10n_in_edi_gstr)*. * Configure *Indian integration* with required credentials (E-Invoicing, E-Way bill, etc.). * Save the settings. * Create a *customer invoice*. * Post the invoice. * Send the invoice through *E-Invoicing (EDI)*. * Open the generated *Invoice PDF* and the *form view*. **Observed behavior:** * The *IRN number* is correctly present in the *Invoice PDF*. * However, it is *not saved/displayed* in the invoice form view. **Cause:** * The invoice flow did not store the *IRN number* on the invoice after receiving the EDI response, even though the value was available. **Fix:** * Inherit *_l10n_in_edi_send_invoice*. * Add a condition after the invoice is sent and the JSON response is received. * When the *IRN number* is present in the response, set it on the *l10n_in_irn_number* field of the invoice (in lower case). opw-6097923 Forward-Port-Of: odoo/enterprise#115522 Forward-Port-Of: odoo/enterprise#114350
This update corrects a bug that prevented users from successfully booking appointments with flexible hour slots. The issue stemmed from a rounding error in how appointment URLs were generated, leading to a 404 error. The fix ensures accurate slot durations are used, allowing for seamless appointment booking.
Original PR description
Steps to reproduce: 1. Install `appointment` 2. Create an appointment type with the followings 3. Schedule type flexible and a slot of 20 min. 4. Share this appointment and try to book appointment…
Steps to reproduce: 1. Install `appointment` 2. Create an appointment type with the followings 3. Schedule type flexible and a slot of 20 min. 4. Share this appointment and try to book appointment for 20 min. Issue: - 404 Error occurs after selectiong the slot Cause: - In 19.0, commit https://github.com/odoo/enterprise/commit/9bae0e13e7bf5e0db25a60fc2683bc51eccb4447 started using slot.duration when building the booking URL for flexible slots. However, slot.duration is rounded to 2 decimals, so a 20-minute slot is serialized as 0.33 instead of its exact value. During validation, the end datetime is recomputed from this rounded duration, which no longer matches the original slot boundaries. This mismatch causes the slot to be considered invalid and the controller raises NotFound. Solution: - Compute the slot duration directly from slot_start_dt_tz and slot_end_dt_tz when building the URL, preserving the full precision. opw-5924312 Forward-Port-Of: odoo/enterprise#108870
This update fixes an issue where the POS system was loading all employees, regardless of their employee type, leading to performance slowdowns. Now, the POS session only loads employees based on the configured employee type (basic, advanced, or minimal), optimizing performance and resource usage. This change ensures a smoother and faster POS experience.
Original PR description
Before this commit, when some employee was assigned to advanced or minimal employee, all of the employees were loaded in the POS session, because there was no basic employee assigned to the POS config. opw-5898068 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#260805 Forward-Port-Of: odoo/odoo#247043
This update resolves an issue where the POS system was loading all employees, regardless of their role, leading to performance slowdowns. The fix ensures that only the necessary employee types are loaded during a POS session, improving system responsiveness and efficiency. This change addresses a technical issue impacting POS performance.
Original PR description
Before this commit, when some employee was assigned to advanced or minimal employee, all of the employees were loaded in the POS session, because there was no basic employee assigned to the POS config. opw-5898068 Forward-Port-Of: odoo/enterprise#115109
This update corrects a bug in the ESG module's calculations for emission factors. The previous system incorrectly handled unit and currency conversions, leading to inaccurate emission values. This fix ensures that emission calculations are now precise and reliable.
Original PR description
Issue: ---------------------------------------- The conversions using Emission factors are done in the wrong way. Steps to reproduce: ---------------------------------------- - Install `esg` module - Create an Emission Factor from ton to kg of 1000 - Create a new Emission using the new factor, set the unit as kg - Notice the conversion is wrong Cause: ---------------------------------------- The two uom are inverted when calling `_compute_quantity()`. Same occured for the currencies. opw-6152413 Forward-Port-Of: odoo/enterprise#115575 Forward-Port-Of: odoo/enterprise#115246
This update fixes an issue where the leave balance report was incorrectly calculating employee leave accruals and balances, particularly when leaves were taken over multiple periods. The fix ensures accurate reporting by addressing timezone discrepancies and improving the calculation of overlapping leave allocations. This improves the reliability of leave tracking data.
Original PR description
__ISSUE__: - FIFO balance miscalculation for non-overlapping allocations. cumulative_allocated_days was partitioned globally by (employee, leave_type), but taken_per_allocation scoped leaves to each…
__ISSUE__:
- FIFO balance miscalculation for non-overlapping allocations. cumulative_allocated_days was partitioned globally by (employee, leave_type), but taken_per_allocation scoped leaves to each allocation's date range. This caused the FIFO formula to silently absorb leaves from one period into another's allocation capacity.
ex:
Alloc A (20 days) 2025, taken leaves 15 days
Alloc B (20 days) 2026, taken leaves 5 days
report: 2025: (15 taken), (5 left)
2026: (7 taken), (20 left)
- Left" rows shifted by one year in non-UTC timezones. Allocation date_from/date_to (Date fields) were cast to timestamp as midnight UTC. In negative-UTC /positive-UTC timezones midnight UTC of Dec 31 renders as the prev/next day.
__FIX__:
- detect overlap groups using a running MAX(date_to) and partition the cumulative sums within each overlap group. This way non-overlapping allocations are treated as independent, while overlapping or open-ended allocations still share FIFO within their group.
- offset allocation dates by 12 hours so no timezone can shift them across a day boundary.
- opw-5169606
- opw-5352114
Forward-Port-Of: odoo/odoo#259083
Forward-Port-Of: odoo/odoo#257873This update resolves an issue where power buttons were incorrectly displayed and overlapping other menu items in the HTML editor, particularly on smaller screens. The fix adjusts the editor's width to determine when to hide the buttons, ensuring a cleaner and more usable experience for users.
Original PR description
Problem: Power buttons are shown regardless of the editor field's actual rendered width, causing them to overlap other menus when the field is small. Solution: Instead of relying solely on the global…
Problem: Power buttons are shown regardless of the editor field's actual rendered width, causing them to overlap other menus when the field is small. Solution: Instead of relying solely on the global `ui.isSmall` (mobile detection), check the editor field's own width and hide power buttons whenever it falls below the overlap threshold. Before: <img width="576" height="301" alt="image" src="https://github.com/user-attachments/assets/dcebed55-5c80-4fe5-8d33-c320549cf347" /> After: <img width="542" height="336" alt="image" src="https://github.com/user-attachments/assets/34a233bb-9958-43ac-adb9-04702a2e403d" /> Steps to reproduce: - Change languange (French to have a long placeholder). - Settings > Customer Invoices > Default Terms & Conditions. - Check "Add a Note". - Resize the screen to smaller size. - Observe the power buttons overlap with the translate button. task-6117734 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261881 Forward-Port-Of: odoo/odoo#259094
This update resolves an issue where the IoT box pairing dialog could freeze indefinitely. Additionally, it corrects a localization problem where the 'Connecting' placeholder wasn't properly translated, ensuring a smoother user experience for all languages. This improves the reliability of the IoT integration.
Original PR description
This commit fixes two small issues with IoT pairing: 1. When pairing an IoT box, even after the DB has discovered the IoT box and added it as a record, the dialog will keep waiting forever. If you close the dialog manually the IoT box appears as expected. 2. If using a language other than English, the placeholder 'Connecting' name will not be replaced when the IoT finishes pairing. The IoT box can still be used as normal however. Forward-Port-Of: odoo/enterprise#115627
This update corrects a reporting issue within the GSTR-3B report for Indian companies using the '18% IGST (IMP-Service)' tax. The previous configuration resulted in the tax not being accurately reflected in the report. This fix ensures correct reporting of service taxes, aligning with Indian tax regulations.
Original PR description
**Steps to reproduce:** - Install l10n_in_reports - Switch to an Indian company (e.g. IN Company) - Create a bill with a Service product and "18% IGST (IMP-Service)" tax - Confirm the bill - Go to "Accounting / Reporting / India / GSTR-3B" **Issue:** In "4. (A) (2) Import of services" section of the report, the bill that has been created is not taken into account. **Cause:** The "18% IGST (IMP-Service)" tax has a distribution of: * 100% of the tax in "100570" account with "IGST" tax tag * -100% of the tax in same account with "IGST" tax tag Therefore, both distribution lines with the tax tag cancel themselves. opw-6116356 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#260631
This update resolves a bug that prevented users from scheduling rental tours correctly. Previously, attempts to set times in the past would cause scheduling errors. The fix ensures tours always use valid, future dates and times, improving the reliability of the rental booking process.
Original PR description
Since 605c8b47d52854e36c7cb822ccb2d40de44c4c3c, rourly rental products default to a rental period starting at the next hour of the current day. Tours override the time of the rental period, but not the date. If the tours tries to set a time which is in the past, the datepicker will discard the time, breaking the tour. Scenario: Running the tour between 11am-11:59am breaks test_website_sale_renting_ui: - Product page shows default times: today 12pm (start) to 1pm (end) - Tour tries to set start time to 8am - Datepicker rejects 8am (it's before the current time 11am-11:59am) and keeps 12pm - Tour then sets end time to 12pm, resulting in equal start and end times - Equal times are invalid, cart addition fails, tour breaks This commit, extracts the existing utility function into a shared helper that returns a guaranteed future date (next Monday + offset), ensuring tours always set valid times regardless of when they run. runbot-243459
This update resolves an issue where multi-company invoices were experiencing errors due to incorrect tax calculations. The change ensures tax information is handled securely, preventing crashes when products from different companies are involved. This improves the reliability of invoicing for businesses using Odoo's multi-company functionality.
Original PR description
Description of the issue this commit addresses: In multi-company setups, invoice line tax recomputation could read product or account taxes in user context and trigger an AccessError on account.tax when products carry taxes from another company. Desired behavior after this commit is merged: This patch hardens account.move.line tax recomputation paths by reading tax M2M values in sudo and returning filtered results in normal env, so compute logic no longer crashes on cross-company tax records. Ticket [link](https://www.odoo.com/odoo/project.task/6036638) opw-6036638 Forward-Port-Of: odoo/odoo#260948
This update corrects a technical issue within the Odoo Enterprise system's onboarding tour for overtime rules. Previously, the tour incorrectly targeted a UI element, leading to a confusing user experience. This fix ensures the tour accurately guides users through the correct process for setting up overtime.
Original PR description
The tour previously matched the input field underneath the popup and interacted with the wrong element, the fix scope the selector to the modal. task-6172067 Forward-Port-Of: odoo/enterprise#115458
This update resolves an issue where backorder returns weren't properly associated with the original delivery. The change ensures that when a backorder is created from a return, the return's ID is correctly set, allowing for accurate tracking and reporting of returned items within the delivery context. This improves inventory accuracy and reporting.
Original PR description
### Steps to reproduce: - Create, confirm and validate a delivery for 2 units of a product A - Click Return > Return All - Validate the return for 1 unit and backorder #### > The backorder does not belong to the return list of the delivery ### Cause of the issue: Backorder pickings are created by copying the picking to backorder: https://github.com/odoo/odoo/blob/9ad995ff6b59a6a2fdfbbd6cf385fe27568dd3ea/addons/stock/models/stock_picking.py#L1580-L1593 https://github.com/odoo/odoo/blob/9ad995ff6b59a6a2fdfbbd6cf385fe27568dd3ea/addons/stock/models/stock_picking.py#L1571-L1578 However, the `return_id` is a `copy=False` field that is not manully set during this copy process: https://github.com/odoo/odoo/blob/9ad995ff6b59a6a2fdfbbd6cf385fe27568dd3ea/addons/stock/models/stock_picking.py#L558 opw-6111544 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261831 Forward-Port-Of: odoo/odoo#259804
This update resolves an issue where users could unintentionally bypass tax group restrictions when changing Argentinian fiscal packages. The system now explicitly checks for a 'force_delete' flag, ensuring that tax group deletions are handled correctly and restoring the previous behavior. This prevents data inconsistencies and maintains the integrity of Argentinian tax reporting.
Original PR description
Commit 947e4dc9de3a replaced MODULE_UNINSTALL_FLAG with an explicit 'force_delete' context flag, and as the commit message warns, callees that relied on the previous flag must now detect 'force_delete' on their own. There is no automatic bypass anymore. **STEP TO REPRODUCE** 1.- Install l10n_ar 2-. Select one argentinian regime (fiscal package) & save 3-. Try to change package **FIX** Detect 'force_delete' in check_uninstall_required and return early, restoring 19.0 behavior. Manual deletions of the tax group are still blocked. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261561
This update reduces excessive spacing within the Odoo dashboard side panel, making it easier for users to navigate and view information. The changes refine the layout by adjusting spacing and padding, improving the overall visual clarity and user experience. This is a minor fix to enhance usability.
Original PR description
## Description: The dashboard side panel had too much vertical spacing between the fold button, dashboard sections, and dashboard entries. Reduce the top and section spacing by one Bootstrap step, and use smaller vertical padding on dashboard entries while keeping their vertical margin for readability. Task: [6138870](https://www.odoo.com/odoo/project/2328/tasks/6138870) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update corrects a calculation error in the 2033D-2 report for French tax filings. Specifically, it ensures that the total amounts are accurately aggregated, including those generated by dynamic line items. This resolves a discrepancy that could have impacted reported tax figures.
Original PR description
Fixed an issue where the total aggregation for line I-C in the 2033D-2 report didn't add the amount of dynamic lines. no task id
This update enhances the visual clarity of form fields by ensuring consistent styling for required, readonly, invalid, and focused states. The changes prioritize clear indication of the active field and improve overall usability. This ensures users can easily identify and interact with the correct fields within the application.
Original PR description
Before this commit, the focus style wasn't applied on a required field. We now have improved consistency across field states and their visual design. Here’s how to visually distinguish each field state: **Required**: the border is more prominent **Readonly**: the label is greyed out (as well as the background color) **Invalid**: the background color is red **Focused**: the border color is green We no longer want to change the label color for simplicity. For example, if you see a thicker green border with a greyed-out background, it means the field is **focused**, **readonly**, and **required**. Note: the "invalid" style is removed when the field receives focus. We chose not to support this combination. 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 update corrects a formatting issue in the e-waybill document date field. Previously, the date displayed included the time, which is not the required format. This change ensures consistent and accurate date representation for e-waybill documents.
Original PR description
The `document_date` field in e-waybill form view previously included both date and time, which is not as per the expected format. This fix ensures that only the date is shown, hiding the time component. Forward-Port-Of: odoo/odoo#261253 Forward-Port-Of: odoo/odoo#259656
This update resolves an issue where pressing 'Enter' within the emoji suggestion list in the discuss HTML composer would unexpectedly send a message. The fix prevents this behavior, ensuring users can now properly manage their messages without unintended submissions when the emoji picker is open.
Original PR description
In discuss html composer, when emoji suggestion list is open, pressing enter sends the message. This commit fixes this issue by preventing the default action of the enter key when the emoji suggestion list is open. task-6173045 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261965
This update resolves an issue where generating timesheets would produce errors when a previously employed employee was archived and a related holiday was still active. The fix prevents timesheet generation from considering time off requests for archived employees, ensuring accurate timesheet calculations. This improves data integrity and prevents potential reporting discrepancies.
Original PR description
[FIX] project_timesheet_holidays: Exclude archived employees from time-off # Description of the issue/feature this PR addresses: ## Steps to Reproduce: 1. Create a time off for Employee A (it should…
[FIX] project_timesheet_holidays: Exclude archived employees from time-off # Description of the issue/feature this PR addresses: ## Steps to Reproduce: 1. Create a time off for Employee A (it should affect the timesheets). 2. Create a new public holiday (global time off) that overlaps with Employee A’s time off. 3. Archive Employee A. 4. Delete the public holiday created in step 2. 5. An error related to timesheet generation appears. ## Expected Behavior: - The public holiday / global time off should be deleted without any error. # Desired behavior after PR is merged: ## Fix (Implemented): When regenerating timesheets due to changes in holidays or time off, leaves related to archived employees should not be taken into account. A check was added inside the `_generate_timesheets` function in `project_timesheet_holidays/models/hr_holidays.py` to exclude leaves belonging to archived employees. ## Alternative Fix (Not Implemented): Instead of filtering out leaves linked to archived employees, we could delete those leaves when an employee is archived. However, this approach is not ideal, as archived employees may be reactivated later and would still need their previously requested time off to be preserved. ## Version: This bug appears in both version 17.0 and 19.0. I assumed that it also appears in 18.0 but didn't directly test ## Task: [5474038](https://www.odoo.com/odoo/project/4105/tasks/5474038) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261909 Forward-Port-Of: odoo/odoo#244203
This update resolves a technical issue where an incorrect import statement was present in the core driver for our Point of Sale IoT integration. This fix ensures the driver functions correctly, preventing potential disruptions to the system's ability to process data from IoT devices. The change is a routine bug fix.
Original PR description
This PR fixes the wrong import in adam_scale_driver.py opw-6173563 Forward-Port-Of: odoo/enterprise#115625
This update fixes a discrepancy in the sale details report by accurately reflecting cash rounding adjustments. Now, the report displays the total cash rounding applied during a session, ensuring a more precise representation of sales transactions, especially when cash payments are involved. This improves reporting accuracy and provides a clearer picture of financial data.
Original PR description
The sale details report total_paid was computed from sum(order.amount_total), which does not include the cash rounding adjustment. This caused a discrepancy between the displayed total and the sum of individual payment lines when cash rounding is enabled. Use the sum of actual payment amounts instead, which naturally includes cash rounding since payments are recorded with their rounded values. opw-5253018 Forward-Port-Of: odoo/odoo#261884 Forward-Port-Of: odoo/odoo#254401
This update fixes an issue where byproducts added directly to manufacturing orders weren't correctly linked to the stock moves, preventing accurate tracking of origin. The change ensures byproducts added through the MO are properly associated with stock movements, resolving a discrepancy in location data. This improves the reliability of production tracking.
Original PR description
When we add a byproduct followed by SN directly in the MO it will not have the its location as production. Steps to reproduce: ------------------- * Create product tracked by Serial number * Create a…
When we add a byproduct followed by SN directly in the MO it will not have the its location as production. Steps to reproduce: ------------------- * Create product tracked by Serial number * Create a Manufacturing order * Add the Product tracked by serial number on the MO as byproduct * Confirm the MO * Go to shop floor * Add the by-product quantity and create a new serial number. * Close production and go back to the MO in manufacturing * Open stock moves -> the by-product does not have "production" for origin Observation: ------------- When we add the byproduct directly in the MO, they will be added to move_byproduct_ids in the MO but not in byproduct_ids on the stock.move because byproduct_ids it's a [link](https://github.com/odoo/odoo/blob/d14bf6289da21065860ff959185c47b947a7418c/addons/mrp/models/stock_move.py#L50-L52) between the stock.move and the BOM. When adding the byproduct in shopfloor, it will create the quant: https://github.com/odoo/enterprise/blob/06be616bb4d74f0a089e8e318d25c2424594f813/mrp_workorder/static/src/mrp_display/mrp_record_line/stock_move.js#L163-L171 Additionaly when creating the quant it will decide the source location depending if the product its a byproduct: https://github.com/odoo/enterprise/blob/1d10ee238a50e7bdb552efdeafc068c5127cd49a/mrp_workorder/static/src/mrp_display/mrp_record_line/stock_move.js#L189-L192 The issue arise because it check if the product it's a byproduct by checking byproduct_ids and since our product was added directly on the MO and not from the BOM it will not appear in byproduct_ids https://github.com/odoo/enterprise/blob/dc5bb0fe8e15063f977970841bdaf8aff1a61e41/mrp_workorder/static/src/mrp_display/mrp_record_line/stock_move.js#L108-L110 #### Additional notes: The default value for [byproduct_id](https://github.com/odoo/odoo/blob/abb5777cc8324cff0cdf841a8ae42413060dcf92/addons/mrp/models/mrp_production.py#L1263) when creating the stock move is false opw-5974582 Forward-Port-Of: odoo/enterprise#110122
This update ensures that survey invitations are sent in the recipient's preferred language, regardless of whether they are part of a mixed-language group. Previously, invitations were consistently sent in English, even when recipients had set their language preference to Dutch or other languages. This change improves the user experience and avoids confusion.
Original PR description
When sending survey invitations to a group of recipients with different language preferences, some recipients would receive the invitation in the incorrect language. ### Steps to reproduce 1. Install…
When sending survey invitations to a group of recipients with different language preferences, some recipients would receive the invitation in the incorrect language. ### Steps to reproduce 1. Install the "Surveys" module and activate a second language (e.g., Dutch). 2. Create a survey and ensure its invitation template has translations for both languages. 3. Create two contacts: one with English as their language and another with Dutch. 4. On the survey, click "Share" and add both contacts as recipients. 5. Send the invitations. 6. The contact with Dutch preferred language receives the email in English. ### Cause By default, the wizard uses a single language for every email in a batch. While it can switch this language if everyone in the group speaks the same tongue, it fails to do so for mixed-language groups. Adding compute_lang=True fixes this by telling the system to look up and use the correct language for each recipient one by one. opw-5868581 Forward-Port-Of: odoo/odoo#260028 Forward-Port-Of: odoo/odoo#246778
This update ensures that missing payslip alerts are triggered only when the payroll closing date has passed. This prevents unnecessary notifications for payslips that are already outdated, streamlining payroll reporting and reducing potential user confusion. This change improves the accuracy and efficiency of our payroll processing system.
Original PR description
In this commit, missing payslip in a payrun issue will be raised only if the payroll closing date is due (old conditions remains). task-6008297 Forward-Port-Of: odoo/enterprise#112182
This update resolves a technical issue that prevented demo flows from running correctly when new Peppol and Nemhandel response data was added. The fix ensures demo mode functions smoothly by updating the mock data, preventing errors and improving the demo experience.
Original PR description
With the recent addition of responses in Peppol and Nemhandel, we forgot to adapt the mocking data for demo flows, which resulted in tracebacks in demo mode. Forward-Port-Of: odoo/odoo#258655
This update fixes an issue where the ewallet discount was incorrectly included in delivery calculations. The change adjusts the pricing logic to accurately exclude ewallet amounts from delivery costs, ensuring consistent and accurate shipping charges for customers. This improves the overall customer experience and billing accuracy.
Original PR description
Issue: --- Ewallet line is included in delivery pricing. Steps to reproduce: 1- Create a delivery method `based on rules`. 2- In pricing set rules: - price <= 99 => delivery price = 30 - price > 99…
Issue: --- Ewallet line is included in delivery pricing. Steps to reproduce: 1- Create a delivery method `based on rules`. 2- In pricing set rules: - price <= 99 => delivery price = 30 - price > 99 => delivery price = 50 3- Create a eWallet with balance = 200. 4- Create a SO add a line with product. Set the SOL unit amount to 100. 5- Click on `add a shipping`. Add the created delivery method. 6- As you see, the delivery amount is correctly calculated. 7- Now apply the eWallet using `reward` button. 8. Update the shipping cost. The shipping cost is 30, which is not expected. Cause: --- In calculation of shipping amount the ewallet is not taken into account. Even though technically it's considered a discount, ewallet is not functionally the same as discount. However, in `_get_price_available` we cannot have information about `ewallet` lines as it doesn't depend on `sale_loyalty`. Fix: --- We use `_compute_amount_total_without_delivery` which is overridden in `sale_loyalty_delivery` to exclude ewallet and gift cards. opw-6124209 Forward-Port-Of: odoo/odoo#260134
This update corrects a minor issue where the 'delete' option was missing from the user interface for certain returns and checks reports. This was caused by lingering references to the previous default 'new' state value. Removing this value improved database efficiency.
Original PR description
In odoo/enterprise#103149, the default value for the state of the returns and checks were removed to use False by default to reduce space in the database. However, a few reference were left using the default value which was 'new'. To reproduce (using working files): - Create a working files - Open the menu of the working file in the working files kanban view - The option delete isn't present
This update resolves an issue where toggling attendees in the calendar filter section was not working correctly. The fix corrects a technical error related to how the calendar filter ID was being accessed, ensuring that attendees are toggled as intended. This improves the user experience when managing calendar events.
Original PR description
Steps to reproduce ==================== - Open the calendar. - Try to toggle any attendees through the label. - By default, it will toggle the first attendee. Technical =========== Recently, here https://github.com/odoo/odoo/pull/253294, we started using `this` when accessing a component properties, but here in our case, `filterId` is not a component property; it is declared using `t-set` in the parent template, and using it like `this.filterId` leads to undefined, which leads to the same ID for all the inputs, which was causing the issue. Task-6124794
This update resolves an issue where certain expense accounts were incorrectly included in default reporting filters. Specifically, accounts related to exchange differences and early payment discounts were added. This ensures that financial reports accurately reflect all expense transactions, improving data accuracy and reporting reliability.
Original PR description
[FIX] account: Default account filter include Other Expense In "Defalut Accounts" setting, adding `expense_other` in the search domain for the following accounts: - Exchange difference entries > Loss - Early payment discounts > Early Discount Gain - Early payment discounts > Early Discount Loss task-6116375 Forward-Port-Of: odoo/odoo#259583
This update fixes an error in the Datev general ledger export that resulted in incorrect tax rates. The issue stemmed from how multiple tax repartition lines were handled, leading to amounts being replaced instead of added. This ensures accurate tax reporting for Datev customers.
Original PR description
With l10n_de_reports: - Configure a foreign currency with an exchange rate. - Configure a tax with multiple repartition lines. - Create a vendor bill in this foreign currency with this tax. - In the general ledger export the datev csv. In the datev csv the rate is wrong. In the method _l10n_de_datev_get_csv, we build a tax_amount dict. However when one tax has multiple lines, the amount is replaced and not added. opw-6010097 Forward-Port-Of: odoo/enterprise#115535 Forward-Port-Of: odoo/enterprise#110453
This update resolves an issue that prevented users from switching between company types (Companies vs. Associations & Foundations) for their Belgian accounting setup. The fix clears references to old accounts within cash rounding configurations, allowing the localization switch to complete without errors. This ensures seamless switching between fiscal localization types.
Original PR description
### Issue before this commit: Switching the fiscal localization of a Belgian company from "Companies" to "Associations and Foundations" caused a traceback during the chart reload process. The…
### Issue before this commit:
Switching the fiscal localization of a Belgian company from "Companies" to "Associations and Foundations" caused a traceback during the chart reload process. The operation failed because some accounts from the previous localization could not be deleted.
### Steps to reproduce the issue:
1. Download Accounting
2. Create a new Belgian company
3. Switch to that company
4. Go into Settings -> Fiscal Localization
5. Switch to Associations and Foundations
6. Traceback: The operation cannot be completed: Another model is using the record you are trying to delete. The troublemaker is: 'Account Cash Rounding' (account.cash.rounding). Thanks to the following constraint: 'Profit Account' (profit_account_id). How about archiving the record instead?
### Cause of the issue:
The Belgian localization creates a default cash rounding method ("Round to 0.05") linked to specific profit and loss accounts. When switching localization, it was tried to delete the old chart of accounts, but these accounts are still referenced by account.cash.rounding through profit_account_id and loss_account_id, which use ondelete='restrict'. This prevents account deletion and blocks the localization change. Commit that caused the issue: https://github.com/odoo/odoo/commit/412fc9bed36645dd950c9a60d9b6ffdd9b4bce67
### Reason to introduce the fix:
Before reloading the Belgian chart template, the fix clears the profit_account_id and loss_account_id on the existing cash rounding records. This removes the blocking references, allows the old accounts to be deleted safely, and lets the fiscal localization switch complete successfully without affecting existing cash rounding configurations.
opw-6050537
Forward-Port-Of: odoo/odoo#260145This update fixes a previous removal of a payslip line edition wizard, now replaced with an inline computation method. This change streamlines the payroll calculation process, ensuring more accurate and immediate pay results for employees. It addresses a previous issue related to the wizard's functionality.
Original PR description
We replace the previously removed payslip line edition wizard with inline payslip computation Forward-Port-Of: odoo/enterprise#112972
This update corrects a display issue on rental product pages when 'continue selling' is enabled. Previously, the available quantity was incorrectly shown as 2 regardless of the rental period. The fix ensures the quantity accurately reflects the remaining availability based on the selected rental timeframe, improving the customer experience.
Original PR description
**Issue**: The displayed available quantity on the ecommerce product page is incorrect for rental products when "continue selling" is enabled. **Steps to reproduce**: - Create a rental product…
**Issue**: The displayed available quantity on the ecommerce product page is incorrect for rental products when "continue selling" is enabled. **Steps to reproduce**: - Create a rental product tracked in stock with a quantity of 5 - Enable "continue selling" and "show available quantity below 10" - Go to the ecommerce page of this product - Rent 3 units for a given period, confirm and pay - Return to the ecommerce product page -> Whatever the selected renting period, the displayed quantity is always 2 **Cause**: The website displays `free_qty`: https://github.com/odoo/enterprise/blob/41c729e22c5fd1abb690f8335e933f793be0b319/website_sale_stock_renting/static/src/xml/website_sale_stock_renting_product_availability.xml#L15 `free_qty` is computed in: https://github.com/odoo-dev/odoo/blob/0935829ddaecd7b2b6eec9157f8f790b546d06ff/addons/website_sale_stock/models/product_template.py#L36 which leads to: https://github.com/odoo/enterprise/blob/41c729e22c5fd1abb690f8335e933f793be0b319/website_sale_stock_renting/models/website.py#L10 and ultimately relies on: https://github.com/odoo/odoo/blob/37bf1703c7478a3010b71cd60bbb43b3295a605b/addons/stock/models/product.py#L213 This computation does not take the selected renting period into account. There is a period-aware computation here: https://github.com/odoo/enterprise/blob/41c729e22c5fd1abb690f8335e933f793be0b319/website_sale_stock_renting/models/website.py#L15C17-L21C1 but it is only triggered when `product.allow_out_of_stock_order` is False (i.e. when "continue selling" is disabled). opw-[5354163](https://www.odoo.com/web#id=5354163&view_type=form&model=project.task) Forward-Port-Of: odoo/enterprise#108493 Forward-Port-Of: odoo/enterprise#103333
This update resolves an issue where a website tour wasn't functioning correctly due to an outdated assumption about the search bar size. A new fix ensures the tour accurately detects scrolling behavior, preventing interruptions and improving the overall user experience. This addresses a technical detail related to website performance.
Original PR description
__Problem__ odoo/odoo@9394e17a added a step to this tour that assumes the website search bar is small. However, this search bar was smaller than it should have been because of an issue fixed by the enterprise PR (odoo/enterprise#115694). __Fix__ Remove the step added in odoo/odoo@9394e17a runbot-242449
This update resolves a problem where the size of the product search bar was incorrectly set, causing a tour to fail in the SaaS version of Odoo. The fix ensures the size attribute is only applied to the product search bar, preventing issues with other website search elements.
Original PR description
__Before commit__ odoo/enterprise@1111381c set the size attribute of the product search bar. However, the view inherits from `website.website_search_box`, which means that all search boxes on the website will have the size attribute set if `website_sale_renting` is installed. The tour `dropdowns_and_header_hide_on_scroll` fails in saas-19.3 because odoo/odoo@9394e17a added a step that depends on the size of the main website search bar, which may vary because of this. __After commit__ Only set the size attribute to the product search bar as intended. runbot-242449 Forward-Port-Of: odoo/enterprise#115433
This update resolves an issue impacting the accuracy of real margin reports within the Odoo system. The changes adjust the report's structure to ensure correct aggregation of data, leading to more reliable margin calculations. This improves the financial reporting capabilities for sales teams.
Original PR description
Adjust the real margins pivot report structure --- task-6168613
This update fixes an issue preventing users from inserting snippets within the Odoo Forum's help pages. The system was incorrectly filtering dropzones, blocking snippet group clicks. The fix allows snippet group clicks, ensuring users can easily add snippets to the forum, improving usability.
Original PR description
Steps to reproduce the issue: - Go to Forum, then go to the Help page - Enter Edit mode - Try to drag and drop a snippet => The dropzone in the s_cover at the top of the page are available - Try to click on a snippet group => Nothing happen, because all dropzones are filtered The s_cover element has the [data-snippet] attribute. When clicking on a snippet group, the editor filters out dropzones inside other snippets. Since s_cover is treated as a snippet, its dropzones are excluded, even though they are the only ones available on the page. The solution is to treat dropzones inside snippets as low priority instead of strictly forbidden. If no other valid dropzones exist, we allow these as a fallback to ensure snippet insertion remains possible. task-5938138 Forward-Port-Of: odoo/odoo#260400 Forward-Port-Of: odoo/odoo#256078
This update fixes an issue where the system incorrectly generated purchase orders when stock was sufficient to fulfill manufacturing orders. The change ensures that purchase orders are only created when there's a genuine stock shortage, streamlining the procurement process and reducing unnecessary costs. This improvement focuses on the 'mts_else_mto' move type.
Original PR description
Steps to reproduce: - Create a product P1 with a BoM containing component C1 - Set C1 route to MTO + Buy: - add any vendor - Have 10 units of C1 in stock - Confirm a Manufacturing Order for 2 units…
Steps to reproduce:
- Create a product P1 with a BoM containing component C1
- Set C1 route to MTO + Buy:
- add any vendor
- Have 10 units of C1 in stock
- Confirm a Manufacturing Order for 2 units of P1
- The MO confirmation triggers the move for C1, which gets assigned from stock
- Increase the quantity_producing on the MO
- This updates product_uom_qty on the move and calls _run_procurement
Expected behavior:
- No Purchase Order should be created since stock covers the demand
Current behavior:
- A Purchase Order is created even though stock is sufficient
- In case of partial stock, the full procurement_qty is used instead of the uncovered delta
Fix:
- Track mts_else_mto moves separately before calling _action_assign()
- After _action_assign(), subtract the newly covered quantity (move.quantity - old_qty)
from the procurement_qty before running the stock rule
- Skip the procurement entirely if the adjusted qty <= 0
opw-6042283
Forward-Port-Of: odoo/odoo#261538
Forward-Port-Of: odoo/odoo#258871This update corrects a bug where timesheets were incorrectly assigned to the wrong employee when created through the timesheet systray. The issue stemmed from a formatting error in how user IDs were passed, now ensuring timesheets are accurately linked to the correct user. This prevents data discrepancies and ensures accurate time tracking.
Original PR description
# Steps to reproduce: - Open a billable project and a task - Open the timesheet systray - Create timesheet # Current behaviour The timesheet is recorded for another employee. # Root cause This happens because the `user_id` passed to the record update was lacking the right `[id, name]` format. As a consequence, the timesheet was created without a user (`False`), thereby linking it to another employee than the current user. task-6169253 Forward-Port-Of: odoo/enterprise#115420
This update resolves a bug that prevented users without project access from correctly linking project values to sales quotations or rental orders. The change uses a temporary access method to ensure project information is included, allowing for accurate order creation even without direct project permissions. This improves the functionality of the sales and rental processes.
Original PR description
Before this commit if a lead is converted into either of sales quotation or rental quotation, if the user doesnt have access to project but has access to sales and CRM, it throws a error to read the project. Rationale: - Even when there is no access to project, it is neccesary for the project value to be linked in the suqsequent quotation or rental order. So using a sudo to to read the value and assigning it as a default value is neccessary.
This update fixes a potential timeout issue in the website builder when users upload multiple images simultaneously. Previously, a 10-second timeout would trigger errors, but this change allows builder actions to remain active until image uploads are complete, improving the user experience and preventing frustrating errors.
Original PR description
Steps to reproduce: 1.Drop a gallery slider 2.Add multiple images at the same time 3.Wait more than 10 seconds before finalizing uploads 4.A toaster error is displayed Before this change: Builder actions relying `this.dependencies.media.openMediaDialog` inside their `apply` method could time out while the media dialog was still open. This was due to OperationPlugin enforcing a default 10s timeout, even when the user was still interacting with the dialog (e.g., selecting or uploading images).(see PR[1]) After this change: The timeout is disabled by using `this.canTimeout = false` for actions that open the media dialog using apply method, ensuring they remain active until the media dialog completes and preventing false timeout errors. [1]:https://github.com/odoo/odoo/pull/233079 task-6131455
This update fixes an issue impacting how sales advantages are calculated. Specifically, discounts are now excluded from these calculations, ensuring more accurate and reliable advantage reporting. This change improves the accuracy of sales performance analysis.
Original PR description
for the computation of advantages. Only exclude rewards of type discount for the advantage calculation. opw-6159239
This update resolves an error that occurred when generating VSME reports with an invalid base year (e.g., '1'). The fix ensures that only valid 4-digit years are accepted, preventing the report generation from failing. This improves the reliability of VSME report exports.
Original PR description
Currently, an error occurs when exporting VSME reports if the base year is not a valid 4-digit year. **Steps to Reproduce:** 1. Install the ESG module with demo data. 2. Create new "**VSME Reports**"…
Currently, an error occurs when exporting VSME reports if the base year is not a valid 4-digit year. **Steps to Reproduce:** 1. Install the ESG module with demo data. 2. Create new "**VSME Reports**" with `Base Year = 1`. 3. Now, click on "**Print**". **Error:** `ValueError - Invalid isoformat string: '1-01-01'` **Cause:** The base year is directly used to build a date in [1], resulting in `datetime.date(1, 1, 1)`. In [2], this is formatted to **"1-01-01"** and used in a domain search, which raises a ValueError due to an invalid ISO date format. **Fix:** - Adds a **constraint on base year** to ensure only valid years (1000–9999) are allowed for new VSME Report records. - Adds a **helper validation method** to verify base year before performing computations on existing records. [1]: https://github.com/odoo/enterprise/blob/4cef86708a9fe3cd00855fa18e2a8cbd4fd77e66/esg_hr/models/esg_report.py#L515 [2]: https://github.com/odoo/enterprise/blob/4cef86708a9fe3cd00855fa18e2a8cbd4fd77e66/account_accountant/models/res_company.py#L176-L184 sentry-7419431039 Forward-Port-Of: odoo/enterprise#114453
This update fixes an issue where test tags containing brackets or special characters weren't parsed correctly, leading to errors. The changes ensure that nested brackets and escaped characters are handled properly, improving the reliability of test execution. This resolves a technical detail that ensures consistent test results.
Original PR description
When using test tags for js tests, some tests are not parsed correctly when containing brackets `[ ]` This is because the tag selector use them to set parameters bounds as in…
When using test tags for js tests, some tests are not parsed correctly when containing brackets `[ ]` This is because the tag selector use them to set parameters bounds as in `.test_method[test_param]` The previous implementation using a regex could generate invalid test tags when the params contains comma, and brackets This commits fixes this two ways: Nested brackets will work, meaning that something like `.test_method[test, witch brackets[]]` will be correctly parsed as a tag `.test_method` with a parameter `test, witch brackets[]`since the brakets are balanced. Before, it was parsed as twho tags, `.test_method[test` and ` witch brackets[]]` the second one being considered invalid and spamming logs with errors Brackets and backslashes can be escaped with a backslash, meaning that something like `.test_method[test, with brackets], and backslash\]` can be escaped as `.test_method[test, with brackets\], and backslash\\]` Note that the parser will make it's best effort to parse non escaped strings, so a \ not followed by and escaped character will remain there in order to make potential copy paste easier. For the previous example `.test_method[test, with brackets\], and backslash\` will work the same way. So in practice, we only really need to escape \ when followed by another backslash or a bracket, and we need to escape brackets when they are not balanced. Comma in hoot tests are also a problem since they are used to separate parameters, this can easily be tested by only splitting on `,@` since all hoot test should start with a @ and parameters of test_unit_* tests should be a list of hoot tests. A followup pr will introduce the params to the canonical tag, and escape it as needed Forward-Port-Of: odoo/odoo#262101 Forward-Port-Of: odoo/odoo#261516
This update fixes a slow performance issue that occurred when searching for stock quantities based on certain product selections. Previously, the system would fetch all available quantities regardless of a specified limit, leading to delays. Now, the system respects the limit defined in the search criteria, resulting in significantly faster response times.
Original PR description
## Problem: During an `onchange` call, if a field is defined in the `fields_spec` with a `limit` attribute, the `fetch` method doesn't respect it, and will fetch all records satisfying the domain. In…
## Problem: During an `onchange` call, if a field is defined in the `fields_spec` with a `limit` attribute, the `fetch` method doesn't respect it, and will fetch all records satisfying the domain. In certain circumstances, this leads to slow requests. ## Solution: Enforce the `limit` when fetching if it is present. ## Steps to reproduce: - Have a product with many quant records 1. Open a picking for this product in Barcode 2. Change the lot/serial The frontend will send an `onchange` request that includes `product_stock_quant_ids` in the `fields_spec` (with default `limit` 40). Odoo will fetch all quants for this product regardless of the limit, and the request will take a while to resolve. ## Benchmark: <table> <thead> <tr> <th># of quants</th> <th>Before</th> <th>After</th> </tr> </thead> <tbody> <tr> <td>17193</td> <td>~9s</td> <td>~400ms</td> </tr> </tbody> </table> opw-6041705 Forward-Port-Of: odoo/odoo#261904 Forward-Port-Of: odoo/odoo#259983
This update resolves an issue where users without HR access rights couldn't apply filters in the Time Off overview search. The fix adjusts the search domain to avoid errors related to a field not present in the public employee model, ensuring all users can effectively filter their time off data.
Original PR description
More general fix for https://github.com/odoo/enterprise/pull/113973 or https://github.com/odoo/enterprise/pull/115478 Steps to reproduce: - Log using a user with no HR access rights (e.g. Marc Demo) - Go to Time Off > Overview - Remove all filters - Add a new custom filter on "Department is equal to ..." or "Job is equal to ..." Instead of the filter being applied, a traceback occurs. This originates from the fact that in _get_gantt_data_groupby_employee, the employee domain is mapped to address the field current_version_id. However, if a user has no HR rights, the search will be redirected to the employee public model, on which that field isn't defined. This commit adapts the domain in search_fetch to avoid current_version_id being present in it. No related task Co-authored by @BeBel42 Forward-Port-Of: odoo/odoo#261937
This update adds a new test case to ensure that employees with public access permissions can correctly view their holiday schedules within the HR Gantt chart. This resolves a previous issue and strengthens the security and reliability of the HR module. It ensures consistent access for all users.
Original PR description
Add a test for the issue resolved in https://github.com/odoo/odoo/pull/261937 No related task Forward-Port-Of: odoo/enterprise#115593
This update prevents a bug where the shared worker's response processing could get stuck, leading to delays. The fix ensures that only one promise is created when `isOnMainTab` is called, improving responsiveness and stability. This resolves a technical issue that could have impacted user experience.
Original PR description
Whenever `isOnMainTab` is called, it creates and returns a new promise that will be resolved once the shared worker sends its response. However, if `isOnMainTab` is called twice in quick succession, before the shared worker answers, only the last promise is resolved, leaving the first one hanging forever. This commit fixes the issue by not recreating a promise if there is already one pending. Forward-Port-Of: odoo/odoo#262021
This update corrects a minor visual issue where icons within thumbnails weren't positioned correctly. The change adjusts the base layout to allow for overrides, ensuring a consistent and professional appearance for attachments. This improves the overall user experience.
Original PR description
We modify the base layout to allow a fix of the icon position in an override. Task-5152517 Forward-Port-Of: odoo/odoo#261299 Forward-Port-Of: odoo/odoo#249055
This pull request addresses several minor bugs in the Documents module, enhancing the user experience. Specifically, it fixes issues with scrolling after file uploads, the placement of icons in thumbnails, and incorrect badge display with long user names. These changes improve stability and visual consistency.
Original PR description
[FIX] documents: scroll to uploaded record How to reproduce: - install documents - open the "All" folder and ensure there is enough folder to not see any file - open the detail panel - drag&drop file…
[FIX] documents: scroll to uploaded record How to reproduce: - install documents - open the "All" folder and ensure there is enough folder to not see any file - open the detail panel - drag&drop file in that folder The file is uploaded, but you have to scroll to it to see it. Moreover, despite the file being selected, it's not shown the detail panel. We solve the problem here by waiting the record to be rendered before scrolling to it and notify the detail panel. [FIX] documents: fix icon position in thumbnail How to reproduce: - install documents - open the chatter of a document - click on log a note, and upload a file The "trash" icon/button in the thumbnail is weirdly positioned (neither centered, neither on a border). We fix here the position the icon/button. [FIX] documents: fix "you" badge layout How to reproduce: - install documents - change the name of Mitchell Admin to a very long name - open the share panel owned by Mitchell Admin The "You" badge is not correctly displayed (the badge appears as "Y..." instead of "You" and the shape is not correct). We fix here the layout of the badge in such configuration. Task-5152517 Forward-Port-Of: odoo/enterprise#115103 Forward-Port-Of: odoo/enterprise#107686
This update resolves a bug that prevented users from scheduling rental tours correctly. Previously, attempts to set times in the past were rejected, leading to broken booking processes. The fix ensures tours always use valid, future dates, improving the reliability of the rental booking experience.
Original PR description
Since 605c8b47d52854e36c7cb822ccb2d40de44c4c3c, rourly rental products default to a rental period starting at the next hour of the current day. Tours override the time of the rental period, but not the date. If the tours tries to set a time which is in the past, the datepicker will discard the time, breaking the tour. Scenario: Running the tour between 11am-11:59am breaks test_website_sale_renting_ui: - Product page shows default times: today 12pm (start) to 1pm (end) - Tour tries to set start time to 8am - Datepicker rejects 8am (it's before the current time 11am-11:59am) and keeps 12pm - Tour then sets end time to 12pm, resulting in equal start and end times - Equal times are invalid, cart addition fails, tour breaks This commit, extracts the existing utility function into a shared helper that returns a guaranteed future date (next Monday + offset), ensuring tours always set valid times regardless of when they run. runbot-243459