Tuesday, September 22, 2026
20 changes · 19.0
New functionality added to Odoo
Swiss payroll now supports daily allowance entries that keep an employee's net pay the same when employers promise compensation during sickness, accident, maternity, military service, disability, or loss-of-earnings absences. The system automatically calculates the needed net/gross adjustment while preserving manual override options and matching accounting treatment.
Original PR description
Daily allowances paid by an insurance (sickness, accident, maternity, military service, disability, loss of earnings) are entered as third party payments: the automatic 2050 line deducts them from…
Daily allowances paid by an insurance (sickness, accident, maternity, military service, disability, loss of earnings) are entered as third party payments: the automatic 2050 line deducts them from the payslip so the employee is not paid twice, and takes them out of the insurance bases. Because that insurance money is free of social contributions, the deductions no longer match a normal month and the final net drifts away from what the employee normally receives. Some employers promise the employee the exact same net as if nothing had happened. This commit adds a "(Net compensation)" variant of each daily allowance wage type to express that promise: when one of them is used, the payslip computes wage type 4900 (Net/Gross compensation) on its own, with the amount that brings the final net back to exactly what it would be without the allowance lines. The employer takes over, or gets back, the whole difference in contributions and source tax. There is no direct formula for that amount, since the 4900 line changes its own deductions (source tax brackets, insurance ceilings, 5 cents rounding). The payslip is therefore recomputed a few times with different amounts until the net matches, each attempt running inside a database savepoint that is rolled back afterwards, so nothing of those attempts is kept. A manual 4900 input keeps priority over the automatic computation, the same way a manual 2050 input does. The new wage types get the same accounting mapping as their normal counterparts. Forward-Port-Of: odoo/enterprise#128243
Resolved issues and error corrections
Duplicating a section in a sales quotation now keeps any manually adjusted unit prices on the copied product lines. This prevents incorrect price resets and helps sales teams maintain accurate quotes without re-entering custom pricing.
Original PR description
Problem: When duplicating a section containing sale order lines, the system should preserve any manually modified unit prices on those lines. However, the system would reset the unit price back to…
Problem: When duplicating a section containing sale order lines, the system should preserve any manually modified unit prices on those lines. However, the system would reset the unit price back to the product's default because the duplication process triggers a standard product onchange, which wipes the custom price on the new duplicated order line. Solution: Override onchange_batch on sale.order.line to intercept line initialization during a duplication. If a copied price_unit is present in the payload, the method restores that modified price and recalculates the subtotal and tax amounts accordingly, ensuring the duplicated line accurately reflects the original line's pricing. Steps to reproduce(runbot v19): 1. Create a Quotation and add a product line under a section. 2. Manually modify the unit price on the product line to a custom value (e.g., set $10 to $99). 3. Click the section dropdown menu and click Duplicate. 4. Notice the duplicated product line incorrectly resets its unit price back to the default price ($10) instead of preserving the manually modified price ($99). opw-6421922
This fix prevents an error when users select text from inside the HTML editor and extend the selection outside the editor boundaries. It makes the editor more stable in Chrome and Edge and aligns behavior with Firefox, reducing interruptions while editing content.
Original PR description
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from…
## Description Fixes issue #187539 where selecting text that extends outside the HTML editor boundaries throws an uncaught promise error. ## Problem When using Ctrl+A or dragging a selection from inside the editor to outside its boundaries, the `updateSelectionTable` method in `table_plugin.js` would process the invalid selection and call `setSelection()` with nodes outside the editable area, causing: ``` UncaughtPromiseError: Selection is not in editor ``` This only affected Chromium-based browsers (Chrome, Edge) as Firefox already had protection against this scenario. ## Solution Added a safety check in `updateSelectionTable` to verify that the document selection is within the editable area before processing table selection updates. This extends the same protection Firefox had to all browsers. ## Changes - `addons/html_editor/static/src/main/table/table_plugin.js`: Added validation check - `addons/html_editor/static/tests/table/selection.test.js`: Added regression test ## Testing The new test verifies that selecting from inside a table to outside the editor does not throw an error. Closes #187539
Payslips validated from the list view now correctly generate or queue their PDF attachments. This prevents missing payslip documents and removes the need for users to click a separate confirmation action.
Original PR description
Validating payslips through the Payslips list view's "Validate" header button does not generate their PDF, nor does it queue them for the "Payroll: Generate pdfs" scheduled action to pick up later.…
Validating payslips through the Payslips list view's "Validate" header button does not generate their PDF, nor does it queue them for the "Payroll: Generate pdfs" scheduled action to pick up later. The PDF only gets generated if the user separately clicks the "Confirm" button afterwards.
### Steps to reproduce:
- Install Payroll with demo data.
- Go to Payroll > Payslips > Pay Run, generate payslips for a few
employees ("Regular Pay").
- Select one or more of those draft payslips in the list view, and click the
"Validate" header button.
OR
- Run the "Payroll: Generate pdfs" scheduled action.
- Open any affected payslip.
### Observed behavior:
No PDF attachment is ever created for the payslips validated this way, even after the cron runs.
### Expected behavior:
PDF attachment should be generated when validating the payslip, via "Validate" Button or cron
### Root cause:
[action_payslip_done()](https://github.com/odoo/enterprise/blob/7b309f2e74f9fa37b109375de6387db3241c2bcb/hr_payroll/models/hr_payslip.py#L607-L641) only runs its PDF-generation block when `self.env.context.get('payslip_generate_pdf')` is truthy. The Payslips list view's "Validate" button at [1] was missing
`context="{'payslip_generate_pdf': True}"`, unlike the "Confirm" button right next to it and the Pay Run's own "Validate" button, both of which already carry it. Without it, the whole PDF block is silently skipped: queued_for_pdf never gets set, so there is nothing for the cron to act on either.
[1]-
https://github.com/odoo/enterprise/blob/7b309f2e74f9fa37b109375de6387db3241c2bcb/hr_payroll/views/hr_payslip_views.xml#L10-L12
### FIX:
Add the missing context="{'payslip_generate_pdf': True}" to the "Validate" button in the Payslips list view header.
**opw-6401393**Fixes two Slovak accounting setup entries that assigned fixed assets to the wrong balance sheet accounts. This prevents vendor bills from creating assets under land or the wrong asset category, improving accuracy of depreciation and financial reporting for Slovak companies.
Original PR description
In `account.asset-sk.csv` the asset-account column has slipped by one row for the last two entries, so each of those two models pairs an asset account with the accumulated depreciation account of a…
In `account.asset-sk.csv` the asset-account column has slipped by one row for the last two entries, so each of those two models pairs an asset account with the accumulated depreciation account of a different asset class.
As shipped (Slovak account names glossed in English):
sk_asset_basic_herd_and_draft_animals
asset 029000 Ostatný dlhodobý hmotný majetok "Other tangible fixed assets"
depreciation 086000 Oprávky k základnému stádu "Accumulated depreciation, basic herd"
sk_asset_other_tangible_fixes_assets
asset 031000 Pozemky "LAND"
depreciation 089000 Oprávky k ostatnému DHM "Accumulated depreciation, other tangible"
Every other row in the file pairs an account with the accumulated depreciation account that names it: 012000 with 072000, 021000 Stavby ("Buildings") with 081000 Oprávky k stavbám ("Accumulated depreciation, buildings"), and so on. Only these two break the pattern, and both break it the same way.
`account.account-sk.csv`, in the same directory, disagrees with them and is right: it links 026000 Základné stádo a ťažné zvieratá ("Basic herd and draft animals") to the herd model and 029000 to the other-tangible one, and gives 031000 Pozemky no asset model at all, land not being depreciable under Slovak law or any other.
The consequence is not cosmetic.
`account.account.asset_model_ids` creates an asset when a vendor bill line hits the account, and the asset takes its accounts from the model. So on a Slovak database a bill booked to 029000 "Other tangible fixed assets" produces an asset whose value sits on 031000 "Land" and depreciates over six years, while the account that should have carried it never does.
Corrected to the accounts that both the accumulated depreciation side and `account.account-sk.csv` point at:
sk_asset_basic_herd_and_draft_animals asset 026000 Základné stádo a ťažné zvieratá "Basic herd and draft animals"
sk_asset_other_tangible_fixes_assets asset 029000 Ostatný dlhodobý hmotný majetok "Other tangible fixed assets"
Verified by loading the SK chart on a clean database: all ten asset models now pair with their own accumulated depreciation account, and none points at 031000.
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-prPoint of Sale now blocks opening a register when the server connection is unavailable instead of saving the action for later. This prevents sessions from closing with sales while never being properly opened on the server, ensuring opening dates, session names, and cash amounts are recorded correctly.
Original PR description
Steps to reproduce: - Open a PoS so that the Opening Control popup is displayed. - Cut the connection to the server without the browser going "offline" (e.g. the router stays up but the internet link…
Steps to reproduce: - Open a PoS so that the Opening Control popup is displayed. - Cut the connection to the server without the browser going "offline" (e.g. the router stays up but the internet link is down). - Click on "Open Register". - Restore the connection, make some orders, close the session. Issue: The session is closed with orders but was never opened on the server: it has no opening date, keeps its temporary name "<config>/00000", and the opening cash was never recorded. `set_opening_control` was called with the `queue` flag. On a ConnectionLostError the call was silently pushed to the in-memory unsync queue, and the popup marked the session as opened locally and closed itself. That queue is only replayed on the browser `online` event, which is never fired when the device stayed connected to its local network, and it is lost when the tab is closed or reloaded. Meanwhile the server accepts orders on a session in `opening_control`, and nothing checks it at closing. Opening the register is not an operation that can be deferred: the session must be opened before any order is made. The call is no longer queued. When the connection is lost, the user is told that the register cannot be opened offline, and the popup stays open so the opening can be retried once the connection is back. opw-6580262 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288899
Product images are now preserved when multiple variants of the same product are created or imported at the same time. This prevents images from disappearing during CSV imports, helping product catalogs remain accurate without manual rework.
Original PR description
Steps to reproduce: - Import a product CSV with one row per attribute value ("Product Values" column) and an image URL on every row, or create several variants of the same template in one `create()`…
Steps to reproduce:
- Import a product CSV with one row per attribute value ("Product Values" column) and an image URL on every row, or create several variants of the same template in one `create()` call with an image.
- Open the product: neither the template nor the variants have an image. Products without attribute values imported in the same file are fine.
The import creates all variants of a template in a single batch. The inverse of `image_1920` is applied record by record: the first variant finds no image on the template and writes its image there. Writing `image_1920` on the template invalidates the image cache of every product.product, including the sibling variants being created, whose `image_1920` is protected during the inverse and therefore reads back as empty. The next variant then takes the "clear the image from the template" branch and writes `False` on the template, undoing the first write. Nothing is stored.
Read the value to inverse for every record before writing anything, so the invalidation triggered by the template write cannot change what the following records write.
opw-6545651
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prLive Chat now checks URL matching rules before they are saved, preventing invalid entries from breaking chat initialization on the website. This helps administrators catch configuration mistakes early and keeps the website experience stable for visitors.
Original PR description
Currently, an error occurs when a user opens the website and Live Chat is initialized with an invalid URL regex. Steps to Reproduce: - Install `website_livechat` without demo data. - Open `Live Chat`…
Currently, an error occurs when a user opens the website and Live Chat is initialized with an invalid URL regex. Steps to Reproduce: - Install `website_livechat` without demo data. - Open `Live Chat` and, in the `YourWebsite.com` channel, click the `three-dot` menu and select `Configure Channel`. - Go to the `Rules` tab. - Add a rule, select `Welcome Bot` in `Chatbot`, and set an invalid regex such as `*` in `URL Regex` and save. - Click `Join Channel`. - Open the `website`. `re.PatternError: nothing to repeat at position 0 when serializing dict item 'result' ` - when `URL Regex is '['` `re.error: unterminated character set at position 0 when serializing dict item 'result'` When a user opens the website, Live Chat is initialized by checking operator availability and finding the matching country/URL rule [1]. The rule matching checks whether `regex_url` matches the current page URL. It uses `re.search()` [2], which compiles the regex pattern before searching. If `regex_url` contains an invalid pattern such as `*`, `re.search()` raises `nothing to repeat at position 0` because `*` must follow a preceding regex element. Similarly, `[` starts a character set (`[...]`) but has no closing `]`, causing `unterminated character set at position 0`. This commit adds a constraint to validate `regex_url` and reject invalid regex patterns when creating or updating Live Chat rules. [1]- https://github.com/odoo/odoo/blob/f962f1eccecfdbc95375f795174c8f687459b521/addons/im_livechat/controllers/main.py#L87 [2]- https://github.com/odoo/odoo/blob/f962f1eccecfdbc95375f795174c8f687459b521/addons/im_livechat/models/im_livechat_channel.py#L387 sentry-7679767396 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Barcode app now assigns scanned package quantities to the correct lot for products tracked by lots. This prevents over-counting and ensures warehouse receipts reflect the intended quantities when staff scan packaging and lot numbers.
Original PR description
Issue ===== Currently, the Barcode app doesn't work very well when the user scans packaging and lots. If they want to apply one or multiple packagings to a specific lot, the quantity is not quite…
Issue ===== Currently, the Barcode app doesn't work very well when the user scans packaging and lots. If they want to apply one or multiple packagings to a specific lot, the quantity is not quite exact since scanning a lot already increases the quantity by 1. Also, if there are multiple lots, the packaging quantity isn't added to the last scanned lot which is problematic. How to reproduce ================ 1. Active "Units of Measure & Packagings" and "Lots & Serial Numbers" settings; 2. Create a product tracked by lots with a barcode; 3. In the "Sales" tab, add a packagings (for example, "Pack of 6"); 4. Configuration > Units & Packagings > Selected the added UoM > Click on "Packaging Barcodes"; 5. Create a new barcode for the created product; 6. Create and confirm a receipt for 12 units of this product and open the operation in Barcode app; 7. Scan the product's barcode, then a lot then the packaging barcode -> First issue: the line has 7 units. 8. Scan a second lot and scan the packaging again -> Second issue: the packaging quantity is added to the first line, not the second one. Explanation =========== About the first issue, it is the expected behavior. When the user scans a lot, we increase the line quantity by 1. When the user scans a packaging, we increase the line quantity by this packaging quantity. The issue here is the user expects to scan a lot and then to apply a packaging quantity to it. For example, scanning a lot then a pack of six should result by a line with 6 units, not 7. Fix === With this commit, the scanned quantity (thus the scanned packaging) for product tracked by lots aren't apply directly on some line but keep in a buffer, with a message to let the user what they scanned until here. Then, when the user scans a lot, only then the quantity is assigned to the scanned lot. So, the logic and expected flow is: 1. The user scans a product or a packaging; 2. Then they scan a lot; 3. The previously scanned quantity is assigned to the scanned lot. [task-6216921](https://www.odoo.com/odoo/966/tasks/6216921)
Helpdesk reports now calculate SLA success consistently with the underlying tickets. This prevents report totals from disagreeing with the ticket lists opened from those totals, giving managers more reliable SLA performance data.
Original PR description
1. Open Helpdesk > Configuration > SLA Policies and create a policy on the team "Customer Care" with "In Progress" as target stage 2. Create two tickets on that team and move them to "In Progress"…
1. Open Helpdesk > Configuration > SLA Policies and create a policy on the team "Customer Care" with "In Progress" as target stage 2. Create two tickets on that team and move them to "In Progress" well before their deadline, so their SLA is reached on time, and leave a third one in "New" with its SLA still running 3. Open Helpdesk > Reporting > Ticket Analysis, enable the "SLA Success" filter and group by "Helpdesk Team", then by "Stage" 4. Click the cell of the "In Progress" stage -> the list of tickets it opens does not hold the number of tickets the cell was showing Ticket Analysis only offers aggregated views, so its cells count rows of helpdesk.ticket.report.analysis while clicking one re-queries helpdesk.ticket, and the two evaluate sla_success differently. Both analysis reports compute it as "the deadline lies in the future", which is what helpdesk.ticket.sla_success did until odoo/enterprise#85412 aligned it on "reached before the deadline". The two SQL views were left behind, so they label an ongoing SLA as a success and a reached one as a failure once its deadline has passed. With this commit, Ticket Analysis mirrors helpdesk.ticket.sla_success and SLA Status Analysis mirrors the 'reached' branch of its own sla_status, so the cells agree with the tickets they drill down to. Task-6523661
The default Terms and Conditions page now keeps interactive content blocks such as accordions intact when edited and saved. This prevents broken layouts and errors when users add or update accordion items in invoice terms content.
Original PR description
# How to reproduce - Go to Settings > Default Terms & Conditions > Preview - Open the editor - Add a Content Block - Add an Accordion in that Block - Save # The issue The accordion gets messed up and…
# How to reproduce - Go to Settings > Default Terms & Conditions > Preview - Open the editor - Add a Content Block - Add an Accordion in that Block - Save # The issue The accordion gets messed up and trying to add a new item to that accordion raises a traceback # Cause `invoice_terms_html` is an html field with some sanitization enabled : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/account/models/company.py#L172 This sanitization will remove the accordion snippet's buttons that controls the functionality of the snippet : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/website/views/snippets/s_accordion.xml#L9 This issue was already addressed by : https://github.com/odoo/odoo/commit/b6b4db5fb5690436a4284f6a22abf9f3b346a324 But it is not enough in the case of the accordion. The buttons will be removed by the lxml clean.Cleaner : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/odoo/tools/mail.py#L364 # Proposed Solution Disable sanitization entirely, like for blog's content, which can also be edited in the website editor : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/website_blog/models/website_blog.py#L29 opw-6500378 Forward-Port-Of: odoo/odoo#285255
This fix ensures Spanish TicketBAI/Batuz credit notes keep the equivalence surcharge rate as a positive percentage. This prevents tax authority validation errors and allows affected credit notes to be sent successfully.
Original PR description
In credit notes, `TipoRecargoEquivalencia` was multiplied by the reversal sign, producing negative values (e.g. -1.40) that violate the Batuz `Tipo3.2Type` pattern and get rejected by the tax agency (`B4_2000001: cvc-pattern-valid`). Unlike `BaseImponible`/`CuotaImpuesto` (amounts that must be negative), `TipoRecargoEquivalencia` is a percentage and must stay positive, as `TipoImpositivo` does. Steps to reproduce: 1. Configure a Spanish company with TicketBAI (Bizkaia). 2. Create a credit note (out_refund) with an equivalence surcharge tax. 3. Send it to TicketBAI; the send fails with a schema validation error. Task: MT-15939 OPW: https://www.odoo.com/es_ES/my/tasks/6573801 @jco-odoo could you review? It's essential to be able to send to Tbai/Batuz with equivalence surcharge tax. Forward-Port-Of: odoo/odoo#289618
Click-and-collect orders where customers choose to pay on site now remain visible in the Point of Sale until payment is collected. This prevents orders from being incorrectly treated as already paid online and ensures cashiers can settle the full amount at pickup.
Original PR description
When a customer orders online with click and collect and chooses to pay on site, the order is marked as paid on the eCommerce even though no money has been received yet. Because of this, the order was considered fully paid and no longer showed up in the Point of Sale, so the cashier had no way to settle it and collect the payment when the customer came to pick up the goods. Orders paid on site now stay visible in the Point of Sale with the full amount left to pay. Task-6466932
This fixes Malaysian MyInvois consolidated invoicing so point-of-sale orders are grouped by PoS configuration before invoices are created. Businesses with multiple PoS configurations should now receive the expected consolidated invoice grouping instead of unexpectedly generating hundreds of small documents.
Original PR description
#### Description of the issue/feature this PR addresses: When consolidating PoS orders for MyInvois, about one Consolidated Invoice is expected per PoS config(although could be more than 1). When…
#### Description of the issue/feature this PR addresses:
When consolidating PoS orders for MyInvois, about one Consolidated Invoice is expected per PoS config(although could be more than 1). When several configs sell during the same period, the wizard instead creates hundreds of documents each covering only around 100 orders.
#### Current behavior before PR:
_split_pos_orders_in_lines groups the session orders with itertools.groupby keyed on config_id, but the recordset is sorted using pos.order._order ("date_order desc, name desc, id desc"), which never orders by config. Since groupby only groups consecutive equal keys, orders of concurrently selling configs interleave and each config change starts a new group, appending an extra line. The wizard creates one document per MAX_LINE_COUNT_PER_INVOICE lines, so the spurious lines multiply the documents.
#### Desired behavior after PR is merged:
The session orders are sorted by config before grouping, so each config forms a single group and yields one line per continuous receipt range, giving one Consolidated Invoice per config. Sorting is stable, so date ordering within a config and the existing receipt continuity check are unchanged.
Ticket [link](https://www.odoo.com/odoo/project.task/6448122)
opw-6448122Belgian payroll now uses the employee's actual departure date when preparing holiday departure attestations instead of the notice period end date. This helps ensure the documents and related payroll calculations reflect the correct employment end date.
Original PR description
- Use `departure_date` instead of `end_notice_period` Task: 5391142
Starshipit delivery rates now handle incomplete wallet addresses without stopping checkout. If Starshipit cannot quote a rate because details like the street are missing, other delivery options can still appear so customers can complete Apple Pay or Google Pay purchases.
Original PR description
#### Description of the issue/feature this PR addresses: Rating a Starshipit shipment for an incomplete delivery address makes the whole rating loop fail instead of skipping that carrier. This breaks…
#### Description of the issue/feature this PR addresses:
Rating a Starshipit shipment for an incomplete delivery address makes the whole rating loop fail instead of skipping that carrier. This breaks express checkout (Apple Pay / Google Pay), where the wallet only discloses a partial address before authorization: the customer gets "update postal address" and cannot pay, while manual checkout works.
#### Current behavior before PR:
delivery_starshipit is the only connector that rates a shipment without validating the addresses first, and it ignores the express_checkout_partial_delivery_address context key set by website_sale. The empty street is sent to /api/rates, which answers 200 with {"success": false, "errors": [{"details": "street parameter value is required"}]}, and _send_request turns that into a UserError. As starshipit_rate_shipment lets it propagate, it aborts the rating of every carrier instead of only this one, so no delivery method reaches the wallet. The same happens for errors only the api can report, such as invalid credentials.
#### Desired behavior after PR is merged:
Both the recipient and the warehouse address are validated before the api is called, as the other connectors already do, and the message is returned as an unsuccessful rate rather than raised. Starshipit requires the street, so a streetless address is still refused, but with a neutral message in the express checkout flow since the customer cannot complete it before paying. The Starshipit carrier is simply not proposed among the wallet's options, the other carriers are rated normally, and express checkout completes.
opw-6393393
Forward-Port-Of: odoo/enterprise#129640
Forward-Port-Of: odoo/enterprise#125117Users who enabled browser notifications could receive two alerts for the same mention when their Odoo tab was open but not active. This fix ensures Odoo only shows the appropriate notification, reducing confusion and notification noise.
Original PR description
Steps to reproduce: - Grant browser notification permission (subscribes to web push). - Set user A's `notification_type` to `inbox` in preferences and reload. - Keep A's tab open but unfocused. - As user B, post an @-mention on a non-channel chatter record targeting A. A receives two alerts: the JS one from the `mail.message/inbox` bus event and the native notification from web push. `OutOfFocusService.notify()` gated JS skip on `message.thread.model`, but the server never sends `mail.thread`, so chatter fell through. Replace it with a `message_type` + `!isSelfAuthored` filter mirroring the server side `_notify_get_recipients_for_extra_notifications`. Also swap the `isInbox` SW handshake workaround added by [1] when the user is busy. [1]: https://github.com/odoo/odoo/pull/232431
This fix prevents older approved leaves from being reprocessed when a new employee version is created or updated. It helps keep historical leave records stable and ensures only leave relevant to the current employee version is considered.
Original PR description
Description of the issue/feature this PR addresses: When creating or updating an employee version, hr_holidays looks for leaves from the contract start date. If the contract started before the current employee version, this can include validated leaves belonging to older versions and older working schedules. This is a backport of the PR: https://github.com/odoo/odoo/pull/264997 Current behavior before PR: Creating a new employee version may process historical validated leaves that ended before the new version starts. Those leaves can be moved back to approval or recomputed even though they are not relevant to the new version. Desired behavior after PR is merged: Only leaves relevant to the employee version being created or updated are processed. Leaves ending before the effective version start date are ignored and remain unchanged. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes Factur-X invoice exports so they use the company or parent contact name when an invoice address has no name of its own. This helps prevent rejected e-invoices caused by missing buyer name information.
Original PR description
Description of the issue/feature this PR addresses: The Factur-X Refactoring (https://github.com/odoo/odoo/pull/284123) introduced an error that can result in the generated XMLs not being compliant due to the partner name being missing (BT-44) Current behavior before PR: When a contact has an invoicing address without a name set, then the new Factur-X generation does not fall back to the parent name, leaving the corresponding XML entry empty, which is therefore pruned, leaving the Factur-X without a BT-44 and thus failing BR-07 Desired behavior after PR is merged: The new generation method uses the same data source and fallback method as the old Factur-X generator, pulling the `display_name` of the `commercial_partner_id` if the address doesn't have a `name`. This greatly reduces the chance of a generated Factur-X failing BR-07. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#289498
Fixes Peruvian PLE sales and purchase ledgers so ISC tax is not incorrectly included in taxable base amounts or counted multiple times. This keeps ledger declarations aligned with electronic invoices sent to SUNAT and prevents overstated tax reporting.
Original PR description
When a tax affects the base of the subsequent ones — `include_base_amount`, which is how the ISC is set up in Peru — Odoo links the tax line it generates to those subsequent taxes, so the ISC tax…
When a tax affects the base of the subsequent ones — `include_base_amount`, which is how the ISC is set up in Peru — Odoo links the tax line it generates to those subsequent taxes, so the ISC tax line ends up carrying the IGV in its `tax_ids`. The PLE ledgers build their base columns by summing every move line related to a given tax group, so that ISC tax line was added to `base_igv` as well and the ISC ended up reported twice: once inside the taxable base and once in its own column. A line of 1000.00 with 100.00 of ISC and 198.00 of IGV was declared with a base of 1100.00 instead of 1000.00. `l10n_pe_edi` already subtracts the ISC from the `cbc:TaxableAmount` of the CPE sent to SUNAT, so the electronic invoice and the ledger disagreed on the very same document. ### What changed The base taxes are now joined only for base lines, instead of repeating the condition on each of the twelve `base_*` columns. Besides keeping tax lines out of the base columns, this also stops a tax line from being counted once per subsequent tax: an ISC followed by both the IGV and the ICBPER produced two rows in the join, and its own column was reported twice. ### Scope `_get_ple_report_data` is shared, so this covers the sales ledger 14.1 and the purchase ledgers 8.1 and 8.2. A configuration where no tax affects the base of a later one is unaffected: the standard Peruvian chart template has no purchase ISC, so ordinary IGV-only purchases report exactly the same values as before. ### Tests - `test_sale_report_isc_base`: an ISC preceding the IGV, on an invoice and on a credit note; the base column excludes the ISC. - `test_sale_report_isc_reported_once`: an ISC followed by the IGV and the ICBPER is reported once. Both fail without the fix. Supersedes odoo/enterprise#128300, which targeted 19.0. Forward-Port-Of: odoo/enterprise#131475