Friday, September 25, 2026
15 changes · saas-19.4
Enhancements to existing features
Xendit payments now use Xendit's newer hosted payment flow, keeping checkout available as older Xendit APIs are phased out. Customers are redirected to Xendit for all payment methods, existing saved cards remain usable, and businesses receive a one-time reminder to update webhook settings so payment statuses keep syncing.
Original PR description
The legacy v2/invoices and credit_card_charges endpoints for new payments are deprecated by Xendit. This migrates checkout and card sessions to the /sessions Payment Sessions API redirect flow and…
The legacy v2/invoices and credit_card_charges endpoints for new payments are
deprecated by Xendit. This migrates checkout and card sessions to the
/sessions Payment Sessions API redirect flow and charges v3 tokens through
/v3/payment_requests, while still accepting pre-existing v2 tokens (there is
no documented migration path for them) through the legacy
credit_card_charges endpoint.
Session creation:
- endpoint: v2/invoices -> sessions; response invoice_url -> payment_link_url
- external_id -> reference_id, with session_type PAY or SAVE (for
validation operations) and mode PAYMENT_LINK
- validation operations use the company's own currency instead of the
generic fallback's arbitrary pick, as Xendit's payment channels are
activated per country and an unrelated currency can be rejected
- customer.given_names -> customer.individual_detail.given_names and
surname, split via payment_utils.split_partner_name and sanitized to
alphanumeric (API constraint)
- customer reference_id is made unique per session creation attempt
(customer{partner_id}{random suffix}) as Xendit rejects reused
references and never reuses existing customers
- success_redirect_url/failure_redirect_url ->
success_return_url/cancel_return_url
- payment_methods -> allowed_payment_channels
- addresses removed (not supported by the sessions API)
- country derived from partner.country_id.code, with company fallback
- mobile_number sanitized to E.164 (+digits only, no spaces)
- card payments with tokenization set allow_save_payment_method=FORCED
and card_on_file_type=CUSTOMER_UNSCHEDULED
- removed the inline card form and its direct flow (payment_form.js), the
Xendit SDK and the /payment/xendit/payment route: all methods now
redirect to the Xendit-hosted payment link; the passthrough
_get_redirect_form_view override is dropped as well, and so are the
processing values only the inline form used (rounded_amount, currency,
access_token)
- the now-unneeded xendit_public_key credential is no longer required nor
shown in the provider form; the field itself is kept, as dropping a field
is not allowed in stable
- the session id is saved as soon as the session is created, rather than
waiting for the webhook, so it is available even if the customer returns
first
Tokenized payments:
- charge v3 tokens (prefixed 'pt-') through /v3/payment_requests
(api-version 2024-11-11) instead of /credit_card_charges, using
payment_token_id; channel_code is omitted as it is rejected when paying
with a token
- tokens saved before the migration to the v3 Payment Tokens API aren't
prefixed 'pt-' and aren't accepted by /v3/payment_requests; since there is
no documented way to migrate them, they are still charged through
/credit_card_charges with is_recurring set, same as before this migration
- card_on_file_type is CUSTOMER_UNSCHEDULED for a customer actively paying
with a saved card, or MERCHANT_UNSCHEDULED for an unattended charge
(operation 'offline', e.g. a subscription renewal), to reduce the odds
of a 3DS challenge without forcing skip_three_ds: that flag requires a
dashboard feature most merchants haven't activated, and isn't needed
for card-on-file charges since the card network's own MIT exemption
rules already cover them
- channel_properties requires success_return_url/failure_return_url even
for these off-session charges; built without an access token as the
charge may run outside of a request context (e.g. from a cron)
- if a v3 token charge unexpectedly still requires 3DS authentication
(status REQUIRES_ACTION), a customer-present charge exposes the
authentication URL Xendit returns as the pending_authentication_url
processing value, and the frontend navigates the top window to it
directly rather than submitting a form, since Xendit's page requires the
query string carrying the API key, only accepts GET and refuses to render
inside a frame;
an unattended charge has no cardholder to redirect, so it is set to
error instead of being left pending indefinitely
- create the payment token from payment_token_id; the masked card number
is not included in payment/payment request notifications, but is
included in `payment_token.activation` webhook notifications, which are
handled directly to avoid the extra request; otherwise, it is fetched
with a GET on /v3/payment_tokens/{id} (_xendit_make_request now
supports the GET method)
Status sync on return:
- a customer returning from checkout, or from a 3DS challenge for a v3
token charge, is checked directly against Xendit (via a new
_xendit_sync_from_provider) before falling back to the previous
pending-by-default behavior, in case the webhook is delayed or dropped
- the token charge's success return URL now carries the reference and an
access token when issued from a live request, so a customer returning
from a 3DS challenge can also be checked this way; a charge with no
request context (e.g. a cron renewal) is unaffected, as there is no
cardholder to redirect in that case
- the return controller now also matches `pending` transactions, since a
v3 token charge is already in that state by the time of the 3DS return
Webhook handling:
- unwrap the {event, data} envelope sent for session events
- look up transactions by exact reference_id match (or external_id for
legacy credit_card_charges notifications), falling back to stripping the
random suffix Xendit appends to payment request references
- store payment_session_id or payment_request_id as provider reference
- extend the status mapping: pending (PENDING, ACTIVE, REQUIRES_ACTION),
done (SUCCEEDED, PAID, CAPTURED, COMPLETED),
cancel (CANCELLED, EXPIRED, CANCELED)
Upgrade compatibility:
- the inline_form template is emptied rather than removed: the provider
record is noupdate, so existing databases keep inline_form_view_id
pointing to it, and removing the view would abort the module update
(ondelete='restrict')
- _should_build_inline_form is overridden to never build the inline form:
until the module is updated, the view keeps its old card inputs in
database, which would otherwise still be displayed and whose values would
be silently discarded, as the payment goes through the redirect flow
- bump the module version so partners upgrading notice the change
Webhook migration notice:
- Xendit replaced the single "Invoices paid" webhook field with separate v3
event groups (Payment tokens v3 / Payment requests v3). Databases that had
Xendit configured before this change stop receiving payment and card token
status updates until the Xendit Dashboard is updated with the new fields
- remind admins of this by hooking into the daily autovacuum cron instead of
a dedicated one or an upgrade-triggered migration script: SaaS/.sh don't
force module upgrades, so a migration script would only catch the
providers, companies, and admins that existed at the exact moment of the
upgrade, and a dedicated ir.cron record wouldn't exist on already-installed
databases until then either. @api.autovacuum methods are discovered
directly from the Python class, so they run immediately everywhere, keep
covering providers and admins added later, and can be removed later by
deleting the method, with no leftover cron record to clean up through a
migration
- track whether admins were already notified by checking for an existing
pending activity instead of adding a stored field on payment.provider: a
new column wouldn't exist either on databases where the module isn't
upgraded, and the ORM would error on any domain filtering on it. This
means marking the reminder done re-schedules it on the next run, since
there's no way to tell "resolved" apart from "dismissed" without a field;
that's accepted, as the underlying condition (the webhook still needs
reconfiguring) hasn't actually changed either way
- notify base.group_system, account.group_account_manager, and
sales_team.group_sale_manager rather than only Sales admins: those are the
groups actually granted write access to payment.provider and its Xendit
credential fields, or otherwise likely to own the provider's
configuration, and covering all three means databases using Xendit
without the Sales app installed (e.g. through Invoicing or Point of Sale)
still have someone to notify. A user in more than one of these groups is
only notified once
- the activity note links the "webhook configuration" and "Xendit Dashboard"
mentions inline to the Odoo documentation and to the Xendit Dashboard's
webhook settings, respectively, calling out the October 1, 2026 date after
which the old webhook field stops being honored. The cron itself stops
scheduling new activities after October 7, 2026, since there's nothing
left to prevent once Xendit has already cut over
Task-6373405
Forward-Port-Of: odoo/odoo#277626Resolved issues and error corrections
This fix ensures contact merges are blocked when they would incorrectly attach more than one user account to the same contact, even when company switching hides those users from view. It protects data consistency for multi-company setups and keeps portal user links reliable.
Original PR description
Switching companies can let a contact merge bypass the check that prevents multiple users from ending up linked to the same contact. ### Steps to reproduce 1. As a user with Contact Creation rights…
Switching companies can let a contact merge bypass the check that prevents multiple users from ending up linked to the same contact. ### Steps to reproduce 1. As a user with Contact Creation rights and access to companies A and B, select company A. 2. Create two contacts with no company set and grant each portal access. 3. Select only company B. 4. Merge the contacts. The merge succeeds and links both portal users to the surviving contact. With company A selected, the same merge is correctly rejected. ### Cause The wizard checks that the contacts have at most one linked user in total, including archived users. However, it reads `user_ids` with the acting user's permissions. Company record rules hide both portal users when only B is selected, so the check finds none. The subsequent SQL update still moves both users' contact links to the surviving contact. ### Fix Use `sudo()` only for this check, since it must count every user whose link the merge would move. Retain `active_test=False` to include archived users. Add a regression test covering both company selections. opw-6586945 Forward-Port-Of: odoo/odoo#290537 Forward-Port-Of: odoo/odoo#290296
This fixes a search issue where bills of materials linked to archived products could be missed when filtering for archived records. Users can now find archived BOMs even when the related product is also archived, improving reliability for manufacturing data lookup.
Original PR description
When searching for an archived bom (filer archived activated), if the main product is also archived, the search on the produt will not find the bom. Steps to reproduce: ------------------- * Create…
When searching for an archived bom (filer archived activated), if the main product is also archived, the search on the produt will not find the bom.
Steps to reproduce:
-------------------
* Create product AA
* Create a bom for product AA
* Archive product AA
* Products > bom
* Search for "Archived" and Product: "AA" -> It will not find the bom
Observation:
-------------
When doing the search, it will start a web_search_read ['&', ('active', '=', False), ('product_tmpl_id', 'ilike', 'AA')].
While in _search it will decide if we filter active elements: https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/models.py#L5363-L5369 In the first level since "active = False" is in the domain (for the bom) it will not add the filter.
The _search will optimize the domain query level by level by calling optimize_full.
First the outher layer :
optimize_full (Domain '[]')> _optimize (DomainNary '&')> _optimize_step (DomainNary '&')
Then the children [1: (active bom), 2: (product template display name)]:
_optimize>optimize_step> ...
While optimizing the second child the domain condition is: [('product_tmpl_id', 'any', [('display_name', 'ilike', 'AA')])]
Since it's a any operation, in optimize_step, it will use the _optimize_any_domain_at_level:
https://github.com/odoo/odoo/blob/16aaaafd7d314c04f39b1f633af3b5e15b46d78b/odoo/orm/domains.py#L970-L972
After doing some checks it will continue to optimize per level with the related comodel:
https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/domains.py#L1389-L1393
Since it's an "any" operator it need to respect the following guidelines : https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/domains.py#L98-L101
this is no done in `_optimize_any_domain_at_level` since it resolves a relational field's 'any' domain by looking up a fresh comodel recordset without adjusting its context.
Which means that for the following level, on the product template, the active_name is not on the domain anymore and the context was not passed corretly, so _search will automaticly add the filter ("active = True").
opw-6514762
Forward-Port-Of: odoo/odoo#290380
Forward-Port-Of: odoo/odoo#285942All-day calendar events created from approved time off now keep the correct multi-day span when their start time changes, including during Google Calendar sync. This prevents approved leave from appearing shortened or incorrect in calendars.
Original PR description
**Problem:** When modifying the start time of an all-day Calendar Event created from the Validation of a Time Off Request, the end time will be shortened dramatically, no longer spanning the correct…
**Problem:** When modifying the start time of an all-day Calendar Event created from the Validation of a Time Off Request, the end time will be shortened dramatically, no longer spanning the correct timespan. In the case of the ticket referred to below, this occurs during Google Calendar sync. **Cause:** When a Calendar Event is created from a Time Off Request being Validated, its duration is set according to the amount of days the Time Off Request spans multiplied by the employee's daily work hours as defined on their contract, even if the Calendar Event will be an all-day event. When the start time of a Calendar Event is modified, the end time is recomputed according to the duration added to the new start time. As such, if the duration of the Event does not match the duration of the dates covered, it will be shortened dramatically. **Purpose:** Modify the values defined when creating a Calendar Event as a result of a Validated Time Off Request so that, if the created Event will be an all day Event, the duration is computed according to `calendar.event._get_duration`. **Steps to Reproduce in Runbot:** 1. Create and Validate a Time Off Request of the Paid Time Off leave type spanning multiple days. 2. Modify the start time of the Calendar Event that is created as a result of the validation. (This step will likely require a nonstandard flow as the start time field is normally hidden for all-day Calendar Events) opw-6426586 Forward-Port-Of: odoo/odoo#290315 Forward-Port-Of: odoo/odoo#284552
This fix prevents Point of Sale receipts from accidentally reusing the same receipt number when orders are restored after a browser reload or second tab is opened. It helps avoid duplicate invoice references being printed or sent to fiscal integrations, reducing accounting and compliance risks.
Original PR description
Steps to reproduce: - Add a product to a draft order, do not pay - Open the same PoS in a second tab of the same browser (the first tab is sent to the backend), or reload while a draft is being…
Steps to reproduce: - Add a product to a draft order, do not pay - Open the same PoS in a second tab of the same browser (the first tab is sent to the backend), or reload while a draft is being removed - Pay the restored draft, then make and pay a new order Issue: Both orders carry the same pos_reference (e.g. 264-3-000701). The number is printed on both receipts and sent to fiscal integrations that use it as the unique invoice number. Cause: The receipt number is issued client side by DeviceIdentifierSequence, a per-device counter kept in localStorage that all tabs of a browser share. When a draft is removed, its number is pushed on `unsynced_number_stack` to be reused by the next order. `removeOrder` pushes the number synchronously, then deletes the order from IndexedDB without waiting for the transaction. If the page unloads before it commits (the tab is closed by another tab opening the same PoS, a redirect to the backend, a reload), the next load restores the draft from IndexedDB with its number while the stack hands that same number to the next new order. Both are then created on the server with different uuids and the same pos_reference: the server only deduplicates by uuid. Fix: Recycle the number only once the order is gone from IndexedDB. A new `recycleOrderNumber` helper waits on the deletion of the order before calling `saveUnusedNumber`; `removeOrder` uses it after `localDeleteCascade`. `deleteOrders` awaits the recycling so that a delete followed by a new order still reuses the freed number, as before. opw-6558992 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#290305 Forward-Port-Of: odoo/odoo#287667
The Google Merchant Center feed now excludes product videos from extra image listings and avoids loading full image files just to check them. This prevents video thumbnails from being sent as product photos and reduces the risk of feed generation running out of memory for large catalogs.
Original PR description
Description of the issue/feature this PR addresses: The Google Merchant Center feed builds the extra image URL list of each product, and must skip the `product.image` records holding a video, as…
Description of the issue/feature this PR addresses:
The Google Merchant Center feed builds the extra image URL list of each product, and must skip the `product.image` records holding a video, as their image is only a thumbnail of that video. It filters those out by accessing `image_128`, which is wrong on two counts:
- A video record does have an `image_128`: the thumbnail, fetched from the video URL or uploaded by the user. Video thumbnails were therefore listed in the feed as if they were product images.
- Binary fields are read and base64-encoded in full by default, so the binary content of every extra product image was loaded into memory just to evaluate a truthiness check. On a database with ~4261 products with images, rendering the feed exhausts the worker memory limit and raises an out-of-memory error.
Current behavior before PR:
`_get_extra_image_1920_urls()` keeps every extra image whose `image_128` is set, including video thumbnails, and forces the ORM to fetch and base64-encode the full content of every extra image attachment.
<img width="2038" height="1462" alt="image" src="https://github.com/user-attachments/assets/e3b375e5-ac17-4b5c-b0c3-59264d7e36f7"/>
Desired behavior after PR is merged:
The records are filtered on `video_url`, the field that actually flags a video, so videos are properly excluded and no image binary is read at all.
opw-6513194
Forward-Port-Of: odoo/odoo#288323
Forward-Port-Of: odoo/odoo#286782The employee registration test now opens the correct menu before running in community editions. This prevents false test failures caused by starting from the wrong screen, helping keep HR event registration quality checks reliable.
Original PR description
**Trigger:** Running `test_onsite_employee_registration` in a community database starts in Discuss instead of enterprise's usual home menu which crashes the test on the first step. This commit adds the `showAppsMenuItems` util to the test. To ensure the test starts in the correct view. runbot-946289 Forward-Port-Of: odoo/odoo#289486
Refunds in Point of Sale now keep the manually calculated tax amounts for discounted orders when rounding differences occur. This prevents session closing from failing due to unbalanced accounting entries, improving reliability for stores processing refunds.
Original PR description
Closing a session fails with an unbalanced journal entry when it holds the refund of an order whose global discount falls on a specific rounding. Steps to reproduce: - configure a 20% tax and sell 2…
Closing a session fails with an unbalanced journal entry when it holds the refund of an order whose global discount falls on a specific rounding. Steps to reproduce: - configure a 20% tax and sell 2 x 2.12 in the PoS - apply a 10% global discount, pay the order, then refund it - close the session The tax of the discount line cannot be recomputed from its price: the UI splits the rounded tax included amount, 0.51 here, into 0.42 of base and 0.09 of tax, where 0.42 taxed at 20% would give 0.08. It pins both amounts in 'extra_tax_data' with the quantity they were computed for, and they are used again only if the line still matches that snapshot. Refund orders reverse that quantity, so the discount line the UI builds for them no longer matches and its taxes are computed again. Reverse those amounts only when the quantity they were computed for has the opposite sign to the one now used. Refunds made with "Return Products" copy the line with its quantity already negated, so reversing them as well would break those instead. opw-6533657 Forward-Port-Of: odoo/odoo#289735 Forward-Port-Of: odoo/odoo#288573
French companies can now select and send multiple invoices without the process crashing. This restores access to the batch sending wizard for electronic invoicing, helping users send invoices efficiently from the list view.
Original PR description
Sending several invoices at once from the list view of a French company crashes instead of opening the batch sending wizard. Steps to reproduce: - Activate French Electronic Invoicing - Create a French customer with a valid PDP endpoint - Post two invoices for that customer, select them in the invoice list and click 'Send' Issue: The batch sending wizard raises ``` AttributeError: 'account.move.send.batch.wizard' object has no attribute 'company_id' ``` opw-6558940 Forward-Port-Of: odoo/odoo#289666 Forward-Port-Of: odoo/odoo#289547
The Time Off Ledger now shows expected hours based on each employee's actual schedule for the specific day, rather than using a weekly average. This makes reports accurate for employees with shorter or longer workdays on certain weekdays and prevents misleading attendance and time-off calculations.
Original PR description
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to…
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to reproduce: 1) Install hr_holidays_attendance. 2) Create/assign a working schedule where daily hours vary across the week (e.g. Monday-Thursday 8.5h, Friday 5h). 3) Go to Attendance > Reporting > Time Off Ledger, filter on that employee. ### Observed behavior: "Expected Hours" is identical on every day (the schedule's average hours/day), regardless of which weekday the row is for. ### Expected behavior: "Expected Hours" should match the hours actually scheduled for that specific weekday, so a shorter Friday shows less than a full Monday. ### Root cause: `Time off Ledger` is a SQL view. Its `_select()` builds `expected_hours` from `rc.hours_per_day` at [1], i.e. `resource.calendar.hours_per_day`, a field explicitly labelled **"Average Hour per Day"** and is computed via [_get_hours_per_day] as `hours_per_week / days_per_week`, a single flat value for the whole calendar. The per-weekday hours are available in `resource.calendar.attendance` it stores `dayofweek`, `hour_from`/`hour_to` and a computed `duration_hours` per line, and can legitimately differ per weekday (e.g. Monday 8h vs Friday 4h) at [2]. The report's own [_cte_cal_workday()] CTE already reads this table to decide whether a weekday is a working day (`cal_workday`, joined as `cw` in `_from()`), but discarded the actual hours, keeping only `(calendar_id, dayofweek)`. `_select()` then fell back to the calendar-wide average via `rc.hours_per_day` instead of the per-day value. Example: calendar with Monday 8h and Tuesday 4h -> `hours_per_day` averages to 6h; the report showed 6h on both Monday and Tuesday instead of 8h and 4h respectively. [1]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L256 [_get_hours_per_day]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar.py#L703-L707 [2]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar_attendance.py#L15-L31 [_cte_cal_workday()]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L82-L89 ### Fix: `_cte_cal_workday()` now aggregates `SUM(duration_hours)` per `(calendar_id, dayofweek)` from `resource_calendar_attendance`, instead of just selecting the distinct keys. `_select()` reads this per-weekday value (`cw.hours_per_day`) instead of the calendar-wide `rc.hours_per_day` for both `expected_hours` and `difference_hours`. **opw-6425167** Forward-Port-Of: odoo/odoo#289034 Forward-Port-Of: odoo/odoo#280664
This fix prevents users from receiving duplicate alerts for certain mail tracking updates. It aligns browser notification handling with server-sent push messages, making notifications less noisy and more consistent.
Original PR description
Since #290266 there is a whitelist of message types matching what the server sends via web push, used by the JS `out-of-focus` notifier to skip its own alert and avoid duplicates. `tracking` was added as a server-side push message type in `saas-19.3` (#235719) and should be considered in the JS whitelist as well. Forward-Port-Of: odoo/odoo#290514
Managers assigned as an employee's Time Off approver can now see the employee's Time Off button even if they do not otherwise have Time Off access rights. This ensures approvers can manage requests for their team members as intended and avoids missing access in employee records.
Original PR description
Issue: A user without any rights on Time Off should still be able to manage the requests of the users he's manager of. However, the computation for `show_leaves` only considers Time Off Officers/Admins and the employee themselves, while it should also consider the employee's Time Off approver. Fix: Include the employee's Time Off approver when computing `show_leaves`, allowing them to access the employee's Time Off smart button. Reproduction Steps: 1. Set a user (e.g. Marc Demo) to "No" for Time Off. Note: If reproducing in v19, Administrator rights on Employees are also needed to access the private employee form view where the issue occurs. 2. Set that user as another employee's Time Off approver. 3. Log in as that user and open the employee's form view. 4. Observe that the Time Off smart button is not visible. Related Tickets: opw-6445714 Forward-Port-Of: odoo/odoo#290047 Forward-Port-Of: odoo/odoo#281589
Ecuadorian electronic invoicing now avoids a confirmation error when a company's tax ID is not a valid Ecuadorian RUC. This prevents invoice processing from failing for companies whose partner record contains a foreign or non-numeric VAT number.
Original PR description
When the VAT of an Ecuadorian company's partner contains characters other than decimal digits, confirming an invoice raises a traceback. Steps to reproduce the error: - Install ``l10n_ec_edi`` module…
When the VAT of an Ecuadorian company's partner contains characters other than decimal digits, confirming an invoice raises a traceback. Steps to reproduce the error: - Install ``l10n_ec_edi`` module and switch to EC Company - Open the partner of EC Company > Set a VAT containing non-digit characters - Create a new invoice > fill the required fields > Confirm Traceback: ```py ValueError: invalid literal for int() with base 10: 'E' ``` During invoice confirmation, ``_l10n_ec_set_authorization_number()`` method is called to generate the authorization number at [1], It builds the ``key_value`` using the vat of company's partner at [2]. The generated ``key_value`` is then passed to ``_l10n_ec_get_check_digit`` method, which converts each character of ``key_value`` to an integer using ``int()`` at below line. https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L830 When the VAT contains a character that cannot be converted to an integer, It will raises the above traceback. To build the ``key_value``, the company's partner vat should be RUC consisting of 13 digits. Ref: https://www.sri.gob.ec/o/sri-portlet-biblioteca-alfresco-internet/descargar/fb95cafc-a8ca-4a4c-afb6-12c4153165f0/FICHA%2520TECNICA%2520COMPROBANTES%2520ELECTR%25C3%2593NICOS%2520ESQUEMA%2520OFFLINE.pdf Solution: If the identification type of the company's partner is not RUC, return an empty string. [1]: https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L806 [2]: https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L825-L826 sentry-7642961403 Related Community PR: https://github.com/odoo/odoo/pull/280047 Forward-Port-Of: odoo/enterprise#132478 Forward-Port-Of: odoo/enterprise#126519
German quarterly tax report XML exports now correctly include the reporting period field. This helps ensure submitted tax files contain the expected information and avoids manual checks or corrections for quarterly filers.
Original PR description
The `Zeitraum` tag was not set when the tax return periodicity is `quarterly`. The `account_tax_periodicity` field defines `trimester` as the key for the `quarterly` label. In [account_generic_tax_report.py] though, the code compared the periodicity with the `quarterly` label instead of the `key` trimester. **Steps to reproduce**: - Install the `l10n_de_reports` module and switch to the DE Company. - Go to Accounting settings and set `Tax Return Periodicity` to `quarterly`. - Navigate to Accounting > Reporting > Tax Return. - Export the tax report as `XML`. - Open the generated XML file and observe the `Zeitraum` tag. [account_generic_tax_report.py]: https://github.com/odoo/enterprise/blob/338630decbec76580766dd3b38c3241ecfb4c77a/l10n_de_reports/models/account_generic_tax_report.py#L54 Ticket [link](https://www.odoo.com/odoo/project.task/6581536) opw-6581536 Forward-Port-Of: odoo/enterprise#132824 Forward-Port-Of: odoo/enterprise#132593
Australian payroll now defaults casual employees to the regular casual tax treatment instead of daily casual. This helps ensure eligible employees can have student loan withholding applied correctly.
Original PR description
Issue: Odoo doesn't provide tax treatments for Daily casual employee. However, it the employement basis is set as casual, it incorrectly defaults to RDXXXX (which is Daily Casual). This prevents the employee from having student loan withhold. This commit defaults it back to regular casual based on the tax free threshold status. Most common case. Daily casual case to be handled in Master. task - 6387496 Forward-Port-Of: odoo/enterprise#131469 Forward-Port-Of: odoo/enterprise#130752