Branch
[I18N] stock, *: translate the stock Location as Dia diem in Vietnamese A stock location reads "Dia diem" in the Vietnamese UI of the inventory apps instead of "Vi tri": source and destination locations, child and parent locations, location types, storage categories of locations, the view location type, the warehouse view location and the relocation of stock. "Vi tri" stays where it means a position. 246 entries updated across 14 modules (stock, stock_account, stock_fleet, stock_picking_batch, mrp, mrp_subcontracting, mrp_subcontracting_dropshipping, repair, purchase_stock, sale_stock, spreadsheet_dashboard_stock_account, point_of_sale, barcodes_gs1_nomenclature, and the mrp, sale_amazon and account_add_gln descriptions in base); translations only.
[FIX] l10n_vn_edi_viettel: fetch only the missing SInvoice files on re-send Steps to reproduce: - Send & Print a posted customer invoice with "Send to SInvoice" ticked: the invoice is issued and its PDF and XML files are fetched from SInvoice and stored on the invoice. - Send & Print the same invoice again, for instance to email it to the customer. Current behavior: `_call_web_service_after_invoice_pdf_render` calls `_l10n_vn_edi_fetch_invoice_files` for every invoice already sent, so both files are fetched from SInvoice again on every Send & Print. Each run creates a second attachment on the same binary field and posts a new "Invoice sent to SInvoice" note in the chatter. Besides, when SInvoice answers with an empty `fileToBytes`, the retry loop gives up and the empty content is stored as the PDF file; a reply without the `fileToBytes` key raises a KeyError in that loop, and a reply that is not a ZIP archive raises zipfile.BadZipFile out of `_l10n_vn_edi_fetch_invoice_xml_file_data`. After this commit: A file already stored on the invoice is not fetched again; only a missing one is. An empty reply, a reply without the file key and a non-ZIP reply are returned as error messages, which Send & Print shows on the invoice like the other SInvoice errors. The two new messages are added to the translation template and the Vietnamese catalog. Closes Viindoo/odoo#1328
[FIX] l10n_vn_edi_viettel: fetch only the missing S-Invoice files on re-send Re-sending an invoice already issued to Viettel S-Invoice no longer fetches the PDF and XML again, so no duplicate attachment or chatter note appears. A file answer that is empty, lacks the file key or is not a ZIP archive becomes a clear error on the invoice instead of a traceback or a silent empty PDF. Closes Viindoo/odoo#1328
[FIX] account_edi_ubl_cii: skip deferred dates tests without accountant test_invoice_deferred_dates_with_delivery_date, test_invoice_deferred_dates_on_some_lines and test_invoice_deferred_dates_on_some_lines_with_delivery_date create invoice lines with deferred_start_date/deferred_end_date, fields defined by account_accountant. Without it they raised ValueError: Invalid field 'deferred_start_date'. Guard them with ensure_installed('account_accountant'), as test_invoice_deferred_dates already does.
[FIX] pos_self_order: stop kiosk receipt print after leaving page The kiosk confirmation page schedules the automatic receipt print with setTimeout (first after 500 ms, then retried every 500 ms until the order is loaded) but only clears the default-navigation timeout when it unmounts. A print timer could therefore fire after the customer left the page. Keep the print timeout ids and clear them in onWillUnmount, as is already done for the default timeout. The self_simple_order tour also ended while the automatic print was still preloading the company logo. When the browser was closed right after, the aborted image load was reported by the printer service as an error and logged as 'Error received after termination'. Make the last step wait for the confirmation page to be gone and for the printer to be idle, so the tour waits for the work it started.
[FIX] website_sale: wait for each cart line removal in the reorder tour The fourth reorder of website_sale_reorder_from_portal empties the cart by clicking the first delete link four times, one second apart. The quantity change is debounced (500ms) then sent by RPC, and the input ignores changes until the answer comes back; on a loaded runbot the round trip exceeds the remaining 500ms, so a click hits the line still being removed and is lost. A line stays in the cart, the reorder then asks whether to clear it, and the tour fails at "Checking if Reorder Product 1 is in the cart" (runbot build 225353). Wait for the cart lines to be re-rendered (the clicked link is detached) before removing the next line instead of sleeping a fixed second.
[I18N] *: fix Vietnamese translations and terminology Correct the Vietnamese catalogs of the core modules against a glossary researched per business domain (accounting per Circular 99/2025 and 133/2016, inventory, manufacturing, sales/purchase, HR, platform). - Definite defects: wrong meanings, broken placeholders and markup, untranslated user-facing strings, a real third-party e-mail address in auth_signup. - Key terms unified across modules, e.g. Journal Item = "Phát sinh kế toán", Credit Note = "Hóa đơn điều chỉnh giảm", Fiscal Position = "Chế độ thuế", Sales/Purchase Order = "Đơn bán"/"Đơn mua", Transfer = "Phiếu kho", Work Center = "Trung tâm sản xuất", Work Order = "Lệnh công đoạn", Analytic Account = "Tài khoản quản trị". - Records shared between modules (model names, re-declared fields) carry the same translation in every module, so the result no longer depends on module load order. - Old-style tone marks everywhere (hóa, hủy, khóa). - Residual defects found while aligning the Viindoo catalogs: the field type "float" read "dự trữ" (reserve), a garbled overdue line in the activity status help, Send Money = "Gửi tiền" (a deposit), the Blacklist family now "Danh sách chặn / Bị chặn", "Nếu được cài đặt" for settings, statement = "Sao kê", SEO meta labels, and a tax-mode label colliding with Fiscal Position. - l10n_vn_edi_viettel: catalog follows the renamed invoice type 5 label. Only msgstr content changes; msgids, references and layout are the exporter's and stay as they are.
[I18N] *: fix Vietnamese translations and terminology Correct the Vietnamese catalogs of the core modules against a glossary researched per business domain (accounting per Circular 99/2025 and 133/2016, inventory, manufacturing, sales/purchase, HR, platform). - Definite defects: wrong meanings, broken placeholders and markup, untranslated user-facing strings, a real third-party e-mail address in auth_signup. - Key terms unified across modules, e.g. Journal Item = "Phát sinh kế toán", Credit Note = "Hóa đơn điều chỉnh giảm", Fiscal Position = "Chế độ thuế", Sales/Purchase Order = "Đơn bán"/"Đơn mua", Transfer = "Phiếu kho", Work Center = "Trung tâm sản xuất", Work Order = "Lệnh công đoạn", Analytic Account = "Tài khoản quản trị". - Records shared between modules (model names, re-declared fields) carry the same translation in every module, so the result no longer depends on module load order. - Old-style tone marks everywhere (hóa, hủy, khóa). - l10n_vn_edi_viettel: catalog follows the renamed invoice type 5 label. Only msgstr content changes; msgids, references and layout are the exporter's and stay as they are.
[FIX] point_of_sale: pass noupdate True for pos load demo data Backport of odoo/odoo@1bed05062d1a76763156220e6626ab071b3c578d (17.0), adapted to 16.0 where the onboarding data is loaded by pos.session.check_product_wizard.load_demo_products(). When a database has no PoS product, the user can load sample products from the PoS opening wizard. They are created from data/point_of_sale_onboarding.xml, which is not listed in the manifest, with noupdate=False. On the next upgrade of point_of_sale these xmlids are not reloaded, so _process_end() tries to delete the records. Once one of the sample products has been sold, the upgrade crashes: psycopg2.errors.ForeignKeyViolation: update or delete on table "product_product" violates foreign key constraint "pos_order_line_product_id_fkey" on table "pos_order_line" and the unsold sample products are silently deleted. Load the file with noupdate=True so the records are left alone on upgrade. Databases which already loaded the sample products keep their noupdate=False xmlids and must be fixed by setting noupdate on them.
[FIX] website_sale: a garbage ?category= on a product page must not answer 500 SQL-injection scanners request product pages with payloads in ?category= (e.g. "1' AND 1=1 UNION SELECT NULL-- -"): int(category) raised ValueError in _prepare_product_values and the page answered 500 - 1,031 times in one week on a production 16.0 shop. The earlier guard only covered the shop listing. The category only drives the breadcrumb and the back link here, so an invalid one is ignored, as upstream 19.0 does.
[FIX] website_sale: a garbage ?category= on a product page must not answer 500 SQL-injection scanners request product pages with payloads in ?category= (e.g. "1' AND 1=1 UNION SELECT NULL-- -"): int(category) raised ValueError in _prepare_product_values and the page answered 500 - 1,031 times in one week on a production 16.0 shop. The earlier guard only covered the shop listing. The category only drives the breadcrumb and the back link here, so an invalid one is ignored, as upstream 19.0 does.
[FIX] website: deflake link_tools tour when the editor activates blocks late test_15_website_link_tools fails at "Pick '/contactus" on the 17.0 runbot builds with every addon installed (226014: 3 attempts out of 3, 225958: 1 out of 2): the '/contactus (Contact Us)' suggestion is listed, but in a hidden dropdown. The editor activates the blocks the tour clicks one after the other. The tour clicks the image of the block, then the link of its text, and uses the link tools right away. On a loaded machine the activation of the link's block ends while the link tools already search suggestions. That activation first closes the widgets of the options it replaces, still the ones of the image, and closing the URL picker of the image hides the first website URL dropdown of the page: the one of the link tools. The searches that follow keep that hidden dropdown alive. Wait for the editor to show the options of the image after the click on it, then for the options of the link's block to replace them after the click on the link, before using the link tools. Reproduced on a database with 832 of the addons of the runbot build, with the suggestion requests slowed down to their runbot duration and the editor slowed down by debug logging: the tour failed at that step in 3 runs out of 8, each time right after the URL picker of the image had hidden the link tools dropdown, which it did in no passing run. With the waits it passed 6 runs out of 6 in the same conditions: the URL picker of the image still closed twice per run, always between the two waits, and never while the link tools were in use.
[FIX] mrp: align open transfer bom lines before updating component quantity _link_bom now points the raw move and its open same-product origins at the new BoM line before writing product_uom_qty. A phantom kit puts bom_line_id in the merge key, so a quantity delta can fold into the open transfer instead of becoming a return. Co-authored-by: Cursor <cursoragent@cursor.com>
[FIX] website_event: call the parent start with the widget as `this` `this._super.apply(this.arguments)` passes `this.arguments` (undefined) as the receiver and no arguments at all. The parent start therefore ran with an undefined `this`, which has worked by accident only as long as the inherited start never touched `this`: as soon as a module includes `publicWidget.Widget` to wrap `start`, the registration form crashes with "Cannot read properties of undefined (reading '_super')" and every website_event tour breaks. Pass the widget and the real arguments, as `init` and `destroy` already do.
[FIX] website_sale: wait for each cart line removal in the reorder tour The fourth reorder of website_sale_reorder_from_portal empties the cart by clicking the first delete link four times, one second apart. The quantity change is debounced (500ms) then sent by RPC, and the input ignores changes until the answer comes back; on a loaded runbot the round trip exceeds the remaining 500ms, so a click hits the line still being removed and is lost. A line stays in the cart, the reorder then asks whether to clear it, and the tour fails at "Checking if Reorder Product 1 is in the cart" (runbot build 225353). Wait for the cart lines to be re-rendered (the clicked link is detached) before removing the next line instead of sleeping a fixed second.
[FIX] website_sale: a garbage ?category= on a product page must not answer 500 SQL-injection scanners request product pages with payloads in ?category= (e.g. "1' AND 1=1 UNION SELECT NULL-- -"): int(category) raised ValueError in _prepare_product_values and the page answered 500 - 1,031 times in one week on a production 16.0 shop. The earlier guard only covered the shop listing. The category only drives the breadcrumb and the back link here, so an invalid one is ignored, as upstream 19.0 does.
[FIX] mail: keep the new mention search on a late answer Steps to reproduce: - In a chatter, type "@xxx" (no partner matches) and wait for the search to be sent - Before its answer comes back, replace it with "@Georges" Georges is not suggested (unless the client already holds that partner), and typing more letters does not search again until characters are deleted. The answer of a mention search is applied to the search that is current when it arrives. When the user changed the search meanwhile, the empty answer of "xxx" is recorded as the answer of "Georges", and the search is cleared because that answer has no result. The fetch scheduled for "Georges" then has nothing to search, and the next letters are taken as narrowing a search already known to have no result. This commit records the answer for the search that was fetched, and clears the search only when it is still the one that was fetched. The tour of project's test_05_project_sharing_chatter_mention_users fails this way on a loaded machine: it waits exactly the 250ms debounce between "@xxx" and "@Georges", so the answer of "xxx" can land after "@Georges" is typed. (cherry picked from commit c2b3f1631f1bf5cde4f2075dd774957a86a97049)
[FIX] tools: convert PDFs using simple fonts to PDF/A Steps to reproduce: - Install the optional `fonttools` package (it is also pulled by other packages, e.g. `pyHanko[opentype]`) - Run `odoo-bin -i base --test-tags /base:TestPdf` Issue: `test_convert_to_pdfa_without_outlines` fails with `KeyError: '/DescendantFonts'`. Cause: When `fonttools` is installed, `convert_to_pdfa` rewrites the glyph widths of the fonts used in the PDF and reads `/DescendantFonts` of every font, assuming they are all composite (Type0) fonts like the ones embedded by wkhtmltopdf. Simple fonts (Type1, TrueType, ...), such as the Arial font of `base/tests/minimal.pdf`, have no descendant fonts, so the conversion crashes. Without `fonttools` the whole block is skipped, which hides the issue. The callers (Factur-X/ZUGFeRD and ZATCA invoice PDFs) catch the exception and only log it, so such a PDF is sent without being converted to PDF/A. Fix: Only collect the descendant fonts of composite fonts. Simple fonts keep their widths in `/Widths` (or use the standard 14 font metrics) and are left untouched. (cherry picked from commit c0604707ec5db1e309b78d196a2aec2fb3e08a7f) Signed-off-by: David Tran <david.tran@tvtmarine.com>
[FIX] pos_loyalty: make the next order coupon tours robust on slow runners PosLoyaltyTour11.1 reloads the page right after the receipt "done" synchronization while it can still be in flight: the reloaded page replays the order, which stays in the cache without new_coupon_info, and the Paid filter never reads it again ("Next order coupon is missing"). The last step also read the paid order once, while the navbar spinner it waited on is cleared by whichever concurrent request ends first, so the order could still be missing from the model (TypeError reading 'new_coupon_info'). PosLoyaltyTour6 has the same reload after payment. Runbot build 225871 (subbuild 427841) failed on the first attempt; build 225816 (subbuild 427570) failed attempts 1 and 2. Wait for the synchronization to end before every reload, and make the last step wait until the paid order carries the coupon information instead of reading it once. Reproduced on core with latency injected in sync_from_ui, notify_synchronisation and read_pos_orders: the tours failed before the fix and pass after it. Signed-off-by: David Tran <david.tran@tvtmarine.com>
[FIX] test_website: make test_replace_media tour robust on slow runners After an image is replaced by another image, the step "ensure image size is displayed" was satisfied by the old Image options container, which stays mounted until the builder re-targets the new media. The next "Replace" click then acted on the detached element ("Invalid selection. Node is not part of the editable: None") and the Icon container never appeared. Runbot signature: "[18/36] Tour test_replace_media -> Step ensure icon block is displayed". The tour now marks the container of the media being replaced and waits for an Image container without that mark. That is the container of the new media, a new component since containers are keyed per element. Verified by injecting latency on /html_editor/get_image_info (+1 s): the tour passes 3/3 with the fix and fails at the same step with the same warning when the fix is reverted. Signed-off-by: David Tran <david.tran@tvtmarine.com>
[FIX] web: increase test_unit_desktop suite timeout In 19.0 the desktop unit suite runs the Hoot tests of all installed addons in one browser session (about 17,900 tests). Under runbot load the run can exceed the 3600 s overall browser_js timeout even though no test fails or hangs: 7 of 13 web-slice builds between 2026-10-13 and 2026-10-20 were cut at 95-99.8% of the suite, with 0 test failures. Raise the limit to 5400 s, the value upstream uses for a full run from 19.1 onward. Splitting the suite per sub-build as 19.1 does needs odoo.tests.CrossModule, which does not exist in 19.0. This follows upstream commit a7017b504c5c, which raised the same timeout from 3000 to 3600 for the same reason.
[FIX] account_edi_ubl_cii: prevent deferred tests crash on Community test_invoice_deferred_dates_with_delivery_date, test_invoice_deferred_dates_on_some_lines and test_invoice_deferred_dates_on_some_lines_with_delivery_date create invoice lines with deferred_start_date/deferred_end_date, but only the Enterprise addon account_accountant defines them. Without it installed, they crashed with "Invalid field" on every Community build instead of being skipped. Guard each with ensure_installed("account_accountant"), the same idiom already used by test_invoice_deferred_dates. Signed-off-by: David Tran <david.tran@tvtmarine.com> (cherry picked from commit 5e828c61ed0f222af0f195d46fb2b34e53888bc0)
[FIX] account: compare the exchange lines of the payments widget unordered _get_all_reconciled_invoice_partials unions its partial queries without an ORDER BY and the payments widget lists the exchange lines in that order, which depends on the plan PostgreSQL picks: with a merge join the rows come sorted by the join key rather than by line id, and test_reconcile_special_mexican_workflow_2 fails with "{'line_ids': [4182, 4180, 4184], ...} != {'line_ids': [4180, 4182, 4184], ...}" (reproducible locally with enable_hashjoin and enable_nestloop off). The order of those ids only feeds the list view opened from the widget, so sort both sides in the assertion helper.
[FIX] account: fix wrong test assert `TestAccountAccount.test_compute_account_type_on_code_change` was asserting a the `reconcile` field which is not present in the view when test is running with community addons only. With this commit, we now only assert the field value outside of the Form view. runbot-955454 closes odoo/odoo#294751 X-original-commit: 76ba0089989c947719a1532c9a47a6b222a2ed9f Signed-off-by: Hubert Van De Walle <huvw@odoo.com> Signed-off-by: Guillaume Vanleynseele (guva) <guva@odoo.com> (cherry picked from commit 50b51284b32960690293c5f9510e8ce6834664aa)
[FIX] survey: keep the action helper alive when the RPC is unreachable `SurveySurveyActionHelper` (shown when the survey list/kanban is empty) fetches the sample templates in `onWillStart` and lets any error through. In the offline clickbot (`TestMenusAdminLight.test_02_click_apps_menus_ as_admin_offline`) that RPC is cut and the whole Surveys app fails: Error found: - Current testing app is Surveys (survey.menu_surveys) - Currently running offline Connection to "/web/dataset/call_kw/survey.survey/get_survey_templates_data" couldn't be established or was interrupted Odoo's runbot never renders the helper during that test because the enterprise `hr_appraisal_survey` ships surveys as regular data; a community-only database without demo data has an empty list and hits it. Swallow `ConnectionLostError` only and render the helper without templates: "Start from scratch" stays available, other errors still propagate. Verified with `-i survey,web --test-enable --test-tags=/web:TestMenusAdminLight` on a community checkout without demo data: test_02 fails without this change and passes with it (2/2).