From 9252a7df40177dad1c0daa4fd37f8deccb0fef32 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Wed, 22 Apr 2026 01:50:58 +0000 Subject: [PATCH] [IMP] report_positioned_image: reflect oca changes --- report_positioned_image/README.rst | 5 ++ .../models/ir_actions_report.py | 89 +++++++++---------- .../models/report_positioned_image.py | 43 ++++++--- report_positioned_image/readme/CONFIGURE.md | 2 + report_positioned_image/readme/DESCRIPTION.md | 3 + .../static/description/index.html | 4 + .../tests/test_report_positioned_image.py | 40 +++++++++ .../views/report_positioned_image_views.xml | 7 +- .../views/res_company_views.xml | 8 +- 9 files changed, 136 insertions(+), 65 deletions(-) diff --git a/report_positioned_image/README.rst b/report_positioned_image/README.rst index 1be5aebe..35c7aeb9 100644 --- a/report_positioned_image/README.rst +++ b/report_positioned_image/README.rst @@ -41,6 +41,9 @@ The module supports two types of images: - *Report-specific Images*: Configure specific images for individual reports, filtered by company context and always shown when configured +Images can be assigned to a specific company or left as shared records +(without company assignment) for use across multiple companies + **Table of contents** .. contents:: @@ -68,6 +71,8 @@ To configure company-level images: height automatically adjusts the other dimension to maintain aspect ratio. Uncheck for manual control of both dimensions. - *First Page Only*: Check to show only on the first page + - *Company*: Automatically set to the current company when creating + from the company form. To create shared images, leave empty. To configure report-specific images: diff --git a/report_positioned_image/models/ir_actions_report.py b/report_positioned_image/models/ir_actions_report.py index 979a727e..9fdca590 100644 --- a/report_positioned_image/models/ir_actions_report.py +++ b/report_positioned_image/models/ir_actions_report.py @@ -22,42 +22,6 @@ class IrActionsReport(models.Model): string="Report Images", ) - def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): - """Set company context so _get_positioned_image_configs uses the - correct company. - """ - company = self._get_report_company(res_ids) - return super(IrActionsReport, self.with_company(company))._render_qweb_pdf( - report_ref, res_ids, data - ) - - def _prepare_html(self, html, report_model=False): - image_configs = self._get_positioned_image_configs() - if not image_configs: - return super()._prepare_html(html, report_model=report_model) - result = super()._prepare_html(html, report_model=report_model) - if not isinstance(result, tuple): - return result - bodies, res_ids, header, footer, specific_paperformat_args = result - if image_configs: - header = self._inject_images_into_header(header, image_configs) - return bodies, res_ids, header, footer, specific_paperformat_args - - def _inject_images_into_header(self, header, image_configs): - image_html = self._build_image_html(image_configs) - return self._insert_html_into_header(header, image_html) - - def _insert_html_into_header(self, header, html_to_inject): - if Markup("") in header: - return header.replace( - Markup(""), html_to_inject + Markup(""), 1 - ) - if Markup("") in header: - return header.replace( - Markup(""), Markup("") + html_to_inject, 1 - ) - return header + html_to_inject - @staticmethod def _build_image_html(images): parts = [] @@ -85,15 +49,20 @@ def _build_image_html(images): ) return Markup("".join(parts)) - def _get_report_company(self, res_ids): - if not res_ids or not self.model: - return self.env.company - model = self.env[self.model] - if "company_id" not in model._fields: - return self.env.company - records = model.browse(res_ids).exists() - companies = records.mapped("company_id") - return companies[0] if len(companies) == 1 else self.env.company + def _insert_html_into_header(self, header, html_to_inject): + if Markup("") in header: + return header.replace( + Markup(""), html_to_inject + Markup(""), 1 + ) + if Markup("") in header: + return header.replace( + Markup(""), Markup("") + html_to_inject, 1 + ) + return header + html_to_inject + + def _inject_images_into_header(self, header, image_configs): + image_html = self._build_image_html(image_configs) + return self._insert_html_into_header(header, image_html) def _get_positioned_image_configs(self): company = self.env.company @@ -114,3 +83,33 @@ def _get_positioned_image_configs(self): for img in images if img.image ] + + def _prepare_html(self, html, report_model=False): + image_configs = self._get_positioned_image_configs() + if not image_configs: + return super()._prepare_html(html, report_model=report_model) + result = super()._prepare_html(html, report_model=report_model) + if not isinstance(result, tuple): + return result + bodies, res_ids, header, footer, specific_paperformat_args = result + header = self._inject_images_into_header(header, image_configs) + return bodies, res_ids, header, footer, specific_paperformat_args + + def _get_report_company(self, res_ids): + if not res_ids or not self.model: + return self.env.company + model = self.env[self.model] + if "company_id" not in model._fields: + return self.env.company + records = model.browse(res_ids).exists() + companies = records.mapped("company_id") + return companies[0] if len(companies) == 1 else self.env.company + + def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): + """Set company context so _get_positioned_image_configs uses the + correct company. + """ + company = self._get_report_company(res_ids) + return super(IrActionsReport, self.with_company(company))._render_qweb_pdf( + report_ref, res_ids, data + ) diff --git a/report_positioned_image/models/report_positioned_image.py b/report_positioned_image/models/report_positioned_image.py index 2a283f0e..edfb5f9d 100644 --- a/report_positioned_image/models/report_positioned_image.py +++ b/report_positioned_image/models/report_positioned_image.py @@ -36,6 +36,19 @@ class ReportPositionedImage(models.Model): def _default_company_id(self): return self.env.context.get("default_company_id") + @api.constrains("pos_top", "pos_left", "width", "height") + def _check_positive_values(self): + """Ensure position and dimension fields have positive values.""" + for record in self: + if record.pos_top < 0: + raise ValidationError(_("Top position must be a positive value.")) + if record.pos_left < 0: + raise ValidationError(_("Left position must be a positive value.")) + if record.width <= 0: + raise ValidationError(_("Width must be greater than zero.")) + if record.height <= 0: + raise ValidationError(_("Height must be greater than zero.")) + def _get_aspect_ratio(self): """Get image aspect ratio (width/height).""" if not self.image: @@ -83,15 +96,21 @@ def _onchange_height(self): self.height * ratio, 2 ) - @api.constrains("pos_top", "pos_left", "width", "height") - def _check_positive_values(self): - """Ensure position and dimension fields have positive values.""" - for record in self: - if record.pos_top < 0: - raise ValidationError(_("Top position must be a positive value.")) - if record.pos_left < 0: - raise ValidationError(_("Left position must be a positive value.")) - if record.width <= 0: - raise ValidationError(_("Width must be greater than zero.")) - if record.height <= 0: - raise ValidationError(_("Height must be greater than zero.")) + @api.onchange("company_id") + def _onchange_company_id(self): + """Prevent assigning to a different company when created from company form.""" + default_company_id = self.env.context.get("default_company_id") + if not default_company_id: + return + if self.company_id and self.company_id.id != default_company_id: + self.company_id = default_company_id + return { + "warning": { + "title": _("Company Assignment"), + "message": _( + "You cannot assign this image to a different company. " + "Please use the dedicated wizard to assign images to other " + "companies." + ), + } + } diff --git a/report_positioned_image/readme/CONFIGURE.md b/report_positioned_image/readme/CONFIGURE.md index 9ffc4d6d..0342fbd9 100644 --- a/report_positioned_image/readme/CONFIGURE.md +++ b/report_positioned_image/readme/CONFIGURE.md @@ -14,6 +14,8 @@ To configure company-level images: automatically adjusts the other dimension to maintain aspect ratio. Uncheck for manual control of both dimensions. - *First Page Only*: Check to show only on the first page + - *Company*: Automatically set to the current company when creating from + the company form. To create shared images, leave empty. To configure report-specific images: diff --git a/report_positioned_image/readme/DESCRIPTION.md b/report_positioned_image/readme/DESCRIPTION.md index ddc38649..0220a9c0 100644 --- a/report_positioned_image/readme/DESCRIPTION.md +++ b/report_positioned_image/readme/DESCRIPTION.md @@ -9,3 +9,6 @@ The module supports two types of images: included in reports by enabling the *Include Company Images* option - *Report-specific Images*: Configure specific images for individual reports, filtered by company context and always shown when configured + +Images can be assigned to a specific company or left as shared records +(without company assignment) for use across multiple companies diff --git a/report_positioned_image/static/description/index.html b/report_positioned_image/static/description/index.html index 15539810..a88a5593 100644 --- a/report_positioned_image/static/description/index.html +++ b/report_positioned_image/static/description/index.html @@ -382,6 +382,8 @@

Report Positioned Image

  • Report-specific Images: Configure specific images for individual reports, filtered by company context and always shown when configured
  • +

    Images can be assigned to a specific company or left as shared records +(without company assignment) for use across multiple companies

    Table of contents

    diff --git a/report_positioned_image/tests/test_report_positioned_image.py b/report_positioned_image/tests/test_report_positioned_image.py index a76a99fe..e52e5302 100644 --- a/report_positioned_image/tests/test_report_positioned_image.py +++ b/report_positioned_image/tests/test_report_positioned_image.py @@ -232,3 +232,43 @@ def test_global_images_appear_for_all_companies(self): self.company_b )._get_positioned_image_configs() self.assertEqual(len(configs_b), 1) + + def test_company_id_onchange_with_context(self): + image = ( + self.env["report.positioned.image"] + .with_context(default_company_id=self.company_a.id) + .new( + { + "name": "Test Image", + "image": self.test_image, + "width": 10.0, + "height": 10.0, + "company_id": self.company_a.id, + } + ) + ) + image.company_id = self.company_b + result = image._onchange_company_id() + self.assertIsNotNone(result) + self.assertIn("warning", result) + self.assertEqual(image.company_id, self.company_a) + image.company_id = self.company_a + result = image._onchange_company_id() + self.assertIsNone(result) + self.assertEqual(image.company_id, self.company_a) + image.company_id = False + result = image._onchange_company_id() + self.assertIsNone(result) + self.assertFalse(image.company_id) + image_no_context = self.env["report.positioned.image"].new( + { + "name": "Free Image", + "image": self.test_image, + "width": 10.0, + "height": 10.0, + "company_id": self.company_b.id, + } + ) + result = image_no_context._onchange_company_id() + self.assertIsNone(result) + self.assertEqual(image_no_context.company_id, self.company_b) diff --git a/report_positioned_image/views/report_positioned_image_views.xml b/report_positioned_image/views/report_positioned_image_views.xml index f98dc7c3..25e21faf 100644 --- a/report_positioned_image/views/report_positioned_image_views.xml +++ b/report_positioned_image/views/report_positioned_image_views.xml @@ -10,7 +10,10 @@ - + @@ -31,7 +34,7 @@ - + diff --git a/report_positioned_image/views/res_company_views.xml b/report_positioned_image/views/res_company_views.xml index a14398b9..d69a373e 100644 --- a/report_positioned_image/views/res_company_views.xml +++ b/report_positioned_image/views/res_company_views.xml @@ -11,7 +11,7 @@ name="report_positioned_image_ids" nolabel="1" context="{'default_company_id': id}" - domain="[('company_id', '=', id)]" + domain="['|', ('company_id', '=', id), ('company_id', '=', False)]" > @@ -26,11 +26,7 @@ - +