Files
quality_app-v2/app/modules/labels/routes.py
Quality App Developer e53e3acc8e Implement print labels module with PDF generation, QZ Tray integration, and theme support
- Migrate print_labels.html and print_lost_labels.html to standalone pages with header and theme toggle
- Implement dark/light theme support using data-theme attribute and CSS variables
- Add PDF generation endpoints for single and batch label printing
- Copy pdf_generator.py from original app with full label formatting (80mm x 105mm)
- Fix data response handling to correctly access data.orders from API endpoints
- Synchronize table text sizes across both print pages
- Remove help buttons from print pages
- Database column rename: data_livrara → data_livrare for consistency
- Update routes to use correct database column names
- Add 7 test orders to database (4 unprinted, 3 printed)
- Implement QZ Tray integration with PDF fallback for label printing
- All CSS uses theme variables for dark/light mode synchronization
2026-02-04 23:57:51 +02:00

339 lines
12 KiB
Python

"""
Labels Module Routes
Handles label printing pages and API endpoints
"""
from flask import Blueprint, render_template, session, redirect, url_for, jsonify, request
import logging
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
)
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('/help/<page>', 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': '''
<h3>Print Labels - Thermal Printer Guide</h3>
<p>This module helps you print labels directly to thermal printers.</p>
<h4>Features:</h4>
<ul>
<li>Live label preview in thermal format</li>
<li>Real-time printer selection</li>
<li>Barcode generation</li>
<li>PDF export fallback</li>
<li>Batch printing support</li>
</ul>
<h4>How to use:</h4>
<ol>
<li>Select orders from the list</li>
<li>Preview labels in the preview pane</li>
<li>Select your printer</li>
<li>Click "Print Labels" to send to printer</li>
</ol>
'''
},
'print_labels': {
'title': 'Print Labels Help',
'content': '''
<h3>Print Labels - Thermal Printer Guide</h3>
<p>This module helps you print labels directly to thermal printers.</p>
<h4>Features:</h4>
<ul>
<li>Live label preview in thermal format</li>
<li>Real-time printer selection</li>
<li>Barcode generation</li>
<li>PDF export fallback</li>
<li>Batch printing support</li>
</ul>
<h4>How to use:</h4>
<ol>
<li>Select orders from the list</li>
<li>Preview labels in the preview pane</li>
<li>Select your printer</li>
<li>Click "Print Labels" to send to printer</li>
</ol>
'''
},
'print_lost_labels': {
'title': 'Print Lost Labels Help',
'content': '''
<h3>Print Lost Labels - Reprint Guide</h3>
<p>Use this page to search and reprint labels for orders that need reprinting.</p>
<h4>Features:</h4>
<ul>
<li>Search orders by production code</li>
<li>Filter previously printed orders</li>
<li>Reprint with updated information</li>
</ul>
<h4>How to use:</h4>
<ol>
<li>Enter the production order code</li>
<li>Click "Search" to find the order</li>
<li>Select the order and preview</li>
<li>Click "Reprint Labels" to print again</li>
</ol>
'''
}
}
help_data = help_pages.get(page, help_pages.get('index', {'title': 'Help', 'content': 'No help available'}))
return f'''
<html>
<head>
<title>{help_data['title']}</title>
<style>
body {{ font-family: Arial, sans-serif; padding: 20px; }}
h3 {{ color: #333; }}
ul, ol {{ margin: 10px 0; padding-left: 20px; }}
li {{ margin: 5px 0; }}
</style>
</head>
<body>
{help_data['content']}
</body>
</html>
'''
# ============================================================================
# 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/<int:order_id>', 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/<int:order_id>/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