Skip to content

[IMP] base_company_dependent: extend widget to settings, ORM-mode fields and dark mode#409

Open
JrAdhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-62336-jr-cem-3
Open

[IMP] base_company_dependent: extend widget to settings, ORM-mode fields and dark mode#409
JrAdhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-62336-jr-cem-3

Conversation

@JrAdhoc

@JrAdhoc JrAdhoc commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes the multicompany-UX widget work for v19 (task #62336). On top of the Many2One-only MVP that landed earlier in 19.0:

Backend (models/base_company_dependent.py)

  • ORM-mode strategy (companion to JSONB-native) for fields whose values do not live in a JSONB column: computed fields with depends_context('company') and related fields. Auto-detected by _detect_field_strategy; can be forced from views via options=\"{'company_dependent_mode': 'orm'}\".
  • _resolve_orm_target maps res.config.settings related to company_id.* to res.company directly, and product.template.standard_price to product.product when there is a single variant (fallback for multi-variant keeps the existing inverse-on-template behaviour).
  • Company hierarchy up to 3 levels exposed to the dialog for the copy-to-children action.

Frontend

  • fields_patch.js extends the building-icon button to CharField, IntegerField, FloatField, MonetaryField, BooleanField, SelectionField and DateTimeField (was Many2One only). templates.xml provides the extended OWL templates with matching field shapes and the o_cd_fallback muted-display class.
  • settings_patch.js / settings_patch.xml patch Setting / SearchableSetting so the static fa-building-o on <setting company_dependent=\"1\"> is replaced by the interactive CompanyDependentButton. When upstream's compiler does not pass fieldName (settings whose first child is a wrapper <div>, e.g. default_taxes, default_stock_valuation_accounts, main_currency), the patch discovers the first visible .o_field_widget[name] from the rendered DOM and falls back to that field's string as label. Strict-equality check on the companyDependent prop avoids running the lookup on every setting (upstream passes the literal string \"false\" for non-CD settings, which is truthy in JS).

CSS (company_dependent.css)

  • Replace hardcoded light backgrounds (var(--bs-white), var(--bs-light), var(--bs-gray-100/200), rgba(0, 0, 0, *)) with Bootstrap 5.3 dark-aware variables (var(--bs-tertiary-bg), rgba(var(--bs-emphasis-color-rgb), *)) so the modal hierarchy tinting and hover render correctly in dark mode too.

Views

  • product_template_views.xml adds the orm-mode opt-in on product.template.standard_price.

Bump to 19.0.1.2.0 and sync README.rst.

Test plan

  • odoo-bin -d <db> -u base_company_dependent --test-enable --stop-after-init — 37 tests green (new ones cover ORM set on res.company, target resolution for res.config.settings related fields, and the non-CD is_specific fallback).
  • Open the Income Account modal on a product, set/copy/reset values across companies — JSONB path unchanged.
  • In Sales / Accounting Settings, click the building icon on a <setting company_dependent=\"1\"> whose first child is a <div> (e.g. default_taxes, main_currency, default_stock_valuation_accounts) — the modal opens for the first visible field and persists changes per company.
  • Toggle dark mode and re-open the modal — hierarchy tinting, hover, header and badges remain legible.
  • product.template.standard_price (single-variant) — open the modal, edit per company, the value is delegated to product.product JSONB.

Copilot AI review requested due to automatic review settings June 11, 2026 18:49
@roboadhoc

Copy link
Copy Markdown
Contributor

Pull request status dashboard

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR extends the company-dependent UX to support “ORM mode” for fields whose per-company behavior is not backed by a native JSONB company_dependent column (e.g., res.config.settings related-to-company fields and product.template.standard_price).

Changes:

  • Added backend support for dual persistence strategies (json vs orm) with new mode RPC kwargs and ORM-mode getters/setters.
  • Patched OWL components/templates to pass a mode into the company-dependent dialog and to replace the Settings building icon with an interactive button.
  • Added a product view opt-in for standard_price and expanded tests/docs; updated styling for Bootstrap dark-mode variables.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
base_company_dependent/views/product_template_views.xml Opts standard_price into ORM mode via field options.
base_company_dependent/tests/test_base_company_dependent.py Adds ORM-mode backend tests around _resolve_orm_target / setters / “specific” heuristic.
base_company_dependent/static/src/templates.xml Passes mode from patched fields into CompanyDependentButton.
base_company_dependent/static/src/settings_patch.xml Replaces settings static icon with CompanyDependentButton in SearchableSetting.
base_company_dependent/static/src/settings_patch.js Patches Setting to show the interactive button and discover field name when missing.
base_company_dependent/static/src/fields_patch.js Adds explicit opt-in behavior and exposes cdMode to templates.
base_company_dependent/static/src/company_dependent_dialog.js Sends mode to backend RPC calls (get/set values).
base_company_dependent/static/src/company_dependent_button.js Accepts mode, warns when no fieldName, forwards mode to dialog.
base_company_dependent/static/src/company_dependent.css Switches table/tint colors to Bootstrap dark-aware variables.
base_company_dependent/models/base_company_dependent.py Implements strategy detection, ORM-mode value load/save, and meta updates.
base_company_dependent/manifest.py Adds product dependency and installs the new view; bumps version.
base_company_dependent/README.rst Documents ORM mode, settings integration, and expanded supported field types.


@api.model
def set_company_dependent_values(self, res_model, res_id, field_name, values_dict):
def set_company_dependent_values(self, res_model, res_id, field_name, values_dict, mode=None):
Comment on lines 817 to 825
if mode is None:
try:
mode = self._detect_field_strategy(res_model, field_name)
except ValueError:
mode = "json"

if mode == "orm":
return self._set_values_orm(res_model, res_id, field_name, values_dict)
self.env["ir.model.access"].check(res_model, "write")
Comment on lines +406 to +408
for company_id_str, value in values_dict.items():
company_id = int(company_id_str)
company = self.env["res.company"].browse(company_id)
Comment on lines +200 to +206
# Related genérico: seguir la cadena
record = self.env[res_model].browse(res_id)
current = record
for part in related_parts[:-1]:
current = current[part]
if current:
return current._name, related_parts[-1], current.id
Comment on lines +208 to +215
# Computed/inverse: buscar si el campo existe como CD en un modelo
# relacionado. Caso emblemático: product.template → product.product.
if hasattr(field, "related_field") and field.related_field:
return self._resolve_orm_target(
field.related_field.model_name,
res_id,
field.related_field.name,
)
rec = self.env["res.company"].browse(company_id)
else:
rec = record.with_company(company)
write_value = False if value == "RESET" else value
self.assertEqual(target_field, company_field)
self.assertEqual(target_id, self.env.company.id)

def test_set_values_orm_res_company_branch(self):
original_c1 = self.company_1[bool_field]
original_c2 = self.company_2[bool_field]

result = self.helper._set_values_orm(
* by `invisible=...` or by `groups=...` the user doesn't belong to).
*/
_discoverFieldFromDOM() {
const el = this.settingRef?.el || this.__owl__?.bdom?.el;
/* ── Diálogo general ─────────────────────────────────────────────────────── */
.o_cd_dialog .table th {
background-color: var(--bs-light, #f8f9fa);
background-color: var(--bs-tertiary-bg);
@JrAdhoc JrAdhoc force-pushed the 19.0-t-62336-jr-cem-3 branch 2 times, most recently from 6f8e215 to 0acbce5 Compare June 11, 2026 19:53
…lds and dark mode

Backend (models/base_company_dependent.py):
* Add ORM-mode strategy (companion to JSONB-native) for fields whose values do
  not live in a JSONB column: computed fields with depends_context('company')
  and related fields. Auto-detected by _detect_field_strategy; can be forced
  from views via options="{'company_dependent_mode': 'orm'}".
* _resolve_orm_target maps res.config.settings related to company_id.* to
  res.company directly, and product.template.standard_price to product.product
  when there is a single variant. Generic related traversal restricted to
  many2one (one2many/many2many would silently pick an arbitrary record);
  related_field recursion guards on browse(...).exists() so res_id mismatches
  across models do not silently resolve to the wrong target.
* set_company_dependent_values: write access check moved before both branches
  (was missing on the ORM path); _set_values_orm additionally checks write on
  the resolved target_model and skips company_ids outside env.companies, so
  crafted RPC payloads cannot bypass record rules or cross-company guards.
* Verbose [CD ORM SET/GET] traces lowered from info to debug — they fire per
  dialog open and per company, which spams logs on multi-company setups.
* Company hierarchy up to 3 levels (parent -> child -> grandchild) exposed to
  the dialog for the copy-to-children action.

Frontend:
* fields_patch.js extends the building-icon button to CharField, IntegerField,
  FloatField, MonetaryField, BooleanField, SelectionField and DateTimeField
  (was Many2One only). templates.xml provides the extended OWL templates.
* settings_patch.js / settings_patch.xml patch Setting and SearchableSetting
  so the static fa-building-o on <setting company_dependent="1"> becomes the
  interactive CompanyDependentButton. When upstream's compiler does not pass
  fieldName (settings whose first child is a wrapper <div>, e.g. default_taxes,
  default_stock_valuation_accounts, main_currency), the patch discovers the
  first visible .o_field_widget[name] from the rendered DOM via the public
  settingRef and falls back to that field's string as label. Strict-equality
  check on the companyDependent prop avoids running the lookup on every
  setting (upstream passes the literal string "false" for non-CD settings,
  which is truthy in JS).

CSS (company_dependent.css):
* Replace hardcoded light backgrounds (var(--bs-white), var(--bs-light),
  var(--bs-gray-100/200), rgba(0, 0, 0, *)) with Bootstrap 5.3 dark-aware
  variables so the modal hierarchy tinting and hover render correctly in
  both light and dark themes. Header keeps a literal fallback in case the
  theme does not define --bs-tertiary-bg.

Views: product_template_views.xml adds the orm-mode opt-in on
product.template.standard_price.

Tests: cover ORM-mode set/get on res.company, target resolution for
res.config.settings related fields, the non-CD is_specific fallback, and
the public RPC path enforcing write access + filtering inaccessible
companies (39 tests, all green).

Bump to 19.0.1.2.0 and sync README. Reset semantics on ORM-mode targets
documented: when the resolved target is a plain res.company column (no JSONB),
reset == write the type's falsy value because there is no per-company key
to remove.

Task-id: 62336
@JrAdhoc JrAdhoc force-pushed the 19.0-t-62336-jr-cem-3 branch from 0acbce5 to 2ea10f7 Compare June 11, 2026 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants