Tuesday, September 15, 2026
42 changes · saas-19.4
Resolved issues and error corrections
This fixes exports of binary fields so file data is delivered in the expected base64 format again. It helps preserve compatibility with existing export workflows and integrations that rely on this format.
Original PR description
Export base64 data like we used to do. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288195
This fixes Greek e-invoicing so successful provider operations are preserved even if a later Odoo step fails, reducing the risk of mismatched invoice status between Odoo and the external service. It also updates QR code handling so generated invoice QR codes work correctly in this version.
Original PR description
e-invoo operations occur outside the Odoo transaction, so a later failure could roll back local state while the corresponding external operation had already occurred. Commit the provider issuance result at the respective transaction boundaries. Also adapt the QR code generation as here in 19.3 upwards it doesn't use base64.b64encode anymore, but rather pass the image bytes directly. Note: regarding the commit thing, this is partially what we already have in 18.0 till 19.2, we just removed it from 19.3 at the time because it was failing the ci/style test and it needed an exception from the framework team but we didn't have the time back then we needed it to be merged asap, that's why we're adding them again right now. related: https://github.com/odoo/odoo/pull/281739 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285581
Portal users can now submit product reviews with file attachments when product discussions and ratings are enabled. This prevents a confusing “Not Found” error and lets customers complete reviews without needing support or retrying without files.
Original PR description
A portal user cannot add an attachment when posting a review on a product page: submitting the review with a file attached fails with a generic "Not Found" error, while posting the exact same review…
A portal user cannot add an attachment when posting a review on a product page: submitting the review with a file attached fails with a generic "Not Found" error, while posting the exact same review without an attachment works fine.
Steps to reproduce:
-------------------
* Enable "Discussion and Rating" on a product page (Website > Customize)
* Log in as a portal user and open that product page
* Write a review, attach a file, then submit
> Observation:
"Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again."
Why the fix:
------------
`product.template._get_mail_message_access()` demotes a portal user's create access to 'write' whenever
`env['website'].is_view_active('website_sale.product_comment')` is False, since 'write' access is a deliberate way to block posting when the feature is toggled off for the website. `is_view_active` only picks the correct, website-specific view when `website_id` is present in `self.env.context`; that key is injected by the website frontend only for routes declared `website=True`.
`/mail/attachment/upload` is not `website=True` (attachments used to go through the dedicated, `website=True` `/portal/attachment/add` route, removed when portal's attachment uploader was unified with mail's), so `website_id` is missing from context on that route, `is_view_active` falls back to the generic, shipped-`active="False"` view, and always reports the feature as disabled - even when it is actually enabled for the current website. Create access is then wrongly demoted to 'write', which a portal user never has on `product.template`, so the thread lookup fails and the controller raises `NotFound()`.
Resolve the current website from the request itself (`env['website'].get_current_website()`) and inject its id into context before checking `is_view_active`, instead of relying on `website_id` already being in context. This makes the check accurate regardless of which route triggered it.
opw-6539184
Forward-Port-Of: odoo/odoo#288116
Forward-Port-Of: odoo/odoo#287345Leads created from WhatsApp conversations in Discuss now correctly include the customer contact. This helps sales teams keep WhatsApp-originated CRM leads complete and avoids manual contact updates.
Original PR description
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss -…
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss - Click on the 'Create Lead' smart button - Check the created lead > The Customer/Contact field is empty ### Cause of Issue: When a lead is created from a Discuss conversation, `_convert_visitor_to_lead` in `crm_livechat` attempts to set the lead's customer. It does this by checking if the channel has `livechat_customer_partner_ids`. However, in whatsapp discuss conversations, the client is saved in `whatsapp_partner_id` and `livechat_customer_partner_ids` is empty. https://github.com/odoo/odoo/blob/29556fda44b9f1e6cf08129443ca47fa6cda34f9/addons/crm_livechat/models/discuss_channel.py#L54-L64 ### Fix: Kept the fix local to `crm_livechat` and checked whether `whatsapp` is installed before reading `whatsapp_partner_id`, which would otherwise raise an `AttributeError` since `crm_livechat` does not depend on `whatsapp` and vice versa. opw-6371274 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283008
Users who follow a task in a private project can now open that task without errors when older or mismatched attachments are present. The fix prevents attachment metadata loading from being blocked by project access rules while keeping normal document and attachment permissions intact.
Original PR description
Issue: A user following a task in a private project can be allowed to read the task without having access to its parent project. If the task contains a legacy or desynchronized attachment without a…
Issue: A user following a task in a private project can be allowed to read the task without having access to its parent project. If the task contains a legacy or desynchronized attachment without a related document, opening the task can nevertheless raise an AccessError while loading the chatter. Steps to reproduce: - Create a follower only private project and a task in that project - Add an internal user as a follower of the task but not of the project - Leave a task attachment without its expected documents.document link - Open the task as that follower Cause: When computing the attachment's linked document, `_exclude_documents_mixin()` calls `_check_create_documents()` on the linked business record with the requesting user's permissions. That eligibility hook may consult protected records such as the task's private project, even though it is only used to classify the attachment. https://github.com/odoo/enterprise/blob/656469d08453a823979153200cddd3688f815468/documents/models/ir_attachment.py#L41-L53 Solution: Evaluate only the document creation eligibility hook with elevated rights. This keeps chatter metadata independent from access to the hook's related configuration while leaving attachment access, document lookup, and explicit document creation checks under the requesting user's permissions. opw-6479654 Forward-Port-Of: odoo/enterprise#128929
Sales users can once again drag and drop request for quotation files onto the quotations list or kanban view. This fixes a broken upload path that showed an error instead of importing the file, reducing friction in quotation processing.
Original PR description
Versions -------- - saas-19.4+ Steps ----- 1. Install Sales (with `sale_management`); 2. open Sales > Orders > Quotations; 3. drag a request for quotation file onto the list. Issue ----- The drop…
Versions -------- - saas-19.4+ Steps ----- 1. Install Sales (with `sale_management`); 2. open Sales > Orders > Quotations; 3. drag a request for quotation file onto the list. Issue ----- The drop zone appears, but dropping the file only raises a "Could not upload files" notification. Same in the kanban view. Cause ----- `UploadDropZone.onDrop` hands the dropped files to the closest input matching `.document_file_uploader.o_input_file`. Commit d1742c42 moved the RFQ upload into the cog menu with a template overriding `account.DocumentFileUploader`'s, but without forwarding `fileUploadClass` to the `FileUploader`, so that input never carried the class. This went unnoticed because `saleFileUploadListView` inherited `account.FileuploadListView.Buttons`, which renders `account.DocumentViewUploadButton` and therefore a matching (hidden) input the drop zone could fall back on. Commit 00545bdd patched `saleFileUploadListView` and `saleFileUploadKanbanView` to use the `sale_management` button templates, which inherit `web.ListView.Buttons` instead, removing that input and leaving the page with no drop target at all. Solution -------- Set `fileUploadClass` on the cog menu's `FileUploader`. The drop zone now targets the RFQ uploader itself rather than the account one, so dropped files go through `QuotationRequestUploader.getResModel` as intended. opw-testing-day-v20
This change fixes Belgian payroll salary calculations when an employee has multiple contract versions in the same month. It ensures the 50% rule is applied based on paid versus unpaid hours for each period, helping prevent incorrect basic salary amounts on payslips.
Original PR description
there is a problem that we If we have two versions in the same month, we always follow the second option of the 50% rule. This is because the theoretical hours are calculated for the whole month, but the paid amount is calculated separately for each version. We fixed this by checking If unpaid hours > paid hours, salary is calculated by multiplying the hourly rate by the total hours. Otherwise, salary is calculated as the base wage minus (unpaid hours × hourly rate). And return back the test to what exist before the task with id : 6260378 task Id: 6515980 Forward-Port-Of: odoo/enterprise#129985
This fix makes an automated website media test consistently use a safe mock instead of reaching the real external illustration service. It reduces false test failures and helps keep release validation stable without changing customer-facing website behavior.
Original PR description
The `test_media_dialog_undraw` test monkeypatches `HTML_Editor.media_library_search` to avoid calling the real undraw API during the tour. However, the "routing" ormcache may already hold the controller endpoint built with the original (unpatched) method, if it was resolved by an earlier request served by the same worker. Invalidate the "routing" ormcache after patching so the requests are routed to the patched methods. Same change as done in #271536. https://runbot.odoo.com/odoo/error/939933
Rental orders can now be created correctly when an optional rental product is added, even if the website rental feature is not installed. The system now handles rental dates consistently before calculating prices, preventing a crash and improving order processing reliability.
Original PR description
Problem: When the website_sale_renting module is uninstalled and you try to create a rental order with a product that has an optional rental product, the system crashes with a traceback. This happens…
Problem: When the website_sale_renting module is uninstalled and you try to create a rental order with a product that has an optional rental product, the system crashes with a traceback. This happens because the rental start and end dates are passed to the product pricing calculations as plain text strings instead of proper date formats, which breaks the timezone math. Solution: This commit ensures that the rental start and end dates are converted into proper datetime formats before any duration or pricing calculations occur, preventing the error and allowing the products to be added to the rental order smoothly. Steps to reproduce(runbot v18): 1. Install the Rental (sale_renting) and Website modules. 2. Uninstall the website_sale_renting module. 3. Create a rental product and configure another rental product as its optional product. 4. Open the Rental application and try to add the created product to a rental order. 5. A traceback is raised while calculating the rental price. opw-6485776 Forward-Port-Of: odoo/enterprise#130966 Forward-Port-Of: odoo/enterprise#128668
The spreadsheet command palette now only shows actions that are also available in the regular spreadsheet menu. This prevents users from seeing or selecting inaccessible options, making the spreadsheet experience more consistent and less confusing.
Original PR description
Some items are displayed in the command palette while being hidden in the spreadsheet interface. With this revision, we synchronize the availability of a menu item between the topbar menu and the command palette. Task-6352180 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
The spreadsheet command palette is now consistently available from the topbar across all related spreadsheet actions, not just document views. This fixes an access gap so users can more reliably use the same productivity shortcut wherever they work with spreadsheets.
Original PR description
The command palette was made available as a topbar menu item but it was only exposed in the document action. Task-6352180
This fixes an error that prevented users from customizing certain Kanban cards in Odoo Studio, such as Project task cards. Users can now add or remove fields in these views without the save operation failing.
Original PR description
Issue: Editing the Project task Kanban with Studio fails with an "element cannot be located in parent view" error. This affects Kanban views whose card architecture is provided through `card_id`.…
Issue: Editing the Project task Kanban with Studio fails with an "element cannot be located in parent view" error. This affects Kanban views whose card architecture is provided through `card_id`. Adding or removing a field produces an XPath targeting the inlined `<card>`, but that node cannot be found when the Studio customization is saved. Steps to reproduce: * Open Project > Tasks > My Tasks in Kanban view. * Open Studio. * Add or remove a field from the card. Cause: The client receives a postprocessed Kanban architecture in which the view referenced by `card_id` has already been appended as a `<card>` node: https://github.com/odoo/odoo/blob/1aa1f1967c7b9c8fd2c941fbc0c7b4c563698e46/odoo/addons/base/models/ir_ui_view.py#L3138-L3140 Studio therefore generates paths containing `/kanban/card`. However, Studio normalization applies those paths to the pre-postprocessed architecture, which still contains only the `card_id` attribute: https://github.com/odoo/enterprise/blob/42aa8fafdef159476d708f91467cba6f7fb2c6d3/web_studio/models/ir_ui_view.py#L667-L671 As `<card>` does not exist in that source tree, the inheritance engine cannot locate the target. Solution: We need to inline the referenced card before applying or normalizing Studio customization specifications. This makes Studio operate on the same architecture shape that was presented to the client. The `card_id` attribute is then removed from that temporary source to prevent regular post processing from appending the card a second time. The behavior is limited to Studio customization views and is a no op for Kanban views without `card_id`, preserving the existing inheritance flow for other views. opw-6445398
This fix prevents an error when a check template uses account filters that return no matching accounts. It helps keep accounting report setup stable by handling empty results gracefully instead of showing a traceback.
Original PR description
When a check template has a domain on account.account that would give no result, it would throw a traceback because min cannot operate on an empty result.
This fixes Nilvera e-invoices so amounts written in words use Turkish consistently, including the currency subunit such as cents. It helps prevent mixed-language invoice notes and supports compliance with Turkish e-invoicing requirements.
Original PR description
### Issue before this commit: When generating a Nilvera e-invoice in a foreign currency (e.g., EUR), the amount written in words inside the <cbc:Note> tag contained a mix of languages. While the…
### Issue before this commit: When generating a Nilvera e-invoice in a foreign currency (e.g., EUR), the amount written in words inside the <cbc:Note> tag contained a mix of languages. While the numbers were correctly translated to Turkish, the currency subunit label was fetched using the customer's language. This resulted in a partially translated string (like "... SIFIR CENTS") instead of the expected fully Turkish text (like "... SIFIR SENT"). ### Steps to reproduce the issue: 1. Download Accounting and l10n_tr_nilvera_einvoice 2. Create an API KEY: https://docs.google.com/document/d/1EUzvTBnSm9-VwIfBsX299MHGXIVys-uijnsJ1fpz7vI/edit?tab=t.0#heading=h.e6i8a29lff5t 3. Go to a turkish client on the Accounting tab and click on 'Verify' for the Nilvera status 4. Go to currencies and activate EUR (be sure there is also the translation for currency subunit) 5. Go to invoices, create one for the Turkish customer you already verified setting the currency as EUR and send it with Nilvera 6. See that current output is <cbc:Note>YALNIZ : BEŞYÜZDÖRT EUR SIFIR CENTS</cbc:Note> (Turkish numbers with English subunit) but the expected output is <cbc:Note>YALNIZ : BEŞYÜZDÖRT EUR SIFIR SENT</cbc:Note> (Fully Turkish text) ### Cause of the issue: The issue occurs because the currency_subunit_label field is not translated but fetched using the language of the customer in the invoice. ### Reason to introduce the fix: To ensure that the amount in words inside the <cbc:Note> tag is completely formatted in Turkish, complying with Nilvera and local e-invoicing requirements, regardless of the customer language. opw-6523794 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288037 Forward-Port-Of: odoo/odoo#287630
Validated time off records now keep their calendar blocking entry when dates are shortened, instead of deleting it. This prevents final payslips from incorrectly counting approved time off as worked attendance after an employee departure date changes the leave period.
Original PR description
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the…
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the leave), the linked resource.calendar.leaves record was unconditionally unlinked. To reproduce: 1. Create and validate a time off request covering a whole month. 2. Register the employee's departure with a departure date in the middle of that time off. 3. Generate the employee's last payslip. The leave is correctly cut at the departure date, but since it never leaves the `validate` state, it never goes through `_validate_leave_request()` again, so its resource.calendar.leaves record is never recreated. The days that were covered by the deleted entry are no longer blocked in the employee's resource calendar, so the payslip's worked day lines (computed from resource.calendar.leaves) count them as attendance instead of time off. Cause ----- `hr.leave.write()` removed the resource.calendar.leaves record any time either the state changed away from `validate` or the leave's dates changed, regardless of whether the leave remained validated. Date-only changes on an already-validated leave never re-trigger validation, so the entry was not recreated. Solution -------- Only remove the resource.calendar.leaves record when the leave actually loses its validated state. When a validated leave's dates change but it stays validated, amend the existing resource.calendar.leaves record in place instead, falling back to creating one if none exists. Related PR: odoo#249527 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287862 Forward-Port-Of: odoo/odoo#286465
This fix limits the payment methods that can be selected in expense settings to only those that are consistent with the company configuration. It helps prevent incorrect setup choices that could lead to confusion or processing issues in employee expenses.
Original PR description
Add domain to company_expense_allowed_payment_method_line_ids field to avoid selecting inconsistent data @Tecnativa TT64416 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287131
This update prevents an error when users drag an image and drop it back in the same place in the HTML editor. It improves editing stability, especially in Chrome, by handling the drop position safely when the page content changes during the action.
Original PR description
Steps to reproduce: - Insert an image as the last child of a paragraph. - Drag and drop it below or after itself. Description of the issue: - A traceback occurs. Cause: - When dropping an image below or after itself `document.caretPositionFromPoint()` computes a drop offset equal to the current node size. - The image is then removed from the DOM before being reinserted. Since it is the last child of its parent, removing it shrinks the parent, making the previously computed offset out of bounds. - Restoring the selection at that stale offset results in a traceback. Solution: - Treat dropping the image at its current position as a no-op and skip the remove/reinsert process, since it would not change the DOM. - Clamp the drop offset to the current node size before restoring the selection preventing out-of-bounds offsets. task-6435091 Forward-Port-Of: odoo/odoo#286613 Forward-Port-Of: odoo/odoo#280612
Managers can now open employee records and expense documents without being blocked by unrelated company access restrictions in the organization chart or expense approval flow. Expense team approvers also get more flexibility to create expenses for their subordinates across company boundaries, reducing duplicate employee setup and day-to-day admin friction.
Original PR description
_(updates on July-17th and July-28th for points 2. and 3. in `hr_expense`)_ # Introduction **1\.** A fix to `hr_org_chart` was initially proposed. I think it is mergeable because it fixes the…
_(updates on July-17th and July-28th for points 2. and 3. in `hr_expense`)_ # Introduction **1\.** A fix to `hr_org_chart` was initially proposed. I think it is mergeable because it fixes the user-experience properly like expected in Odoo standard. Other commits are suggested to `hr_expense`. They were designed to allow more flexibility in the management of `hr_employee.company_id` (in multi-company context) and allow not to duplicate the `hr.employee` of each companies of the `res.users`. **2\.** The [FIX] silences a multi-company access error when searching for Expense validators => it seems safe **3\.** The [IMP] largely allow more flexibility for a "Expense: Team Approver" when creating expenses on behalf of its subordinates # 1. in hr_org_chart [FIX] Prevents multi-company error when recursively searching for ancestors. <img width="1035" height="789" alt="image" src="https://github.com/user-attachments/assets/d5517f79-7efc-4223-ae77-0af8b25a3d1e" /> ### Issue description In a multi-company environment, when one of the `hr_employee.company_id` of a hierarchy is not in the allowed companies of a Manager's `res_users.company_ids`, this Manager can view `hr.employee` in the list view but cannot open their forms. This happens when: - `hr_org_chart` module is installed - the Org Chart is displayed on the 1st page of the `hr.employee` form, like when the HR settings "Skills Management" is disabled (in `res.settings`) => thus the whole form becomes inaccessible from the manager When a `hr.employee` form is opened, a multi-company access error is thrown to him, even if the `company_id` of the opened `hr.employee` is in the user's `res_users.company_ids`, because of the hierarchy's `company_id`. It should be expected that the part of the Org Chart which is not allowed to be seen would just be hidden. ### Steps to reproduce Data setup: - Employee "A" in company A - Manager "M" in company A, manager of "Employee A" - Manager of manager "MM" in company A, manager of "Manager M" - And now, in company B (let's say a Holding), the "Director" is manager of "Manager MM" - "Manager M" is only given access access to Company A - the module "hr_org_chart" is installed Actions: - Login with Manager M - Browse to Employees list and try and open the form of "Employee A" (up to tab _"Professional information"_, if it is not the 1st of the notebook) ### Proposed fix This PR re-uses the already existing method `_check_employee` which contains all the logic to solve the issue. Maybe the call to this method was forgotten? This PR simply call this method when finding an ancestor, in the controller of `hr_org_chart`. This fix is thus very limited to the call to the public method `hr_org_chart.get_org_chart()` made by the Org Chart widget. ### Desired behavior after PR is merged The part of the Org Chart not allowed to be seen by "Manager M" is hidden. # 2. hr_expense [FIX] <img width="541" height="415" alt="image" src="https://github.com/user-attachments/assets/d594815b-2b77-4088-878b-9b192b64d6a3" /> ### Issue description As an employee, I click on the button "View Report" on my expense. I get a multi-company access error, preventing me to view and edit my expense report. This is because the manager of the department I belong is in a company I'm not allowed to see. This can also happen just when opening my Expense (instead of Expense Report). ### Current behavior before this PR The employee is blocked to continue editing its Expense or to submit it to a Report. ### Current behavior after this PR The employee can edit and submit its Expense no matter the `company_id` of its hierarchy. Technically: the `can_approve` field on the expense sheet uses a localized `.sudo()` method to bypass multi-company limits when searching if the current user is a validator. # 3. hr_expense [IMP] <img width="1028" height="549" alt="image" src="https://github.com/user-attachments/assets/1096c0d4-d025-46a9-8559-6bab5de71577" /> ### Improvement summary In multi-company environment, allow a "Expense: Team Approver" to create Expenses for its subordinates (`hr_expense.employee_id`) **no matter the `hr_employee.company_id` of its subordinates**. The domain of `hr_expense.employee_id` keeps the security of `check_company=True` => thus the Manager only sees `hr.employee` having their `company_id` in the manager's allowed companies (`res_users.company_ids`). ### Current behavior before this PR Context: a 8-companies environment where the `hr.employee` of each hierarchy chains are splitted in many different companies, like: - top-level (admin board): 1 company - middle management: approx. 2 companies - down level: the other companies The "down level" have `hr.employee` but no `res.users`. The "middle management" have `res.users` and must create the Expenses of their "down level" subordinates on their behalf. Issue: as a manager, as per Odoo proposal, I need to have a `hr.employee` in the same company of my subordinates to be able to create Expense of their behalf. However, this is very inconvenient because as a Manager, I can have employees in various companies. And my own manager it not in the same company than me, so the same issue applies recursively. ### Behavior after this PR is merged The domain of the field `expense_id.employee_id` is more permissive. As a Manager, it allows me to select the Employee I manage in my active company, no matter if I have or not myself a `hr.employee` in this company. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286293 Forward-Port-Of: odoo/odoo#266261
The appraisal workflow no longer forces users to complete a final assessment field. This prevents appraisals from being blocked when that assessment is not needed, making the process more flexible for HR teams.
Original PR description
Forward-Port-Of: odoo/enterprise#131393
Opening the “Insert in Spreadsheet” dialog no longer shows an unwanted horizontal scrollbar or duplicate scrolling on smaller screens. The dialog styling was separated from spreadsheet template styling, improving the user experience and reducing layout side effects between related spreadsheet features.
Original PR description
Opening "Insert in Spreadsheet" displayed an unwanted horizontal scrollbar in the spreadsheet selector. On small viewports, it could also produce a second scrollbar alongside the scrollable modal. The selector reused the `o-spreadsheet-templates-dialog` class, whose styles belong to `documents_spreadsheet`. This applied template-only max-height and overflow rules to the selector and made `spreadsheet_edition` rely on styles from a dependent module. Give selector dialogs their own class and keep the template-specific styles on the template dialog. Move the shared pager layout to `spreadsheet_edition` under a dedicated class, and update both pager consumers and the dashboard document selector. Task: 6526781 Forward-Port-Of: odoo/enterprise#131398 Forward-Port-Of: odoo/enterprise#130114
This fixes a visual issue on the online shop where wishlist heart buttons could appear out of place for Safari users. Product grids now look consistent across browsers, improving the shopping experience without changing functionality.
Original PR description
The wishlist button on the shop page is misaligned in Safari Steps to reproduce: (in Safari) 1. Install eCommerce 2. Go to the shop 3. The wishlist button (heart icon in the top right corner of each product) is misaligned Issue: The wishlist button (`.o_add_wishlist`) is positioned with `position: absolute` with only top and right offsets set (through the `o-position-absolute` mixin), leaving its width and height on `auto`. https://github.com/odoo/odoo/blob/c4de5361fb207916175332dcf6815c0814ecf09c/addons/website_sale_wishlist/static/src/scss/website_sale_wishlist.options.scss#L62-L65 In other browsers, the button resolves to a 38 x 38px square box but in Safari, it is not a square which misaligns it in the product grid. Solution: Force width and height on `.o_add_wishlist`, so the button resolves to the same box size in every browser. opw-6456552 Forward-Port-Of: odoo/odoo#287940 Forward-Port-Of: odoo/odoo#281935
The Navarra SII tax reporting service address has been updated because the previous web service link stopped working. This helps Spanish localization users continue submitting required tax information to the Navarra tax agency without connection failures.
Original PR description
The WSDL URL used for the Navarra tax agency SII web service was no longer working. It has been replaced with the updated endpoint 'ssii_1_1'. task-6457647 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285544 Forward-Port-Of: odoo/odoo#285049
Salary offers can now be created for contracts that start in the future, even when an employee's current contract ends today. This prevents unnecessary validation errors and helps HR teams prepare upcoming contract changes without delays.
Original PR description
Before this commit, creating a salary offer for a contract starting in the future raised a ValidationError on contract dates if the employee had an active contract ending today. This occurred because the salary simulation fallback logic defaulted to using today's date for the default contract start date, causing the simulation to overlap with the current running contract. This commit uses the target contract version's start date instead of today's date, preventing contract date overlap validation errors during offer generation. Task: 6502960 Forward-Port-Of: odoo/enterprise#129378
Belgian payroll DMFA validation now handles cases where the social security website cannot be reached. Instead of stopping with an error, the report is marked invalid with a clear explanation and the issue is logged for follow-up.
Original PR description
Before this PR, when the _fetch_validation_schema method was called (such as when the compute method for validation_state or error_message on dmfa was triggered), if the server could not reach the social security website, an error would be thrown. This is a problem, for instance, in Matt CI in upgrade which are run without internet access, or in case the social security website was down. To avoid this, we catch the error and instead: - Set the validation_state of the dmfa to 'invalid' - Add an explanatory error_message - Log the same error message in the server logs Task: 6528134
Dropshipped purchases are now excluded from average cost calculations, preventing them from changing product costs when goods never enter company inventory. This avoids incorrect negative stock valuation balances and keeps inventory accounting aligned after related bills and invoices are confirmed.
Original PR description
stock_*: stock_account, stock_dropshipping, stock_landed_costs **Problem:** dropship moves impact the average cost of products which can lead to negative balance in stock valuation account **Steps to…
stock_*: stock_account, stock_dropshipping, stock_landed_costs **Problem:** dropship moves impact the average cost of products which can lead to negative balance in stock valuation account **Steps to reproduce:** On a new db with no demo data and stock_dropshipping, sale_management and accountant module installed (bug also reproducible in runbot with same steps, but it's easier to see the negative impact on accounting on a new db) : 1) enable dropshipping 2) create a storable product with average perpetual category 3) in the purchase tab set a vendor with a price of 10 4) in the inventory tab select the dropship route 5) create PO for 1 unit @ 5, validate receipt and confirm bill 6) confirm a SO for 1 unit of the product 7) confirm linked PO and validate dropship move 8) confirm invoice and vendor bill -> see how the standard price is now 7.5 9) remove dropship route from the inventory tab of the product 10) confirm a SO for 1 unit of the product 11) validate delivery and confirm invoice 12) open 'inventory valuation' view **Current behavior:** the initial balance of stock valuation is -2.5 **Expected behavior:** it should be 0 (there shouldn't be a negative initial balance if all invoices and bills are confirmed) **Cause of the issue:** The issue happens after step 8) The problem is that the dropship has an impact on the average price of the product but not on the accounting. Before the dropship we have 1 unit in stock @ 5 and the stock valuation account has a balance of 5 (from the bill), so all is good. The dropship then changes the standard price to 7.5. That's because currently, in _run_average_batch() the dropship move first impacts average cost like an incoming move with a value of 10 (at this point we have 1 move @ 10 and 1 @ 5 so average cost is 7.5) https://github.com/odoo/odoo/blob/bcaa8b6b00fa1f16513d680c5187fd424bc7cf8c/addons/stock_account/models/product.py#L493-L500 https://github.com/odoo/odoo/blob/bcaa8b6b00fa1f16513d680c5187fd424bc7cf8c/addons/stock_account/models/product.py#L505-L510 and then it impacts the value as a regular outgoing move (meaning it leaves the inventory at the average cost of 7.5) and does not impact the average cost (which is the basic behaviour of outgoing moves) https://github.com/odoo/odoo/blob/bcaa8b6b00fa1f16513d680c5187fd424bc7cf8c/addons/stock_account/models/product.py#L515-L517 https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/addons/stock_account/models/product.py#L522 Therefore after step 8), the standard price is 7.5 and we have a unit in stock so, in the inventory valuation view the ending stock is 7.5$. But the dropship did not impact the stock valuation account so the initial balance is still 5$ and we have lines with credits and debits of 2.5$ in the the stock variation section. After steps 9 to 12, both the initial balance and ending stock decrease by 7.5 (which is expected), leading to a negative initial balance in stock valuation. **fix:** We don't take into account the stock move from dropships in the avco computation **tests:** The fix requires modifications in a few tests: - test_dropship_bill_standard_price_update checks that the bill of a dropship move impacts the standard price, so we delete this test - test_lot_normal_3, the asserts on the total_value still make sense but not those on standard_price - test_dropship_kit_bom_updates_component_standard_price test_average_cost_dropship_in_negative_quantity, test_out_move_validate_as_stock_user: standard price should not be impacted by dropship Task 6515358 Forward-Port-Of: odoo/odoo#285576
This fixes an issue where Turkish payroll payment reporting could fail if a payslip did not include stamp tax. Payroll teams can now complete MUHSGK V2 payments reliably even when that deduction is not configured.
Original PR description
## Steps to Reproduce: - Install the `l10n_tr_hr_payroll` and `hr_attendance` modules with demo data. - Switch to the company "My Turkish Company". - Settings > Set `Tax Responsible` and `SGK Workspace Registration Number`. - Pay Structures > `Türkiye: Monthly Pay` > remove `Stamp Tax Deduction (STAX)`. - Create and validate any employee's payslip. - Pay the payslip using the `MUHSGK V2` mode. ## Error: `TypeError: bad operand type for abs(): 'NoneType'` ## Cause: When the STAX code is missing from `totals_per_code`, it returns None. And calling `abs()` on this value raises a TypeError. ## Fix: Use `0` as the default value when STAX is missing. Align with the other values retrieved from `totals_per_code`, such as BTNET, CURTAXABLE. sentry-7719230995
Fixes an error that could stop the Forum app from being installed when website cookie and third-party tracking blocking options were enabled. The website now reuses the existing cookie permission setting during page rendering, avoiding a server error and improving installation reliability.
Original PR description
**Steps to reproduce:** - Install Website app - Go to Settings - Enable `Cookies Bar` and `Block tracking 3rd-party services` - Try to install Forum app (`website_forum`) - `RPC_ERROR: Odoo Server…
**Steps to reproduce:**
- Install Website app
- Go to Settings
- Enable `Cookies Bar` and `Block tracking 3rd-party services`
- Try to install Forum app (`website_forum`)
- `RPC_ERROR: Odoo Server Error`
**Issue:**
During `_render_template`, `RuntimeError: request not bound` is raised due to `self.env['ir.http']._is_allowed_cookie('optional')` in `_should_remove_third_party_trackers` depending on `request`, which is not available (unbound `<LocalProxy>`).
This was introduced by [1] where the value is recomputed instead of relying on the context.
In previous versions, the flow was not triggered as `website_id` was not in the context of `_post_processing_att`,
but it was added by [2].
Before [2] `_prepare_frontend_environment` was being restricted to calls made with `request` set (in < 19.4
versions), but now the context is always set so it triggers `_should_remove_third_party_trackers` check.
Also post 19.4 we have [3] which now sets the editable variable to `true` when `translatable` is `True` and
`edit_translations` is not present in the context. This changes the branding mode from `inherit_branding_auto`
to `inherit_branding`, which short-circuits `_post_processing_att` and prevents the problematic behavior.
**Fix:**
Use the context `cookies_allowed` value that is computed in `website.py` by `_render_template`.
[1] https://github.com/odoo/odoo/commit/0c1799f81bc70a35c8dc429c7425d3c8109ea75a
[2] https://github.com/odoo/odoo/commit/b4d852a250801921ab899eff805ab078c98c374f
[3] https://github.com/odoo/odoo/commit/05c2fb1f364ca61adc65c2b87d020885f8184a9a
opw-6477558Mexican payroll calculations now correctly protect minimum wage employees from IMSS deductions even when they receive extra pay such as commissions. The fix also prevents rounding discrepancies in payroll schedule days and ensures eligible employees are not wrongly excluded from employment subsidies due to unpaid absences.
Original PR description
According to LSS Art. 36, minimum wage workers are exempt from IMSS deductions. Previously, the system evaluated the period's total gross wage instead of the daily wage, which caused incorrect…
According to LSS Art. 36, minimum wage workers are exempt from IMSS deductions. Previously, the system evaluated the period's total gross wage instead of the daily wage, which caused incorrect withholdings when a minimum wage employee received extra pay (like commissions). We now evaluate the `l10n_mx_daily_salary` directly to protect minimum wage workers while ensuring correct deductions for higher earners with unpaid leaves. Since calculations rely on `schedule_days` retrieved from the schedule table, users commonly adjust these values (e.g., from 15 to 15.2, or 30 to 30.4). To prevent discrepancies caused by this practice, we now round the `schedule_days`. Finally, this commit fixes the employment subsidy eligibility threshold. Previously, the limit was incorrectly reduced by unpaid absences. This caused a double-counting effect (since absences already lower the actual gross wage) and wrongly disqualified employees. The subsidy limit is now based strictly on the full payroll schedule length, while the ISR minimum wage exemption correctly continues to consider actual worked days. target: 19.0 task-6370959 Forward-Port-Of: odoo/enterprise#130706 Forward-Port-Of: odoo/enterprise#125061
Upgrading the Website Sale Collect module will no longer turn the Pay on Site payment option back on or overwrite its related settings. This prevents customers from being offered an unpaid checkout option that the merchant had intentionally disabled.
Original PR description
Steps to reproduce: =================== 1. Go to the payment providers and set "Pay on Site" to disabled. 2. Go to Apps, clear the filter, look up "website_sale_collect" and upgrade it. This also…
Steps to reproduce:
===================
1. Go to the payment providers and set "Pay on Site" to disabled.
2. Go to Apps, clear the filter, look up "website_sale_collect" and upgrade it. This also happens on its own, as upgrading any custom module that depends on it upgrades it too.
3. Go back to the payment providers.
=> "Pay on Site" is enabled again, and published as well from 19.0 on. Customers are offered it at checkout and place orders that are never paid, without the merchant ever enabling anything.
Root cause:
===========
`data/payment_provider_data.xml` is loaded in update mode because it carries no `noupdate`, and it hardcodes the state of the provider:
<field name="state">enabled</field>
So every upgrade of the module writes that value back over whatever the merchant configured. Every other provider ships its data with `noupdate="1"` and leaves the state alone, this module is the exception.
The file has been loaded this way since the module was added:
- [1] created the module with an updatable provider record.
Fix:
====
Load the file with `noupdate="1"`. The record is still created, enabled, when the module is installed, it is simply not written again on later upgrades. The flag is read from the file rather than from the `ir.model.data` row, so databases where the provider already exists are covered on their next upgrade, no data migration needed.
[1]: 087c48c4ed2e
opw-6528110
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286913When businesses import CII XML vendor invoices, Odoo now carries over the payment reference onto the generated vendor bill. This helps accounting teams keep payment instructions accurate and avoids manual correction after import.
Original PR description
### Issue before this commit: When importing a CII XML invoice containing a PaymentReference, the value is not transferred to the generated vendor bill in Odoo. ### Steps to reproduce the issue: 1. Download Accounting 2. Try to import the invoice in the ticket 3. See that in the tab other info the payment reference is not imported ### Cause of the issue: During a previous refactoring (ffbdf29a816d0ff4136488d26b55b9707fc37fc6), the helper function responsible for extracting the payment reference during the import process was omitted. ### Reason to introduce the fix: Add the missing extraction logic to ensure the payment reference is correctly retrieved from the XML and assigned to the Odoo invoice. opw-6530627 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287216
List view columns can now be resized reliably on tablets and other touch-screen devices. This prevents touch gestures from interrupting the resize action, making list views easier to adjust for mobile and tablet users.
Original PR description
Steps to reproduce ================== - Use a tablet (touch screen) - Open any list view - Try to resize a column by dragging the column header's right edge => The resize doesn't work properly: it gets interrupted an we can only move a few pixels at a time Cause of the issue ================== The resize handle relies on pointerdown/pointermove/pointerup to run and stop the drag, but nothing tells the browser to opt out of its native touch gestures. On a tablet, the drag can get hijacked as a page scroll, which fires pointercancel instead of pointerup. That event was not listened to, leaving the pointermove handler attached and the resize state stuck. Solution ======== - Set touch-action: none on the resize handle so a touch drag isn't interpreted as scrolling - Listen to pointercancel to properly stop the resize when the browser takes over the gesture anyway Forward-Port-Of: odoo/odoo#288046
Point of Sale X/Z reports now show category totals that match the displayed order lines when taxes are configured as included or excluded. This avoids confusing discrepancies for cashiers and managers while preserving the existing daily sales report behavior.
Original PR description
The per category totals on X/Z reports would use the same total computation that we use for the daily sales report, but the order lines are shown tax included or excluded based on the config for the database. So we could have a category showing a total which is different from the sum of the order lines on that category. This only shows on X/Z reports, but the daily sale report uses the same data, and always displays the tax excluded prices So here I added an extra 'total_paid' field on the report data, which is used on X/Z reports generation, so the category total matches the order lines on X/Z reports, while keeping the original total which is used for daily reports Task-[6538575](https://www.odoo.com/odoo/project/1737/tasks/6538575) Enterprise PR-[#131110](https://github.com/odoo/enterprise/pull/131110) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286954
Restaurant POS orders shared between trusted points of sale now keep the correct session information during synchronization. This prevents valid payments from being blocked when each POS location uses its own payment methods.
Original PR description
**TL;DR** - in certain cases, Session is not updated on order shared on trusted pos Steps to reproduce: - Install pos_restaurant with demo data. - Have two configs: Cloth Shop and Furniture Shop. -…
**TL;DR** - in certain cases, Session is not updated on order shared on trusted pos Steps to reproduce: - Install pos_restaurant with demo data. - Have two configs: Cloth Shop and Furniture Shop. - Ensure both configs have their own individual payment method (e.g., CASH). - In Cloth Shop, add Furniture Shop as a trusted PoS. - In Cloth Shop, enable "Log in with Employee". - Open Cloth Shop and Furniture Shop in separate browsers (different users). - Refresh Cloth Shop once. - In Cloth Shop, create and save an order. - In Furniture Shop, open that order and try to pay with "CASH". Observation: * A validation error is raised: "The payment method selected is not allowed in the config of the POS session." <img width="320" height="201" alt="image" src="https://github.com/user-attachments/assets/8ca80575-76bf-4ecf-a739-08b03d543ed6" /> - although we can pay the order by a shared payment method Cause: - Refreshing Cloth Shop triggers `notify_synchronisation` due to `setCashierUpdateSession` , which pushes Cloth Shop's `pos.session` record into Furniture Shop's IndexedDB (because Cloth Shop trusts Furniture Shop). https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/pos_hr/static/src/app/services/pos_store.js#L61-L63 - When Cloth Shop saves an order, sync also pushes a copy of that order into Furniture Shop, and passed through `processDynamicRecords` - `processDynamicRecords` ensures that the Record created is in sync with server data, as Furniture Shop now has a local record of session of Cloth Shop, the record keeps `session_id` pointing to Cloth Shop's session, else it would have been left `undefined` - from the `res` we get, we set the session_id on `pos.order ` https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/point_of_sale/static/src/app/utils/devices_synchronisation.js#L127-L139 - However with pos_restaurant installed we get its orveride which stores the result of super() and only returns result early when there are no` pos.order ` records in dynamicRecords. When pos.order records are present (our case), the method proceeds with its own logic but never returns result (or its own output) at the end https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/pos_restaurant/static/src/app/utils/devices_synchronisation.js#L5-L9 - so the corrected data from the super call is silently dropped. - we do not get a change to update the session id - The order keeps Cloth Shop's session ID - Furniture Shop's payment validation checks the order against its session's allowed payment methods, but since the session is still Cloth Shop's, the check fails for methods not shared between Cloth Shop and Furniture Shop (e.g., CASH). Why this is hidden in other cases: - without pos_restaurant, or if Furniture Shop had no prior knowledge of Cloth Shop's session (we made is possible by `Log in with Employee` feature, session_id would stay `undefined` and later get correctly set toFurniture Shop's session in PosOrder.setup(). https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/point_of_sale/static/src/app/models/pos_order.js#L27-L30 or from here https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/point_of_sale/static/src/app/utils/devices_synchronisation.js#L127-L139 - Both safety nets happen to be bypassed here. Fix: * Tighten the early return condition so that data is only discarded when the current configuration is not a restaurant configuration. * Also, A trusted PoS configuration cannot be a restaurant, so not returning later makes sense. opw-6465228 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287574 Forward-Port-Of: odoo/odoo#282962
The project overview now shows the upcoming milestone based on the milestone deadline rather than creation order. This keeps the project list consistent with the milestone list and helps users see the correct next deadline at a glance.
Original PR description
Issue: The project list could display the first created unreached milestone as the next milestone, even when another milestone had an earlier deadline. This made the project overview inconsistent…
Issue: The project list could display the first created unreached milestone as the next milestone, even when another milestone had an earlier deadline. This made the project overview inconsistent with the milestone list. Steps to reproduce: - Create a project with milestones enabled. - Create MS1, then MS2. - Give MS1 a later deadline than MS2. - Open the project list and display the Next Milestone column. Cause: `_compute_next_milestone_id()` aggregated unreached milestones as an `id:recordset` and selected its first element. The ORM orders that aggregate by database ID, so creation order was used instead of the milestone model's deadline order. https://github.com/odoo/odoo/blob/765174be270813442df6c497456fe2864517da0b/addons/project/models/project_project.py#L209-L218 https://github.com/odoo/odoo/blob/765174be270813442df6c497456fe2864517da0b/odoo/models.py#L364-L377 Solution: Retrieve unreached milestones through their normal ordered search before grouping them per project. This preserves batched computation while ensuring that the selected record follows the established milestone order. opw-6496938 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287810 Forward-Port-Of: odoo/odoo#285933
This change fixes an intermittent issue in website editing tests caused by the edit button not being ready after a language switch. It removes an unnecessary language change and waits for the editor to be in the correct mode, making test results more stable without changing user-facing behavior.
Original PR description
In [this commit][1] some steps were added to resolve non-deterministic errors. Just before `...clickOnEditAndWaitEditModeInTranslatedPage(),` three steps were added that switch the language back to…
In [this commit][1] some steps were added to resolve non-deterministic errors. Just before `...clickOnEditAndWaitEditModeInTranslatedPage(),` three steps were added that switch the language back to parseltongue. This is most likely since the function call implies we are on a translated page. The choice for this function was most likely due to a race condition. As the language was switched to English recently, the edit button might not have time to have switched. To recreate the race condition, remove the step added in this commit but leave the rest of the changes. Run the tour and it will hang up on the first step of `...clickOnEditAndWaitEditMode()` because the Edit button is still in "translation" mode. This commit removes the redundant language change and adds an extra step to check the Edit button is in the correct "mode" before continuing. [1]: https://github.com/odoo/odoo/commit/7f7cc9617381b1a2f61af0875e7b60ab0b7ab3a0 task-5951393 Forward-Port-Of: odoo/odoo#288035 Forward-Port-Of: odoo/odoo#286764
The Indian salary configurator now shows only one Gross salary line instead of duplicating it. The Monthly Equivalent total is also adjusted so employees and HR teams see an accurate salary breakdown.
Original PR description
Issue: - The salary configurator displays two 'Gross' lines for Indian payroll structures. - The generic salary resume uses the `wage` code, while the Indian payroll defines a structure-specific…
Issue: - The salary configurator displays two 'Gross' lines for Indian payroll structures. - The generic salary resume uses the `wage` code, while the Indian payroll defines a structure-specific 'Gross' resume using the 'GROSS' payslip rule. Both resumes were included in the salary configurator, resulting in duplicate 'Gross' entries. - The 'Monthly Equivalent' total shown in the configurator was also affected, as it included the generic 'wage' amount on top of the actual payslip values. Cause: - The generic 'wage' resume was still included for the Indian payroll structure alongside the India-specific 'GROSS' payslip resume. - The generic 'wage' resume also counts towards the 'Monthly Equivalent' total shown in the configurator, so removing only the duplicate line left this total incorrect. Fix: - Exclude the generic 'wage' resume from the salary configurator results when the selected structure is the Indian employee payroll structure. - This keeps the India-specific 'Gross' resume based on the 'GROSS' payslip rule while preventing the generic contract wage from being displayed as a duplicate. - Subtract the removed 'wage' amount from the 'Monthly Equivalent' total so it stays correct after the duplicate line is removed. task-6511413 Forward-Port-Of: odoo/enterprise#129518
FedEx shipping labels generated in ZPLII format now download with a standard .zpl file extension instead of being saved as .txt files. This prevents confusion and helps ensure labels can be used directly with compatible label printers.
Original PR description
TL;DR When downloading a `.zplii` shipping label from the chatter on a Delivery Order (using FedEx), the browser automatically adds .txt to the end of the filename, saving it as `.zplii.txt` Step to…
TL;DR
When downloading a `.zplii` shipping label from the chatter on a Delivery Order
(using FedEx), the browser automatically adds .txt to the end of the filename,
saving it as `.zplii.txt`
Step to reproduce:
- install `delivery_fedex_rest` with demo
- open shipping method menu -> Fedex Us -> label format = `zplii` -> save
- create a SO, click on 'Add Shipping",
- select fedex as shipping method -> get rate -> add -> confirm SO
- go to delivery and validate
- notice, in thread, a attachment with ZPLII extension appears
- download (.txt is appended to file)
Issue:
- `fedex_rest_send_shipping` post message with documents with extension as
`fedex_rest_label_file_type` i.e. `ZPLII`
https://github.com/odoo/enterprise/blob/735490d7ba9bdc6d0df7a0bd07c0d4e36d1ed2d4/delivery_fedex_rest/models/delivery_fedex.py#L193-L195
- when the attachment is created for this document , it's mimetype is computed
to be `text/plain` from [guess_mimetype](https://github.com/odoo/odoo/blob/5de4a5f6caee09881e7e147062f9eab9f8916a61/odoo/tools/mimetypes.py#L192) method, as data is plain ASCII code
- moreover, when downloading, [_get_stream_from](https://github.com/odoo/odoo/blob/5de4a5f6caee09881e7e147062f9eab9f8916a61/odoo/addons/base/models/ir_binary.py#L89) tries to guess extension
using [get_extension](https://github.com/odoo/odoo/blob/5de4a5f6caee09881e7e147062f9eab9f8916a61/odoo/tools/mimetypes.py#L210) which return `None` as len('zplii') > 4, [see](https://github.com/odoo/odoo/blob/5de4a5f6caee09881e7e147062f9eab9f8916a61/odoo/tools/mimetypes.py#L221)
- finally, as we got `None`, and mimetype is `text/plain`, `.txt` is appended [here](https://github.com/odoo/odoo/blob/5de4a5f6caee09881e7e147062f9eab9f8916a61/odoo/addons/base/models/ir_binary.py#L149)
Fix:
- we use `zpl` as extension for file instead of `zplii` as there is not
difference between them from printing perspective
- as length of 'zpl' is <=4 , `get_extension` will considered it as valid
opw-6410968
Forward-Port-Of: odoo/enterprise#126620This fix prevents preparation tickets from being printed twice when a kiosk uses an Obox-connected printer. It ensures printers handled server-side are not also selected for local device printing, reducing paper waste and staff confusion.
Original PR description
A preparation printer proxied through an Obox is not reachable by the customer device: `pos.order._send_order` queues its ticket server side. Now that the kiosk prints its preparation ticket again, such a printer must be left out of the ones the device prints on itself, otherwise the ticket is printed twice. `hasProxyPreparationPrinters` is superseded by `localPreparationPrinters` and is left deprecated here, to be removed in master. Task-6537765 Community PR: https://github.com/odoo/odoo/pull/287382
Kiosk orders now correctly print preparation tickets when a preparation printer is configured. This ensures kitchen or preparation staff receive the order information at the right time, while avoiding duplicate printouts.
Original PR description
Steps to reproduce: - Configure a kiosk with a preparation printer. - Order and confirm a product from the kiosk. Issue: The preparation ticket is not printed. `_send_order` creates the preparation records before the confirmation page computes the preparation changes, leaving nothing to print. Compute and store the changes before sending the order, then consume them before printing to prevent duplicate tickets. Add `localPreparationPrinters` to exclude printers that should not print locally. Task-6537765 Enterprise PR: https://github.com/odoo/enterprise/pull/130938
The UAE payroll update process now restores required payroll structure information before updating related salary rules. This prevents scheduled payroll updates from failing if UAE payroll structures or structure types were deleted, keeping payroll maintenance running reliably.
Original PR description
Currently, an error occurs when the Payroll Update Data schedule action is run. **Steps to Reproduce:** - Install the `l10n_ae_hr_payroll` module without demo data. - Create a new company with the…
Currently, an error occurs when the Payroll Update Data schedule action is run. **Steps to Reproduce:** - Install the `l10n_ae_hr_payroll` module without demo data. - Create a new company with the country set to `United Arab Emirates`. - Switch to that company. - Go to `Payroll` > `Configuration` > `salary` > `Structures`. - Delete all salary structures related to the `United Arab Emirates`. - Go to `Scheduled Actions` and run `Payroll: Update Data`. `ValueError: External ID not found in the system: l10n_ae_hr_payroll.uae_employee_payroll_structure.` After this [recent commit], hr_rule_parameter_data and hr_salary_rule_data were added to _get_data_files_to_update. If the user deletes the salary structure and then updates the data files [1], an error is raised because the structure is missing. This commit ensures that the salary structure data is updated before updating the salary rule data. It also ensures that the salary structure type data is updated beforehand, as the structure depends on it and the user can delete the structure type data. [recent commit]: https://github.com/odoo/enterprise/commit/f3316e056da817ca31fecdc04e0f8df000f0f038 [1]- https://github.com/odoo/enterprise/blob/0a0b3a50a81df062ddee5c310ce06aaa4533a8a8/l10n_ae_hr_payroll/models/hr_payslip.py#L98-L105 sentry-7496380516 Forward-Port-Of: odoo/enterprise#130654
The Point of Sale now safely ignores coupon or loyalty card records that were removed during an order. This prevents the customer list from crashing after a coupon reward is removed, helping cashiers continue sales without interruption.
Original PR description
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line -…
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line - Click the customer button Issue: The customer list does not open. The PoS crashes with "TypeError: Cannot read properties of undefined (reading 'id')" raised while rendering PartnerLine. Cause: `partnerId2CouponIds` maps a partner to the ids of its `loyalty.card` records. It is filled at boot and on every `loyalty.card` create, but nothing ever removes an id from it: the models only trigger a create event. Removing the reward line of a code activated coupon deletes that card from the local models (`_setValue` in the order summary), so its id stays in the map while the record is gone. `getLoyaltyCards` pushed `models["loyalty.card"].get(id)` unconditionally, hence an `undefined` entry in the list the PartnerLine template iterates over with `t-key="_loyaltyCard.id"`. Fix: Skip the ids whose record no longer exists. The partner keeps its remaining cards, and the path where no card was deleted is unchanged. The stale id is not pruned from the map: it is reached through the reactive store proxy, and mutating it there would notify subscribers in the middle of a render. opw-6517564 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287503 Forward-Port-Of: odoo/odoo#286971
A timing issue in an automated rental shop test was fixed so it waits for the calendar to finish updating before choosing dates. This reduces false test failures and helps keep rental checkout validation stable without changing the customer experience.
Original PR description
The shop_buy_rental_stock_product tour can select a day from the previous month's grid before the date picker finishes rendering the next month. This can leave the rental period unchanged and cause the cart price assertion to fail. Wait for an animation frame after clicking "next month" before selecting a day. runbot-240903 Forward-Port-Of: odoo/enterprise#131467 Forward-Port-Of: odoo/enterprise#130822