""" PDF Label Generator for Print Module Generates 80x110mm labels with sequential numbering based on quantity """ from reportlab.lib.pagesizes import letter, A4 from reportlab.lib.units import mm from reportlab.lib import colors from reportlab.pdfgen import canvas from reportlab.lib.styles import getSampleStyleSheet from reportlab.platypus import Paragraph from reportlab.lib.enums import TA_CENTER, TA_LEFT from reportlab.graphics.barcode import code128 from reportlab.graphics import renderPDF from reportlab.graphics.shapes import Drawing import os from flask import current_app import io def mm_to_points(mm_value): """Convert millimeters to points (ReportLab uses points)""" return mm_value * mm class LabelPDFGenerator: def __init__(self): # Label dimensions: 80mm x 110mm self.label_width = mm_to_points(80) self.label_height = mm_to_points(110) # Match the HTML preview dimensions exactly # Preview: 227.4px width x 321.3px height # Convert to proportional dimensions for 80x110mm self.content_width = mm_to_points(60) # ~227px scaled to 80mm self.content_height = mm_to_points(85) # ~321px scaled to 110mm # Position content in label, leaving space for barcodes self.content_x = mm_to_points(3) # 3mm from left edge self.content_y = mm_to_points(15) # 15mm from bottom (space for bottom barcode) # Row dimensions (9 rows total, row 6 is double height) self.row_height = self.content_height / 10 # 8.5mm per standard row self.double_row_height = self.row_height * 2 # Column split at 40% (90.96px / 227.4px = 40%) self.left_column_width = self.content_width * 0.4 self.right_column_width = self.content_width * 0.6 # Vertical divider starts from row 3 self.vertical_divider_start_y = self.content_y + self.content_height - (2 * self.row_height) def generate_labels_pdf(self, order_data, quantity): """ Generate PDF with multiple labels based on quantity Creates sequential labels: CP00000711-001 to CP00000711-XXX """ buffer = io.BytesIO() # Create canvas with label dimensions c = canvas.Canvas(buffer, pagesize=(self.label_width, self.label_height)) # Extract base production order number for sequential numbering prod_order = order_data.get('comanda_productie', 'CP00000000') # Generate labels for each quantity for i in range(1, quantity + 1): if i > 1: # Add new page for each label except first c.showPage() # Create sequential label number: CP00000711-001, CP00000711-002, etc. sequential_number = f"{prod_order}-{i:03d}" # Draw single label self._draw_label(c, order_data, sequential_number, i, quantity) c.save() buffer.seek(0) return buffer def _draw_label(self, canvas, order_data, sequential_number, current_num, total_qty): """Draw a single label matching the HTML preview layout exactly""" # Draw main content border (like the HTML preview rectangle) canvas.setStrokeColor(colors.black) canvas.setLineWidth(2) canvas.rect(self.content_x, self.content_y, self.content_width, self.content_height) # Calculate row positions from top current_y = self.content_y + self.content_height # Row 1: Company Header - "INNOFA RROMANIA SRL" row_y = current_y - self.row_height canvas.setFont("Helvetica-Bold", 10) text = "INNOFA RROMANIA SRL" text_width = canvas.stringWidth(text, "Helvetica-Bold", 10) x_centered = self.content_x + (self.content_width - text_width) / 2 canvas.drawString(x_centered, row_y + self.row_height/3, text) current_y = row_y # Row 2: Customer Name row_y = current_y - self.row_height canvas.setFont("Helvetica-Bold", 9) customer_name = str(order_data.get('customer_name', ''))[:30] text_width = canvas.stringWidth(customer_name, "Helvetica-Bold", 9) x_centered = self.content_x + (self.content_width - text_width) / 2 canvas.drawString(x_centered, row_y + self.row_height/3, customer_name) current_y = row_y # Draw horizontal lines after rows 1 and 2 canvas.setLineWidth(1) canvas.line(self.content_x, current_y, self.content_x + self.content_width, current_y) canvas.line(self.content_x, current_y + self.row_height, self.content_x + self.content_width, current_y + self.row_height) # Draw vertical divider line (starts from row 3, goes to bottom) vertical_x = self.content_x + self.left_column_width canvas.line(vertical_x, current_y, vertical_x, self.content_y) # Row 3: Quantity ordered row_y = current_y - self.row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Quantity ordered") canvas.setFont("Helvetica-Bold", 11) quantity = str(order_data.get('cantitate', '0')) q_text_width = canvas.stringWidth(quantity, "Helvetica-Bold", 11) q_x_centered = vertical_x + (self.right_column_width - q_text_width) / 2 canvas.drawString(q_x_centered, row_y + self.row_height/3, quantity) current_y = row_y # Row 4: Customer order row_y = current_y - self.row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Customer order") canvas.setFont("Helvetica-Bold", 10) customer_order = str(order_data.get('com_achiz_client', 'N/A'))[:15] co_text_width = canvas.stringWidth(customer_order, "Helvetica-Bold", 10) co_x_centered = vertical_x + (self.right_column_width - co_text_width) / 2 canvas.drawString(co_x_centered, row_y + self.row_height/3, customer_order) current_y = row_y # Row 5: Delivery date row_y = current_y - self.row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Delivery date") canvas.setFont("Helvetica-Bold", 10) delivery_date = str(order_data.get('data_livrare', 'N/A')) if delivery_date != 'N/A' and delivery_date: try: # Format date if it's a valid date from datetime import datetime if isinstance(delivery_date, str) and len(delivery_date) > 8: delivery_date = delivery_date[:10] # Take first 10 chars for date except: pass dd_text_width = canvas.stringWidth(delivery_date, "Helvetica-Bold", 10) dd_x_centered = vertical_x + (self.right_column_width - dd_text_width) / 2 canvas.drawString(dd_x_centered, row_y + self.row_height/3, delivery_date) current_y = row_y # Row 6: Description (double height) row_y = current_y - self.double_row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.double_row_height/2, "Description") # Handle description text wrapping for double height area canvas.setFont("Helvetica-Bold", 9) description = str(order_data.get('descr_com_prod', 'N/A')) max_chars_per_line = 18 lines = [] words = description.split() current_line = "" for word in words: if len(current_line + word + " ") <= max_chars_per_line: current_line += word + " " else: if current_line: lines.append(current_line.strip()) current_line = word + " " if current_line: lines.append(current_line.strip()) # Draw up to 3 lines in the double height area line_spacing = self.double_row_height / 4 start_y = row_y + 3 * line_spacing for i, line in enumerate(lines[:3]): line_y = start_y - (i * line_spacing) l_text_width = canvas.stringWidth(line, "Helvetica-Bold", 9) l_x_centered = vertical_x + (self.right_column_width - l_text_width) / 2 canvas.drawString(l_x_centered, line_y, line) current_y = row_y # Row 7: Size row_y = current_y - self.row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Size") canvas.setFont("Helvetica-Bold", 10) size = str(order_data.get('dimensiune', 'N/A'))[:12] s_text_width = canvas.stringWidth(size, "Helvetica-Bold", 10) s_x_centered = vertical_x + (self.right_column_width - s_text_width) / 2 canvas.drawString(s_x_centered, row_y + self.row_height/3, size) current_y = row_y # Row 8: Article Code row_y = current_y - self.row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Article Code") canvas.setFont("Helvetica-Bold", 10) article_code = str(order_data.get('cod_articol', 'N/A'))[:12] ac_text_width = canvas.stringWidth(article_code, "Helvetica-Bold", 10) ac_x_centered = vertical_x + (self.right_column_width - ac_text_width) / 2 canvas.drawString(ac_x_centered, row_y + self.row_height/3, article_code) current_y = row_y # Row 9: Prod Order (but we'll show sequential number instead) row_y = current_y - self.row_height canvas.setFont("Helvetica", 8) canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Prod Order") canvas.setFont("Helvetica-Bold", 10) # Show original production order prod_order = str(order_data.get('comanda_productie', 'N/A')) po_text_width = canvas.stringWidth(prod_order, "Helvetica-Bold", 10) po_x_centered = vertical_x + (self.right_column_width - po_text_width) / 2 canvas.drawString(po_x_centered, row_y + self.row_height/3, prod_order) # Draw all horizontal lines between rows (from row 3 onwards) for i in range(6): # 6 lines between 7 rows (3-9) line_y = self.content_y + self.content_height - (2 + i + 1) * self.row_height if i == 3: # Account for double height description row line_y -= self.row_height canvas.line(self.content_x, line_y, self.content_x + self.content_width, line_y) # Bottom horizontal barcode - positioned within label bounds barcode_area_height = mm_to_points(12) # Reserve space for barcode barcode_y = mm_to_points(5) # 5mm from bottom of label barcode_width = self.content_width # Use full content width barcode_x = self.content_x try: # Create barcode for sequential number barcode = code128.Code128(sequential_number, barWidth=0.25*mm, # Adjust bar width for better fit barHeight=mm_to_points(10)) # Increase height to 10mm # Always scale to fit the full allocated width scale_factor = barcode_width / barcode.width canvas.saveState() canvas.translate(barcode_x, barcode_y) canvas.scale(scale_factor, 1) barcode.drawOn(canvas, 0, 0) canvas.restoreState() # NO TEXT BELOW BARCODE - Remove all text rendering for horizontal barcode except Exception as e: # Fallback: Simple barcode pattern that fills the width canvas.setStrokeColor(colors.black) canvas.setFillColor(colors.black) bar_width = barcode_width / 50 # 50 bars across width for i in range(50): if i % 3 < 2: # Create barcode-like pattern x_pos = barcode_x + (i * bar_width) canvas.rect(x_pos, barcode_y, bar_width * 0.8, mm_to_points(8), fill=1) # Right side vertical barcode - positioned 3mm further right and fill frame vertical_barcode_x = self.content_x + self.content_width + mm_to_points(4) # Moved 3mm right (1mm + 3mm = 4mm) vertical_barcode_y = self.content_y vertical_barcode_height = self.content_height vertical_barcode_width = mm_to_points(12) # Increased width for better fill try: # Create vertical barcode code vertical_code = f"{current_num:03d}-{total_qty:02d}" # Create a vertical barcode using Code128 v_barcode = code128.Code128(vertical_code, barWidth=0.15*mm, # Thinner bars for better fit barHeight=mm_to_points(8)) # Increased bar height # Draw rotated barcode - fill the entire frame height canvas.saveState() canvas.translate(vertical_barcode_x + mm_to_points(6), vertical_barcode_y) canvas.rotate(90) # Always scale to fill the frame height scale_factor = vertical_barcode_height / v_barcode.width canvas.scale(scale_factor, 1) v_barcode.drawOn(canvas, 0, 0) canvas.restoreState() # NO TEXT FOR VERTICAL BARCODE - Remove all text rendering except Exception as e: # Fallback: Vertical barcode pattern that fills the frame canvas.setStrokeColor(colors.black) canvas.setFillColor(colors.black) bar_height = vertical_barcode_height / 60 # 60 bars across height for i in range(60): if i % 3 < 2: # Create barcode pattern y_pos = vertical_barcode_y + (i * bar_height) canvas.rect(vertical_barcode_x, y_pos, mm_to_points(8), bar_height * 0.8, fill=1) def generate_order_labels_pdf(order_id, order_data): """ Main function to generate PDF for an order with multiple labels """ try: generator = LabelPDFGenerator() # Get quantity from order data quantity = int(order_data.get('cantitate', 1)) # Generate PDF pdf_buffer = generator.generate_labels_pdf(order_data, quantity) return pdf_buffer except Exception as e: print(f"Error generating PDF labels: {e}") raise e def update_order_printed_status(order_id): """ Update the order status to printed in the database """ try: from .print_module import get_db_connection conn = get_db_connection() cursor = conn.cursor() # Check if printed_labels column exists cursor.execute("SHOW COLUMNS FROM order_for_labels LIKE 'printed_labels'") column_exists = cursor.fetchone() if column_exists: # Update printed status cursor.execute(""" UPDATE order_for_labels SET printed_labels = 1, updated_at = NOW() WHERE id = %s """, (order_id,)) else: # If column doesn't exist, we could add it or use another method print(f"Warning: printed_labels column doesn't exist for order {order_id}") conn.commit() conn.close() return True except Exception as e: print(f"Error updating printed status for order {order_id}: {e}") return False