Pending: 0 Building: 0 Running: 0 Failed: 86
Created Date Type Name Commit Description State Age Up Time Life Time Action
merged [FIX] viin_brand_pos: Fix branding issues in customer facing display [FIX] viin_brand_pos: Fix branding issues in customer facing display Killed
merged [FIX] viin_brand_website, viin_backend_theme: one click into the backend, and type to search [IMP] viin_brand_website: one click from the site into the backend Clicking the apps grid on the website used to open a dropdown listing the internal user's apps, so reaching the backend cost an extra click every time. The button now links straight to /odoo and no longer toggles anything. /odoo can take a noticeable moment on an install with many modules, so the icon swaps to a spinner on click: the user can see the click registered. A Ctrl/Cmd/Shift or middle click opens the backend in a new tab and leaves the current page's icon alone, and a Back-button restore from the browser's cache puts the icon back rather than leaving it spinning for good. That spinner is wired by a delegated listener on the document, not by a public Interaction. It was written as an Interaction first and it never ran: the interaction service scans from #wrapwrap, and this button renders outside it, under body > .o_frontend_to_backend_nav > .o_frontend_to_backend_buttons. Core drives this same bar from a plain frontend module for the same reason. Both click and auxclick are bound, because a non-primary click fires the latter and core treats the two as a pair. The spinner is a dedicated child element rather than classes on the anchor, and that distinction is the whole of the difference. Core renders that anchor as both the icon and a styled flex box, so a spin class on it rotates the box - background, padding and all - which is what it did at first. Every pending indicator in Odoo 19 puts fa fa-circle-o-notch fa-spin on an element whose only job is to be the spinner, and this now does the same. Measured with the animation frozen at one angle: the old anchor's bounding box grew from 48x54 to 72x72 as the box turned; the anchor now holds 48x54, identical to an untouched button, and only the glyph inside it moves. The tour asserts the anchor carries no spin class, so putting one back there fails loudly instead of passing. Core's own dropdown markup beside the button is left exactly as core renders it, including the ir.ui.menu lookup it performs on every frontend page. Taking it out looked like a free win and was tried twice; core's website_backend_menus_redirect tour opens that dropdown and clicks an entry inside it, and the entry only exists when the lookup runs. The de-branded title on the button is unchanged. Tests: the click, modifier and cache-restore behaviour are covered on a real DOM rather than by matching the text of an inline handler, which an implementation with the transitions swapped passed just as happily. A tour drives the button on a real rendered page and asserts both that the icon swaps and that the button is NOT inside #wrapwrap, so if a future Odoo moves it the guard fails loudly instead of passing for the wrong reason - a unit test cannot make that assertion, because the test helper fabricates a #wrapwrap of its own, which is precisely why nine green unit tests hid a feature that did not work. Bundle guards assert the module really reaches the browser. Also here, and separable from the behaviour: comments in this module's tests cited source by line number. Each now names the symbol instead. One had already rotted badly, naming a method while pointing fifteen lines above its real definition. Killed
merged [FIX] viin_brand_website, viin_backend_theme: one click into the backend, and type to search [IMP] viin_brand_website: one click from the site into the backend Clicking the apps grid on the website used to open a dropdown listing the internal user's apps, so reaching the backend cost an extra click every time. The button now links straight to /odoo and no longer toggles anything. /odoo can take a noticeable moment on an install with many modules, so the icon swaps to a spinner on click: the user can see the click registered. A Ctrl/Cmd/Shift or middle click opens the backend in a new tab and leaves the current page's icon alone, and a Back-button restore from the browser's cache puts the icon back rather than leaving it spinning for good. That spinner is wired by a delegated listener on the document, not by a public Interaction. It was written as an Interaction first and it never ran: the interaction service scans from #wrapwrap, and this button renders outside it, under body > .o_frontend_to_backend_nav > .o_frontend_to_backend_buttons. Core drives this same bar from a plain frontend module for the same reason. Both click and auxclick are bound, because a non-primary click fires the latter and core treats the two as a pair. Core's own dropdown markup beside the button is left exactly as core renders it, including the ir.ui.menu lookup it performs on every frontend page. Taking it out looked like a free win and was tried twice; core's website_backend_menus_redirect tour opens that dropdown and clicks an entry inside it, and the entry only exists when the lookup runs. The de-branded title on the button is unchanged. Tests: the click, modifier and cache-restore behaviour are covered on a real DOM rather than by matching the text of an inline handler, which an implementation with the transitions swapped passed just as happily. A tour drives the button on a real rendered page and asserts both that the icon swaps and that the button is NOT inside #wrapwrap, so if a future Odoo moves it the guard fails loudly instead of passing for the wrong reason - a unit test cannot make that assertion, because the test helper fabricates a #wrapwrap of its own, which is precisely why nine green unit tests hid a feature that did not work. Bundle guards assert the module really reaches the browser. Also here, and separable from the behaviour: comments in this module's tests cited source by line number. Each now names the symbol instead. One had already rotted badly, naming a method while pointing fifteen lines above its real definition. Killed
merged [FIX] viin_brand_website, viin_backend_theme: one click into the backend, and type to search [FIX] viin_brand_website: wire the apps button off the DOM it is in The apps-button spinner never ran on a real page. It was written as a public Interaction, and the interaction service only ever scans from #wrapwrap, but the button sits outside it - it is a `body > .o_frontend_to_backend_nav > .o_frontend_to_backend_buttons > a`, a #wrapwrap SIBLING, never a descendant. So the interaction never started, the icon never swapped on click, and a Back-button restore never reset it. Replace it with a delegated click/auxclick listener bound on the document at load time, which does not care where the button lives. The three functions that decide what happens on click are unchanged. The nine unit tests over the old Interaction had all been passing for the wrong reason: the Hoot fixture helper wraps bare markup in a #wrapwrap of its own, so the suite was exercising the button in a position it never occupies on the real page. Those tests now mount the fixture directly against the new listener, and a tour drives the real rendered homepage - asserting both that the click swaps the icon and that the button is NOT inside #wrapwrap, so a future core move that reintroduces the mismatch fails loudly instead of passing for the wrong reason again. Failed
open [REF] viin_brand cluster: make each module installable alone [MISC] viin_brand_spreadsheet_dashboard: guard tours on web_tour Tests that boot the webclient via start_tour required the tour engine unconditionally, so they errored instead of skipping under the acceptance CLI's --skip-auto-install flag, which excludes the web_tour/bus auto-install closure. They now probe res.users for the tour_enabled field in setUp and skip cleanly with a stated reason when web_tour is not installed, so a per-module acceptance run stays readable instead of burying a genuine failure under environment-driven errors. A bundle-compile test is added, compiling this module's real asset bundles and asserting on the compiled output. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
open [REF] viin_brand cluster: make each module installable alone [MISC] pyproject: pin the repo's first ruff lint configuration Ruff was running fully unconfigured against this repository. A repo-root pyproject.toml now establishes an explicit configuration, pinning today's implicit rule set so a future ruff upgrade cannot silently change what is enforced. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [FIX] viin_brand_web: resolve dark mode from the user, not the browser [FIX] viin_brand_web: resolve dark mode from the user, not the browser Two reported symptoms, one root cause: the code treated the `color_scheme` cookie as if it were a preference, when it is a cache of a resolution. A preference belongs to a USER; this cookie belongs to a BROWSER and outlives the session that wrote it. Symptom 1 - another person's cookie decided your scheme. color_scheme() ranked the cookie above the stored preference, so on a shared browser the last user's choice served the next user the wrong compiled bundle. Measured before the change: preference 'light' plus a `color_scheme=dark` cookie rendered data-bs-theme="dark", with the dark bundle referenced. Symptom 2 - "System" always came up light. setScheme('auto') writes the matchMedia-resolved scheme to the cookie, then awaits an ORM write whose own handler expired that very cookie. Measured: the write response carried `color_scheme=; Max-Age=0; Path=/`, the host-only cookie left the jar, and the reload that follows then found no cookie, fell through to core's `return "light"`, and served light whatever the device said. The client re-resolved afterwards, so the user saw light, a half-flip, and dark only on the next reload. The resolution order is now the user's own explicit light/dark preference, then the cookie, then super(). The cookie keeps its place above super() because 'auto' is the one case the server genuinely cannot decide: it cannot read the device preference, so the client resolves it and caches the effective light|dark there. For the same reason writing 'auto' no longer expires the cookie - that would throw away the only copy of the answer the imminent reload needs. That cookie is also core's own client-side source of truth for dark mode: core reads it for graph colours, the colour picker, the ace editor and the pdf.js viewer. Expiring it did not merely mis-pick a bundle, it left every one of those drawing light-mode colours inside a dark UI. Two tests pinned the old behaviour and are replaced, not loosened. One asserted the cookie "overrides everything"; it now asserts an explicit preference survives a contrary cookie, with a sibling covering the 'auto' case where the cookie is authoritative. The other asserted the switch to 'auto' expires the cookie, citing the old precedence as its reason; it now asserts the cached resolution is left alone. Both were protecting a defect, so changing them is a corrected contract, not an expectation bent to match output. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
open [REF] viin_brand cluster: make each module installable alone [MISC] pyproject: pin the repo's first ruff lint configuration Ruff was running fully unconfigured against this repository. A repo-root pyproject.toml now establishes an explicit configuration, pinning today's implicit rule set so a future ruff upgrade cannot silently change what is enforced. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [FIX] dark mode: readable pivot/graph, dark spreadsheet and dashboard chrome, calmer Discuss seams [FIX] viin_backend_theme: dedupe the active marker in the Appearance menu The Appearance systray menu's active Theme row and active Density row each drew two selected-markers: core's own `.dropdown-item.active::before` FontAwesome checkmark painted on top of this component's own right-aligned check icon, so the active choice showed a double mark instead of one. Core's marker is suppressed on both DropdownItem loops with core's own opt-out class `dropdown-item_active_noarrow`, leaving the component's own check as the single indicator. mail's action list already reuses that class for the identical situation, so this reuses an existing core lever instead of inventing new CSS. A tour-driven HttpCase asserts the rendered result - the active row's computed `::before` is 'none' and exactly one visible `.fa-check` remains inside it - in both the light and the dark colour scheme, so the contract is pinned on render output rather than on markup. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [FIX] dark mode: readable pivot/graph, dark spreadsheet and dashboard chrome, calmer Discuss seams [FIX] viin_backend_theme: dedupe active marker in Appearance menu The Appearance systray menu's active Theme row and active Density row each drew two selected-markers: core's own `.dropdown-item.active::before` FontAwesome checkmark painted on top of this component's own right-aligned check icon, so the active choice showed a double mark instead of one. Suppressed core's marker on both DropdownItem loops with core's own opt-out class `dropdown-item_active_noarrow`, keeping the component's own check as the single indicator. mail's action list already reuses this same class for the identical situation, so this reuses an existing core lever instead of inventing new CSS. Added a tour-driven HttpCase asserting the rendered result - the active row's computed `::before` is 'none' and exactly one visible `.fa-check` remains inside it - in both light and dark colour scheme, so the contract is pinned on render output rather than on markup. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged Fwd from 18 to 19 20260912 01 Merge remote-tracking branch 'Viindoo/18.0' into 19.0 Killed
merged [FIX] viin_brand_mail: fix chatter contrast in light and dark schemes [FIX] viin_brand_mail: fix chatter contrast, harden dark edited marker DARK: the mail chatter embedded in the Odoo 19 spreadsheet editor's comments side panel was unreadable. Odoo keeps the spreadsheet a light island inside a dark app (its dark-bundle-only o_spreadsheet_extended.dark.scss forces .bg-white back to white and pins .btn to a dark label), but core repaints only the button label, not the fill, leaving half of every pair in the wrong colour scheme. Finish that light island for the embedded chatter instead of darkening it, scoped entirely under .o-spreadsheet. Measured live at 1440px in dark mode, before -> after: Log note 1.19 -> 7.92, Activity 1.19 -> 7.92, Send message 2.17 -> 8.24, author name 2.63 -> 10.31, timestamp 2.59 -> 6.11, message body -> 9.25. The same two message nodes the island fix above touches - the empty-message placeholder (3.37:1 worst) and the (edited) marker (4.04:1 worst) - were unreadable everywhere OUTSIDE that island too, because core dims both a second time on top of a tier already tuned to clear AA once. Fix them product-wide instead of leaving the island as the one readable spot: every form-view chatter, the Discuss thread canvas and every chat window, on the panel and on all three message-bubble tints. With that rule in place the island's own placeholder correction became a pure duplicate of it and was dropped. The island's (edited) rule is kept and hardened with its own colour, because the product-wide fix lands that marker on the dark metadata tier, which measures 2.34-2.59:1 on the island's light bubbles - too dark for that ground. LIGHT: the mail message muted tier renders below AA wherever it lands - 4.06:1 on a message bubble, 4.20:1 on the chatter panel - and two nodes are dimmed further by opacity utilities to 2.7:1 and 2.4:1. The shared $text-muted token is deliberately left untouched: it also paints off/disabled affordances that WCAG exempts, and repointing it would change roughly 380 nodes across 205 templates. Re-point the tier for mail message surfaces only. After: 5.48-6.11 across the panel and all bubble variants. Dark stays provably unaffected, since the light rules compile to zero bytes in the dark bundle. Also drop a rule that went dead once the comments panel lost its background utility - it was this module's only reference to another repo's class. A comment on the read-conversation muted tier said its --secondary-color token "does not flip" in dark - true only while $o-main-color-muted and $body-secondary-color were two independent Sass values. dark_palette.scss has since aliased the former onto the latter, so both names now resolve to the one tier this rule already reads: the rule was right, the explanation had gone stale. Reworded to name the tier through its own alias chain instead of restating a divergence that no longer exists. Guard every case above with tests that assert computed WCAG ratios, never colour values. DARK EDITED MARKER, HARDENED: the product-wide dark rule just added above (.o-mail-Message-edited .opacity-50 in mail_dark.scss) read the bare Bootstrap Sass global $text-muted, which has no !default guard, so any module sharing the web.assets_web_dark compile can reassign it from under this one. Core's hr_skills does exactly that. hr_skills/__manifest__.py:53 wildcard-globs 'hr_skills/static/src/scss/*.scss' into web.assets_backend, separately from the intended web.report_assets_pdf entry on line 68; that glob also catches report_employee_cv.scss, whose line 3 hard-assigns $text-muted: #3b4757 with no !default; and the bundle chain web.assets_web_dark -> web.assets_web -> web.assets_backend carries that value in. So whenever hr_skills is installed alongside the branding modules (always true on Runbot, which installs the whole repo) the marker compiled to the wrong colour and failed WCAG AA contrast (~1.3-2.0:1, needs >= 4.5:1). A one-variable install-topology experiment proved it: same commit, same suite - the three branding modules installed alone compiled $text-muted to #8EA5A8 and passed all 147 tests; adding hr_skills to the install set, nothing else changed, compiled it to #3b4757 and failed 12 subtests. That asymmetry is exactly why the branch stayed green on a local, branding-only run while Runbot - which always installs the whole repo - was red on the same code. Re-point the rule to var(--secondary-color) - the CSS custom property Bootstrap emits from $body-secondary-color (a different Sass variable, hard-assigned in dark_palette.scss:76 with no !default, which hr_skills' write never touches) - matching an existing pattern already used twice in this file (:56 and :245) for the analogous chatter-timestamp / notification-item cases. Updated the neighbouring comment to match, since it previously explained the value in terms of $text-muted. Added a regression guard, test_dark_edited_marker_stays_readable_when_another_module_hijacks_text_muted, that splices hr_skills' exact clobbering literal into the real web.assets_web_dark bundle source and recompiles through Odoo's own AssetsBundle.compile_css / ScssStylesheetAsset.compile (libsass), proving the rule survives a hijacked $text-muted regardless of which modules happen to be installed on whatever DB runs this module's own suite (no cr.commit(), no real hr_skills install needed). Verified RED before (12 genuine assertion failures on a live instance with hr_skills installed) and GREEN after (0 failed, 0 errors across 187 tests in viin_backend_theme + viin_brand_web + viin_brand_mail, same instance, hr_skills genuinely installed). Also fixes a flake8 E303 (too many blank lines) left behind while extending the compiled-CSS test suite above. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [FIX] viin_brand_mail: fix chatter contrast in light and dark schemes [FIX] viin_brand_mail: fix chatter contrast in light and dark schemes DARK: the mail chatter embedded in the Odoo 19 spreadsheet editor's comments side panel was unreadable. Odoo keeps the spreadsheet a light island inside a dark app (its dark-bundle-only o_spreadsheet_extended.dark.scss forces .bg-white back to white and pins .btn to a dark label), but core repaints only the button label, not the fill, leaving half of every pair in the wrong colour scheme. Finish that light island for the embedded chatter instead of darkening it, scoped entirely under .o-spreadsheet. Measured live at 1440px in dark mode, before -> after: Log note 1.19 -> 7.92, Activity 1.19 -> 7.92, Send message 2.17 -> 8.24, author name 2.63 -> 10.31, timestamp 2.59 -> 6.11, message body -> 9.25. The same two message nodes the island fix above touches - the empty-message placeholder (3.37:1 worst) and the (edited) marker (4.04:1 worst) - were unreadable everywhere OUTSIDE that island too, because core dims both a second time on top of a tier already tuned to clear AA once. Fix them product-wide instead of leaving the island as the one readable spot: every form-view chatter, the Discuss thread canvas and every chat window, on the panel and on all three message-bubble tints. With that rule in place the island's own placeholder correction became a pure duplicate of it and was dropped. The island's (edited) rule is kept and hardened with its own colour, because the product-wide fix lands that marker on the dark metadata tier, which measures 2.34-2.59:1 on the island's light bubbles - too dark for that ground. LIGHT: the mail message muted tier renders below AA wherever it lands - 4.06:1 on a message bubble, 4.20:1 on the chatter panel - and two nodes are dimmed further by opacity utilities to 2.7:1 and 2.4:1. The shared $text-muted token is deliberately left untouched: it also paints off/disabled affordances that WCAG exempts, and repointing it would change roughly 380 nodes across 205 templates. Re-point the tier for mail message surfaces only. After: 5.48-6.11 across the panel and all bubble variants. Dark stays provably unaffected, since the light rules compile to zero bytes in the dark bundle. Also drop a rule that went dead once the comments panel lost its background utility - it was this module's only reference to another repo's class. A comment on the read-conversation muted tier said its --secondary-color token "does not flip" in dark - true only while $o-main-color-muted and $body-secondary-color were two independent Sass values. dark_palette.scss has since aliased the former onto the latter, so both names now resolve to the one tier this rule already reads: the rule was right, the explanation had gone stale. Reworded to name the tier through its own alias chain instead of restating a divergence that no longer exists. Guard every case above with tests that assert computed WCAG ratios, never colour values. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [FIX] viin_backend_theme, viin_brand_{web,mail,pos}: stop breaking core Odoo 19 [FIX] viin_backend_theme: restyle core without rewriting it A theme may change how Odoo looks. This one had begun changing what Odoo says, what it does, and how it describes itself to assistive technology - and runbot batch 223955 was where that came due. The theme case-transformed two surfaces it does not own: core's command-palette category block and the list view's column headers. Rendered text is what Odoo's own automated checks, tour assertions, screen readers and a user's copy-paste all read back, so a purely cosmetic rule turned 102 core checks red at once ("Command#" read back as "COMMAND#"). Core already presents the palette's category label in capitals itself, so ours added nothing. It was also a defect users could see: the class it targeted is the block holding every command row, not the label, so its muted colour, 11px size, weight and letter-spacing were inherited by every row in the palette. Aiming the rule at the label alone restores the rows to core's typography. Odoo ends every font stack it ships with two safety nets: a small Noto subset for characters a system font lacks or renders badly, and the four emoji families. This theme replaced Odoo's stacks with its own and dropped both nets from headings and from body text, so an emoji or an uncommon script in ordinary backend text - a customer name, a chatter message, a product label - could land on a system with nothing able to draw it and render as an empty box. Both stacks are whole again and now end identically. The brand heading font was also reaching into the rich-text editor, where an outgoing email body inlines whatever font it is shown in, handing the recipient a Viindoo face their mail client does not have and which is not even vendored yet. Email bodies go back to the portable system stack; the backend keeps Montserrat. Clicking the navbar apps icon threw away whatever the user was working on. The icon opened the home menu as a full page, so an open record, a half filled form or a kanban the user had just filtered was torn down and had to be rebuilt on the way back. Odoo's own contract for that icon is that it shows the app list without navigating, and a large part of Odoo's test suite is written on that assumption: 57 test files click it, some to pick an app, others merely in passing before carrying on with the screen they were already on. The latter could not work here at all, and the former could not either, because our tiles were not the kind of element those tests look for. The icon now shows the same full-screen home menu over the untouched view. Pressing it again puts it away and the user is back where they were - instantly, because nothing was ever destroyed - and so does clicking the page behind it or pressing Escape, which the full-page version offered no way to do. The design is unchanged: one icon, one home menu, and the landing page shown at sign-in is untouched. App tiles are now real links, as they are in stock Odoo, so they can be opened in a new tab, copied, or announced sensibly. They had carried an explicit role telling a screen reader each tile is an item in a list, never that it is something the user can follow; because an explicit role replaces an element's own rather than adding to it, the tiles were also absent from links navigation, one of the main ways people using a screen reader move through a page. That role is gone, and the grid's own list role went with it, since a list is required to contain list items and keeping one alone would have left an empty, malformed list rather than a fix. Wrapping each tile to keep both was rejected: the tiles are laid out directly by the grid and are the elements the drag-to-reorder handles, so a wrapper breaks the layout and the reordering unless hidden by a CSS trick with its own record of removing elements from the accessibility tree. Nothing on screen changes, and keyboard navigation and drag-to-reorder are unaffected. Guards now fail the build if this theme case-transforms anything it did not author, if either font stack loses its fallback, or if a tile stops being announced as a link; the font guard reads the value the browser is actually handed rather than the source. One existing check was inverted rather than weakened: it asserted the previous view was gone after clicking the apps icon, which is the behaviour being fixed, and now asserts the view survives. Owner decisions D1, D2 and D3, 2026-08-17. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged V19 fix imp viin brand cluster [FIX] viin_backend_{web,theme}: remove redundant tests Killed
merged V19 fix imp viin brand cluster [FIX] viin_backend_theme: remove redundant tests Failed
merged V19 fix imp viin brand cluster [IMP] viin_brand_web: remove border-radius override and link override Failed
merged [REF] viin_brand_common: split into viin_brand_web + viin_brand_base_setup [FIX] viin_brand_base_setup, viin_brand_web: split the catalogue to follow the artefacts A stale #: reference silently DROPS the translation - the loader builds ir_model_data keys straight out of the occurrence string, finds no row, and skips the entry with no error and nothing red in any test suite. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [FWD] forward-port 18.0 -> 19.0 (0dafec5..467ac60, 9 commits) [FIX] viin_brand_website: brand the websites a field default cannot reach Removing the x_icon QWeb override was right - it resolved to website's own producer and destroyed it, so a configured favicon could never be served. But that override was also, incidentally, what made unconfigured websites look branded: it forced the brand path at render time for every website regardless of the stored field. With it gone the stored value renders, and for two populations that value is still Odoo's icon - website.default_website, which core creates while loading the website module, before this module's field extension exists, and any website predating this module's install. A field default fires only at record creation, so it reaches neither. So on every existing 19.0 database this branch, as it stood, silently swapped the Viindoo favicon for Odoo's. Reported by Codex on PR #664. Corrected once, from two entry points that share one implementation: post_init_hook for a fresh install, migrations/0.1.2 for a database where the module is already installed. Both rewrite ONLY records byte-identical to core's own default - those are provably nobody's choice - and leave every configured favicon exactly as it is, which is the whole point of having removed the override. Verified on clean databases: a fresh install and an upgrade both end with the default website branded, while a website whose owner set its own favicon keeps it byte-for-byte. UntouchedFaviconsGetBrandedTest locks both halves - doing nothing fails one assertion, overwriting everything fails the other. Failed
merged [FWD] forward-port 18.0 -> 19.0 (0dafec5..467ac60, 9 commits) [FWD] forward-port 18.0 -> 19.0 (0dafec5..467ac60, 9 commits) Absorbs every commit that landed on 18.0 since the last forward-port, so the merge-base advances and none of them is reconsidered next run. Three carry real behaviour to 19.0: * viin_brand_website no longer replaces website.layout's x_icon producer. That xpath resolved to core's own producer and destroyed it, so a customer who configured their own favicon could never serve it. Branding now comes from a field default in models/website.py: a new website starts branded, and the customer's value wins the moment they set one. Locked by tests/test_website_favicon_not_preempted.py. * viin_backend_theme guards _loadDefaultApp before diverting to the home menu, so an environment with a cleaned action registry falls back to the stock default app instead of throwing during boot. * viin_brand_website_sale drops the unverifiable "#1" ranking claim from the eCommerce promo, in the template and both translation catalogs. The rest are absorbed without adapt because 19.0 already moved past them: web_responsive is gone entirely, replaced by viin_backend_theme; QUnit is replaced by Hoot; the web.layout title/favicon fallback was deliberately re-homed onto that template's callers and must not be restored. One auto-merge carried a symbol that no longer exists here: the window-title branding arrived guarded by session.viin_brand, a marker stamped by an ir.http.session_info override 19.0 does not have, which would have left the title permanently unbranded with no error. Reverted to the 19.0 version. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [19.0][UPG] viin_brand_*: upgrade to 19 [FIX] viin_backend_theme: realign radius invariant to owner decision D5 viin_brand_common's brand_variables.scss restores a pre-19 square-corners token ($o-border-radius: 0) in the previous commit; this file's cluster-wide "zero radius overrides anywhere" invariant predates that restore and read it as the exact regression it exists to catch. Owner decision D5 keeps the restore, so the guard is updated to treat that single base-rung declaration as a pinned, SSOT-read exception (-sm/-lg remain unchanged and must still equal core's own scale exactly) instead of a violation. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [19.0][UPG] viin_brand_*: upgrade to 19 [UPG] viin_brand*: upgrade to version 19.0 Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed Not finished
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [REM] web_responsive: drop OCA responsive backend (superseded by viin_backend_theme's native mobile/rail redesign) Was the legacy to_backend_theme's dependency; nothing else in the repo/tvtmaaddons/core depends on it. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [NEW] viin_backend_theme: Odoo 19 CE backend theme (Viindoo redesign), additive on the branding base Purely-additive backend redesign on top of viin_brand_common + viin_brand_mail: instant OPT-IN dark mode (native [data-bs-theme], per-user choice - public pages and the default state stay LIGHT), the always-dark vertical rail + flat "Applications" home menu, form chrome + D6 statusbar->stepper, list/kanban + bulk bar, split-screen login, overlays/skeleton/empty-states, mobile bottom-nav + density toggle, appearance systray, Montserrat/Roboto typography. ZERO --viin-* custom properties; native $o-* / Bootstrap levers + the cluster teal SSOT only; auto_install so the de-branded redesign is the default on every Viindoo 19 DB. Includes the W5 durable test layer (Hoot: theme service + D6 stepper; HttpCase tours: dark toggle, home menu, density, mobile bottom-nav), the full dark-mode AA-contrast pass (form/dialog/grouped list/kanban/pivot/calendar/search-panel/mail menus/chatter re-pointed to the runtime scheme props; Bootstrap .text-* utilities relit in dark; light mode untouched), and the vi_VN translation. Signed-off-by: David Tran <david.tran@tvtmarine.com> Killed Not finished
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [NEW] viin_backend_theme: Odoo 19 CE backend theme (Viindoo redesign), additive on the branding base Purely-additive backend redesign on top of viin_brand_common + viin_brand_mail: instant OPT-IN dark mode (native [data-bs-theme], per-user choice - public pages and the default state stay LIGHT), the always-dark vertical rail + flat "Applications" home menu, form chrome + D6 statusbar->stepper, list/kanban + bulk bar, split-screen login, overlays/skeleton/empty-states, mobile bottom-nav + density toggle, appearance systray, Montserrat/Roboto typography. ZERO --viin-* custom properties; native $o-* / Bootstrap levers + the cluster teal SSOT only; auto_install so the de-branded redesign is the default on every Viindoo 19 DB. Includes the W5 durable test layer (Hoot: theme service + D6 stepper; HttpCase tours: dark toggle, home menu, density, mobile bottom-nav), the full dark-mode AA-contrast pass (form/dialog/grouped list/kanban/pivot/calendar/search-panel/mail menus/chatter re-pointed to the runtime scheme props; Bootstrap .text-* utilities relit in dark; light mode untouched), and the vi_VN translation. Signed-off-by: David Tran <david.tran@tvtmarine.com> Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [FIX] viin_backend_theme: complete dark-mode AA coverage (opt-in, backend) + keep public login light Dark mode is a LOGGED-IN per-user choice applied to the backend only; public pages and the default state stay LIGHT. Two parts: 1. Public/default-light: remove login.scss's `@media (prefers-color-scheme: dark)` block - the PUBLIC pre-auth login must not darken from the visitor's OS. The login now always renders the light split-screen. Guard test_public_login_never_os_darkens bans re-adding it. 2. Complete the backend dark-mode AA coverage the earlier commit started, all dark-scoped ([data-bs-theme=dark]) so light mode is untouched. Odoo 19 CE core dark mode leaves many surfaces on compiled light values that never scheme-flip; re-point them to the runtime scheme props at the layer each is read: - Backgrounds via the core CSS var (fixes the whole family at once): grouped-kanban --Kanban-background/--KanbanGroup-background; form o2m list --ListRenderer-thead/tfoot-bg-color; breadcrumb --breadcrumb-bg; a GLOBAL .bg-view --background-color flip (view-surface utility on ~14 surfaces incl. search panel + calendar container + inputs) - beats .bg-view's !important WITHOUT an !important war since re-pointing its own local var; messaging systray --mail-MessagingMenu-bg (core set it light in its OWN dark stylesheet); search-panel list-group. - Text utilities compiled light + !important: .text-body/.text-muted/.text-900 relit WITH !important (sparing .btn-light/.btn-secondary light chips); --body-color-rgb flipped for dark. - Per-surface: search-panel active row (core hardcodes .text-black), field-selector chain part, pivot cells/headers (beat core's o-hover-text-color mixin), chatter header, command-palette footer, settings form (--settings__* vars + .settings bg + section titles). - Calendar: re-point --fc-* tokens on the .fc element itself (FullCalendar declares them there, so an ancestor rule loses) -> dark grid + readable day names/dates. - .o_web_client color-scheme: correct core's invalid 'bright' ident to light/dark per the app toggle (fixes native scrollbars/controls + the getComputedStyle skew). REVERTED a mis-step from the prior iteration: a global .text-success/.text-info dark flip that, being unscoped, lost contrast on light surfaces (selected rows/subtle alerts) - core's marginal ~3.7-3.9:1 values are accepted known-minors (like white-on-color badges), not regressed. Live getComputedStyle-verified (the review browser runs force-dark, so screenshots are unreliable): all primary daily screens (home/list/form/kanban/pivot/settings/dialog/dropdowns/command-palette/ search-panel/activities+messages menus/calendar/chatter) are AA-clean in dark, regression reverted, 0 console errors, light mode unchanged. ~24 source-invariant guards (TestReviewA11yFixes) protect the intent; 52/52 theme tests green (Python /viin_backend_theme + Hoot bracket). Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [FIX] viin_backend_theme: dark-mode AA contrast (form, dialog, grouped list, kanban, text utilities) A live acceptance review found several dark-mode surfaces still rendering compiled light-mode colors that do not scheme-flip. Re-point them (dark-scoped, so light mode is untouched) to the runtime scheme props, at the correct layer: * Form: record title / headings -> var(--emphasis-color); field values/inputs -> var(--body-color) (core .oe_title uses a compiled $headings-color hex). * Light form labels: brand-purple leak -> neutral var(--secondary-color) (grey, matches the mockup and the dark label tier). * Teal FOREGROUND on dark (nocontent link, bulk-selection count, active notebook tab, statusbar done glyph, many2one o_form_uri links): compiled #007F8E (3.69:1) -> lighter dark teal #4FD4E2 (~9.9:1). Teal fills + the rail accent untouched. * Dialog titles + any var-based heading: flip --heading-color -> var(--emphasis-color) at the scheme engine (core $headings-color-dark is 'inherit', never flips). * Grouped-list header + the Bootstrap .text-body/.text-muted utilities: these are compiled with light values + !important into the dark bundle, so relit with !important in dark (.text-muted spares .btn-light/.btn-secondary chips); also fix the stale --body-color-rgb in the dark scheme. * Overdue/rotting kanban card: core's static light-pink bg -> a dark-red color-mix so its text is readable in dark (light mode keeps the pink). * .o_web_client color-scheme: core's invalid 'bright' ($o-webclient-color-scheme) -> light/dark per the app toggle, so native scrollbars/controls match the scheme. All light-on-light / dark-on-light-chip cases verified non-regressed; light mode unchanged. 13 source-invariant guards added (TestReviewA11yFixes). Live re-verify: form/dialog/list/kanban/labels/links all clear AA in dark; 36/36 theme tests green. Failed
merged [UPG] branding-base cluster (5 modules) to Odoo 19.0 + de-brand/teal SSOT [NEW] viin_backend_theme: W5 durable test layer (Hoot + HttpCase tours) Adds the behavior-guard test layer for the theme, wired into web.assets_unit_tests (Hoot) and web.assets_tests (tours), driven by tests/test_tours.py: * Hoot - viin_theme service: setScheme dark/light flips documentElement data-bs-theme + persists the color_scheme cookie; setDensity compact flips data-viin-density + viin_density cookie; 'auto' resolves via a mocked matchMedia and re-resolves on OS flip. * Hoot - StatusBarField (D6) stepper: getStepInfo maps a mid-pipeline record to done/current/upcoming markers; clicking a stage still fires core selectItem -> web_save (presentation-only patch must not break click-to-change). * HttpCase tours: instant dark toggle + reload persistence; density compact + reload persistence (single-session location.reload, expectUnloadPage); home-menu fuzzy search + keyboard launch; mobile ViinBottomNav single-chrome + slot navigation (375x667 touch). All assert observable state (RED-on-behavior-removal); no assertion is a code snapshot. 23/23 green on 19.0 CE (17 Python/tours + 6 Hoot). Failed