diff --git a/brand_report_qweb_pdf_watermark/README.rst b/brand_report_qweb_pdf_watermark/README.rst new file mode 100644 index 000000000..3ebf95ccd --- /dev/null +++ b/brand_report_qweb_pdf_watermark/README.rst @@ -0,0 +1,118 @@ +============================ +Brand Specific PDF watermark +============================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a8c72309a1abf484472b7477f932aee5d490d101edd647734cf15936dc6ce24a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbrand-lightgray.png?logo=github + :target: https://github.com/OCA/brand/tree/18.0/brand_report_qweb_pdf_watermark + :alt: OCA/brand +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/brand-18-0/brand-18-0-brand_report_qweb_pdf_watermark + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/brand&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module was adds the functionality to use brand watermarks +(backgrounds) to PDF reports. This module will not add any reports on +itself. It rather provides a "Use Brand Watermark" check box on reports. +And allows you to define a brand specific pdf watermark. You will need +to use it in combination with a module which adds branding possibilities +to an model such as ``account_brand``. If the box is checked and pdf +watermark is added to a brand. Then the watermark is added to the pdf. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module depends on ``brand_external_report_layout`` and +``report_qweb_pdf_watermark`` The dependenices are the same as with +those modules, You need to install PyPDF2: + +:: + + $ pip install pypdf2 + +Usage +===== + +To use this module, you need to: + +Define a pdf watermark on brand. + +1. go to settings --> User & Companies --> Brands +2. create or edit an brand +3. upload an pdf watermark. Note that resolutions and size must match, + otherwise you'll have funny results + +To use the Brand watermark, on for example your invoices with the +``account_brand`` module you need to: + +1. go to your report settings --> Technical --> Reports (e.g. + ``account.report_invoice``) +2. Under Advanced Properties, Check the box "Use Brand Watermark". +3. Create a report e.g. an invoice, Set the brand and generate the + report. The brand watermark is added there. + +If no brand information is available. No watermark is added. It is +possible to stack the pdf watermarks. E.g. use the "Company watermark" +and "Brand Watermark" on top of each other. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* OBS Solutions BV + +Contributors +------------ + +- Emiel van Bokhoven + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/brand `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/brand_report_qweb_pdf_watermark/__init__.py b/brand_report_qweb_pdf_watermark/__init__.py new file mode 100644 index 000000000..0447b1510 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/__init__.py @@ -0,0 +1,3 @@ +# © 2025 OBS Solutions BV + +from . import models diff --git a/brand_report_qweb_pdf_watermark/__manifest__.py b/brand_report_qweb_pdf_watermark/__manifest__.py new file mode 100644 index 000000000..77109125d --- /dev/null +++ b/brand_report_qweb_pdf_watermark/__manifest__.py @@ -0,0 +1,23 @@ +# © 2025 OBS Solutions BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Brand Specific PDF watermark", + "version": "18.0.1.0.0", + "author": "OBS Solutions BV, " "Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Technical Settings", + "development_status": "Production/Stable", + "summary": "Add brand specific watermarks to your QWEB PDF reports", + "website": "https://github.com/OCA/brand", + "depends": ["web", "brand_external_report_layout", "report_qweb_pdf_watermark"], + "data": [ + "views/ir_actions_report_xml.xml", + "views/res_brand.xml", + ], + "assets": { + "web.report_assets_pdf": [ + "/brand_report_qweb_pdf_watermark/static/src/css/report_qweb_pdf_watermark.css" + ], + }, + "installable": True, +} diff --git a/brand_report_qweb_pdf_watermark/examples/Watermark_example_1.pdf b/brand_report_qweb_pdf_watermark/examples/Watermark_example_1.pdf new file mode 100644 index 000000000..18322fa99 Binary files /dev/null and b/brand_report_qweb_pdf_watermark/examples/Watermark_example_1.pdf differ diff --git a/brand_report_qweb_pdf_watermark/examples/Watermark_example_2.pdf b/brand_report_qweb_pdf_watermark/examples/Watermark_example_2.pdf new file mode 100644 index 000000000..2a9eac95a Binary files /dev/null and b/brand_report_qweb_pdf_watermark/examples/Watermark_example_2.pdf differ diff --git a/brand_report_qweb_pdf_watermark/i18n/brand_report_qweb_pdf_watermark.pot b/brand_report_qweb_pdf_watermark/i18n/brand_report_qweb_pdf_watermark.pot new file mode 100644 index 000000000..81c3dc50d --- /dev/null +++ b/brand_report_qweb_pdf_watermark/i18n/brand_report_qweb_pdf_watermark.pot @@ -0,0 +1,49 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * brand_report_qweb_pdf_watermark +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-10 11:43+0000\n" +"PO-Revision-Date: 2025-02-10 11:43+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: brand_report_qweb_pdf_watermark +#: model:ir.model,name:brand_report_qweb_pdf_watermark.model_res_brand +msgid "Brand" +msgstr "" + +#. module: brand_report_qweb_pdf_watermark +#: model:ir.model,name:brand_report_qweb_pdf_watermark.model_ir_actions_report +msgid "Report Action" +msgstr "" + +#. module: brand_report_qweb_pdf_watermark +#: model_terms:ir.ui.view,arch_db:brand_report_qweb_pdf_watermark.view_brand_form +msgid "" +"Upload an pdf file to use as an brand specific watermark. You need to " +"activate the setting 'use brand watermark' on the reports where you want to " +"use it." +msgstr "" + +#. module: brand_report_qweb_pdf_watermark +#: model:ir.model.fields,field_description:brand_report_qweb_pdf_watermark.field_ir_actions_report__use_brand_watermark +msgid "Use Brand Watermark" +msgstr "" + +#. module: brand_report_qweb_pdf_watermark +#: model:ir.model.fields,help:brand_report_qweb_pdf_watermark.field_ir_actions_report__use_brand_watermark +msgid "Use the pdf watermark defined globally in the brand settings." +msgstr "" + +#. module: brand_report_qweb_pdf_watermark +#: model:ir.model.fields,field_description:brand_report_qweb_pdf_watermark.field_res_brand__pdf_watermark +msgid "Watermark" +msgstr "" diff --git a/brand_report_qweb_pdf_watermark/models/__init__.py b/brand_report_qweb_pdf_watermark/models/__init__.py new file mode 100644 index 000000000..c8cb579f9 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/models/__init__.py @@ -0,0 +1,4 @@ +# © 2025 OBS Solutions BV + +from . import res_brand +from . import report diff --git a/brand_report_qweb_pdf_watermark/models/report.py b/brand_report_qweb_pdf_watermark/models/report.py new file mode 100644 index 000000000..8a86bbf21 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/models/report.py @@ -0,0 +1,64 @@ +# © 2016 Therp BV +# Copyright 2023 Onestein - Anjeel Haria +# © 2025 OBS Solutions BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from base64 import b64decode +from io import BytesIO +from logging import getLogger + +from odoo import fields, models + +logger = getLogger(__name__) + +try: + # we need this to be sure PIL has loaded PDF support + from PIL import PdfImagePlugin # noqa: F401 +except ImportError: + logger.error("ImportError: The PdfImagePlugin could not be imported") + +try: + from PyPDF2 import PdfFileReader, PdfFileWriter # pylint: disable=W0404 +except ImportError: + logger.debug("Can not import PyPDF2") + + +class Report(models.Model): + _inherit = "ir.actions.report" + + use_brand_watermark = fields.Boolean( + default=False, + help="Use the pdf watermark defined globally in the brand settings.", + ) + + def _postprocess_wkhtmltopdf(self, result, **kwargs): + docids = self.env.context.get("res_ids", False) + if docids: + model_name = ( + self.model or self._get_report(kwargs.get("report_ref", False)).model + ) + first_record_id = docids[0] + record = self.env[model_name].browse(first_record_id) + if hasattr(record, "brand_id"): + brand = record.brand_id + if brand and brand.pdf_watermark: + watermark = b64decode(brand.pdf_watermark) + + # Apply the watermark to the PDF + pdf = PdfFileWriter() + pdf_watermark = PdfFileReader(BytesIO(watermark)) + for page in PdfFileReader(BytesIO(result)).pages: + watermark_page = pdf.addBlankPage( + page.mediaBox.getWidth(), page.mediaBox.getHeight() + ) + watermark_page.mergePage(pdf_watermark.getPage(0)) + watermark_page.mergePage(page) + + pdf_content = BytesIO() + pdf.write(pdf_content) + result = pdf_content.getvalue() + + return result + + def _run_wkhtmltopdf(self, *args, **kwargs): + result = super()._run_wkhtmltopdf(*args, **kwargs) + return self._postprocess_wkhtmltopdf(result, **kwargs) diff --git a/brand_report_qweb_pdf_watermark/models/res_brand.py b/brand_report_qweb_pdf_watermark/models/res_brand.py new file mode 100644 index 000000000..37998f47e --- /dev/null +++ b/brand_report_qweb_pdf_watermark/models/res_brand.py @@ -0,0 +1,9 @@ +# © 2025 OBS Solutions BV + +from odoo import fields, models + + +class ResBrand(models.Model): + _inherit = "res.brand" + + pdf_watermark = fields.Binary("Watermark") diff --git a/brand_report_qweb_pdf_watermark/pyproject.toml b/brand_report_qweb_pdf_watermark/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/brand_report_qweb_pdf_watermark/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/brand_report_qweb_pdf_watermark/readme/CONTRIBUTORS.md b/brand_report_qweb_pdf_watermark/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..3fcdcbb72 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Emiel van Bokhoven \<\> diff --git a/brand_report_qweb_pdf_watermark/readme/DESCRIPTION.md b/brand_report_qweb_pdf_watermark/readme/DESCRIPTION.md new file mode 100644 index 000000000..bb60c284c --- /dev/null +++ b/brand_report_qweb_pdf_watermark/readme/DESCRIPTION.md @@ -0,0 +1,5 @@ +This module was adds the functionality to use brand watermarks (backgrounds) to PDF reports. +This module will not add any reports on itself. It rather provides a "Use Brand Watermark" check box on reports. +And allows you to define a brand specific pdf watermark. +You will need to use it in combination with a module which adds branding possibilities to an model such as `account_brand`. +If the box is checked and pdf watermark is added to a brand. Then the watermark is added to the pdf. diff --git a/brand_report_qweb_pdf_watermark/readme/INSTALL.md b/brand_report_qweb_pdf_watermark/readme/INSTALL.md new file mode 100644 index 000000000..9cb7146dd --- /dev/null +++ b/brand_report_qweb_pdf_watermark/readme/INSTALL.md @@ -0,0 +1,4 @@ +This module depends on `brand_external_report_layout` and `report_qweb_pdf_watermark` +The dependenices are the same as with those modules, You need to install PyPDF2: + + $ pip install pypdf2 diff --git a/brand_report_qweb_pdf_watermark/readme/USAGE.md b/brand_report_qweb_pdf_watermark/readme/USAGE.md new file mode 100644 index 000000000..8efc2c0ab --- /dev/null +++ b/brand_report_qweb_pdf_watermark/readme/USAGE.md @@ -0,0 +1,18 @@ +To use this module, you need to: + +Define a pdf watermark on brand. +1. go to settings --\> User & Companies --\> Brands +2. create or edit an brand +3. upload an pdf watermark. Note that resolutions and + size must match, otherwise you'll have funny results + +To use the Brand watermark, on for example your invoices with the `account_brand` module you need to: + +1. go to your report settings --\> Technical --\> Reports + (e.g. `account.report_invoice`) +2. Under Advanced Properties, Check the box "Use Brand Watermark". +3. Create a report e.g. an invoice, Set the brand and generate the report. + The brand watermark is added there. + +If no brand information is available. No watermark is added. +It is possible to stack the pdf watermarks. E.g. use the "Company watermark" and "Brand Watermark" on top of each other. diff --git a/brand_report_qweb_pdf_watermark/static/description/icon.png b/brand_report_qweb_pdf_watermark/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/brand_report_qweb_pdf_watermark/static/description/icon.png differ diff --git a/brand_report_qweb_pdf_watermark/static/description/index.html b/brand_report_qweb_pdf_watermark/static/description/index.html new file mode 100644 index 000000000..c7d0e8db4 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/static/description/index.html @@ -0,0 +1,463 @@ + + + + + +Brand Specific PDF watermark + + + +
+

Brand Specific PDF watermark

+ + +

Production/Stable License: AGPL-3 OCA/brand Translate me on Weblate Try me on Runboat

+

This module was adds the functionality to use brand watermarks +(backgrounds) to PDF reports. This module will not add any reports on +itself. It rather provides a “Use Brand Watermark” check box on reports. +And allows you to define a brand specific pdf watermark. You will need +to use it in combination with a module which adds branding possibilities +to an model such as account_brand. If the box is checked and pdf +watermark is added to a brand. Then the watermark is added to the pdf.

+

Table of contents

+ +
+

Installation

+

This module depends on brand_external_report_layout and +report_qweb_pdf_watermark The dependenices are the same as with +those modules, You need to install PyPDF2:

+
+$ pip install pypdf2
+
+
+
+

Usage

+

To use this module, you need to:

+

Define a pdf watermark on brand.

+
    +
  1. go to settings –> User & Companies –> Brands
  2. +
  3. create or edit an brand
  4. +
  5. upload an pdf watermark. Note that resolutions and size must match, +otherwise you’ll have funny results
  6. +
+

To use the Brand watermark, on for example your invoices with the +account_brand module you need to:

+
    +
  1. go to your report settings –> Technical –> Reports (e.g. +account.report_invoice)
  2. +
  3. Under Advanced Properties, Check the box “Use Brand Watermark”.
  4. +
  5. Create a report e.g. an invoice, Set the brand and generate the +report. The brand watermark is added there.
  6. +
+

If no brand information is available. No watermark is added. It is +possible to stack the pdf watermarks. E.g. use the “Company watermark” +and “Brand Watermark” on top of each other.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • OBS Solutions BV
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/brand project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/brand_report_qweb_pdf_watermark/static/src/css/report_qweb_pdf_watermark.css b/brand_report_qweb_pdf_watermark/static/src/css/report_qweb_pdf_watermark.css new file mode 100644 index 000000000..83cc2eb02 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/static/src/css/report_qweb_pdf_watermark.css @@ -0,0 +1,3 @@ +body { + background: transparent !important; +} diff --git a/brand_report_qweb_pdf_watermark/tests/__init__.py b/brand_report_qweb_pdf_watermark/tests/__init__.py new file mode 100644 index 000000000..60d1b70f8 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/tests/__init__.py @@ -0,0 +1,3 @@ +# © 2025 OBS Solutions BV + +from . import test_brand_report_qweb_pdf_watermark diff --git a/brand_report_qweb_pdf_watermark/tests/test_brand_report_qweb_pdf_watermakr_tester.py b/brand_report_qweb_pdf_watermark/tests/test_brand_report_qweb_pdf_watermakr_tester.py new file mode 100644 index 000000000..ae646a364 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/tests/test_brand_report_qweb_pdf_watermakr_tester.py @@ -0,0 +1,9 @@ +# © 2025 OBS Solutions BV +from odoo import fields, models + + +class BrandReportQwebPdfWatermarkTester(models.Model): + _name = "brand.report.qweb.pdf.watermark.tester" + _description = "Brand Report Qweb Pdf Watermark Tester" + + brand_id = fields.Many2one(string="Brand:", comodel_name="res.brand") diff --git a/brand_report_qweb_pdf_watermark/tests/test_brand_report_qweb_pdf_watermark.py b/brand_report_qweb_pdf_watermark/tests/test_brand_report_qweb_pdf_watermark.py new file mode 100644 index 000000000..fc40360c8 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/tests/test_brand_report_qweb_pdf_watermark.py @@ -0,0 +1,89 @@ +# © 2025 OBS Solutions BV +import base64 +import os + +from odoo_test_helper import FakeModelLoader + +from odoo.tests import common, tagged + + +@tagged("post_install", "-at_install") +class TestBrandReportWatermark(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context)) + cls.loader = FakeModelLoader(cls.env, cls.__module__) + cls.loader.backup_registry() + + from .test_brand_report_qweb_pdf_watermakr_tester import ( + BrandReportQwebPdfWatermarkTester, + ) + + cls.loader.update_registry((BrandReportQwebPdfWatermarkTester,)) + cls.test_model = cls.env[BrandReportQwebPdfWatermarkTester._name] + cls.tester_model = cls.env["ir.model"].search( + [("model", "=", "brand.report.qweb.pdf.watermark.tester")] + ) + + cls.report_model = cls.env["ir.actions.report"] + cls.brand_model = cls.env["res.brand"] + + # Load PDF watermark from static/images folder + module_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + image_path = os.path.join(module_path, "examples/Watermark_example_1.pdf") + + with open(image_path, "rb") as f: + watermark_content = f.read() + + # Create a brand with a watermark + cls.brand = cls.brand_model.with_context(create_res_partner=False).create( + { + "name": "Test Brand", + "pdf_watermark": base64.b64encode(watermark_content).decode("ascii"), + } + ) + + # Find or create a report action for the temporary model + cls.report_action = cls.env["ir.actions.report"].search( + [("model", "=", "brand.report.qweb.pdf.watermark.tester")], limit=1 + ) + if not cls.report_action: + cls.report_action = cls.env["ir.actions.report"].create( + { + "name": "test_report", + "report_name": "test_report", + "model": "brand.report.qweb.pdf.watermark.tester", + "report_type": "qweb-pdf", + "use_brand_watermark": True, + } + ) + + def tearDown(cls): + cls.loader.restore_registry() + super().tearDownClass() + + def test_postprocess_wkhtmltopdf_with_watermark(self): + record = self.test_model.create({"brand_id": self.brand.id}) + self.env.context = { + "res_ids": [record.id], + "active_model": self.test_model._name, + } + + self.report_action.report_name = "test_report" + + # Load PDF watermark + module_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + image_path = os.path.join(module_path, "examples/Watermark_example_2.pdf") + with open(image_path, "rb") as f: + watermark_content = f.read() + sample_pdf_data = watermark_content + + result = self.report_action._postprocess_wkhtmltopdf( + sample_pdf_data, report_ref=self.report_action.report_name + ) + + self.assertTrue(result) + self.assertNotEqual( + result, sample_pdf_data + ) # Check if the PDF content has changed diff --git a/brand_report_qweb_pdf_watermark/views/ir_actions_report_xml.xml b/brand_report_qweb_pdf_watermark/views/ir_actions_report_xml.xml new file mode 100644 index 000000000..7103766a7 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/views/ir_actions_report_xml.xml @@ -0,0 +1,15 @@ + + + + ir.actions.report + + + + + + + + diff --git a/brand_report_qweb_pdf_watermark/views/res_brand.xml b/brand_report_qweb_pdf_watermark/views/res_brand.xml new file mode 100644 index 000000000..1c4788811 --- /dev/null +++ b/brand_report_qweb_pdf_watermark/views/res_brand.xml @@ -0,0 +1,19 @@ + + + + res.brand + + + + +
+ +
+
+
+
+