|
| 1 | +# Copyright 2026 Camptocamp SA |
| 2 | +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) |
| 3 | + |
| 4 | +from logging import getLogger |
| 5 | + |
| 6 | +from odoo import api, fields, models |
| 7 | +from odoo.orm.domains import Domain |
| 8 | +from odoo.tools.misc import OrderedSet |
| 9 | + |
| 10 | +_logger = getLogger(__name__) |
| 11 | + |
| 12 | + |
| 13 | +class MulticompanyReportingCurrencyMixin(models.AbstractModel): |
| 14 | + """Abstract mixin for models that use multicompany reporting currency""" |
| 15 | + |
| 16 | + _name = "multicompany.reporting.currency.mixin" |
| 17 | + _description = "Multicompany Reporting Currency Mixin" |
| 18 | + |
| 19 | + multicompany_reporting_currency_id = fields.Many2one( |
| 20 | + "res.currency", |
| 21 | + default=lambda self: self._get_default_multicompany_reporting_currency_id(), |
| 22 | + readonly=True, |
| 23 | + ) |
| 24 | + |
| 25 | + @api.model |
| 26 | + def _get_default_multicompany_reporting_currency_id(self): |
| 27 | + # Default to the sys param multicompany reporting currency value |
| 28 | + return self._get_multicompany_reporting_currency_from_sys_param() |
| 29 | + |
| 30 | + @api.model |
| 31 | + def _get_multicompany_reporting_currency_from_sys_param(self): |
| 32 | + """Retrieves the currently configured multicompany reporting currency |
| 33 | +
|
| 34 | + Hook method, can be overridden by inheriting models |
| 35 | + """ |
| 36 | + currency = self.env["res.currency"] |
| 37 | + |
| 38 | + # We try to retrieve the multicompany reporting currency from the system params, |
| 39 | + # but ``get_param(key)`` will return either ``None`` or a ``str`` object; since |
| 40 | + # we cannot be 100% sure we'll be able to convert it to a ``res.currency`` |
| 41 | + # record ID, we use the user's environmental company currency as fallback |
| 42 | + key = "base_multicompany_reporting_currency.multicompany_reporting_currency" |
| 43 | + if value := self.env["ir.config_parameter"].sudo().get_param(key, default=""): |
| 44 | + try: |
| 45 | + currency = currency.browse(int(value)).exists() |
| 46 | + except ValueError: # pylint: disable=except-pass |
| 47 | + pass |
| 48 | + if not currency: |
| 49 | + currency = self.env.company.currency_id |
| 50 | + _logger.warning( |
| 51 | + "Could not get multicompany reporting currency from system" |
| 52 | + f" parameters, using user's company currency '{currency.name}'" |
| 53 | + ) |
| 54 | + return currency |
| 55 | + |
| 56 | + @api.model |
| 57 | + def _update_multicompany_reporting_currency(self): |
| 58 | + """Updates the multicompany reporting currency on all inheriting models""" |
| 59 | + for model in self._get_multicompany_reporting_currency_inheriting_models(): |
| 60 | + # We use the sudo-ed model because this is an automation, and we |
| 61 | + # cannot predict whether the current user has read/write access |
| 62 | + # on all inheriting models |
| 63 | + model = model.sudo() |
| 64 | + ctx = model._get_multicompany_reporting_currency_ctx_for_records_update() |
| 65 | + model = model.with_context(ctx) # pylint: disable=context-overridden |
| 66 | + dom = model._get_multicompany_reporting_currency_domain_for_records_update() |
| 67 | + recs = model.search(dom) |
| 68 | + vals = model._get_multicompany_reporting_currency_vals_for_records_update() |
| 69 | + if recs and vals: |
| 70 | + recs.write(vals) |
| 71 | + |
| 72 | + @api.model |
| 73 | + def _get_multicompany_reporting_currency_inheriting_models(self): |
| 74 | + """Retrieves an ordered set of models that inherit from this mixin |
| 75 | +
|
| 76 | + Hook method, can be overridden by inheriting models |
| 77 | + """ |
| 78 | + inheriting_models: OrderedSet[models.BaseModel] = OrderedSet() |
| 79 | + |
| 80 | + def _add_models_recursively(model: models.BaseModel): |
| 81 | + for model_name in model._inherit_children: |
| 82 | + model = self.env.get(model_name) |
| 83 | + if model is not None and model not in inheriting_models: |
| 84 | + inheriting_models.add(model) |
| 85 | + _add_models_recursively(model) |
| 86 | + |
| 87 | + # NB: ``multicompany.reporting.currency.mixin`` is excluded from the end result |
| 88 | + _add_models_recursively(self.env["multicompany.reporting.currency.mixin"]) |
| 89 | + return inheriting_models |
| 90 | + |
| 91 | + @api.model |
| 92 | + def _get_multicompany_reporting_currency_ctx_for_records_update(self): |
| 93 | + """Prepares a basic context to search/update records |
| 94 | +
|
| 95 | + Hook method, can be overridden by inheriting models |
| 96 | + """ |
| 97 | + # By default, search and update archived records too |
| 98 | + return dict(self.env.context, active_test=False) |
| 99 | + |
| 100 | + @api.model |
| 101 | + def _get_multicompany_reporting_currency_domain_for_records_update(self): |
| 102 | + """Prepares a basic domain to search records to update |
| 103 | +
|
| 104 | + Hook method, can be overridden by inheriting models |
| 105 | + """ |
| 106 | + currency = self._get_multicompany_reporting_currency_from_sys_param() |
| 107 | + return Domain([("multicompany_reporting_currency_id", "!=", currency.id)]) |
| 108 | + |
| 109 | + @api.model |
| 110 | + def _get_multicompany_reporting_currency_vals_for_records_update(self): |
| 111 | + """Prepares basic record values for the update |
| 112 | +
|
| 113 | + Hook method, can be overridden by inheriting models |
| 114 | + """ |
| 115 | + currency = self._get_multicompany_reporting_currency_from_sys_param() |
| 116 | + return {"multicompany_reporting_currency_id": currency.id} |
0 commit comments