From e1feeb6d75bb19a8bb63dceacdeafe640759a425 Mon Sep 17 00:00:00 2001 From: "matthias.lotz" Date: Thu, 11 Dec 2025 21:53:21 +0100 Subject: [PATCH] feat(aspl_equipment_qrcode_generator): Fix QR-code generation and PDF rendering - Move QR-code generation from report to wizard (before PDF generation) - Add attachment=True to qr_code Binary field for filestore storage - Simplify report class - only provides data, no QR generation - Change template from t-field to with base64 data URL for proper PDF rendering - Fix JSONB field extraction (name, category) for de_DE/en_US - Add equipment_ids field to wizard form (invisible) - Apply changes to all 3 label formats (2x5, 2x7, 4x7) QR-codes now display correctly in generated PDFs and contain equipment details plus direct link to Odoo equipment record. --- .../models/maintenance_equipment.py | 2 +- .../report/custom_qrcode.xml | 21 ++-- .../report/custom_qrcode_generator.py | 98 +++++-------------- .../wizard/equipment_label_layout.py | 67 ++++++++++++- .../wizard/equipment_label_layout_views.xml | 1 + 5 files changed, 101 insertions(+), 88 deletions(-) diff --git a/aspl_equipment_qrcode_generator/models/maintenance_equipment.py b/aspl_equipment_qrcode_generator/models/maintenance_equipment.py index 4e7b217..5ff8432 100644 --- a/aspl_equipment_qrcode_generator/models/maintenance_equipment.py +++ b/aspl_equipment_qrcode_generator/models/maintenance_equipment.py @@ -6,7 +6,7 @@ from odoo import models, fields class MaintenanceEquipment(models.Model): _inherit = 'maintenance.equipment' - qr_code = fields.Binary("QR Code") + qr_code = fields.Binary("QR Code", attachment=True) comp_serial_no = fields.Char("Inventory Serial No", tracking=True) serial_no = fields.Char('Mfg. Serial Number', copy=False) diff --git a/aspl_equipment_qrcode_generator/report/custom_qrcode.xml b/aspl_equipment_qrcode_generator/report/custom_qrcode.xml index 91e70c3..9d72327 100644 --- a/aspl_equipment_qrcode_generator/report/custom_qrcode.xml +++ b/aspl_equipment_qrcode_generator/report/custom_qrcode.xml @@ -12,7 +12,7 @@
-
+
@@ -40,7 +40,7 @@
-
+
@@ -66,19 +66,19 @@ Name : - + Model : - + - -
+ + @@ -87,23 +87,22 @@ Mfg Serial : - + Serial : - + Warranty Date : - + - diff --git a/aspl_equipment_qrcode_generator/report/custom_qrcode_generator.py b/aspl_equipment_qrcode_generator/report/custom_qrcode_generator.py index 88bb0fe..c5f6a4c 100644 --- a/aspl_equipment_qrcode_generator/report/custom_qrcode_generator.py +++ b/aspl_equipment_qrcode_generator/report/custom_qrcode_generator.py @@ -1,84 +1,34 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import base64 -import math -from io import BytesIO - -import qrcode from odoo import models -def generate_qr_code(value): - qr = qrcode.QRCode( - version=1, - error_correction=qrcode.constants.ERROR_CORRECT_L, - box_size=20, - border=4, - ) - qr.add_data(value) - qr.make(fit=True) - img = qr.make_image() - temp = BytesIO() - img.save(temp, format="PNG") - qr_img = base64.b64encode(temp.getvalue()) - return qr_img - - -def _prepare_data(env, data): - equipment_label_layout_id = env['equipment.label.layout'].browse(data['equipment_label_layout_id']) - equipment_dict = {} - equipment_ids = equipment_label_layout_id.equipment_ids - for equipment in equipment_ids: - if not equipment.name: - continue - equipment_dict[equipment] = 1 - combine_equipment_detail = "" - - # Generate Equipment Redirect LInk - url = env['ir.config_parameter'].sudo().get_param('web.base.url') - menuId = env.ref('maintenance.menu_equipment_form').sudo().id - actionId = env.ref('maintenance.hr_equipment_action').sudo().id - - equipment_link = url + '/web#id=' + str(equipment.id) + '&menu_id=' + str(menuId) + '&action=' + str( - actionId) + '&model=maintenance.equipment&view_type=form' - - # Prepare main Equipment Detail - main_equipment_detail = "" - main_equipment_detail = main_equipment_detail.join( - "Name: " + str(equipment.name) + "\n" + - "Model: " + str(equipment.model) + "\n" + - "Mfg serial no: " + str(equipment.serial_no) + "\n" - "Warranty Exp. Date: " +str(equipment.warranty_date) + "\n" - "Category: " +str(equipment.category_id.name) - ) - # main_equipment_detail = equipment_link + '\n' + '\n' + main_equipment_detail - - # Prepare Child Equipment Detail - combine_equipment_detail = main_equipment_detail - - combine_equipment_detail += '\n' + '\n' + equipment_link - - # Generate Qr Code depends on Details - qr_image = generate_qr_code(combine_equipment_detail) - equipment.write({ - 'qr_code': qr_image - }) - env.cr.commit() - page_numbers = (len(equipment_ids) - 1) // (equipment_label_layout_id.rows * equipment_label_layout_id.columns) + 1 - - dict_equipment = { - 'rows': equipment_label_layout_id.rows, - 'columns': equipment_label_layout_id.columns, - 'page_numbers': page_numbers, - 'equipment_data': equipment_dict - } - return dict_equipment - - class ReportProductTemplateLabel(models.AbstractModel): _name = 'report.aspl_equipment_qrcode_generator.maintenance_quip' _description = 'Equipment QR-code Report' - def _get_report_values(self, docids, data): - return _prepare_data(self.env, data) + def _get_report_values(self, docids, data=None): + """ + QR-Code-Generierung erfolgt im Wizard (equipment_label_layout.py). + Dieser Report rendert nur die bereits generierten QR-Codes. + """ + if not data: + return {} + + equipment_label_layout_id = self.env['equipment.label.layout'].browse(data.get('equipment_label_layout_id')) + + equipment_dict = {} + for equipment in equipment_label_layout_id.equipment_ids: + equipment_dict[equipment] = 1 + + page_numbers = (len(equipment_label_layout_id.equipment_ids) - 1) // ( + equipment_label_layout_id.rows * equipment_label_layout_id.columns + ) + 1 + + return { + 'rows': equipment_label_layout_id.rows, + 'columns': equipment_label_layout_id.columns, + 'page_numbers': page_numbers, + 'equipment_data': equipment_dict + } diff --git a/aspl_equipment_qrcode_generator/wizard/equipment_label_layout.py b/aspl_equipment_qrcode_generator/wizard/equipment_label_layout.py index 71598c8..1223f2b 100644 --- a/aspl_equipment_qrcode_generator/wizard/equipment_label_layout.py +++ b/aspl_equipment_qrcode_generator/wizard/equipment_label_layout.py @@ -16,6 +16,16 @@ class EquipmentLabelLayout(models.TransientModel): rows = fields.Integer(compute='_compute_dimensions') columns = fields.Integer(compute='_compute_dimensions') + @api.model + def default_get(self, fields_list): + """Override to properly set equipment_ids from context""" + res = super().default_get(fields_list) + if self.env.context.get('active_ids'): + res['equipment_ids'] = [(6, 0, self.env.context.get('active_ids', []))] + elif self.env.context.get('default_equipment_ids'): + res['equipment_ids'] = [(6, 0, self.env.context.get('default_equipment_ids', []))] + return res + @api.depends('print_format') def _compute_dimensions(self): for wizard in self: @@ -28,9 +38,62 @@ class EquipmentLabelLayout(models.TransientModel): def process_label(self): + # Generiere QR-Codes für alle Equipment VOR dem Report + self._generate_qr_codes() + xml_id = 'aspl_equipment_qrcode_generator.report_equipment_label' data = { - 'equipment_label_layout_id':self.id + 'equipment_label_layout_id': self.id } - return self.env.ref(xml_id).report_action(None, data=data) \ No newline at end of file + # report_action benötigt die Equipment IDs als docids + return self.env.ref(xml_id).report_action(self.equipment_ids.ids, data=data) + + def _generate_qr_codes(self): + """Generiert QR-Codes für alle ausgewählten Equipment""" + import base64 + from io import BytesIO + import qrcode + + # Hole die base_url für die Equipment-Links + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + menu_id = self.env.ref('maintenance.menu_equipment_form').sudo().id + action_id = self.env.ref('maintenance.hr_equipment_action').sudo().id + + for equipment in self.equipment_ids: + # Extrahiere Namen aus JSONB falls nötig + equipment_name = equipment.name + if isinstance(equipment_name, dict): + equipment_name = equipment_name.get('de_DE') or equipment_name.get('en_US') or str(equipment_name) + + category_name = equipment.category_id.name if equipment.category_id else "" + if isinstance(category_name, dict): + category_name = category_name.get('de_DE') or category_name.get('en_US') or str(category_name) + + # Erstelle Equipment-Link + equipment_link = f"{base_url}/web#id={equipment.id}&menu_id={menu_id}&action={action_id}&model=maintenance.equipment&view_type=form" + + # Erstelle Equipment-Details für QR-Code + qr_data = f"Name: {equipment_name}\n" + qr_data += f"Model: {equipment.model or ''}\n" + qr_data += f"Mfg serial no: {equipment.serial_no or ''}\n" + qr_data += f"Warranty Exp. Date: {equipment.warranty_date or ''}\n" + qr_data += f"Category: {category_name}\n\n" + qr_data += equipment_link + + # Generiere QR-Code + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_L, + box_size=20, + border=4, + ) + qr.add_data(qr_data) + qr.make(fit=True) + img = qr.make_image() + temp = BytesIO() + img.save(temp, format="PNG") + qr_image = base64.b64encode(temp.getvalue()) + + # Speichere QR-Code im Equipment + equipment.write({'qr_code': qr_image}) \ No newline at end of file diff --git a/aspl_equipment_qrcode_generator/wizard/equipment_label_layout_views.xml b/aspl_equipment_qrcode_generator/wizard/equipment_label_layout_views.xml index 9e7dc5c..76548da 100644 --- a/aspl_equipment_qrcode_generator/wizard/equipment_label_layout_views.xml +++ b/aspl_equipment_qrcode_generator/wizard/equipment_label_layout_views.xml @@ -7,6 +7,7 @@
+