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
Table of contents