diff --git a/setup/zip_product_image/odoo/addons/zip_product_image b/setup/zip_product_image/odoo/addons/zip_product_image new file mode 120000 index 00000000..814407c6 --- /dev/null +++ b/setup/zip_product_image/odoo/addons/zip_product_image @@ -0,0 +1 @@ +../../../../zip_product_image \ No newline at end of file diff --git a/setup/zip_product_image/setup.py b/setup/zip_product_image/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/zip_product_image/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/zip_product_image/README.rst b/zip_product_image/README.rst new file mode 100644 index 00000000..5c3c32e7 --- /dev/null +++ b/zip_product_image/README.rst @@ -0,0 +1,62 @@ +================== +Zip Product Images +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:2918847fc8cec3732a3cbdc65ee2f5cb8dfef637c526f6cfd9424bcd6dccd6fc + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |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-akretion%2Fak--odoo--incubator-lightgray.png?logo=github + :target: https://github.com/akretion/ak-odoo-incubator/tree/16.0/zip_product_image + :alt: akretion/ak-odoo-incubator + +|badge1| |badge2| |badge3| + +Wizard to generate a zip images file of selected variant products. + +This module is compatible with fs_product_multi_image module (from OCA/storage) but doesn't depends on it. + +**Table of contents** + +.. contents:: + :local: + +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 +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +Kevin Khao + +Maintainers +~~~~~~~~~~~ + +This module is part of the `akretion/ak-odoo-incubator `_ project on GitHub. + +You are welcome to contribute. diff --git a/zip_product_image/__init__.py b/zip_product_image/__init__.py new file mode 100644 index 00000000..aee8895e --- /dev/null +++ b/zip_product_image/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/zip_product_image/__manifest__.py b/zip_product_image/__manifest__.py new file mode 100644 index 00000000..f1a3bc5a --- /dev/null +++ b/zip_product_image/__manifest__.py @@ -0,0 +1,17 @@ +{ + "name": "Zip Product Images", + "version": "16.0.1.0.0", + "author": "Akretion", + "category": "Product", + "depends": [ + "product", + ], + "website": "https://github.com/akretion/ak-odoo-incubator", + "data": [ + "wizards/export_image.xml", + "security/security.xml", + ], + "maintainer": ["kevinkhao", "bealdav"], + "license": "AGPL-3", + "installable": True, +} diff --git a/zip_product_image/models/__init__.py b/zip_product_image/models/__init__.py new file mode 100644 index 00000000..9649db77 --- /dev/null +++ b/zip_product_image/models/__init__.py @@ -0,0 +1 @@ +from . import product diff --git a/zip_product_image/models/product.py b/zip_product_image/models/product.py new file mode 100644 index 00000000..9b7e5650 --- /dev/null +++ b/zip_product_image/models/product.py @@ -0,0 +1,36 @@ +# Copyright 2024 Akretion +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 +import imghdr + +from odoo import api, models + +LIMIT = 500 + + +class ProductProduct(models.Model): + _inherit = "product.product" + + @api.model + def _generate_image_zipfile(self): + prd_ids = self.env.context["active_ids"] + if prd_ids: + if len(prd_ids) > LIMIT: + raise ValueError(f"Too many selected products : limit = {LIMIT}") + return ( + self.env["zip.product.image"] + .create({}) + ._get_zip_product_images(self.browse(prd_ids)) + ) + + def _product_image_data(self): + self.ensure_one() + if "fs_product_multi_image" in self.env.registry._init_modules: + # Here fs_product_multi_image is installed + if self.image: + return (base64.b64encode(self.image.getvalue()), self.image.extension) + return (False, False) + image_data = base64.b64decode(self.image_1920) + image_type = imghdr.what(None, h=image_data) + return (self.image_1920 or False, f".{image_type}") diff --git a/zip_product_image/readme/CONTRIBUTORS.rst b/zip_product_image/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..14b2aed7 --- /dev/null +++ b/zip_product_image/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +Kevin Khao diff --git a/zip_product_image/readme/DESCRIPTION.rst b/zip_product_image/readme/DESCRIPTION.rst new file mode 100644 index 00000000..e8618348 --- /dev/null +++ b/zip_product_image/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +Wizard to generate a zip images file of selected variant products. + +This module is compatible with fs_product_multi_image module (from OCA/storage) but doesn't depends on it. diff --git a/zip_product_image/security/security.xml b/zip_product_image/security/security.xml new file mode 100644 index 00000000..7302176f --- /dev/null +++ b/zip_product_image/security/security.xml @@ -0,0 +1,16 @@ + + + + + + wizard zip product image access + + + + + + + + + diff --git a/zip_product_image/static/description/index.html b/zip_product_image/static/description/index.html new file mode 100644 index 00000000..717b008e --- /dev/null +++ b/zip_product_image/static/description/index.html @@ -0,0 +1,414 @@ + + + + + +Zip Product Images + + + +
+

Zip Product Images

+ + +

Beta License: AGPL-3 akretion/ak-odoo-incubator

+

Wizard to generate a zip images file of selected variant products.

+

This module is compatible with fs_product_multi_image module (from OCA/storage) but doesn’t depends on it.

+

Table of contents

+ +
+

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

+
    +
  • Akretion
  • +
+
+
+

Contributors

+

Kevin Khao

+
+
+

Maintainers

+

This module is part of the akretion/ak-odoo-incubator project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/zip_product_image/wizards/__init__.py b/zip_product_image/wizards/__init__.py new file mode 100644 index 00000000..3050e96a --- /dev/null +++ b/zip_product_image/wizards/__init__.py @@ -0,0 +1 @@ +from . import export_image diff --git a/zip_product_image/wizards/export_image.py b/zip_product_image/wizards/export_image.py new file mode 100644 index 00000000..6956a108 --- /dev/null +++ b/zip_product_image/wizards/export_image.py @@ -0,0 +1,44 @@ +# Copyright 2024 Akretion +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 +import logging +import zipfile +from io import BytesIO + +from odoo import _, exceptions, fields, models + +logger = logging.getLogger(__name__) + + +class ZipProductImage(models.TransientModel): + _name = "zip.product.image" + _description = "Wizard to generate a zip images of selected products" + + zipfile = fields.Binary(string="Images Archive") + name_zipfile = fields.Char() + + def _get_zip_product_images(self, products): + """Generate a zip file with the images of the products.""" + zip_buffer = BytesIO() + with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file: + for prd in products: + img_data, extension = prd._product_image_data() + if img_data: + img_data = base64.b64decode(img_data) + name = "" + for field in self._product_field_names(): + name = name or prd[field] + zip_file.writestr(f"{name}.{extension}", img_data) + else: + logger.warning(f"Product {prd.display_name} has no image") + if zip_file.filelist: + self.zipfile = base64.b64encode(zip_buffer.getvalue()) + self.name_zipfile = "product_images.zip" + action = self.get_formview_action() + action["target"] = "new" + return action + raise exceptions.UserError(_("No image for this product selection")) + + def _product_field_names(self): + return ["barcode", "default_code", "name"] diff --git a/zip_product_image/wizards/export_image.xml b/zip_product_image/wizards/export_image.xml new file mode 100644 index 00000000..e501e740 --- /dev/null +++ b/zip_product_image/wizards/export_image.xml @@ -0,0 +1,35 @@ + + + + + + zip.product.image + +
+ + + + +
+
+
+
+
+ + + 📎 Zip images export + + + code + action = env["product.product"]._generate_image_zipfile() + + +