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(""), 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("") 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 @@ Images can be assigned to a specific company or left as shared records
+(without company assignment) for use across multiple companies
@@ -415,6 +417,8 @@
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.
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 @@
-
+