Saturday, August 22, 2026
55 changes · master
New functionality added to Odoo
Adds Sri Lanka-specific tax invoice numbering, VAT registration detection, and invoice PDF wording so businesses can meet local tax invoice requirements. This helps companies issue compliant tax invoices, show payment mode details, and resequence invoices using the required Sri Lankan format.
Original PR description
This commit introduces the `l10n_lk_invoice` module to support specific tax invoicing requirements for the Sri Lankan localization. Key features include: * Custom Sequence Format: Implements the…
This commit introduces the `l10n_lk_invoice` module to support specific tax invoicing requirements for the Sri Lankan localization. Key features include: * Custom Sequence Format: Implements the mandatory Sri Lankan tax invoice sequence format `YYMMM_QQQQ_XXXXX` (e.g., `26MAY_BRN01_00001`), utilizing the journal code as the `QQQQ` component. * VAT Registration Tracking: Adds a `l10n_lk_vat_registered` boolean field to `res.partner` and `res.company`. This auto-computes based on the Sri Lankan VAT format (requiring >= 13 digits and ending in the "7000" suffix). * PDF Report Modifications: * Replaces the "Invoice" title with "Tax Invoice" when both the supplier and the customer are VAT registered, AND the invoice contains taxable supplies (excludes fully exempt invoices). * Replaces "Delivery Date" with "Supply Date" on tax invoices. * Injects "Mode of Payment" into the document header when a preferred payment method is selected on a tax invoice. * Resequencing Wizard Support: Overrides `account.resequence.wizard` to seamlessly handle Sri Lanka's specific month abbreviation formatting during mass resequencing. Task-6209151 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273592
Enhancements to existing features
This update brings the spreadsheet component to a newer version, improving visual consistency and fixing several usability issues. Users should see cleaner icons and styling, more reliable scorecard charts, and better behavior when viewing spreadsheets in standalone or dashboard contexts.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/a73559ee4f [REL] 19.5.0-alpha.13 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
Resolved issues and error corrections
Starting a new chat now notifies only active users who can access the conversation. This prevents failed chat creation or broadcast errors caused by archived users, making messaging more reliable for active employees.
Original PR description
Before this commit, starting a chat with a partner that has an archived user broadcast the new channel to that user too. This happens because _get_or_create_chat searches the partners with active_test=False, only to check that the given ids exist, and a recordset union keeps the environment of its left operand. The flag therefore reaches user_ids, which stops filtering on active. Archived users only reach the broadcast since "[REF] mail, im_livechat: use user in channel._broadcast", as the main_user_id it replaced is always active. The problem is that _broadcast passes each user to with_user, so the whole channel payload is computed with the rights of a user the caller never asked for, and lands on a bus channel no session can subscribe to. This commit fixes the issue by keeping active_test=False on the search alone, and by asking for active users at the broadcast. task-6483179 Forward-Port-Of: odoo/odoo#283593
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/a73559ee4f [REL] 19.5.0-alpha.13 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/bb965c63da [FIX] icons: align data filter icon with material icon [Task: 6276321](https://www.odoo.com/odoo/2328/tasks/6276321) https://github.com/odoo/o-spreadsheet/commit/8900dbec98 [IMP] icons: replace script as cjs instead of python [Task: 6276321](https://www.odoo.com/odoo/2328/tasks/6276321) https://github.com/odoo/o-spreadsheet/commit/a5ef0a0405 [FIX] css: fix issues with the new material design [Task: 6475791](https://www.odoo.com/odoo/2328/tasks/6475791) https://github.com/odoo/o-spreadsheet/commit/08b1a1d180 [FIX] standalone_viewport: stop following selection [Task: 6481678](https://www.odoo.com/odoo/2328/tasks/6481678) https://github.com/odoo/o-spreadsheet/commit/f66629eea7 [IMP] charts: scorecard now uses formula instead of cell reference [Task: 6343843](https://www.odoo.com/odoo/2328/tasks/6343843) https://github.com/odoo/o-spreadsheet/commit/b289465e09 [IMP] menu_registry: ensure children unicity [Task: 5224033](https://www.odoo.com/odoo/2328/tasks/5224033) https://github.com/odoo/o-spreadsheet/commit/9358c3ddc8 [FIX] inputs: explicitly define border-style [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
This change increases a temporary lookup cache used while importing data, reducing repeated database queries for large files with many reused references. It keeps the imported data unchanged while lowering database round trips during certain module installations, with only a small temporary memory increase.
Original PR description
`BaseModel.load()` resolves every `field:id` column through `ir.model.data` one row at a time, behind a fixed 1024-entry LRU. A data file that references more distinct external ids than the cache…
`BaseModel.load()` resolves every `field:id` column through `ir.model.data` one row at a time, behind a fixed 1024-entry LRU. A data file that references more distinct external ids than the cache holds, and reuses them across rows, overflows it immediately, so almost every row misses and costs a query. `l10n_us/data/res.city.csv` is the only file in the codebase that does both: 31,236 rows over 3,172 counties. Installing the module costs 14,610 queries. Raising the cache to 10000 brings that to 3,859 with no change to the data loaded. Module load time is unchanged, the gain is in database round trips. An entry is an external id mapped to a (model, id) tuple, and the LRU keeps a second dict to track ordering, so about 190 bytes each. The cache is built per `load()` call and dropped when it returns, and it fills to the number of distinct external ids in the file rather than to its cap: | entries | case | bytes | MB | |---|---|---|---| | 1,024 | today's cap | 207,248 | 0.2 | | 3,223 | res.city.csv working set | 640,240 | 0.6 | | 10,000 | cap reached | 1,878,288 | 1.9 | No data file in either repo reaches the cap. task-none
Manufacturing work orders now behave more consistently for continuous production, reducing accidental quantity changes that could disrupt component consumption. The update also improves safeguards when multiple users work on the same order and streamlines work order behavior after completion.
Original PR description
In this commit, we continue cleaning and aligning continuous production BoM with normal BoM alongside some UX changes and improvements. In case of a continuous production BoM: - Marking a WO as done, doesn't update qty producing - Interacting with the WO card in shopfloor also will not update qty producing, because it used to mess up component's consumption. Move demand should only be updated when we update MO's quantity producing. In General: - No production state tracking in the chatter for WO Form - Allow splitting when WO is done - Ensuring that if there are 2 users working on the same time and one of them marks the WO is done, the other won't be able to update the `qty_produced` on accident and will be shown an error to avoid inconsistencies. Task: 6384174 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275973
Invoice rounding can now be managed directly from the main invoice screen and edited more easily. Rounding rules can also be applied automatically only when configured conditions are met, helping sales invoices use the right rounding without changing the original sales order or recalculating incoming supplier values.
Original PR description
This commit improves rounding methods usage on sales moves The improvements are the following: 1) Allow users to manage and adjust rounding directly from the main invoice form view (instead of the "Other Info" tab) and enable inline editing on the rounding list view. 2) Restrict automatic rounding calculations strictly to active sales/invoicing flows. Passive incoming flows must respect received values without recomputation. 3) Introduce configurable conditions directly on rounding methods so they apply automatically only when specific criteria are met (e.g., currency-specific rules). 4) Ensure rounding automatically applies to invoices generated from Sales Orders (when conditions are met) without requiring structural changes to the Sales App or forcing the original SO to be rounded. task-6479888
Website page-building snippets were refined with better layouts, categorization, and visual polish, including a new intro spotlight option. These updates make it easier for users to create more attractive website pages with less manual adjustment.
Original PR description
- Requires https://github.com/odoo/design-themes/pull/1321 task-6374308 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Users can now transfer checks in bulk between companies or sibling branches they can access. The check’s company is kept aligned with its latest movement, improving accuracy for multi-company operations.
Original PR description
[IMP] l10n_latam_check: allow transfer between accessible branches Allow to mass transfer checks between active companies, and in particular between sibling branches. By doing this, we exposed even more the fact that a check can be received in a journal belonging to a company, then be moved to another company's journal. Previously, when that hapenned (it was already possible to transfer from children to parent), the company_id of the check stayed where the creation payment hapenned. Which was never correct but not too exposed. We fix it by making it a computed stored field based on the value of the last operation related to the check. We also simplify the domain of the check list view to just rely on the initial payment method used. task-5247520
Accounting validation errors now include more helpful details, such as the affected account code or journal entry reference. This makes FEC import issues and related accounting errors easier to identify and resolve without extra investigation.
Original PR description
Some generic validation errors raised by core account models lack enough context to identify which record caused the issue, making FEC imports harder to troubleshoot. This commit improves the two…
Some generic validation errors raised by core account models lack enough context to identify which record caused the issue, making FEC imports harder to troubleshoot. This commit improves the two error cases identified for this use case: - `account.account._check_account_code` now includes the invalid account code in the error message. - `account.move.write` now includes the move name/reference and displays human-readable field labels instead of technical field names when attempting to modify read-only fields on posted entries. Although motivated by FEC import, these are generic core validations, so the improvements are implemented at the source to benefit all callers rather than only the FEC import flow. Enrichment is scoped to the two cases above, other constraints/errors across these models are intentionally left unchanged for now, since editing core error messages more broadly should be done deliberately and on a case-by-case basis, not as a blanket rewrite task-5346068 Forward-Port-Of: odoo/odoo#283279 Forward-Port-Of: odoo/odoo#281746
Portal task searches now avoid a slow lookup pattern that caused large task lists to be scanned unnecessarily. Users should see much faster search results when looking up tasks by title, especially in databases with many tasks.
Original PR description
The portal task list searched titles with
['|', ('name', 'ilike', search), ('id', 'ilike', search)]. Applying ilike to the integer id casts it to text, which no index can serve, and OR-ing that branch with the title prevents the trigram index on name from being used at all, so every search fell back to a full scan of project_task. The id branch is now added only when the term is numeric, as an equality on the primary key, keeping the title lookup on its trigram index.
Benchmark on 200k tasks, PostgreSQL EXPLAIN ANALYZE, selective term matching 5 rows, median of 3 runs:
before Parallel Seq Scan on project_task ~150 ms
after Bitmap Index Scan (name gin_trgm_ops) ~0.5 ms
opw-5478903
Forward-Port-Of: odoo/odoo#278548Odoo's Amazon sales connector has been updated to use Amazon's newer Orders API ahead of the old version being retired in 2027. This keeps Amazon order synchronization compatible and should improve performance by retrieving order and item details together instead of through repeated extra calls.
Original PR description
Amazon has announced the deprecation of the Orders v0 API, with a removal date of March 27, 2027. In this commit, we migrate to the new v2026-01-01 API. This new version restructures how order data is queried and delivered, shifting from a multi-request architecture to a nested consolidated payload. This optimizes our sync performance by eliminating the N+1 query problem when fetching order items. Key changes: - Operation Consolidation: `getOrders` is replaced by `searchOrders`. Because Amazon now embeds orderItems directly inside each order object natively, we remove our secondary item-fetching loops. - Financial aggregation: Item prices, taxes, shipping, and discounts are no longer flat fields on the item but are centralized into a `proceeds` object. - Replacing of deprecated flags. - Reorganization of order-related fields. task-5972714 Forward-Port-Of: odoo/enterprise#128763 Forward-Port-Of: odoo/enterprise#114591
Shopfloor handling for continuous production has been tightened to avoid creating backorders in this flow for now. Users must enter a produced quantity before closing a manufacturing order, helping prevent unclear or incorrect production records.
Original PR description
Forward-Port-Of: odoo/enterprise#124696
Fixed an issue where entering a time on mobile could close the time selector after the first digit and shift focus behind the bottom sheet. This makes scheduling and planning date-time entry more reliable on touch devices.
Original PR description
Steps to reproduce ================== - Use a mobile viewport - Go to planning - Click on an empty cell - Click on the date - On the bottomsheet, click on the time at the bottom - Try to type 12:34…
Steps to reproduce ================== - Use a mobile viewport - Go to planning - Click on an empty cell - Click on the date - On the bottomsheet, click on the time at the bottom - Try to type 12:34 => Only 1 is entered and then the start_datetime field is focused behind the bottom sheet Cause of the issue ================== The <input type="time"/> listen to the onchange event. We listen to rawPickerProps changes using a reactive call. shouldFocus is then set to true, and the focus is done after the next render. The onchange event is called at a different time depending on the platform. On IOS and Firefox desktop: after changing hours or minutes On Android: once the apply button is clicked On Chrome desktop: After entering a single char Solution ======== The bottomsheet is only displayed when env.isSmall && hasTouch(). It doesn't make sense to focus the input, since we don't handle the keyboard in that case. opw-6386252 Forward-Port-Of: odoo/odoo#283782 Forward-Port-Of: odoo/odoo#276617
Odoo now identifies the right company or contact when multiple records use the same email address but have different display names. This prevents emails and chatter messages from showing the wrong sender, helping businesses avoid confusion in multi-company setups.
Original PR description
### Issue: When multiple partners share the same email address, `_mail_find_partner_from_emails` may resolve to the wrong partner when the input is a formatted email like "`Name <email>`" This…
### Issue:
When multiple partners share the same email address, `_mail_find_partner_from_emails` may resolve to the wrong partner when the input is a formatted email like "`Name <email>`"
This affects use cases like email templates using `{{object.company_id.email_formatted}}` as sender, where the wrong company partner could be selected
### Cause:
The lookup in `done_partners` only matched on `email_normalized`, which cannot distinguish partners sharing the same email but with different names
The `email_formatted` field carries both name and email, allowing an exact match when the input is a formatted email
### Steps to reproduce:
- Install `account`
- Create an Email Template (Applies to: account.move, From: {{object.company_id.email_formatted}})
- Create a second company B with the same email as the default (e.g. info@yourcompany.com)
- In Settings (logged in as company B), set a Fiscal Position (e.g. US Taxable)
- Create an Invoice on company B
- In the chatter, click Send message, click the expand arrows button, use the three dots menu to select the template
- Send and check the Sender in the chatter
Before the fix, the sender resolves to the default company even though the invoice belongs to company B
opw-6260992
Forward-Port-Of: odoo/odoo#283875
Forward-Port-Of: odoo/odoo#269509Refunds in Point of Sale now use the same fiscal position as the order being refunded, even when that order had no special fiscal position. This prevents refunds from silently using the default preset's taxes, helping businesses avoid incorrect tax calculations and later reconciliation surprises.
Original PR description
Steps to reproduce: - Enable presets and set a preset carrying a tax-replacing fiscal position (e.g. "Takeout") as the default preset of the POS config - Take an order with another preset that has no…
Steps to reproduce: - Enable presets and set a preset carrying a tax-replacing fiscal position (e.g. "Takeout") as the default preset of the POS config - Take an order with another preset that has no fiscal position (e.g. "Dine In") - Refund that order from the ticket screen Issue: The refund is taxed with the fiscal position of the default preset instead of the one of the refunded order. The ticket screen displays the refund lines with the taxes of the original order, but the refund that is actually created maps them through the wrong fiscal position. With tax-included prices the totals still match on screen, so the operator only sees the discrepancy after going back. Cause: The destination order of a refund is an empty order, created with the default preset and therefore with that preset's fiscal position. In TicketScreen.onDoRefund, the fiscal position of the refunded order was only copied onto it when the refunded order had one, so an order taken without a fiscal position kept the default preset's one. When an already existing empty order is reused as destination, whatever fiscal position was last set on it survives for the same reason. Fix: Always assign the fiscal position of the refunded order to the destination order, an empty one included, so a refund is taxed exactly like the order it refunds instead of silently switching. The preset itself is left untouched: it drives the ordering workflow (timing slot, customer identification) which must not be imposed on a refund. opw-6442664 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282560 Forward-Port-Of: odoo/odoo#280672
Forms now keep the desktop-style layout on tablet-sized screens instead of switching too early to the mobile view. This provides a more consistent and usable experience for users working on tablets or medium-sized devices.
Original PR description
Change the breakpoints to keep the desktop configuration on tablet resolutions instead of switching to the mobile display. task-6379696 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283818 Forward-Port-Of: odoo/odoo#283765
Sales automatic invoicing now creates each invoice for the actual latest payment amount, rather than using the total paid so far. This prevents over- or under-invoicing when customers pay a sales order in multiple installments, improving billing accuracy and customer trust.
Original PR description
Steps to produce: --- - Install the `Sales` module. - In Settings, enable `Automatic Invoice`. - Also enable the Demo payment provider. - Create a sale order with a total of `800` and confirm it. -…
Steps to produce: --- - Install the `Sales` module. - In Settings, enable `Automatic Invoice`. - Also enable the Demo payment provider. - Create a sale order with a total of `800` and confirm it. - Generate a payment link for `200` from the gear icon and pay it. - Generate a second payment link for `300` and pay it. - Generate a final payment link for the remaining `300` and pay it. Issue: --- - After the first payment (200), an `invoice of 200` is created. Correct. - After the second payment (300), an` invoice of 500` is created instead of 300. - After the third payment (300), an `invoice of 100` is created instead of 300. Root cause: --- - The down payment invoice uses `order.amount_paid`, the cumulative sum of all transactions on the order, instead of the amount of the latest payment. This causes invoices to be sized off the running total instead of the individual payment delta. Fix: --- - Compute the invoice amount as `order.amount_paid - order.amount_invoiced` (the unpaid) instead of passing the cumulative `amount_paid` directly. opw-6324036 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283492 Forward-Port-Of: odoo/odoo#273099
This fixes an inefficiency that could slow down opening Sign templates for users with access to their own templates. The system now loads related template item data in a batch instead of one item at a time, reducing unnecessary database work and improving responsiveness.
Original PR description
Steps to reproduce: - with a user with "Sign / User: Own Templates" access rights - go to Sign / Templates - click on a template to open it => reading `sign.item.role.item_ids.template_id` triggers the computation of the related field `sign.item.template_id`, whose inverse `sign.template.sign_item_ids` carries a domain. When applying the domain, the sign.item's fields need to be fetched but they are fetched with one query per sign.item instead of a single batched one. task-6478942 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283864 Forward-Port-Of: odoo/odoo#282994
Imported UBL invoices with document-level discounts or charges now correctly associate their tax amounts with the relevant tax totals. This prevents incorrect tax adjustments during import, helping ensure accounting data is accurate for affected invoices.
Original PR description
When importing UBL invoices that contain document-level allowances or charges with percentage taxes, the tax values were not linked to their corresponding `TaxSubtotal` group (`related_taxes_values`). As a result, the tax correction step (`_import_ubl_invoice_fix_taxes_amounts`) was unaware of document-level taxes, which caused wrong tax corrections. opw-6388544 Forward-Port-Of: odoo/odoo#279350
Fixed an issue that could crash the chat interface when an operator closed multiple live chat conversations at once. This improves reliability for support teams handling several conversations and prevents disruptions in the chat hub.
Original PR description
Before this commit, clicking "Close all conversations" from the chat hub as an operator with several live chat windows open (fresh database with im_livechat demo data, logged in as admin) sometimes…
Before this commit, clicking "Close all conversations" from the chat hub as an operator with several live chat windows open (fresh database with im_livechat demo data, logged in as admin) sometimes crashed:
TypeError: Cannot read properties of undefined (reading
'findIndex')
at Proxy.close (mail/static/src/core/common/chat_window_model)
or with "RangeError: Maximum call stack size exceeded" in the record machinery. The overflow interrupts that machinery anywhere, hence the varying crash.
This happens because closing all windows leaves every live chat channel at once, and the resulting bus payloads reach the store out of their transaction order. Resolving an out-of-order many command derives a REPLACE from the field's history, and applying it updates the inverse field of each removed record. The problem is that these inverse updates are resolved against the same out-of-order revision:
- on the many side, they derive another REPLACE on the field they come from, re-entering the resolution before the field is updated, until the stack overflows;
- when their revision is older than the field's last replace, they are dropped, leaving the two sides of the relation out of sync.
Reminder that inverse updates go through `updateFields()` since "[FIX] mail: no infinite loop on discuss page load", so that the version history records them.
This commit fixes the issue by always applying client-generated updates (inverse echoes, computes, direct field writes): they reflect state the client already changed elsewhere, so only server data is subject to version resolution. They are still recorded in the history.
task-6450249
Forward-Port-Of: odoo/odoo#283204Odoo now shows the specific error details returned by Serbia's eFaktura service when an invoice submission fails. This helps users understand why an invoice was rejected and take the right corrective action without relying on generic technical error messages.
Original PR description
**Steps to reproduce:** - Install the Serbian EDI module `l10n_rs_edi`. - Configure eFaktura credentials on the company. - Create and confirm a Serbian customer invoice. - Send the invoice to…
**Steps to reproduce:**
- Install the Serbian EDI module `l10n_rs_edi`.
- Configure eFaktura credentials on the company.
- Create and confirm a Serbian customer invoice.
- Send the invoice to eFaktura.
**Observed Behavior:**
When the eFaktura API returns an HTTP error, Odoo only displays the generic exception generated by `requests`, for example an HTTP 400/500 error.
The actual error information returned by eFaktura in the response body is not shown to the user, making it difficult to understand why the invoice was rejected.
**Cause:**
`_l10n_rs_edi_send` catches `HTTPError`, `Timeout`, and `ConnectionError`, but the error message is built only from the Python exception.
For HTTP errors, the eFaktura API may return a response containing more precise information such as:
```json
{
ErrorCode: ...,
Message: ...
}
```
This response was not being used when displaying the error in Odoo.
**Fix:**
When an HTTP response is available and contains an eFaktura error payload, use the returned `ErrorCode` and `Message` as the error displayed on the invoice. Fallback to the existing connection/HTTP exception message when no usable API response is available.
opw - 6453653
Forward-Port-Of: odoo/odoo#281490Intercompany deliveries can now automatically unpack products after shipment, preventing package records from carrying over incorrectly between companies. This keeps inventory balances clearer and avoids confusing leftover stock artifacts in intercompany locations.
Original PR description
Issue: Compare to lot and serial number package are not multi company. It means that the package don't pass from a company to the other. So when a company deliver to another. The delivery will create a quant with the package. However the receipt in the other company will create a new quant without package (or a new package). It means that the quants are never reconcile and it could become difficult to understand what remains in intercompany location and what are artifact from past movements. In order to fix it, we introduce a new system parameter to directly unpack after the delivery. This way the receipt is always without source package and will automatically decrease the quant. opw-6376983 Forward-Port-Of: odoo/odoo#282448 Forward-Port-Of: odoo/odoo#277133
Vendor bills created from incoming Peppol/UBL email files now include the PDF embedded inside the XML. This prevents missing invoice documents when suppliers send electronic invoices through an email alias, improving document completeness for accounting teams.
Original PR description
When receiving a Peppol/UBL XML file containing an embedded PDF via an email alias, the PDF is not extracted and attached to the resulting vendor bill. Steps to reproduce: - Set up a BE Company - Configure an incoming mail server - Set up an email alias for the Vendor Bill journal - Receive a Peppol XML with embedded PDF via alias - Check the created Bill Issue: PDF has not been extracted from the xml This occurs because the received xml is set as main attachment for the record and in this case we skip extraction opw-6075250 Forward-Port-Of: odoo/odoo#278806 Forward-Port-Of: odoo/odoo#262047
Razorpay payment captures and refunds now use the original transaction reference when communicating with the provider. This helps ensure follow-up payment actions are matched to the correct Razorpay transaction, reducing failed or incorrect capture/refund operations.
Original PR description
Forward-Port-Of: odoo/odoo#283432 Forward-Port-Of: odoo/odoo#282554
French e-Invoicing eligibility now checks a customer's SIREN or SIRET instead of relying only on a VAT number. This prevents French business customers without a VAT number from being incorrectly treated as consumers, so the correct invoice sending option remains available.
Original PR description
**Steps to reproduce:** - Install module `l10n_fr_pdp` and configure French e-Invoicing. - Create a customer has a valid SIREN/SIRET (company_registry) but no VAT number. - Create an invoice for the customer and confirm the invoice. - Check the available sending methods. **Observed Behavior:** The French E-Invoicing option is disabled because the customer is identified as a B2C partner when no VAT number is set. **Cause**: The B2C detection relies on the partner's VAT number instead of its SIREN/SIRET. As a result, French companies without a VAT number but with a valid SIREN are classified as B2C. **Fix**: Determine whether a partner is B2C based on the presence of a valid SIREN/SIRET (derived from `company_registry`) instead of the VAT number. This correctly identifies French business partners that are eligible for French e-Invoicing even when they do not have a VAT number configured. opw-6357756 Forward-Port-Of: odoo/odoo#283418 Forward-Port-Of: odoo/odoo#278060
Sales orders now calculate delivery status based only on products that are actually deliverable, instead of including services or delivery fees. This prevents orders from being shown as partially delivered when all physical items that need delivery have already been shipped.
Original PR description
Delivery status considered every order line, including service, delivery and etc. When we ship products without stock, we deliver only consumable products, leaving the order wrongly partially delivered when there are other types of products included. Filter to consu lines when computing the order's delivery status. Forward-Port-Of: odoo/odoo#283206
Exchanged subcontracted products are now correctly received into warehouse stock instead of remaining in the subcontractor location. Purchase orders also show the correct received quantity after an exchange, improving inventory and procurement accuracy.
Original PR description
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10…
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7 instead of 10. `mrp_subcontracting` overrides `_prepare_move_default_values` to force the move `location_dest_id` to the subcontractor location for every `is_subcontract` move: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/wizard/stock_picking_return.py#L20-L25 That is correct for the return, but the same override also runs for the exchange re-receipt, an `incoming` picking whose destination should be the stock location from `return_type.default_location_dest_id`: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/wizard/stock_picking_return.py#L137-L153 The exchange move then goes from the subcontracting location back to itself, so validating it nets zero and `WH/Stock` never receives the units. Skipping the override when `new_picking.picking_type_id.code` is `incoming` lets the exchange land in stock. The received quantity must also count that receipt. `_should_count_for_quantity_received` only counts `supplier` or `transit` sources: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/models/stock_move.py#L330-L331 so the exchange, sourced from the internal subcontracting location, is skipped while the return still subtracts its quantity. Counting subcontracting-sourced moves: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/models/stock_move.py#L312-L314 restores `qty_received` to 10. opw-6410978 Forward-Port-Of: odoo/odoo#282666 Forward-Port-Of: odoo/odoo#279431
Accrued expense entries can now be created for purchase orders whose quantity was changed to zero after receipt. This prevents an error dialog and lets accounting users continue their accrual workflow normally.
Original PR description
### Steps to Reproduce: 1. Have a product where Track Inventory is enabled and the product category is FIFO and Perpetual 2. Create a PO for the product 3. Validate the receipt 4. Update the quantity…
### Steps to Reproduce: 1. Have a product where Track Inventory is enabled and the product category is FIFO and Perpetual 2. Create a PO for the product 3. Validate the receipt 4. Update the quantity on the PO to 0 5. Create Accrued Expense Entry > Traceback ### Description of the issue/feature this PR addresses: **Issue:** Currently when generating an Accrued Expense Entry for a PO where quantity on the line is updated to 0, the system crashes with an RPC error. This happens because reducing the line quantity to 0 sets the overall order amount to 0.0. Then. when the accrued orders wizard tries to calculate line-item ratios, it triggers a `ZeroDivisionError`. **Solution:** We can add a zero-check fallback condition when computing the line ratio inside `_compute_move_vals` in the `AccountAccruedOrdersWizard` class. The ratio calculation now defaults to 0.0 if the order total is zero, preventing division by zero. ### Current behavior before PR: Triggering the Accrued Expense Entry wizard on a PO with a changed quantity of 0.0 causes a `ZeroDivisionError` server error. The user receives an RPC error dialog and cannot proceed with creating the journal entry. ### Desired behavior after PR: The wizard should be able to process Purchase Orders with a line quantity of 0 without throwing an RPC error. The system should now cleanly generate the accrual entry based on received quantities. opw-6459403 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281808
Portal and live chat users can now mention someone in a message without the mention being lost. This ensures the mentioned person is properly notified, improving communication reliability in conversations.
Original PR description
Before this commit, a portal or livechat visitor mentioning someone in a message did not notify them: the mention was silently dropped. This happens because the client cleans up empty recipient fields by checking `postData[field].length`, and `partner_ids_mention_token` is an object, so its `length` is always undefined. The tokens were dropped as empty, and the server then filtered out every partner the sender cannot read. This commit fixes the issue by checking the number of keys instead. Forward-Port-Of: odoo/odoo#282782
Fixed an issue that could prevent users from opening the calendar in month view when their linked employee record belonged to another company. The calendar now handles missing schedule information gracefully, avoiding an error and keeping the user experience uninterrupted.
Original PR description
Currently, an error occurs when a user opens the calendar. Steps to Reproduce: - Install the `hr_calendar` module. - Go to `Employees` and create an `employee`. - Under the `Settings tab`, set the…
Currently, an error occurs when a user opens the calendar. Steps to Reproduce: - Install the `hr_calendar` module. - Go to `Employees` and create an `employee`. - Under the `Settings tab`, set the employee's `user` to `Administrator`. - Create a `new company` and switch to it. - Open the `Calendar` and set the `scale` to `Month`. `TypeError: reduce() of empty iterable with no initial value` When the user opens the calendar, it fetches the unusual days for the selected attendees. By default, the current user's partner is set as an attendee [1]. It then computes the schedule for the attendee [2], which returns an empty dictionary [3] because the linked employee belongs to a different company than the current company, as restricted by the domain [4]. This empty dictionary is then passed to reduce() to intersect the schedules, which raises an error because the iterable is empty. This commit ensures that when no schedule is found for the attendees, an empty set is returned. [1]: https://github.com/odoo/odoo/blob/658018684d781fef8bf77a77f1e050d1eb16937c/addons/hr_calendar/models/calendar_event.py#L38-L40 [2]: https://github.com/odoo/odoo/blob/658018684d781fef8bf77a77f1e050d1eb16937c/addons/hr_calendar/models/res_partner.py#L123-L130 [3]- https://github.com/odoo/odoo/blob/658018684d781fef8bf77a77f1e050d1eb16937c/addons/hr_calendar/models/res_partner.py#L40-L42 [4]- https://github.com/odoo/odoo/blob/658018684d781fef8bf77a77f1e050d1eb16937c/addons/hr_calendar/models/res_partner.py#L19-L25 sentry-7672490836 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282866
Fixes an issue where validating inventory receipts could fail when automatic reception report printing was enabled. Transfers and manufacturing-related validations can now complete normally, and printed reports contain the correct records.
Original PR description
Issue before this commit: ======================== Currently, when the reception report option is enabled for the operation type under Print on Validation, validating a transfer with reserved…
Issue before this commit: ======================== Currently, when the reception report option is enabled for the operation type under Print on Validation, validating a transfer with reserved quantities for the next transfer automatically tries to print the reception report. However, the print fails with `External ID not found in the system: stock.stock_reception_report_action`, preventing the transfer from being validated. Steps to Reproduce: ========================= - Install the stock module. - In Inventory → Configuration → Operation Types → Receipts, enable Reception Report under Print on Validation. - Create a delivery and receipt for Product A, both with a quantity of 4. - Open the Allocation smart button on the receipt and reserve the incoming quantity for the created delivery. - Validate the receipt. Observation: The system tries to print the reception report, but validation fails with `External ID not found in the system: stock.stock_reception_report_action`. Cause of the issue: ========================= In this [PR](https://github.com/odoo/odoo/pull/264299/changes#diff-79ba983a362d3fed28458daad5f2902e914fd731308157aa73f350f1597265a6L30-L35), the reception report action was removed, and the reception report is now printed through the Picking Operations report using `do_print_picking()`. However, the auto-print methods in both Stock and MRP still referenced the old reception report action, causing the `External ID not found` error when the reception report auto-print is enabled After This Commit: ========================= - The obsolete reception report reference is replaced with the appropriate reports in Stock and MRP, preventing the traceback and allowing users to complete validation when the reception report is enabled. - The unnecessary context override in `_get_autoprint_done_report_actions()` was removed. It was replacing the context returned by report_action() with the `unnecessary default_production_ids`, which removed the `active_ids` generated by report_action(). These IDs are required by the report renderer to identify the production records, and removing them caused a blank PDF report. Forward-Port-Of: odoo/odoo#283194
Electronic invoice XML now uses the reference from the invoiced child contact when one is set, instead of incorrectly taking the parent company reference. This helps ensure Peppol invoices carry the right buyer identifier and reduces misrouting or processing issues for customers with multiple contacts.
Original PR description
**Steps to reproduce:** * Set up a French company and configure Peppol E-invoicing. * Install `account_edi_ubl_cii` module. * Create a company partner (customer) and set a **Reference** value on the…
**Steps to reproduce:**
* Set up a French company and configure Peppol E-invoicing.
* Install `account_edi_ubl_cii` module.
* Create a company partner (customer) and set a **Reference** value on the company contact under
**Customer** -> **Settings** -> **Sales and Purchase**.
* Create a child contact under that company and set a different Reference value.
* Create an invoice using the child contact as the invoice partner and confirm the invoice.
* Send it via Peppol.
**Observed Behaviour:**
* The BuyerReference in the generated XML contains the reference of the parent
(commercial partner) Instead of the child contact used on the invoice.
**Cause:**
* The buyer reference was taken from the commercial partner instead of the
invoice partner.
**Fix:**
* Update the condition to use the invoice partner's reference when available;
Otherwise, fall back on the commercial partner's reference.
opw - 6330649
Forward-Port-Of: odoo/odoo#283324
Forward-Port-Of: odoo/odoo#273700DIN5008 invoice PDFs sent by post now place the recipient address correctly in the required window. This prevents postal delivery failures through Pingen while keeping the normal DIN5008 layout unchanged for non-postal documents.
Original PR description
**Steps to reproduce:** - Install l10n_din5008 and accountant. - Enable Snailmail from Accounting → Settings. - Create a German customer (Fiscal Country: Germany). - Create and post a customer…
**Steps to reproduce:** - Install l10n_din5008 and accountant. - Enable Snailmail from Accounting → Settings. - Create a German customer (Fiscal Country: Germany). - Create and post a customer invoice using the DIN5008 report layout. - Select Send by Post. - Enable Developer Mode and navigate to `Settings → Technical → Email → Snailmail Letters`. - Open the generated letter and send it. **Current behavior:** The letter fails to be sent to Pingen with the following error: An error occurred when sending the document by post. Error: ` The attachment of the letter could not be sent. Please check its content and contact the support if the problem persists.` **Cause:** For Snailmail documents, Pingen validates that the recipient address is located within the DIN5008 address window. The current l10n_din5008 report renders additional document information instead of the address in the address area, preventing the compliance validation to fail. **Fix:** When rendering the report for Snailmail, ensure that only the recipient address is displayed in the DIN5008 address window while suppressing the additional information that would otherwise occupy this area. This preserves the standard DIN5008 layout for regular reports while generating a Snailmail-compliant PDF that passes Pingen’s validation. **Reference:** [Pignen Recipient Address Validation Rule](https://help.pingen.com/en/fix-and-enhance-letters/issue-with-recipient-address#040201) Ticket [link](https://www.odoo.com/odoo/project.task/6387869) opw-6387869 Forward-Port-Of: odoo/odoo#280320
The Point of Sale feedback screen now adjusts better to Android phones and tablet displays. This prevents messages and confirmation visuals from appearing too small or overflowing, giving customers and staff a clearer checkout experience across devices.
Original PR description
In this commit: - The feedback screen was not scaling properly on Android devices and tablet displays, causing content to appear too small or overflow. - Fixed by making the checkmark and text sizes responsive using units so the layout adapts correctly across different screen sizes. Task: 6420543 Forward-Port-Of: odoo/odoo#283181 Forward-Port-Of: odoo/odoo#279328
Posted customer invoices now keep their original delivery date even if later deliveries are added to the same sales order with earlier completion dates. This prevents already-finalized invoices from changing silently and improves reliability for invoicing and audit records.
Original PR description
Steps to Reproduce: 1. Confirm a Sales Order with one line -> creates delivery P1. Validate P1 with date_done = Day_A. 2. Create an invoice from the SO and post it -> invoice.delivery_date = Day_A.…
Steps to Reproduce: 1. Confirm a Sales Order with one line -> creates delivery P1. Validate P1 with date_done = Day_A. 2. Create an invoice from the SO and post it -> invoice.delivery_date = Day_A. 3. Add a new line to the same SO -> creates delivery P2. 4. Validate P2 with date_done = Day_B, where Day_B is earlier than Day_A. Issue: The already-posted invoice's `delivery_date` silently changes from Day_A to Day_B after step 4, even though nobody edited the invoice. This only happens when a delivery validated after posting has an earlier `date_done` than what was already used. Root Cause: `account.move.delivery_date (sale_stock)` is computed in `_compute_delivery_date()`, which depends on `sale.order.effective_date.effective_date` is itself computed as the earliest `date_done` among all done, facing deliveries on the order. Neither compute method checks whether the invoice is posted, so validating P2 triggers a chain reaction: the delivery is saved -> the sale order recalculates -> the invoice recalculates -> delivery_date gets overwritten on an already-posted invoice. `sale_stock` also marks `delivery_date` as protected, but this protection only works when the invoice itself is saved (write/create). Here, the change starts from saving the delivery (stock.picking), which never goes through the invoice's save method, so the protection never kicks in. `delivery_date` is also not on the list of fields Odoo normally blocks from editing after posting. Fix: `_compute_delivery_date()` now splits invoices into posted and non-posted before running. Non-posted invoices work exactly as before. Posted invoices are skipped from the sync and simply keep their current value instead of taking the newly calculated one. `sale.order.effective_date` itself is untouched only its effect on an already-posted invoice is blocked. Result: Once an invoice is posted, its `delivery_date` now stays fixed no matter what happens with later deliveries on the same sale order. `effective_date` keeps updating normally either way, confirming the fix only affects the invoice. Verified with both a script and a manual UI test. opw-6409171 Forward-Port-Of: odoo/odoo#283069 Forward-Port-Of: odoo/odoo#280978
The website analytics page now correctly shows Plausible Analytics data when opened, instead of incorrectly appearing disconnected. This restores access to website reporting in Odoo, including the free Plausible Analytics integration available on Odoo SaaS.
Original PR description
**Description** The website analytics page always appears as if the website is not connected to Plausible Analytics, even when the connection is actually in place. This means that the user can't read…
**Description** The website analytics page always appears as if the website is not connected to Plausible Analytics, even when the connection is actually in place. This means that the user can't read analytics directly from Odoo, and that the free Plausible Analytics offered is Odoo SaaS is not usable. **How to reproduce** Set up Plausible Analytics (it is active by default on SaaS) and visit `/odoo/website-analytics`, or go to "Reporting" -> "Analytics". **Origin** 1. `WebsiteDashboard` component fetches analytics data via an RPC call to `fetch_dashboard_data`. For how the component is built, the first call misses a `website_id` argument. 2. `fetch_dashboard_data` defaults to `self.env.website` when called without `website_id` argument. After [1], `self.env.website` is empty on `jsonrpc` routes, because this protocol should be URL-agnostic. As result, the `WebsiteDashboard` appears empty when first loaded, and data is restored when a website button is clicked on the top right. **Fix** 1. The argument `website_id` in `fetch_dashboard_data` is made optional. 2. `base.default_website` is used instead of `self.env.website`. `base.default_website` is guaranteed to exist and to point to the first website in the website sequence. [1]: https://github.com/odoo/odoo/commit/5eba3e99ebd4f928fc5de620a38ab127e95be890 task-6402001 Forward-Port-Of: odoo/odoo#282003
This fixes crashes and inconsistent selections in the timesheet assistant when saving or updating suggestions. Helpdesk and timesheet users should see a more reliable experience when working with timesheets from the assistant.
Original PR description
The `helpdesk_timesheet` override of `_getLocalConfigValsOnTake` called `this._is_record()`, a method that does not exist. This PR makes it call `_getResId` instead Task-6385031 Forward-Port-Of: odoo/enterprise#128675 Forward-Port-Of: odoo/enterprise#124075
German SEPA credit transfer files now exclude an unsupported company identifier when using the older German XML format. This keeps exported payment files compliant with bank requirements and helps prevent payment batches from being rejected.
Original PR description
### Issue before this commit: When generating a SEPA Credit Transfer batch using the German XML format (pain.001.001.03), the <LEI> tag is erroneously included in the exported file if an LEI is…
### Issue before this commit: When generating a SEPA Credit Transfer batch using the German XML format (pain.001.001.03), the <LEI> tag is erroneously included in the exported file if an LEI is configured on the company. This invalidates the XML, causing banks to reject the file. ### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Go to Settins > Vendor Payments > SEPA Credit Transfer / ISO20022 and set Name Identification as 529900T8BM49AURSDO55 and Issuer as LEIMAN 3. Go to companies and set 529900T8BM49AURSDO55 as LEI in the DE company 4. Go to Accounting dashboard and click the 3 dots of the bank group, go to Configuration and set the Account Number and be sure in the Outgoing Payments tab XML Format is German 5. Create a new German company from Contacts with: 1. Country as Germany 2. VAT 3. Account Number in the Bank Accounts by adding one line: 1. example Account Number: DE65100500007201811026 2. example Bank: BNP Paribas 3. activate the Send Money button 7. Then go to Vendor > Payments and create a new one with Payment Method as SEPA Credit Transfer for the German company created 8. Go back and select the new payment from the list and click create batch and print it 9. In the XML of pain.001.001.03.(DE) file, the LEI tag should not be included. ### Cause of the issue: The XML generation logic does not filter out the <LEI> element for older schema versions like pain.001.001.03, which do not support this tag. ### Reason to introduce the fix: To ensure strict schema compliance and prevent bank rejections. The <LEI> element is now properly omitted from pain.001.001.03 files and restricted only to newer formats (e.g., pain.001.001.09) where it is valid. opw-6428150 Forward-Port-Of: odoo/enterprise#128709 Forward-Port-Of: odoo/enterprise#127758
Colombian contacts using the NIT identification type are now only classified as companies when a VAT number is provided. This prevents individual child contacts from being incorrectly created as companies, improving contact accuracy for Colombian records.
Original PR description
Before this change: When creating a child contact under a Colombian company from another company context, the identification type defaults to NIT. The system evaluated the child contact as a company immediately, regardless of whether a VAT number was entered, preventing proper individual contact creation. To reproduce: 1. Create a new contact and set the country to Colombia. 2. Set the identification type to NIT. 3. Leave the VAT field empty. 4. Observe that the "Is a Company" checkbox becomes checked automatically. After this change: The company computation logic explicitly verifies that a valid VAT number is present before evaluating NIT contacts as companies, allowing individual child contacts to retain their correct entity status. opw-6468848 Forward-Port-Of: odoo/enterprise#128656
This fix ensures that Peruvian electronic invoices with untaxed lines show the correct validation error during batch sending instead of causing a generic failure. It prevents one problematic invoice from blocking the background process from handling other invoices, improving reliability for SUNAT submissions.
Original PR description
In l10n_pe_edi, invoices containing lines without tax can't be submitted to SUNAT.
When sending a single invoice, an error message is displayed. However, sending multiple invoices processes them in the background by a cron job. In this case, EDI document creation fails without error handling, raising a generic parsing error and blocking the cron from processing other invoices.
Steps to reproduce:
1. Create and post two invoices with no tax on some lines.
2. From the list view, select both invoices and click "Send" and mark "SUNAT".
3. An exception is raised: `ValueError: XMLSyntaxError("Start tag expected, '<' not found, line 1, column 1")`.
opw-6390480
Forward-Port-Of: odoo/enterprise#127990
Forward-Port-Of: odoo/enterprise#125143Colombian point-of-sale orders that include combo products now send compliant electronic invoice data to DIAN. This prevents card-paid combo sales from being rejected because zero-priced combo parent lines were included in the report.
Original PR description
Issue: When ordering through POS combo items won't be accepted by DIAN. Steps to reproduce: Set company to Colombia and activate the DIAN module. Simulate a sell of an combo item with POS. Pay with card. Error will ensue. Cause: The XML sent to DIAN is not accepted because one of the items has 0 price (the combo item). Solution: Not sending lines that are combo items. opw-6232599 Forward-Port-Of: odoo/enterprise#123928 Forward-Port-Of: odoo/enterprise#119652
This fix ensures that a customer manually assigned to a planning or field service shift is kept when the shift is started, signed in, or completed. It prevents the shift customer from being unintentionally replaced or removed based on the linked sales order, improving reliability for scheduling and field service workflows.
Original PR description
Before this commit, when `sale_planning` module is installed after `planning_field_service` and the user sets a customer onto a shift, the customer could be removed when the user signs in or complete the shift. This issue is because `sale_planning` module defined `partner_id` field as a related field `related="sale_order_id.partner"` and `planning_field_service` module stores the field and so the field will always follows the partner set on the SO linked even if the user sets a customer on the shift. This commit removes the related attribute to replace it by a compute and a search method to have the exact same behavior but the search method will be short-circuited if the partner_id field is stored. task-5264800 Forward-Port-Of: odoo/enterprise#128417 Forward-Port-Of: odoo/enterprise#122034
Users working in French and other translated languages can now download the General Ledger report as a CSV without the export failing. The fix ensures translations are handled through the active export process, preventing the error that stopped the file from being generated.
Original PR description
Currently an exception is generated when the user tries to export (downlaod) a `CSV` file of the `General Ledger` report as the below step: - Install the `accountant` module with demo data - Enable…
Currently an exception is generated when the user tries to export (downlaod) a `CSV` file of the `General Ledger` report as the below step: - Install the `accountant` module with demo data - Enable and change to the `French` language - Go to `Comptabilité` > `Analyse` > `General Ledger` - Click the cog icon in the menu > Click on `CSV` - An error occurs in the log, and nothing is downloaded Error: `TypeError: 'NoneType' object is not subscriptable` This issue occurs after the recent refactoring changes in [1]. When `_generate_csv_lazy_export` is called, it uses the `_()` method for translation, which accesses `self.env`. However, at this point, the cursor is already closed. As a result, when the code at [2] is reached from `ormcache`, it raises the above error because `model.env.transaction.ormcaches__` is `None` (code ref [3]). This commit fixes the above issue by performing the translation using the existing `handler` variable, which contains an environment with the new cursor (see code ref [4]). [1]: https://github.com/odoo/odoo/commit/13c3adf3a8b5ba6325190d6b9aea45fb8a6a8b2f [2]: https://github.com/odoo/odoo/blob/5ef7829895b2e05650da394c1e35dfdc3a23c066/odoo/orm/cache.py#L111 [3]: https://github.com/odoo/odoo/blob/5ef7829895b2e05650da394c1e35dfdc3a23c066/odoo/orm/environments.py#L1012 [4]: https://github.com/odoo/enterprise/blob/912a47b8f0828ef8316b7e4ecdabf8a2f305b313/account_reports/models/account_general_ledger.py#L524 Sentry-7608119520 Forward-Port-Of: odoo/enterprise#126778
When payroll teams include additional unpaid payslips in a SEPA payment file, those payslips are now correctly marked as paid after confirming the payment. This prevents paid employees' payslips from incorrectly remaining in a validated but unpaid status.
Original PR description
Steps to reproduce: - Open the payment report wizard on a payslip or a pay run - Tick "Include Unpaid" and keep the extra payslips selected - Generate the SEPA file, then click "Mark as Paid" Issue: the extra payslips listed in the file stay in state "validated". Cause: mark_as_paid() paid payslip_ids, while the file is built from unpaid_payslips. Fix: pay the payslips that are actually listed in the file. Task 6428919 Forward-Port-Of: odoo/enterprise#127889
The timesheet timer no longer disappears when the server reaches UTC midnight while it is still the previous day for the user. This helps employees in non-UTC time zones keep accurate running timers until their own local midnight.
Original PR description
**Problem:** The running timesheet timer in the systray stops when the Odoo server clock passes UTC midnight, even though it is still the same day in the user's own timezone. **Steps to reproduce:**…
**Problem:** The running timesheet timer in the systray stops when the Odoo server clock passes UTC midnight, even though it is still the same day in the user's own timezone. **Steps to reproduce:** 1. Set the user's timezone to one behind UTC (e.g. America/Guadeloupe, UTC-4). 2. Start a timesheet timer while it is before local midnight but after the server has passed UTC midnight (e.g. 20:00 local = 00:00 UTC). 3. Look at the running timer in the systray. **Current behavior:** At server (UTC) midnight the running timer disappears and its ongoing count is lost. **Expected behavior:** The timer keeps running until the user's own local midnight, regardless of the server timezone. **Cause of the issue:** The systray controller derives its reference day from `date.today()`, which returns the server's (UTC) local date. The running timer's timesheet is created dated the user's local day (`hr_timesheet` uses `fields.Date.context_today`). Once the server crosses UTC midnight, `date.today()` advances to the next day while the user's local day has not, so `timesheet_systray_user_data` (searching `date == today`) and `get_timer_start_time` (searching `date` within today's bounds) no longer match the running timesheet, and the systray reports no running timer. **Fix:** Deriving the reference day from `fields.Date.context_today` aligns the systray's notion of "today" with the timezone the timesheet was recorded in, so the timer is tied to the user's local day rather than the server's. This keeps recording and retrieval consistent, since the timesheet is already dated with the user-local day on creation. opw-6343442 Forward-Port-Of: odoo/enterprise#125635
Philippine payroll now uses the proper salary bases when calculating mandatory SSS and Pag-IBIG monthly contributions. This helps ensure employee deductions and employer payroll obligations are computed accurately, with tests updated to confirm the corrected behavior.
Original PR description
. SSS Mandatory contribution is categories['TAX_CASH_EARNINGS'] . Pag-IBIG Contribution is categories['PH_BASIC'] + categories['ECOLA'] . Update the corresponding tests task-6431960 Forward-Port-Of: odoo/enterprise#126527
Fixes an issue in Accounting where resetting a bank statement line to draft could trigger an error instead of completing normally. This helps accounting users correct or reopen bank transactions without being blocked by a system failure.
Original PR description
Currently, an error occurs when resetting a **bank** statement line to draft. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting Dashboard` and click the `three-dot` on…
Currently, an error occurs when resetting a **bank** statement line to draft. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting Dashboard` and click the `three-dot` on bank journal. - Open `Transactions`. - Create a new `statement line`. - Select the `statement line`, click the `gear action`, and click `Reset to Draft`. `AttributeError: 'bool' object has no attribute 'setdefault'` After the [recent commit], when resetting the statement line to draft, the server action run [1] and the linked move is going reset to draft, and the method returns the result [2]. After the mentioned commit, the method returns True [3]. When the result from [4] is passed to clean_action, it raises an error [5]. This commit ensures that it returns None after resetting the statement line linked to the invoice to draft, as it previously returned None and same as like [6]. [recent commit]: https://github.com/odoo/odoo/commit/712718d9df0fd5044ac57fdd9ec58e64bece36c0 [1]- https://github.com/odoo/enterprise/blob/7ca28a1c079d22b60e3756ca9b4f404771f214e8/account_accountant/views/bank_rec_widget_views.xml#L541-L551 [2]- https://github.com/odoo/enterprise/blob/eae51e3ca155ca29e1de54f4bd223e7540b2aa7f/account_accountant/models/account_bank_statement.py#L112-L114 [3]: https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/account/models/account_move.py#L6236-L6251 [4]: https://github.com/odoo/odoo/blob/a6f99706c6a62fc65666a0ff5e58fa465b41a6fb/addons/web/controllers/action.py#L53-L59 [5]: https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/web/controllers/utils.py#L24 [6]: https://github.com/odoo/odoo/blob/d7df2e8acff9eb7066993fa6a0b0c6d7c85baabc/addons/account/models/account_payment.py#L1206-L1208 sentry-7354160052 Forward-Port-Of: odoo/enterprise#127189
The General Ledger now avoids showing misleading foreign currency amounts when companies with different currencies share the same chart of accounts. Initial balances will no longer combine amounts from different currencies, helping finance teams trust multi-company reports.
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Create two companies with different currencies (e.g. **USD** and **CAD**) sharing the same **Chart of Accounts**. * Open a shared…
**Steps to reproduce:** * Install the **Accounting** module. * Create two companies with different currencies (e.g. **USD** and **CAD**) sharing the same **Chart of Accounts**. * Open a shared account and: * Add both companies in the **Company** field. * Under the **Mappings** tab, configure a mapping for each company. * In each company, create and post a journal entry on the same shared account (for example, a receivable account) using the company's own currency. * Set the journal entry dates to the **current month**. * Open **Accounting → Reporting → General Ledger**. * Change the reporting period to the **following month** so the posted entries are shown as the **Initial Balance**. * Open the report separately for each company. **Observed behavior:** * From the **CAD company**, the Initial Balance displays **USD 2,000** instead of the expected **USD 1,000**. * From the **USD company**, the **Currency** column on the Initial Balance is **blank**. **Cause:** * The SQL query for the `id_with_accumulated_balance` groupby used `SUM(amount_currency)` and `MIN(currency_id)` to aggregate all pre-period lines into a single Initial Balance row. * In a multi-company shared Chart of Accounts, lines from different companies (each with their own currency) were collapsed into the same group, causing `SUM(amount_currency)` to add amounts across currencies and `MIN(currency_id)` to return an arbitrary currency ID. * Additionally, the Python accumulation loop incorrectly performed **integer addition** on `currency_id` (a foreign key), further corrupting the displayed currency. **Fix:** * Replace `SUM(amount_currency)` and `MIN(currency_id)` with `CASE` expressions `MIN = MAX` is a uniformity check that works for **any number of currencies**: if every row in the group shares the same currency the condition is true and the correct sum is returned; if even one row differs the condition is false and both fields return `NULL`. The original three-column `GROUP BY (id, date, account_id)` is preserved. * The Initial Balance row now correctly shows a **blank** currency column, consistent with the Odoo 18 behavior, instead of an incorrect aggregated foreign currency amount. opw-6375310 Forward-Port-Of: odoo/enterprise#124823
Payroll processing now ignores time off requests that were refused after being marked for review on a future payslip. This prevents HR teams from seeing unnecessary pay run errors or review items for absences that are no longer valid.
Original PR description
Have a payslip created and validated in a period. Have a unprocessed payrun in the same period. Create a time off that is automatically validated, it will have a Payslip State set to 'To defer to next payslip' as it is required to be deferred. The HR team decide to refuse the time off for any reason, before processing the pay run. Now, while processing the pay run, the refused time off 'to defer' will trigger an error and show in the 'time off to review'. This is not correct, as the refused time off should not have any effect on a payrun. This fix backports another fix done in 19.5 and add a test to assert the fix. Backport of https://github.com/odoo/enterprise/commit/a5e78deee10a772b628dd60a84dfe2df9cd52cd2 Task 6484459 Forward-Port-Of: odoo/enterprise#128543
Australian payroll batches now handle employees with no leave allocations correctly. This prevents batch payslip creation from failing when employees with and without unused leave are processed together.
Original PR description
Creating a batch of payslips mixing employees with and without leave allocations raises a KeyError: ``` File "l10n_au_hr_payroll/models/hr_payslip.py", line 869, in _add_unused_leaves_to_payslip…
Creating a batch of payslips mixing employees with and without leave allocations raises a KeyError:
```
File "l10n_au_hr_payroll/models/hr_payslip.py", line 869, in _add_unused_leaves_to_payslip
annual_gross = leaves_totals[payslip.id]['annual'] * daily_wage
~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 22
```
Current Issue:
`_l10n_au_get_unused_leave_by_type` only materialises leaves_by_date[payslip.id] inside the allocation loop, so a payslip whose employee has no matching allocation never gets a key. `_l10n_au_get_unused_leave_totals` then rebuilt a plain dict out of those entries and only fell back to a defaultdict when leaves_by_date was completely empty. A mixed batch is not empty, so the plain dict was returned and `_add_unused_leaves_to_payslip` raised on the payslips that were missing from it.
This never showed up in the UI, **where payslips are created one at a time**: a single slip either has an allocation, or produces an empty mapping that hits the fallback.
Approach:
Build the totals on a defaultdict and update it instead of returning a plain dict, so any payslip without allocation resolves to 0 rather than being absent. This also drops the need for the empty special case, and keeps the mapping consistent with the defaultdict returned by `_l10n_au_get_unused_leave_by_type`, which `_l10n_au_get_leaves_for_withhold` indexes the same way.
task-6465229
Forward-Port-Of: odoo/enterprise#127623The salary calculator now keeps simulations separate from an employee's existing draft payslip. This prevents fields from being cleared and avoids misleading missing-field errors when payroll teams test salary scenarios.
Original PR description
Steps:- 1. Navigate to Payroll->Employees menu->Salary Calculator 2. Select Employee who already have a draft payslip. 3. You will see all the fields will get emptied and give "Missing required fields". Root cause:- Opening the salary simulator temporarily writes the simulated values onto the employee's record. If that employee already had a draft payslip, this write also refreshed that payslip behind the scenes, even though the payslip had nothing to do with the simulation. Fix:- Mark the simulation clearly as a simulation so it no longer refreshes the employee's existing payslip. task-6392171 Forward-Port-Of: odoo/enterprise#128403 Forward-Port-Of: odoo/enterprise#127223
This fixes issues in the Social app where liking a tweet could crash, Facebook likes did not refresh correctly, edited comments did not show immediately, and duplicate images could appear in post views. Users get a smoother, more reliable experience when managing social media interactions from Odoo.
Original PR description
Bugs ==== When liking a Tweet in the feed view, a traceback is raised. Since https://github.com/odoo/odoo/commit/c0c82927f2e7 the likes does not update in the comments modal, `record` is now a snapshot of the kanban record (and so the template is not reactive). When editing a comment, we need to close / re-open the modal to see the change. This is because we try to change the prop from inside the component. Since https://github.com/odoo/enterprise/commit/89d87b7177e20ba1d3daee791d5ff55af0fafaf0 we have many records per media, and so the duplication check should have been updated. Task-6425391 Forward-Port-Of: odoo/enterprise#125639
Accounting users without Point of Sale access can now export Spanish VAT record books that include POS transactions. The report safely reads the required POS information in the background, preventing access errors and allowing finance teams to complete tax reporting.
Original PR description
Steps to reproduce:
- With an ES Company
- Open a POS session, add product with tax and pay
- As a user with only accounting access
- Go to Accouting > Reporting > Tax report
- Select Generic Tax report
- Print "VAT record Books"
Issue:
An AccessError will raise
```
Access Error
You are not allowed to access 'Point of Sale Session' (pos.session) records.
This operation is allowed for the following groups:
- Point of Sale/User
Contact your administrator to request access if necessary.
```
Analysis:
Vat Record Books handler for POS needs to read pos.session and pos.order records. Currently, the action is performed with the rights of the user running the report, so accounting-only user face an error.
As POS records are only read internally to build the report, we add sudo call to get the data.
opw-5862529
Forward-Port-Of: odoo/enterprise#126590
Forward-Port-Of: odoo/enterprise#125980This fix prevents branch reconciliation settings from being incorrectly copied when matching internal transfers between a branch and its parent company. Users can now reconcile these transfers without company inconsistency errors, reducing manual corrections in accounting workflows.
Original PR description
When reconciling an internal transfer between a branch and its parent company, an User Error is raised if the branch transaction has been processed via reconciliation model. Steps to reproduce: -…
When reconciling an internal transfer between a branch and its parent company, an User Error is raised if the branch transaction has been processed via reconciliation model. Steps to reproduce: - Have a company with branch both selected - On the branch, create a reconciliation model "Internal transfer" that assigns the whole balance to the liquidity transfer account - Have a Bank journal on the company and a Bank journal on the branch - On the branch bank journal, create a -100 transaction 'testb' and reconcile it using the branch internal transfer model - On the company bank journal, creata a 100 transaction, open the reconciliation widget and select the branch transaction to match it Issue: The reconciliation is refused with a company inconsistency error ``` Uh-oh! You’ve got some company inconsistencies here: - “BNK1/2026/00011 test” belongs to company “YourCompany” while “Reconciliation Model” (reconcile_model_id: 'Internal Transfer branch') belongs to another company. To avoid a mess, no company crossover is allowed! ``` However, if user manually assign the transfer account to the branch transaction, the reconciliation proceed as expected Analysis: When reconciling, we build the counterpart journal item by cloning the values of the matched move line, copying also the reconcile model. That field is company dependent and flagged copy=False, so it should not be propagated. opw-6365856 Forward-Port-Of: odoo/enterprise#127517
German Datev ATCH exports now work when an invoice's main attachment was added through a log note. This prevents accounting users from being blocked by an access error when exporting supporting invoice attachments.
Original PR description
### Issue: When an invoice has an image as its main attachment added via a log note, any non-admin user who did not create the attachment gets an `AccessError` when exporting the Datev ATCH zip ###…
### Issue: When an invoice has an image as its main attachment added via a log note, any non-admin user who did not create the attachment gets an `AccessError` when exporting the Datev ATCH zip ### Cause: Since commit `e7c93e5a6f`, attachments uploaded via certain flows can be "orphaned" — their `res_model` is set to `False` and `res_id` to `0` via `_fix_attachments_on_record_from_files_data` This allows the attachment to appear in the chatter without being linked to the move's attachment list However, `_message_set_main_attachment_id` can still set such an orphaned attachment as `message_main_attachment_id` When a user without system rights tries to read it, the ORM access check uses `res_model=False` and `res_id=0`, which does not match the move the user has access to, raising an `AccessError` ### Steps to reproduce: - Install `l10n_de_reports` and switch to the DE company - Create and confirm an Invoice (any lines, any customer) - Add a Log Note with an image - Set Demo user's Accounting rights to `Invoicing & Banks` - Log in as Demo - Open the General Ledger - In the cog menu, choose `Datev ATCH (zip)` Before the fix, an `AccessError` is raised opw-6397804 Forward-Port-Of: odoo/enterprise#128484 Forward-Port-Of: odoo/enterprise#127787