Monday, August 24, 2026
20 changes · saas-19.4
Enhancements to existing features
Mail activity and message searches now load records in smaller chunks when a limit is used. This helps views open reliably while still applying access rules, avoiding cases where simple screens could fail because too much data was loaded at once.
Original PR description
When a limit is applied in the search method, fetch the data in small batches like we do in ir.attachment. This allows to search with a limit on views while applying security access. Without this, simple views cannot be opened because the ORM searches for all records before filtering them in memory. Note that this cannot be avoided for group by queries and that if we don't set a limit, we will eventually still fetch everything in smaller batches. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265203
Odoo restores the default grouping option in the search area for affected views. This helps users keep familiar list organization behavior and avoids disruption from the previous removal.
Original PR description
This reverts commit 7413e08c166ef3123ac7ed6623db1fb366f44e3c (#265574) task-6425644
Resolved issues and error corrections
This fixes an issue where store pickup orders marked as pay-on-site were treated as already paid, so they did not appear for fulfillment in the Point of Sale order list. Pay-on-site amounts are now excluded from the unpaid amount calculation used by POS, making these orders visible and easier for staff to process.
Original PR description
Step To Reproduce: - install `sale_management` and `website_sale_collect` and POS with demo - go to shop page, add a product -> checkout - Use `Pick Up in Store` feature and `Pay on site` payment…
Step To Reproduce:
- install `sale_management` and `website_sale_collect` and POS with demo
- go to shop page, add a product -> checkout
- Use `Pick Up in Store` feature and `Pay on site` payment method -> confirm
- notice a Sale order is created for this
- Open POS, try to fulfill the linked SO, by clicking on `Quotation/Order`
Observation:
- The SO is not visible in the list view
Cause:
- clicking on `Quotation/Order` initiates a search with domain `["amount_unpaid", ">", 0]`
https://github.com/odoo/odoo/blob/21e3b547e486951421e39733c19a8a2d54361e00/addons/pos_sale/static/src/app/components/screens/product_screen/control_buttons/control_buttons.js#L16
- `amount_unpaid` depends on `amount_paid` which is sum of all transaction in ("authorized", "done") state
https://github.com/odoo/odoo/blob/21e3b547e486951421e39733c19a8a2d54361e00/addons/sale/models/sale_order.py#L913-L916
- after commit [1], transactions with payment method of type `postpaid` are considered as `done` (`Pay on site` is postpaid)
https://github.com/odoo/odoo/blob/21e3b547e486951421e39733c19a8a2d54361e00/addons/website_sale_collect/models/payment_method.py#L9-L14
https://github.com/odoo/odoo/blob/21e3b547e486951421e39733c19a8a2d54361e00/addons/payment_custom/models/payment_transaction.py#L40-L41
- this makes `amount_paid ` to be full order amount and `amount_unpaid = 0` for the order and search fails in pos
[1] https://github.com/odoo/odoo/commit/9d01784fa9980fb9e4989e6e4e9f35909665e6ca
Fix:
- Fix the compute of `amount_unpaid` and do not count transactions amount from
postpaid payment method
Note: issue occurs for POS only, hence, we avoid changing anything from sale side
while keeping changes to minimum
opw-6424288
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prCredit card and cash journal statement lists can now open individual statements in the form view. This fixes a dashboard workflow issue that prevented users from reviewing or editing statement details after opening the Statements list.
Original PR description
Issue: When opening the credit card statements list view from clicking the "Statements" button in the accounting dashboard of a credit card journal, the resulting list view does not allow clicking on any of the items to enter the form view Steps to reproduce: 1. Create a credit card journal and some credit card statements 2. Go to the accounting dashboard, and click on the button with three dots to the upper right of the credit card journal card and click "Statements" 3. Try to click on any of the statements in the list view and it won’t open any of them Cause: The window action for credit card journals (action_credit_statement_tree) was missing the form view in the view_mode Solution: Add form to the view_mode of action_credit_statement_tree. The cash journal bank statements window action (action_view_bank_statement_tree) was also missing the form view, so it was added as well opw-6449315 Forward-Port-Of: odoo/odoo#282816
Auto planning for sales-related planning work now correctly includes the last working day of a selected month. This prevents planned time from being cut short, helping teams schedule the full expected workload without manual corrections.
Original PR description
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To…
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To Plan" button, then click "Auto Plan". 4. Make sure the "Month" filter is selected in the scale options and observe the planned slots. Issue: -------- When auto planning slots for a month, the last day of the month is excluded. For example, slots are scheduled only until July 30th, even though July 31st is a working day. Cause: -------- While preparing the context, `stopDate` is set to July 31st at 00:00. It is then passed to [serializeDateTime()](https://github.com/odoo/odoo/blob/dacaad91bba8f959daf5d89a046c5a1c11e48eec/addons/web/static/src/core/l10n/dates.js#L553-L560), which converts the datetime to UTC. Depending on the user's timezone, this can shift the date to the previous day, causing the last day of the month to be excluded. Solution: ------------ Use `localEndOf()` to set `stopDate` to the local end of the selected range before passing it to `serializeDateTime()`. This ensures the last day of the month is preserved during UTC conversion. **NOTE:** Forward-port the solution from the 18.0 version, which was adapted to the publish shift use case in 18.3 and introduced this issue. Add a HOOT test case to prevent this regression in future versions. References: [18](https://github.com/odoo/enterprise/commit/bc3db24f83473d5646f7c2cfca8ed1c5b064ea2e) and [saas-18.3](https://github.com/odoo/enterprise/commit/c81fba31780869940f726b695ad46a87f69798fb) opw-6391495 Forward-Port-Of: odoo/enterprise#128775 Forward-Port-Of: odoo/enterprise#127950
A recent change that removed the default grouping option in Gantt views has been reverted. This restores expected grouping behavior in planning and related Gantt screens, helping users keep their usual view organization.
Original PR description
This reverts commit d35390a534fc60354a3c0174d6aa7cd158805f63 (#117918) task-6425644
Cancelling a payslip now correctly makes any related time off available to be recalculated in that payslip period. This prevents approved absences from being missed when payroll teams cancel and reset a payslip to draft.
Original PR description
How to reproduce: - Create a payslip for an employee and validate it - Create a new time off for said employee during the same period as the payslip and validate it - Go back to the payslip, cancel it and reset it to draft - The new time off is not included in the payslip Reason: When a payslip is cancelled, if there are time off during the same period as the payslip, their state is not reset to "to compute in next payslip" and instead stays in "to defer to next payslip", causing the issue How it was fixed: Now, when a payslip is cancelled, the new function "return_time_off_to_normal" will catch all leaves that are in the same time frame as the payslip to reset their state to "to compute in next payslip". Task ID: 6431576 Forward-Port-Of: odoo/enterprise#126868
Creating shortcuts for documents shared with a group now works correctly. The update also makes ownership access logging more reliable when group access is involved, reducing errors and improving consistency for shared document workflows.
Original PR description
Reproduce: try creating a shortcut for a document shared with a group. \+ increase robustness of logging owner access when creating a documents with an access command regarding a group. Task-6344800 Forward-Port-Of: odoo/enterprise#128926
Manual product replenishment now correctly shows a notification when it creates a purchase order. This helps inventory and purchasing users immediately see the result of ordering stock, while preserving existing purchase order line merging behavior.
Original PR description
Currently when the user does manual replenishment no notification is displayed. ## Steps to produce: - Install Inventory and Purchase - Create a product `Chocolate Icecream` and Enable `Track…
Currently when the user does manual replenishment no notification is displayed. ## Steps to produce: - Install Inventory and Purchase - Create a product `Chocolate Icecream` and Enable `Track Inventory` - Purchase > Add a Vendor `Ice cream man` - Reordering rules > Create a new reordering rule and save: - Trigger: Manual - Min: 5 - Max:10 - Press the `Order` button ## Observed Behavior: No notification is displayed about the newly created purchase order. ## Root cause: When the Order button is pressed, the `action_replenish` method is called. This method invokes `_procure_orderpoint_confirm` at [1]. The `_procure_orderpoint_confirm` function is responsible for creating procurements from orderpoints. During this process, it retrieves the procurement values using `_prepare_procurement_values` that are later used at [2]. However, `_prepare_procurement_values` only includes the orderpoint in the procurement values when the orderpoint's trigger is set to automatic, and not when it is manual, as shown at [3]. These procurement values are then used by `_run_buy` to create a purchase order and purchase order line at [4]. Since the orderpoint is not linked to the purchase order line in this case, no matching order is found at [5], which leads to the reported issue. **Which commit caused this unintentional behavior?** This behavior was unintentionally introduced by this [commit](https://github.com/odoo/odoo/commit/2a0d2c64d0027f540101447289b4c1a10cb3ecdf) . That commit fixed an issue where purchase order lines were not being merged for temporary manual orderpoints that are created dynamically based on product demand. [1]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L342-L349 [2]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L737-L741 [3]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L687-L701 [4]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/purchase_stock/models/stock_rule.py#L156-L165 [5]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/purchase_stock/models/stock.py#L276-L296 [6]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L365 [7]- https://github.com/odoo/odoo/blob/6a84d3e519892be333552e2e0ebf8da87e0a760c/addons/purchase_stock/models/purchase_order_line.py#L380-L384 ## Solution: Instead of removing the orderpoint ID from the procurement values, we reuse the same conditions used to identify temporary orderpoints for cleanup at [6]. Based on this, we determine how purchase order lines should be merged in the `_run_buy` method. With the previous implementation, no orderpoint was included in the procurement values. As a result, the condition at [7] checking for orderpoints always evaluated to True, causing the system to identify an existing purchase order line for the same product as a merge candidate. This solution allows us to retain that fix as well as avoid the error of notifications not showing up. opw-6311520 Forward-Port-Of: odoo/odoo#283853 Forward-Port-Of: odoo/odoo#271993
Point of Sale bills no longer show the self-service invoicing QR code while an order is still in draft. This prevents customers from invoicing or paying through the QR flow before the sale is finalized, avoiding inconsistent order and payment records.
Original PR description
Step to reproduce: - install point_of_sale - have a pos, with `Early Receipt Printing` and `Self-service invoicing` enabled - open a pos ,select a product - from action button, click on "Bill" Observation: - We can see QR code in bill, using which a person can invoice itself, even when order is in draft state. - This cause a lot of anomoly like payment line not visible in pos order, even after successful payment Cause: - Prior to this version, `Qr` related data is shown only when `order.finalized` i.e. `status = draft` . https://github.com/odoo/odoo/blob/6f64942cbbbf2355f7328394a6d484f6828a80f1/addons/point_of_sale/static/src/app/components/receipt/order_receipt.xml#L76 - After commit https://github.com/odoo/odoo/commit/aeaca097ae39b293bff47458ae8af019585f9224 we removed this condition Fix: - The condition is brought back. opw-6427152 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279382
This fixes a performance issue that could make Sign templates slow to open for users with limited template access. The system now loads related Sign template items in a single batch instead of repeating many separate lookups, improving responsiveness without changing user workflows.
Original PR description
Steps to reproduce: - with a user with "Sign / User: Own Templates" access rights - go to Sign / Templates - click on a template to open it => reading `sign.item.role.item_ids.template_id` triggers the computation of the related field `sign.item.template_id`, whose inverse `sign.template.sign_item_ids` carries a domain. When applying the domain, the sign.item's fields need to be fetched but they are fetched with one query per sign.item instead of a single batched one. task-6478942 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283864 Forward-Port-Of: odoo/odoo#282994
The translation button now saves edits made inside related-record pop-up forms before opening the translation window. This ensures users see and translate the current text instead of outdated or empty content, while preserving existing behavior for editable lists.
Original PR description
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened…
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened in an x2many form dialog keeps its changes for itself until the dialog is saved, see https://github.com/odoo/odoo/blob/242f6d3cf7288853f163ac6986a3b7aa4279efaf/addons/web/static/src/model/relational_model/static_list.js#L193. Its pending changes are not part of the root record changes, so saving the root sends nothing to the server, and the translation dialog then shows the stored terms instead of the current content, or no terms at all when the stored value is empty. The fix changes openTranslationDialog in translation_button.js, the place that decides which record to save. When the record keeps its changes for itself (record._noUpdateParent), the record is saved directly, like the button did before the commit above. The root record is still saved in the other cases, so the editable list case that commit fixed keeps working. Steps to reproduce: 1. Activate a second language in Settings > Translations > Languages 2. Open the Surveys app, open a survey and click a question in the Questions tab 3. In the Description tab, change the description 4. Click the EN button on the description field => the translation dialog shows the terms of the previous description, not the current one Ticket [link](https://www.odoo.com/odoo/project.task/6237291) opw-6237291 Forward-Port-Of: odoo/odoo#269507
This update corrects issues caused by a framework change so timers, active timesheet restoration, and the POS Urban Piper search bar behave reliably again. It prevents repeated timer resets and ensures users can resume active timesheets as expected.
Original PR description
OWL3's useEffect takes one argument, so the OWL2 deps callback is dropped: timer_start_field re-arms its interval on every render instead of on a timer_start it compares by value, and timesheet_systray never binds its `loaded` parameter, so it never restores the active timesheet. Came in with odoo/enterprise#128151 and odoo/enterprise#125368. Effects kept: they arm an interval and call into the timer service, not a derivation, so useOnChange restores both declared dependency lists verbatim. see https://odoo.github.io/owl/documentation/v3/owl/reference/hooks.html#useeffect community: https://github.com/odoo/odoo/pull/283883
Euro payments sent through non-euro bank journals can now be configured to use the correct SEPA payment indicators in generated ISO20022 files. This helps avoid bank rejection or processing issues while leaving existing behavior unchanged unless the new option is enabled.
Original PR description
Steps to reproduce: - Configure a bank journal whose currency isn't EUR (e.g. SEK, USD, GBP). - Use the generic ISO20022 payment method to send a payment in EUR to a SEPA-zone IBAN. - Generate the…
Steps to reproduce: - Configure a bank journal whose currency isn't EUR (e.g. SEK, USD, GBP). - Use the generic ISO20022 payment method to send a payment in EUR to a SEPA-zone IBAN. - Generate the pain.001 file: SvcLvl/Cd is NURG and ChrgBr is SHAR instead of the SEPA-mandated SEPA/SLEV. Cause of the issue: SvcLvl/Cd and ChrgBr are derived purely from the technical payment method code, not from whether the transaction actually qualifies as SEPA. The 'sepa_ct' payment method (which hardcodes SvcLvl=SEPA and ChrgBr=SLEV) is only ever offered on journals whose own currency is EUR. A journal in any other currency that occasionally sends a EUR payment therefore always falls back to the generic 'iso20022' payment method, which unconditionally reports NURG/SHAR. The same gap already exists, and is already solved, for Switzerland via the 'iso20022_ch_force_sepa' parameter, which dynamically remaps 'iso20022_ch' batches to 'sepa_ct' when their currency is EUR. No equivalent existed for any other country. Solution: Generalize that mechanism with a new opt-in parameter, 'account_iso20022.force_sepa_for_eur'. When set, a EUR-denominated batch generated through the generic 'iso20022' payment method is remapped to 'sepa_ct' for XML-generation purposes, so it correctly reports SvcLvl=SEPA and ChrgBr=SLEV. The parameter defaults to disabled, so the default behavior is unaffected unless explicitly turned on. opw-6006230
The barcode delivery flow now uses the real storage location of a scanned serial item instead of falling back to the parent warehouse location. This prevents incorrect stock deductions, stale inventory records in sublocations, and negative stock in parent locations when processing deliveries.
Original PR description
Steps to reproduce --- 1. Enable Storage Locations and Lots/Serial Numbers. 2. Set the delivery operation type's "Source Location" to "Do not scan". 3. Create a serial-tracked product with a serial…
Steps to reproduce --- 1. Enable Storage Locations and Lots/Serial Numbers. 2. Set the delivery operation type's "Source Location" to "Do not scan". 3. Create a serial-tracked product with a serial stored in a sublocation (e.g. WH/Stock/Section 2). 4. Confirm a sale order for it, open the delivery in Barcode, and scan an unreserved serial. Issue --- Scanning the unreserved serial creates a new move line that falls back to _defaultLocation() because the decoded scan carries no source location (the operation type does not require scanning one) and never carries the serial's quant location. https://github.com/odoo/enterprise/blob/f42cfa7265ce32bd95f7f805cf4fb54d8b79cce1/stock_barcode/static/src/models/barcode_model.js#L937-L944 For a delivery, that default resolves to the picking's own source location (the parent WH/Stock), so the line is sourced from the parent instead of the sublocation where the serial physically sits. https://github.com/odoo/enterprise/blob/f42cfa7265ce32bd95f7f805cf4fb54d8b79cce1/stock_barcode/static/src/models/barcode_picking_model.js#L1542-L1544 On validation the unit is deducted from the parent location instead of the sublocation, leaving a stale quant of the serial in the sublocation and a negative quant at the parent. opw-5864414 Forward-Port-Of: odoo/enterprise#128523 Forward-Port-Of: odoo/enterprise#121375
Colombian electronic invoicing users can now generate invoice PDFs for vendor bills after completing the reception and acceptance flow. The fix correctly reads wrapped invoice attachments, preventing a server error that blocked PDF creation.
Original PR description
**Steps to reproduce:** * Install the **l10n_co_edi** module. * Go to **Settings** and, under **Colombian Electronic Invoicing**: * Disable **Testing Mode**. * Enable **DIAN Demo**. * Create a vendor…
**Steps to reproduce:**
* Install the **l10n_co_edi** module.
* Go to **Settings** and, under **Colombian Electronic Invoicing**:
* Disable **Testing Mode**.
* Enable **DIAN Demo**.
* Create a vendor bill with a tax and confirm it.
* Click **Acknowledge Reception**.
* Click **Receive Goods**.
* Click **Accept**.
* From the gear menu, click **Print → Invoice PDF**.
**Observed behavior:**
* A server error is raised:
```
lxml.etree.XMLSyntaxError: Start tag expected, '<' not found, line 1, column 1
```
* The PDF cannot be generated.
**Cause (two-step):**
1. **ZIP not unwrapped:** The original code called `etree.fromstring(self.l10n_co_edi_attachment_id.raw)` directly for all move types. For vendor bills (`in_invoice`) the attachment is stored as a ZIP file, so `raw` is compressed binary data — not XML. Passing it to `etree.fromstring` directly produces the `XMLSyntaxError` above.
2. **AttachedDocument wrapper not unwrapped:** Once the ZIP is correctly decompressed with `xml_utils._unzip`, the resulting XML is an `AttachedDocument` wrapper, not a plain `Invoice`. The actual invoice XML is embedded as CDATA inside `cac:Attachment/cac:ExternalReference/cbc:Description`. `_get_qr_code_value` expects the inner document and searches for nodes like `cac:AccountingSupplierParty`, `cac:LegalMonetaryTotal`, and `sts:QRCode` — none of which exist on the outer wrapper, so the QR code was blank or the method crashed.
**Fix:**
* In `_l10n_co_edi_get_invoice_report_qr_code_value`, for vendor bills (`in_invoice`/`in_refund` without support document), unzip the attachment and immediately attempt to extract the inner invoice XML from `cbc:Description` using `findtext('.//{*}Description')` (lxml namespace wildcard). If the node is present, parse its text as the actual document; otherwise fall back to the unzipped bytes directly.
**Note:**
* A unit test for the `AttachedDocument` unwrapping path was not added because the test would require a zipped fixture file (the vendor bill attachment is stored as a ZIP) which is not appropriate to commit.
* A regression test was added in `test_accept_by_customer`: after the full commercial event flow the method is called inside a `try/except etree.XMLSyntaxError` block so that any XML parse failure surfaces as a proper test *failure* rather than an unhandled test *error*.
opw-6417422
Forward-Port-Of: odoo/enterprise#128694
Forward-Port-Of: odoo/enterprise#126463Fixes an error that occurred when users clicked the "Recorded" button on field service interventions in setups without Project planning integration. The button now reliably shows the timesheets logged for the intervention, improving visibility into recorded work and billing details.
Original PR description
**Problem:** Clicking the "Recorded" smart button on a field service intervention raises an AttributeError instead of listing the timesheets logged on it. **Steps to reproduce:** 1. Install the field…
**Problem:** Clicking the "Recorded" smart button on a field service intervention raises an AttributeError instead of listing the timesheets logged on it. **Steps to reproduce:** 1. Install the field service billing feature (Planning > Configuration > Settings > Field Service > "Invoice your time and materials to your customers") without installing Project's planning integration 2. Set the project in Planning > Configuration > Settings > Field Service 3. Create an intervention and assign a resource to it 4. Click the "Recorded" smart button **Current behavior:** 'super' object has no attribute 'action_open_timesheets' **Expected behavior:** The timesheets logged on the intervention are listed. **Cause of the issue:** `action_open_timesheets` on `planning.slot` is defined in two modules only: `project_timesheet_forecast` provides the base implementation, and `planning_field_service_sale_timesheet` extends it through `super()`. Since `59a04091c403` dropped `project_timesheet_forecast_sale` from the dependencies of `planning_field_service_sale_timesheet`, nothing guarantees the base implementation is part of the model's inheritance chain anymore: `project_timesheet_forecast` is auto installed only together with `project_forecast`, which no module of the field service stack depends on. The smart button is rendered by `planning_field_service_sale_timesheet` itself, so it stays visible in such an installation while the `super()` call has nothing to resolve to. **Fix:** That same commit already made the field service module self-sufficient for `_get_timesheetable_project`, `_compute_allow_timesheets` and `_compute_allow_billable`, leaving the project forecast flavour to the `project_timesheet_forecast_field_service_sale` bridge, and `action_open_timesheets` is the only method that was left relying on the other stack. Building the action from the module's own hooks preserves that separation, whereas depending on `project_timesheet_forecast` again would pull Project back into every field service installation, which is precisely what that commit set out to avoid. opw-6391972
The project Sales button now shows all sales linked to a project, including rental orders, so the list matches the total shown on the button. This prevents users from missing rental-related revenue or order details when reviewing project sales.
Original PR description
Steps to Reproduce --- 1. Install sale_renting_project. 2. Create a Project linked to 1 standard Sales Order and 1 Rental Order. 3. Observe the "Sales" stat button counts 2 Sales. 4. Click the stat button. Only the standard Sales Order is displayed. Issue --- In saas-18.4, the project Sales stat button calls action_view_sos without the from_embedded_action context key. As a result, _get_sale_orders_domain applies the non-rental filter by default, causing rental orders to be excluded from the action even though they are included in the displayed counter. Expected Behavior --- The Sales stat button should display all orders linked to the project, including both standard and rental orders, matching its total counter. Fix --- Return the base project domain unmodified when from_embedded_action is not set in the context. task-6140201 Forward-Port-Of: odoo/enterprise#128468 Forward-Port-Of: odoo/enterprise#121449
Annual report articles can now be exported to PDF reliably in newer trial environments. This prevents users from hitting an export error caused by an outdated PDF library reference, ensuring reports remain downloadable as expected.
Original PR description
When exporting an article to PDF, `export_article_to_pdf` uses `PdfReader.outlines` to retrieve the PDF bookmarks at [1]. This causes the following error on Trial DBs: `AttributeError: 'PdfReader' object has no attribute 'outlines'` The `outlines` property has been deprecated in PyPDF2 in favor of `outline` PyPDF2 doc [ref](https://pypdf2.readthedocs.io/en/3.x/modules/PdfReader.html?utm_source=chatgpt.com#PyPDF2.PdfReader.outlines). The issue is reproducible on Trial DBs running **Ubuntu 26.04** with **Python 3.14**, while older environments only raise a deprecation warning. Solution: Replace `PdfReader.outlines` with `PdfReader.outline` to use the supported API and prevent the PDF export from failing with an `AttributeError`. [1]: https://github.com/odoo/enterprise/blob/912a47b8f0828ef8316b7e4ecdabf8a2f305b313/accountant_knowledge/controller/main.py#L462 opw-6418289
Opening the timesheet systray from a task no longer clears an already running timer. This prevents users from losing tracked time when the form is automatically prefilled with the related project and task.
Original PR description
Before this commit, the prefill used when the user opens the timesheet systray when his current loaded page is a task, will alter the form view in the timesheet systray to set the project and task when those fields are unset. The problem is `unit_amount` is also given to 0 and so the timer is reset due to the prefill system. This commit makes sure the `unit_amount` field is not inside the prefill data to make sure the timer is no longer reset. opw-6481443 Forward-Port-Of: odoo/enterprise#128672 Forward-Port-Of: odoo/enterprise#128531