Monday, August 31, 2026
39 changes · saas-19.2
Security fixes and vulnerability patches
The Attendance Gantt view no longer shows employees without attendance records to users who are only allowed to manage their own attendance. This prevents employees from being unnecessarily exposed in the schedule view and keeps the display aligned with each user's access rights.
Original PR description
Issue: ---------------------------------------- A user with the group "Attendance: Self Attendance Edit" sees all employees when opening the Attendance Gantt view even though they can only see their attendances. Steps to reproduce: ---------------------------------------- - Have a user with the group "Attendance: Self Attendance Edit" - Connect as this user - Open Attendances - In the Gantt view they can see all employees (not their attendances) Cause: ---------------------------------------- `_get_gantt_data_group_by_employee()` is adding all the employees without attendances to the result of `get_gantt_data()`. Solution: ---------------------------------------- We condition the addition of employees without attendances with the ability of the user to see other employees' attendances. opw-6295888
Enhancements to existing features
Settling large sale orders in Point of Sale is now faster and smoother. The update reduces repeated background calls and screen refreshes, improving checkout performance especially for orders with many lines or loyalty rewards.
Original PR description
In `settleSO`, two per-line bottlenecks were addressed: 1. `has_valued_move_ids` was called once per order line via a separate RPC, causing N sequential HTTP round-trips. It is now computed server-side inside `read_converted`, which is already called once for all lines. 2. `addLineToCurrentOrder` was awaited per line, yielding to the event loop each iteration and triggering a full Owl re-render for every line. Lines are now created directly, batching all mutations into a single render. `recomputeOrderData()` is called once after the loop. `updatePrograms` is moved to a `pos_sale_loyalty` patch on `settleSO` so the loyalty concern belongs to the bridge module. opw-6319922 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285162 Forward-Port-Of: odoo/odoo#271742
Resolved issues and error corrections
The Ecuador EDI module now installs correctly in cases where related payment features are not installed. This prevents setup failures and keeps withholding portal pages displaying the right status information without affecting regular invoices.
Original PR description
When installing l10n_ec_edi with --skip-auto-install we receive an error on Runbot. Anchored on the sidebar title (always present on account.portal_invoice_page) rather than div[name='invoice_paid_badge'], which only exists when account_payment (not a dependency of this module) is installed and inherits this view to add it. Hides the account_payment "Paid" badge, if present, without requiring it to exist. runbot-237864 Forward-Port-Of: odoo/enterprise#126918
The French PDP configuration no longer shows the pilot phase option because the pilot deadline has passed. This simplifies setup for companies by removing an outdated choice from the registration and settings flow.
Original PR description
The pilot phase was there if people wanted to send before the deadline. The deadline has been reached, so we can remove the field from the view. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285268 Forward-Port-Of: odoo/odoo#284186
Manufacturing workcenter scheduling now finds available time slots much faster when calendars are heavily booked. This reduces delays and system load during production planning, especially for very short work orders.
Original PR description
### Description of the issue/feature this PR addresses: The workcenter planning logic in _get_first_available_slot can become inefficient when searching for very short available slots. The method…
### Description of the issue/feature this PR addresses: The workcenter planning logic in _get_first_available_slot can become inefficient when searching for very short available slots. The method repeatedly builds small candidate time windows and checks them against existing workorder and leave intervals, potentially iterating many times before finding a free slot. This leads to unnecessary computational overhead in scenarios where a large number of busy intervals exist and the remaining duration to schedule is small. ### Current behavior before PR: The planner checks for conflicts by computing the intersection between the candidate window and the busy intervals. When a conflict is detected, the candidate window is shifted forward (or backward) to the end (or start) of the intersection, and the process is repeated until a free slot is found. This approach requires repeatedly performing full interval merge operations, which becomes disproportionately expensive when the candidate windows are very small and the loop iterates many times. ### Desired behavior after PR is merged: The planner uses a new Intervals.conflicting() helper to retrieve the entire busy interval that overlaps with the candidate window. Instead of advancing only to the end of the intersection slice, the planner can jump directly to the end (or start) of the full busy interval. This avoids repeated full-merge work, reduces the number of iterations needed to find a valid slot, and prevents pathological performance slowdowns in short-duration planning scenarios. ### Benchmarks Profiling _get_first_available_slot with different workorder durations. Database has multiple months that are fully booked. Speedup is more dramatic with shorter durations but there is at minimum minor improvements across the board. | Work Order Duration | Before | After | | --- |---|---| | 1sec | ~2.5min | <1sec | | 1min | ~2sec | <1sec | ### References opw-5437256 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284278 Forward-Port-Of: odoo/odoo#246015
This fix prevents an error that could appear when creating a second time off accrual allocation with the same plan. It improves reliability for HR users by ensuring temporary calculation records are fully cleaned up after balance simulations.
Original PR description
**Steps to Reproduce:** - Install the Time Off app and Belgian Payroll (l10n_be_hr_payroll). - Create and validate an accrual allocation for currently logged-in user with: No end date Any start date…
**Steps to Reproduce:** - Install the Time Off app and Belgian Payroll (l10n_be_hr_payroll). - Create and validate an accrual allocation for currently logged-in user with: No end date Any start date An accrual plan configured with a carry-over milestone Any Time Off Type - Create another allocation for the same employee, using the same accrual plan and same Time Off Type, but with a different start date that is not in the future. - Select the accrual plan. The error is raised immediately during the onchange. **Issue:** - When the accrual allocation onchange computes the accrued balance, a temporary allocation is used internally to simulate the accrual computation. - This temporary record is discarded after the computation. - The discarded temporary record can remain pending for computed field recomputation. - When the onchange later triggers recomputation, the stale temporary NewId can cause: `KeyError: <NewId origin=18>` **Root Cause:** - Temporary allocations created with 'new(origin=allocation)' can add computed fields to `env.transaction.tocompute`. - `invalidate_recordset()` clears the temporary record's cache but does not remove the `NewId` from the pending recomputation queue. - Since the temporary record has no database row to recompute from, the NewId can later be picked up during recomputation. - This can lead to a KeyError when the framework tries to access cached data for the discarded temporary record. **Solution:** - Properly discard temporary allocations after the accrual simulation. - In addition to invalidating the cache, remove the temporary NewId from `env.transaction.tocompute` using `remove_to_compute()`. - Use this cleanup for temporary allocations created with `new(origin=...)`. **Result** - Prevents the KeyError during accrual allocation onchange. - Allows users to create another allocation with the same accrual plan without triggering the RPC error. **opw-6390559** --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixed an issue that could stop Swiss payroll ISO20022 payment reports from being generated when an employee uses a Revolut bank account. The change updates payroll bank-account checks to match the latest bank data structure, helping payroll teams process payments reliably.
Original PR description
res.bank and res.partner.bank.bank_id were removed in saas-19.2: the bank's identity now lives directly on the bank account as bank_bic and bank_name. The Revolut detection added in fba51a6a77a still read bank_account.bank_id, raising an AttributeError when generating the ISO20022 payment report for Swiss payslips.
This fix ensures Mexican CFDI invoice XML files show units of measure in the customer's configured language when invoices are sent in bulk. It prevents mismatches where the PDF could be localized but the official XML still displayed English unit names.
Original PR description
### Issue before this commit: When a CFDI invoice was sent through a mass action (Send & Print executed by OdooBot), the unit of measure in the generated CFDI appeared in English instead of the…
### Issue before this commit: When a CFDI invoice was sent through a mass action (Send & Print executed by OdooBot), the unit of measure in the generated CFDI appeared in English instead of the configured language (e.g. Spanish. ### Steps to reproduce the issue: 1.Download Accounting, Contacts and l10n_mx 2. Switch to Spanish (MX) language 3. Set the language of "Inmoviliaria CVA" and "XENON INDUSTRIAL ARTICLES" to Spanish (MX) 4. In contacts select Archived in filters and switch OdooBot language to Spanish (MX) 5. Create and confirm two invoices in the database, one for each customer but don't send these invoices 6. Go to the list view and select these two invoices, and click on "Send and print" and select CFDI 7. Check any of the XML files generated in any of the invoices (in the CFDI tab of the invoice) 8. See that the UoM in the XML file, will be set in english rather than Spanish (MX) ### Cause of the issue: The context under which the batch action runs does not contain the lang key. As a result, translated fields (such as product_uom_id.name) were read in the source language (English) instead of the executing user's language, because nothing in the CFDI generation chain explicitly forced the correct lang into the context. ### Reason to introduce the fix: A CFDI must always report translated fields in the correct language. The fix ensures the invoice is read with the executing user's language when the context doesn't already specify one, so translated fields are consistently correct across both flows. opw-6399860 Forward-Port-Of: odoo/enterprise#129769 Forward-Port-Of: odoo/enterprise#125698
POS invoice settlements no longer create a separate zero-value invoice when the order only settles existing debt. This prevents validation failures in Argentinean electronic invoicing and lets payments against existing invoices be recorded correctly.
Original PR description
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices",…
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices", select that invoice, pay the balance by bank transfer and validate Issue: Validation fails with "There should be a single tax from the "VAT" tax group per line, but this is not the case for line ..." and the settlement cannot be recorded at all. The invoice being settled is correct; the rejected line belongs to a second invoice the PoS creates for the settlement itself. Cause: `setToInvoice` already refuses to invoice a settlement, but its condition, `is_settling_account and no line`, only describes a deposit: the deposit line is added at validation. When settling a due or an invoice, `is_settling_account` stays false and the order does carry lines - the settle lines - so the guard never fires. l10n_ar_pos then sets `to_invoice` on mount, as a sale must generate an electronic document in AR, and the settle line, untaxed on purpose since it pays an existing document rather than selling anything, reaches `_check_argentinean_invoice_taxes`. Fix: Refuse the flag as well when every line is a settle line. Such an order generates an empty document - all its lines, the receivable one included, have a zero balance - so it records nothing and only consumes a document number, which in AR means an AFIP number for a zero-amount invoice. An order that also sells something keeps its invoice, since the sale still has to be reported. Reconciliation is unaffected: it happens in `_reconcile_account_move_lines` at session close, and is already covered for both invoiced and non-invoiced settlement orders. opw-6464675 Forward-Port-Of: odoo/enterprise#128975
This fix prevents access errors when Belgian companies import SODA XML files without the Analytic Accounting permission enabled. Users can now complete these accounting imports normally when Analytic Accounting is not in use.
Original PR description
**Description of the issue/feature this PR addresses:** When importing a SODA XML file, users without the Analytic Accounting group encounter an access rights error even if Analytic Accounting is not…
**Description of the issue/feature this PR addresses:** When importing a SODA XML file, users without the Analytic Accounting group encounter an access rights error even if Analytic Accounting is not enabled. This occurs because the import wizard reads the `analytic_account_id` field on the `soda.analytic.mapping` model to build an internal dictionary of departments. Because this field is restricted to the Analytic Accounting group, the evaluation of this field crashes the import for users even when the Analytic Accounting feature is disabled. This commit resolves the issue by using `.sudo()` on the analytic mapping recordset to bypass the field-level group restriction. **Steps to reproduce:** - Log in as Mitchell Admin, change company to “My Belgian Company” - Settings > Users & Companies > Users > Mitchell Admin > Access Rights > Extra Rights > ensure “Analytic Accounting” is unchecked - Also ensure Mitchell Admin is not part of the “Analytic Accounting” group - Accounting Dashboard > remove “Favorites” from filter > drag & drop a SODA XML file to “Miscellaneous Operations” > save > observe Access Error **Current behavior before PR:** - Users who don't belong to the Analytic Accounting group encounter an access error when attempting to import SODA XML files, even when the Analytic Accounting feature isn't enabled **Desired behavior after PR is merged:** - Those users no longer receive an access error opw-6376039 Forward-Port-Of: odoo/enterprise#127867
This fix prevents the Belgian payroll Dimona action from disappearing after employee records are autosaved. It ensures HR teams can reliably access the required Dimona declaration action when creating or editing employees.
Original PR description
Steps to reproduce: - Create an employee, fill in name, CP, contract start date, wage. - Leave the form without saving manually (e.g. open another record). - Select the employee again: the Dimona button/badge is gone and stays gone regardless of further edits. l10n_be_needs_dimona_in and l10n_be_dimona_next_action are server-computed but readonly=False let autosave submit a stale value, permanently blocking the recompute. Drop the stray readonly=False on both and add a regression test. Task 6515877 Forward-Port-Of: odoo/enterprise#129648
Point of Sale now applies category-based pricelist rules when products are added after a session has already started. This prevents customers from being charged the default sale price when a valid category discount or pricing rule should apply.
Original PR description
Steps to reproduce: - Create a pricelist with a rule applied on a product category and make it available in the PoS - Open a PoS session - From the backend, create a product in a new category covered…
Steps to reproduce: - Create a pricelist with a rule applied on a product category and make it available in the PoS - Open a PoS session - From the backend, create a product in a new category covered by such a rule - Back in the PoS, find that product through Search > Search more and add it to the order Issue: The product is priced at its sale price, the pricelist rule set on its category is ignored. Cause: A product that is not part of the initial payload is loaded on the fly by load_product_from_pos, which sends back the rules returned by get_pos_ui_product_pricelist_item_by_product. That domain only matches the rules set on the template or on the variant, never the ones set on a product category, and the payload carries no product.category record either. The client therefore has neither the rule nor the category: parentCategories walks categ_id, which resolves to nothing, so getCategoryRulesIds returns no rule and getPrice falls back to the sale price. This stayed unnoticed because product.category is fully loaded when the session starts, along with every category rule, so only the categories created after the session was opened are missing. Fix: Send the categories of the loaded products, since a rule set on a parent category applies to its children - along with the products, and match the rules set on those categories in get_pos_ui_product_pricelist_item_by_product. The initial loading domain of product.pricelist.item no longer filters the category rules on the loaded categories: such a rule has to be loaded whatever the products sent to the client are, since a product of that category may be loaded later on. opw-6477745 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285411 Forward-Port-Of: odoo/odoo#284660
The WhatsApp integration now sends connection details in the format expected by the proxy service. This helps subscription checks receive the needed database information reliably and reduces the risk of failed WhatsApp setup or communication through the proxy.
Original PR description
The calls to the WhatsApp proxy sent their parameters as a JSON body, which a `type='http'` route does not unpack into its arguments, so the proxy needed a decorator to read them back before `check_subscription` could see `db_uuid`. Send them as form fields instead. `requests` encodes a dict as `application/x-www-form-urlencoded` and sets the header itself, so the routes fill their arguments on their own and the decorator goes away on the proxy side. Forward-Port-Of: odoo/enterprise#129815
Deleting the final task in a recurring task series now correctly turns off the recurring indicator on the remaining related tasks. This prevents users from seeing tasks marked as recurring when no future occurrences will be created, reducing confusion in project follow-up.
Original PR description
**Problem:** Deleting one task of a recurrence suite ends the recurrence, but the tasks that stay behind keep the "Recurrent" option ticked. They look recurrent while no recurrence exists any more,…
**Problem:** Deleting one task of a recurrence suite ends the recurrence, but the tasks that stay behind keep the "Recurrent" option ticked. They look recurrent while no recurrence exists any more, so closing one of them never produces the next occurrence. **Steps to reproduce:** 1. Create a project with "Recurring Tasks" enabled 2. Create a task, tick "Recurrent" and mark it as done 3. Repeat on each generated occurrence until 3 or 4 tasks exist 4. Delete the last generated task 5. Open one of the tasks left in the suite **Current behavior:** The remaining tasks still show "Recurrent" ticked, but marking one as done creates no new occurrence and the recurring tasks smart button is empty. **Expected behavior:** Ending the recurrence should turn the "Recurrent" option off on every task that was part of it. **Cause of the issue:** `unlink` deletes the `project.task.recurrence` when the last task of the suite is removed, and `recurrence_id` is set to NULL on the other tasks by the database. Nothing resets their `recurring_task` boolean, so it stays `True` with no recurrence behind it. The two other places that end a recurrence, `write` and `action_unlink_recurrence`, already clear the flag on the whole suite. **Fix:** Aligning `unlink` with those two paths keeps a single meaning for `recurring_task`: it is only ticked while a recurrence actually exists. The suite has to be read before the recurrence is deleted, since the one2many is empty afterwards, and the tasks of the batch being deleted are left out so that no write lands on records that are about to disappear. opw-6425292 Forward-Port-Of: odoo/odoo#282535
The purchase catalog’s “Add All” action now uses the unit of measure defined on the supplier’s product line, matching the behavior of adding products one by one. This prevents purchase orders from being created with incorrect units or quantities, reducing ordering mistakes and supplier confusion.
Original PR description
Issue ----- When adding all of the catalog's suggestions at once, the uom specified on the product's vendor lines is not respected. Steps to reproduce ----- - Create a product - Add a vendor line in…
Issue ----- When adding all of the catalog's suggestions at once, the uom specified on the product's vendor lines is not respected. Steps to reproduce ----- - Create a product - Add a vendor line in a different uom - Create a past outgoing shipment of 100 of the product - Create a PO (same vendor as vendor line) - Open the Catalog - Click "Add All" in the suggestion section on the left - Go back to the PO > The product is in units instead of the different uom Cause ----- Clicking the button calls `action_purchase_order_suggest` in Python directly https://github.com/odoo/odoo/blob/3aec1c317aad547b1ad0c85a21cc53f735c5cbfd/addons/purchase_stock/models/purchase_order.py#L131-L134 whereas clicking on a product will go through `addProduct` in JS https://github.com/odoo/odoo/blob/3aec1c317aad547b1ad0c85a21cc53f735c5cbfd/addons/purchase_stock/static/src/product_catalog/record/kanban_record.js#L20-L28 Which leads to `_update_order_line_info` where the purchase line is created https://github.com/odoo/odoo/blob/f59783d43f891fea14d03f67d7c017d7ffbbe5a7/addons/purchase/models/purchase_order.py#L1322-L1328 The uom is then retrieved in the create call through `_suggest_quantity` https://github.com/odoo/odoo/blob/f59783d43f891fea14d03f67d7c017d7ffbbe5a7/addons/purchase/models/purchase_order_line.py#L547-L549 Other issue ----- The quantity of the product also doesn't match the suggestion since the product `suggested_qty` is in the product's uom and not the vendor ones. ----- Ticket: opw-6417665 Forward-Port-Of: odoo/odoo#280934
This fixes an automated mail test so emoji shortcuts are handled consistently before posting a message. It helps keep test results reliable and avoids false failures in the messaging area.
Original PR description
Before this commit, "[text composer] Posting message should transform relevant data to emoji." posted no message on runbot:
Failed to find 1 of ".o-mail-Message-body:text('test ...')"
(Timeout of 10 seconds). Found 0 instead.
This happens because "test :P :laughing:" leaves the emoji suggestion of the shortcode it just typed pending, and Composer.onKeydown returns without posting when the NavigableList takes the Enter. The fetch behind that suggestion is debounced by 250ms, so the test posts only while the debounce has not fired.
This comes from "[IMP] web,*: emoji loader": the search reads the emoji data the mail test helpers preload, so the list has an entry to offer where it had none.
This commit types a trailing space, which closes the suggestion and leaves the Enter to post.
https://runbot.odoo.com/odoo/error/946650This fixes an unstable automated test for disabling two-factor authentication by preparing the admin password data before the test starts. It helps keep release validation stable and reduces false failures in the testing pipeline without changing user-facing behavior.
Original PR description
The tour test 'totp_admin_disables' fails indeterministically because the frontend sends parallel requests while the server is processing the securitycheck (before executing the totp_disable functionality). The cause is a session rotation after the password hash is updated, caused by the credential verification function, caused by a difference in hashing parameters which are overridden by the test runtime. The password hash of the admin user is recalculated before starting the tour to prevent this happening during the tour. REF Runbot; https://runbot.odoo.com/odoo/error/242811
Managers with Time Off approval rights can now approve employee leave requests even when the leave overlaps with a paid payslip. This prevents approval errors for managers who are responsible for leave management but do not have Payroll permissions.
Original PR description
### Steps to reproduce: - Create two employees: Employee A (Manager) and Employee B (Employee subordinate to A) - Create a user for Employee A and grant Time Off approval rights, but no Payroll…
### Steps to reproduce: - Create two employees: Employee A (Manager) and Employee B (Employee subordinate to A) - Create a user for Employee A and grant Time Off approval rights, but no Payroll access - Create a payslip for Employee B for the current month - Validate and mark the payslip as Paid - Log in as Employee B and submit a Time Off request for the current month - Log in as Employee A and open Employee B's Time Off request to approve it > AccessError: You do not have enough rights to access the field "payslip_state" on Time Off (hr.leave). Please contact your system administrator. ### Cause of Issue: When a leave overlaps with a validated payslip and no waiting payslip is found, the `hr_payroll` override of `_action_validate()` attempts to set the leave's `payslip_state` to `blocked`. However, the `payslip_state` field is restricted to users with Payroll access (`hr_payroll.group_hr_payroll_user`). Because the manager approving the leave only has Time Off approval rights, the write operation fails with an access rights error. ### Fix: Use `sudo()` to bypass the AccessError, since it's the common approach in this module opw-6501386
This fix prevents rare crashes when Intrastat return logic for Denmark, Lithuania, or Sweden is run in unusual company-access situations. It mainly safeguards future customizations or edge cases, with no expected change for normal users in the standard interface.
Original PR description
Due to some trouble with tests, we found that in some cases, this function is called on the root company, and if the user does not have the access rights to read data from the company (users with system rights have them by default), it will cause a crash. This situation is not possible with the standard UI, but we fix it in case it becomes possible in a future version or customization. Forward-Port-Of: odoo/enterprise#128217
Users configuring the French PDP service now see a consistent message when a migration has been requested. This avoids misleading them with an incorrect "available tomorrow" timeline and sets more accurate expectations.
Original PR description
Currently when the user requested a migration, we don't show it in any way to the user, and the only timeline we give ("available tomorrow") is completely false. Because we don't have any field we could use for this client-side (maybe from 19.3 we can use the catch-all-json field) So just make the message same for everybody.
no-task
Forward-Port-Of: odoo/odoo#283702
Forward-Port-Of: odoo/odoo#283594Calendar invitation files now include the meeting’s video call link as the event URL. This helps recipients open the correct online meeting directly from their calendar app.
Original PR description
Add the meeting's videocall_location as a URL property in generated iCalendar (.ics) invitation files. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283934 Forward-Port-Of: odoo/odoo#283562
This fixes a forum access test so it uses the intended helper forum setup and verifies the right karma requirement for commenting on another user's post or reply. It helps ensure forum permissions are validated accurately and avoids misleading test results caused by demo data from another module.
Original PR description
**Issue:**
`website_helpdesk_forum` overrides the `ref('website_forum.forum_help')` in its demo data, allowing anyone to comment / post on it.
**Fix:**
Use the helper forum instead and properly requires `KARMA['com_all']` when posting a comment on someone else post/reply.
related: https://github.com/odoo/odoo/commit/ba19ef413e400a65e9f43a6fc6c4bc1586ea8983
runbot-944703
Forward-Port-Of: odoo/odoo#281757The timesheet assistant layout has been adjusted so the add and remove suggestion buttons stay properly within their border. This creates a cleaner, more polished experience when users review and manage suggested timesheet entries.
Original PR description
This commit ensures that the buttons to add/remove suggestions fit the border. task-6492978
Away activities in the timesheet timeline now display their duration in line with regular work activities. This makes the timeline easier to read and avoids visual confusion when reviewing time entries.
Original PR description
- Ensure align-items-baseline is applied to 'afk' records so their duration aligns correctly with standard activities. task-6425677
This fix prevents unrelated attendance records from losing their work entry links when early or batch-created attendances cross UTC day boundaries. It keeps payroll and attendance data more accurate by limiting cleanup to only the work entries that can actually overlap.
Original PR description
When an early attendance starts on the previous UTC day, the cleanup uses full UTC days as boundaries. This can include an unrelated work entry and remove its attendance link. Use the generated work entries as cleanup boundaries so only entries that can overlap the new entries are considered. opw-6412221 Forward-Port-Of: odoo/enterprise#129209 Forward-Port-Of: odoo/enterprise#127040
In Swiss payroll, the employee certificate selection now filters out non-Swiss certificate types. This reduces confusion and helps users choose only certificates that apply to Swiss localization workflows.
Original PR description
The certificate field on the employee model was being extended by the swiss localization to add the swiss-specific certificates. This was done using selection_add on the field which was causing the selection to also show the original values defined on the base employee model. We don't want to see the original values but only the swiss ones when we operate in the swiss localization. At the same time, we can't just override the field (without using selection_add) because a warning is triggered. Other possible solustions like using the result of a function or changing the type of the field to Many2one to use a domain are either not working on a record-per-record basis or not stable compliant. The only working solution for stable is to keep the selection_add working and filter the results in the views using the filterable_selection widget. Task: 5948460 Forward-Port-Of: odoo/odoo#250046
The Public Holidays form now requires a work entry type, matching the existing list view behavior. This prevents users from saving incomplete holiday records that could later block payslip creation for the affected month.
Original PR description
**Steps to reproduce:** 1. Install Payroll and Time Off modules on v19.2. 2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type…
**Steps to reproduce:**
1. Install Payroll and Time Off modules on v19.2.
2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type and save.
3. Open Payroll and try to create a payslip for any employee in the same month as the public holiday you will face below traceback.
**Issue:**
The `work_entry_type_id` is required for payroll calculations. If it is null the `_round_days` calculation evaluates an empty recordset, producing a [ValueError](https://github.com/odoo/enterprise/blob/39095789c2b6a7e558d11f479a249871b3b800b6/hr_payroll/models/hr_payslip.py#L1021
).
While in this [PR](https://github.com/odoo/odoo/pull/254666/changes) made this field was made required in the
list view, it was missed in the form view. This allows users to save a holiday without a work entry type, crashing payslip generation later.
**Solution:**
Make the `work_entry_type_id` field required in the form view as well to prevent the creation of inconsistent public holiday records.
**Traceback:**
```.py
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll/models/hr_payslip.py",
line 1021, in _round_days
day_rounded = float_round(days, precision_rounding=precision_rounding,
rounding_method=work_entry_type.round_days_type)
File "/home/odoo/src/odoo/saas-19.2/odoo/tools/float_utils.py", line 152, in
float_round
raise ValueError(msg)
ValueError: unknown rounding method: False
```
opw-6477587
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prCompleted field service shifts linked to sales orders now keep their planned hours instead of being reset to zero. This prevents work that was already scheduled from incorrectly showing as still needing to be planned, giving teams a more accurate view of staffing and sales order progress.
Original PR description
## before: When marking a shift linked to SO as complete. The `allocated_hours` of this shift is reset to 0. So when the `Planned` button tries to compute the `planning_hours_planned` it adds zero to the planned hours, making all the hours go to the `To Plan`, which is incorrect ## after: prevents the `allocated_hours` from being recomputed when making the shift as complete to avoid this issue. This will leave the hours to be calculated in the `Planned` stat button instead --- task-6425357
This fixes a Swedish tax report field that was showing certain sales amounts as negative when they should appear as positive. Businesses using the Swedish localization will now see Field 42 reported with the correct sign, reducing confusion in VAT reporting.
Original PR description
**Steps to reproduce:** - Install the `l10n_se` module and switch to a SE Company. - Navigate to Invoicing > Configuration > Taxes. - Create a tax and set the tax grid to `se_42`. - Create and…
**Steps to reproduce:** - Install the `l10n_se` module and switch to a SE Company. - Navigate to Invoicing > Configuration > Taxes. - Create a tax and set the tax grid to `se_42`. - Create and confirm an invoice for a Swedish customer using this tax. - Navigate to Reporting > Tax Report. - Check the amount of `Fält 42` under `Block E`. **Observation:** The `Fält 42 – Övrig försäljning m.m.` field shows the amount as negative instead of positive. **Root Cause:** At [1], the `se_42` formula is missing the negative sign. These lines were missed by the `tax_tag_invert` revamp done in https://github.com/odoo/odoo/commit/17a6117ed88c29b5bc4db0c872bcdbc109a7d98b. **Fix:** This commit adds the missing negative sign to the `se_42` formula, ensuring that the amount for `Fält 42` is displayed as positive in the Swedish tax report, similar to [2]. [1]: https://github.com/odoo/odoo/blob/a56038c97e807388772ccc3a794cf0bf658a2076/addons/l10n_se/data/account_tax_report_data.xml#L356-L368 [2]: https://github.com/odoo/odoo/commit/b8125f38e80c1977eedda5fc6b9466ece5e9fd89 opw-6457529 Forward-Port-Of: odoo/odoo#281925
This fix prevents invoice printing from crashing when an Argentine company's partner record contains a VAT number that is valid in another country but not as an Argentine CUIT. It helps users continue printing invoices reliably even when partner tax data is not in the expected local format.
Original PR description
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR)…
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR) Exento`` Company - Go to Invoicing > Configuration > Journals > Open ``Ventas Preimpreso`` journal > ARCA POS System: ``Electronic Invoice - Web Service`` > Save - Create a new invoice with ``ADHOC SA`` partner > Confirm the invoice - Open the ``(AR) Exento`` partner and set the VAT to ``BE0477472701`` - Open the Invoice > print Traceback: ```py ValueError: invalid literal for int() with base 10: 'BE0477472701' ``` After this [commit], companies outside the EU can use European VAT numbers. Consequently, an Argentine partner can have a CUIT number such as ``BE0477472701``, which is valid as a Belgian VAT number but not as a CUIT. When printing the invoice, the ``l10n_ar_vat`` field is computed from the partner's VAT and its value is passed to ``int()``, causing a traceback at the following line: https://github.com/odoo/enterprise/blob/d55486866d09f8aa87c2003dab722cfa323068b4/l10n_ar_edi/models/account_move.py#L138 [commit]: https://github.com/odoo/odoo/commit/a2afe3292e1cd0a4f339dc47707e469653d13ea0 Enterprise PR: https://github.com/odoo/enterprise/pull/127820 sentry-7666042143 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284880 Forward-Port-Of: odoo/odoo#282454
Fixes an error that appeared when UAE users clicked the company details link from the General Ledger warning. The link now opens the company form as expected, allowing users to complete required details without interruption.
Original PR description
Currently, an error occurs when trying to fill in the company details from the General Ledger. Steps to Reproduce: - Install `l10n_ae_faf` with demo data. - Switch to the `AE Company`. - Go to…
Currently, an error occurs when trying to fill in the company details from the General Ledger. Steps to Reproduce: - Install `l10n_ae_faf` with demo data. - Switch to the `AE Company`. - Go to `Accounting` > `Reporting` > `Ledgers` > `General Ledger`. - Click on `your company` in the company details warning. `AttributeError: The method 'account.report.action_fill_company_details' does not exist` In this commit, the company details warning was added to the l10n_ae_faf module, similar to account_saft. However, the action_fill_company_details method is only defined in account_saft, which is not a dependency of l10n_ae_faf. Therefore, when the user clicks on "your company" to open the company form [1], the method is not available and an error is raised. This commit ensures that action_fill_company_details is added to l10n_ae_faf so that clicking on `your company` opens the company form, as it does in account_saft. [this commit]: https://github.com/odoo/enterprise/commit/dffc4412df42507457810a0895be3b8f4dc7ec3f [1]- https://github.com/odoo/enterprise/blob/c8c2f13b7fd17e215044fc62774f2b4a378aaf8c/l10n_ae_faf/static/src/components/general_ledger/filters/warnings.xml#L3-L10 sentry-7676719103 Forward-Port-Of: odoo/enterprise#128327
Factur-X e-invoices received through Peppol are now read correctly by extracting the embedded invoice data before checking document type. This prevents failed or incomplete imports, especially for self-billed invoices and credit notes, helping businesses process incoming French e-invoices reliably.
Original PR description
When importing new documents from Peppol into the database, we determine whether they are self-billed by checking a Type Code in the XML file. Factur-X is an hybrid format where the XML is embedded inside a PDF. Currently, we are not extracting the XML before searching for that Type Code, and it leads to an error that prevents the document from being imported correctly: - V17, V18: An empty invoice is created and linked with the attachment. - V19+: Only the attachment is created. Additionnaly, we only check for InvoiceTypeCode or CreditNoteTypeCode, but the CII XML format embedded inside the hybrid Factur-X format use TypeCode instead. This PR aims at fixing both these issues. Ticket: opw-6417682 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285513 Forward-Port-Of: odoo/odoo#280714
Romanian SAF-T D406 XML exports now use the officially expected file version, 2.0, instead of 2.4.8. This helps companies generate compliant accounting declarations and avoid validation issues when submitting reports.
Original PR description
**Steps to reproduce:** - Install the `l10n_ro_saft` module and switch to the RO Company. - Navigate to Accounting > Reporting > General Ledger. - Click the gear icon > SAF-T (D406 Declaration). - Open the generated XML file and check the `AuditFileVersion` node. **Observation:** The `AuditFileVersion` node is set to `2.4.8`. **Expected behavior:** The `AuditFileVersion` node should be set to `2.0` (confirmed with the PO [1]) **Root Cause:** At [2], the `file_version` value is incorrectly set to `2.4.8` instead of `2.0`. [1]: https://www.odoo.com/mail/message/1144385840 [2]: https://github.com/odoo/enterprise/blob/ce2ad80c91aea27b143a80018aa73ed10d16cdbe/l10n_ro_saft/models/account_general_ledger.py#L179-L183 opw-6452918 Forward-Port-Of: odoo/enterprise#127935
Credit card and cash journals configured for file-based transaction imports now show the Import File button on the Accounting dashboard. This makes the existing import option visible and usable for those journal types, avoiding confusion for users who had enabled it but could not access it from the dashboard.
Original PR description
The "Import File" button on the dashboard card only shows up for bank journals. Credit card journals are treated the same way as bank journals pretty much everywhere else: the dashboard card itself,…
The "Import File" button on the dashboard card only shows up for bank journals. Credit card journals are treated the same way as bank journals pretty much everywhere else: the dashboard card itself, the statements list, the "Transaction Feeds" setting on the journal form (where you can pick the file import option), and even `create_document_from_attachment` in this module, which already accepts them. So you end up with a credit card journal set to import files but nothing on its card to actually do it, and people assume the feature is simply not there. Show the button on credit card journals too. Steps to reproduce: - Install account_bank_statement_import_csv (or any other import format) - Create a "Credit Card" journal and set its Transaction Feeds to the file import option - Go to the Accounting dashboard - The bank journal card has an "Import File" button, the credit card one doesn't --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#129546
Fixed an issue that could prevent users from opening the Assets list when some assets contained outdated or invalid analytic account information. The list now remains accessible in read-only mode, avoiding a reload loop and allowing users to continue working while the underlying data can be corrected separately.
Original PR description
Issue - If there are any account.asset records with analytic distributions with accounts that do not exist, it causes a recursive traceback when opening the list view of the `account.asset` model. The issue stems from `jsonToData` attempting to save the distributions json via the `save` call, where one (or multiple) accounts are non existent, which in turn runs `jsonToData` after refetching via the `load` call - overwriting `record.data` with the original, still-corrupt JSON. This creates a loop with no exit condition. Solution - In the Assets list every row is readonly, so `save()` is never reached, so `root.load()` never fires, so there is no reload to re-read the corrupt JSON. Makes the list accessible, even though the JSON values for the `analytic_distribution` are invalid. opw-6500446 Forward-Port-Of: odoo/odoo#285527 Forward-Port-Of: odoo/odoo#285381
Fixed an issue in Argentinian electronic invoicing where printing an invoice could fail if the company's tax ID was entered in an invalid CUIT format. This helps users print invoices reliably and avoids unexpected interruptions caused by invalid partner tax data.
Original PR description
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR)…
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR) Exento`` Company - Go to Invoicing > Configuration > Journals > Open ``Ventas Preimpreso`` journal > ARCA POS System: ``Electronic Invoice - Web Service`` > Save - Create a new invoice with ``ADHOC SA`` partner > Confirm the invoice - Open the ``(AR) Exento`` partner and set the VAT to ``BE0477472701`` - Open the Invoice > print Traceback: ```py ValueError: invalid literal for int() with base 10: 'BE0477472701' ``` The issue occurs because when the partner's identification type is CUIT, At [1] ``_run_check_identification()`` method does not include partners whose identification type has ``is_vat=True``. As a result, CUIT is not validated by ``_run_check_identification()`` method in ``l10n_ar`` module at [2]. So, partner's ``l10n_ar_vat`` field can be computed as ``BE0477472701``. Passing this value to ``int()`` raises the traceback during invoice printing at below line. https://github.com/odoo/enterprise/blob/d55486866d09f8aa87c2003dab722cfa323068b4/l10n_ar_edi/models/account_move.py#L138 [1]:https://github.com/odoo/odoo/blob/c2a39085ba0fbcf8a0e6a55228191e764499caea/addons/l10n_latam_base/models/res_partner.py#L24-L30 [2]:https://github.com/odoo/odoo/blob/c2a39085ba0fbcf8a0e6a55228191e764499caea/addons/l10n_ar/models/res_partner.py#L55-L65 Community PR: https://github.com/odoo/odoo/pull/282454 sentry-7666042143 Forward-Port-Of: odoo/enterprise#129449 Forward-Port-Of: odoo/enterprise#127820
Printing planning schedules now works even when shifts are grouped by role or another non-resource field. This prevents an error for multi-day, multi-resource shifts and ensures each resource is shown with the correct schedule timing.
Original PR description
Steps to reproduce: ------------------- 1. Install planning with demo data 2. Create a shift with a resource spanning multiple days 3. Remove default filters and group by role 4. Click on Actions >…
Steps to reproduce:
-------------------
1. Install planning with demo data
2. Create a shift with a resource spanning multiple days
3. Remove default filters and group by role
4. Click on Actions > Print
Issue:
-------
```python
File "/home/odoo/odoo/enterprise/planning/models/planning_slot.py", line 2029, in action_print_plannings
"start_datetime": print_planning_get_fake_pill_start_datetime(start_datetime_per_resource_per_day, resource.id, current_day + timedelta(days=1), tz_info),
^^^^^^^^^^^
AttributeError: 'bool' object has no attribute 'id'
```
Cause:
------------
After this fbf8b2a, the resource is only assigned when grouping by **resource_ids**;
otherwise, it is set to False. https://github.com/odoo/enterprise/blob/0e93fd1177743a4af0f9af153a46ee38b96e9e95/planning/models/planning_slot.py#L1998
When grouping by anything other than 'resource_ids' (e.g. role), `resource` became `False`.
As a result, accessing `resource.id` raised the above traceback.
https://github.com/odoo/enterprise/blob/0e93fd1177743a4af0f9af153a46ee38b96e9e95/planning/models/planning_slot.py#L2029-L2030
Solution:
------------
Fall back to `slot.resource_ids` instead of `False`, and loop over each resource individually.
This correctly handles multi-resource slots regardless of the active group-by, and gives
each resource its own pills with accurate start/end datetimes derived from its individual work schedule.
opw-6203688The web editor now automatically adjusts pasted or inserted tables that use merged rows or columns so they work reliably in Odoo. This prevents table layouts from breaking editor features that expect a regular grid of cells.
Original PR description
Description of the issue this PR addresses: We don't support colspan/rowspan in the editor, so tables containing them can break other functionality that assumes a rectangular grid (equal cell count per row). This PR expand any rowspan/colspan into individual cells on insert. opw-6347233 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284159 Forward-Port-Of: odoo/odoo#281114
Activity date filters now use each user's local calendar day instead of the UTC date. This prevents activities due today from appearing as future activities for users in time zones ahead of UTC, keeping list filters consistent with activity labels.
Original PR description
#### Description of the issue: Activity filters using context_today() bucket against the UTC date instead of the user's local date, off by one for part of the day. Partial revert of #265250 (e048bb5), scoped to PyDate: UTC getters are right for PyDateTime, wrong for a calendar day. #### Current behavior before PR: A Perth (UTC+8) user finds an activity due today under "Future Activities" from 00:00 to 08:00 local, while the chatter labels the same activity "Today". #### Desired behavior after PR is merged: context_today(), today and current_date return the user's local calendar day, so filters agree with the chatter. PyDateTime and PyTime keep the UTC getters; now and time.strftime() are unchanged. opw-6415985 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281453 Forward-Port-Of: odoo/odoo#278761