""" Labels Module Routes Handles label printing pages and API endpoints """ from flask import Blueprint, render_template, session, redirect, url_for, jsonify, request, flash import logging import json import uuid import os import tempfile from app.database import get_db from .print_module import ( get_unprinted_orders_data, get_printed_orders_data, update_order_printed_status, search_orders_by_cp_code ) from .import_labels import ( process_csv_file, process_excel_file, save_orders_to_database ) logger = logging.getLogger(__name__) labels_bp = Blueprint('labels', __name__, url_prefix='/labels') @labels_bp.route('/', methods=['GET']) def labels_index(): """Labels module home page""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/labels/index.html') @labels_bp.route('/print-module', methods=['GET']) def print_module(): """Label printing interface with thermal printer support""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/labels/print_module.html') @labels_bp.route('/print-labels', methods=['GET']) def print_labels(): """Original print labels interface - complete copy from quality app""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/labels/print_labels.html') @labels_bp.route('/print-lost-labels', methods=['GET']) def print_lost_labels(): """Print lost/missing labels interface""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/labels/print_lost_labels.html') @labels_bp.route('/import-labels', methods=['GET', 'POST']) def import_labels(): """Import labels data from CSV or Excel file""" if 'user_id' not in session: return redirect(url_for('main.login')) if request.method == 'POST': action = request.form.get('action', 'preview') if action == 'preview': # Handle file upload and show preview if 'file' not in request.files: flash('No file selected', 'error') return redirect(request.url) file = request.files['file'] if file.filename == '': flash('No file selected', 'error') return redirect(request.url) filename_lower = file.filename.lower() # Check file type if not (filename_lower.endswith('.csv') or filename_lower.endswith('.xlsx') or filename_lower.endswith('.xls')): flash('Please upload a CSV or Excel file (.csv, .xlsx, .xls)', 'error') return redirect(request.url) try: # Save file temporarily upload_id = str(uuid.uuid4()) temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) file.save(temp_file.name) temp_file.close() # Process file if filename_lower.endswith('.csv'): orders_data, errors, warnings = process_csv_file(temp_file.name) else: orders_data, errors, warnings = process_excel_file(temp_file.name) # Clean up temp file try: os.unlink(temp_file.name) except: pass # Save orders data to temp file temp_data_file = f'/tmp/upload_{upload_id}.json' with open(temp_data_file, 'w') as f: json.dump(orders_data, f) # Store in session session['upload_id'] = upload_id session['import_filename'] = file.filename session.modified = True # Get headers for preview database_fields = [ 'comanda_productie', 'cod_articol', 'descr_com_prod', 'cantitate', 'data_livrare', 'dimensiune', 'com_achiz_client', 'nr_linie_com_client', 'customer_name', 'customer_article_number', 'open_for_order', 'line_number' ] headers = [field for field in database_fields if field in orders_data[0].keys()] if orders_data else database_fields preview_data = orders_data[:10] # Flash any warnings/errors for warning in warnings[:5]: flash(warning, 'warning') if len(warnings) > 5: flash(f'... and {len(warnings) - 5} more warnings', 'warning') for error in errors[:10]: flash(error, 'error') if len(errors) > 10: flash(f'... and {len(errors) - 10} more errors', 'error') if not orders_data: flash('No valid data found in file', 'error') return redirect(request.url) return render_template('modules/labels/import_labels.html', preview_data=preview_data, headers=headers, show_preview=True, filename=file.filename, total_orders=len(orders_data)) except Exception as e: logger.error(f"Error processing import file: {e}") flash(f'Error processing file: {str(e)}', 'error') return redirect(request.url) elif action == 'save': # Save data to database upload_id = session.get('upload_id') if not upload_id: flash('No data to save. Please upload a file first.', 'error') return redirect(url_for('labels.import_labels')) try: # Load orders data from temp file temp_data_file = f'/tmp/upload_{upload_id}.json' with open(temp_data_file, 'r') as f: orders_data = json.load(f) # Save to database inserted_count, errors = save_orders_to_database(orders_data) # Clean up try: os.unlink(temp_data_file) except: pass session.pop('upload_id', None) session.pop('import_filename', None) session.modified = True # Flash results if errors: for error in errors[:5]: flash(error, 'error') if len(errors) > 5: flash(f'... and {len(errors) - 5} more errors', 'error') flash(f'Imported {inserted_count} orders with {len(errors)} errors', 'warning') else: flash(f'Successfully imported {inserted_count} orders for labels', 'success') return redirect(url_for('labels.import_labels')) except Exception as e: logger.error(f"Error saving import data: {e}") flash(f'Error saving data: {str(e)}', 'error') return redirect(url_for('labels.import_labels')) # GET request - show the import form return render_template('modules/labels/import_labels.html') @labels_bp.route('/help/', methods=['GET']) def help(page='index'): """Help page for labels module""" if 'user_id' not in session: return redirect(url_for('main.login')) # Map page names to help content help_pages = { 'print_module': { 'title': 'Print Module Help', 'content': '''

Print Labels - Thermal Printer Guide

This module helps you print labels directly to thermal printers.

Features:

How to use:

  1. Select orders from the list
  2. Preview labels in the preview pane
  3. Select your printer
  4. Click "Print Labels" to send to printer
''' }, 'print_labels': { 'title': 'Print Labels Help', 'content': '''

Print Labels - Thermal Printer Guide

This module helps you print labels directly to thermal printers.

Features:

How to use:

  1. Select orders from the list
  2. Preview labels in the preview pane
  3. Select your printer
  4. Click "Print Labels" to send to printer
''' }, 'print_lost_labels': { 'title': 'Print Lost Labels Help', 'content': '''

Print Lost Labels - Reprint Guide

Use this page to search and reprint labels for orders that need reprinting.

Features:

How to use:

  1. Enter the production order code
  2. Click "Search" to find the order
  3. Select the order and preview
  4. Click "Reprint Labels" to print again
''' } } help_data = help_pages.get(page, help_pages.get('index', {'title': 'Help', 'content': 'No help available'})) return f''' {help_data['title']} {help_data['content']} ''' # ============================================================================ # API Endpoints for Labels Module # ============================================================================ @labels_bp.route('/api/unprinted-orders', methods=['GET'], endpoint='api_unprinted_orders') def api_unprinted_orders(): """Get all unprinted orders for label printing""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: limit = request.args.get('limit', 100, type=int) if limit > 500: limit = 500 if limit < 1: limit = 1 orders = get_unprinted_orders_data(limit) return jsonify({'success': True, 'orders': orders, 'count': len(orders)}), 200 except Exception as e: logger.error(f"Error getting unprinted orders: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @labels_bp.route('/api/printed-orders', methods=['GET'], endpoint='api_printed_orders') def api_printed_orders(): """Get all printed orders""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: limit = request.args.get('limit', 100, type=int) if limit > 500: limit = 500 if limit < 1: limit = 1 orders = get_printed_orders_data(limit) return jsonify({'success': True, 'orders': orders, 'count': len(orders)}), 200 except Exception as e: logger.error(f"Error getting printed orders: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @labels_bp.route('/api/search-orders', methods=['POST'], endpoint='api_search_orders') def api_search_orders(): """Search for orders by CP code""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: data = request.get_json() cp_code = data.get('cp_code', '').strip() if not cp_code or len(cp_code) < 1: return jsonify({'success': False, 'error': 'CP code is required'}), 400 results = search_orders_by_cp_code(cp_code) return jsonify({'success': True, 'orders': results, 'count': len(results)}), 200 except Exception as e: logger.error(f"Error searching orders: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @labels_bp.route('/api/update-printed-status/', methods=['POST'], endpoint='api_update_printed_status') def api_update_printed_status(order_id): """Mark an order as printed""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: data = request.get_json() or {} printed = data.get('printed', True) success = update_order_printed_status(order_id, printed) if success: return jsonify({'success': True, 'message': 'Order status updated'}), 200 else: return jsonify({'success': False, 'error': 'Failed to update order'}), 500 except Exception as e: logger.error(f"Error updating order status: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @labels_bp.route('/api/generate-pdf', methods=['POST'], endpoint='api_generate_pdf') def api_generate_pdf(): """Generate single label PDF for thermal printing via QZ Tray""" if 'user_id' not in session: return jsonify({'error': 'Unauthorized'}), 401 try: from .pdf_generator import LabelPDFGenerator # Get order data from request order_data = request.get_json() if not order_data: return jsonify({'error': 'No order data provided'}), 400 # Extract piece number and total pieces for sequential numbering piece_number = order_data.get('piece_number', 1) total_pieces = order_data.get('total_pieces', 1) logger.info(f"Generating single label PDF for piece {piece_number} of {total_pieces}") # Initialize PDF generator in thermal printer optimized mode pdf_generator = LabelPDFGenerator(paper_saving_mode=True) # Generate single label PDF with specific piece number for sequential CP numbering pdf_buffer = pdf_generator.generate_single_label_pdf(order_data, piece_number, total_pieces, printer_optimized=True) # Create response with PDF data from flask import make_response response = make_response(pdf_buffer.getvalue()) response.headers['Content-Type'] = 'application/pdf' response.headers['Content-Disposition'] = f'inline; filename="label_{piece_number:03d}.pdf"' return response except Exception as e: logger.error(f"Error generating PDF: {e}") return jsonify({'error': str(e)}), 500 @labels_bp.route('/api/generate-pdf//true', methods=['POST'], endpoint='api_generate_batch_pdf') def api_generate_batch_pdf(order_id): """Generate all label PDFs for an order and mark as printed""" if 'user_id' not in session: return jsonify({'error': 'Unauthorized'}), 401 try: from .pdf_generator import LabelPDFGenerator # Get order data from database conn = get_db() cursor = conn.cursor() cursor.execute(""" SELECT id, comanda_productie, cod_articol, descr_com_prod, cantitate, com_achiz_client, nr_linie_com_client, customer_name, customer_article_number, open_for_order, line_number, created_at, updated_at, printed_labels, data_livrare, dimensiune FROM order_for_labels WHERE id = %s """, (order_id,)) row = cursor.fetchone() if not row: cursor.close() return jsonify({'error': 'Order not found'}), 404 # Create order data dictionary columns = [col[0] for col in cursor.description] order_data = {columns[i]: row[i] for i in range(len(columns))} # Ensure date fields are strings if order_data.get('data_livrare'): order_data['data_livrare'] = str(order_data['data_livrare']) cursor.close() logger.info(f"Generating batch PDF for order {order_id} with {order_data.get('cantitate', 0)} labels") # Initialize PDF generator pdf_generator = LabelPDFGenerator(paper_saving_mode=True) # Get quantity from order data quantity = int(order_data.get('cantitate', 1)) # Generate PDF with all labels pdf_buffer = pdf_generator.generate_labels_pdf(order_data, quantity, printer_optimized=True) # Mark order as printed success = update_order_printed_status(order_id, True) if not success: logger.warning(f"Failed to mark order {order_id} as printed, but PDF was generated") # Create response with PDF data from flask import make_response response = make_response(pdf_buffer.getvalue()) response.headers['Content-Type'] = 'application/pdf' response.headers['Content-Disposition'] = f'attachment; filename="labels_{order_data.get("comanda_productie", "unknown")}.pdf"' return response except Exception as e: logger.error(f"Error generating batch PDF: {e}") return jsonify({'error': str(e)}), 500 @labels_bp.route('/api/pairing-keys', methods=['GET'], endpoint='api_pairing_keys') def api_pairing_keys(): """Get QZ Tray pairing keys for printer selection""" if 'user_id' not in session: return jsonify({'error': 'Unauthorized'}), 401 try: conn = get_db() cursor = conn.cursor() cursor.execute(""" SELECT id, printer_name, pairing_key, valid_until FROM qz_pairing_keys WHERE valid_until >= CURDATE() ORDER BY printer_name ASC """) pairing_keys = [] for row in cursor.fetchall(): pairing_keys.append({ 'id': row[0], 'printer_name': row[1], 'pairing_key': row[2] }) cursor.close() logger.info(f"Retrieved {len(pairing_keys)} valid pairing keys") return jsonify(pairing_keys), 200 except Exception as e: logger.error(f"Error fetching pairing keys: {e}") return jsonify({'error': str(e)}), 500