Compare commits

...

2 Commits

Author SHA1 Message Date
Quality App Developer 2b6be2ba49 Fix QZ Tray library loading and add pairing keys API endpoint
- Fix QZ Tray library loading errors (net::ERR_NAME_NOT_RESOLVED) by switching from CDN to local static file
- Add /labels/api/pairing-keys endpoint to fetch valid printer pairing keys
- Update print_labels.html to use local js/qz-tray.js
- Update print_lost_labels.html to use local js/qz-tray.js
- Update print_lost_labels_new.html to use correct path js/qz-tray.js
- Update fg_scan.html to use local js/qz-tray.js
- API returns active pairing keys from qz_pairing_keys table for printer selection
2026-02-15 13:19:19 +02:00
Quality App Developer d5b043c762 Fix Docker build and add labels test data
- Fix Docker build issues:
  * Add missing system dependencies (build-essential, python3-dev, libpq-dev)
  * Fix requirements.txt formatting (separate Flask-Session and openpyxl)
  * Update openpyxl version to 3.1.5 (3.10.0 was invalid)
- Set proper folder permissions for Docker write access (app/ and data/)
- Add comprehensive test data for labels module:
  * 8 sample orders (TEST-ORD-001 through TEST-ORD-008)
  * Mix of printed/unprinted orders for testing
  * Various product types, customers, and delivery dates
  * Ready for QZ Tray printing functionality testing
- Include test data generation scripts for future use
- Application now fully containerized and ready for labels/printing testing
2026-02-15 12:05:20 +02:00
83 changed files with 457 additions and 6 deletions
+3
View File
@@ -11,8 +11,11 @@ ENV PYTHONUNBUFFERED=1 \
# Install system dependencies # Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
python3-dev \
mariadb-client \ mariadb-client \
curl \ curl \
libpq-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Create application directory # Create application directory
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
View File
Regular → Executable
+35
View File
@@ -487,4 +487,39 @@ def api_generate_batch_pdf(order_id):
except Exception as e: except Exception as e:
logger.error(f"Error generating batch PDF: {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 return jsonify({'error': str(e)}), 500
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
Regular → Executable
View File
View File
Regular → Executable
View File
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
+4 -1
View File
@@ -290,8 +290,11 @@
<!-- Add html2canvas library for capturing preview as image --> <!-- Add html2canvas library for capturing preview as image -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<!-- PATCHED QZ Tray library - works with custom server using pairing key authentication --> <!-- PATCHED QZ Tray library - works with custom server using pairing key authentication -->
<script src="{{ url_for('static', filename='js/qz-tray.js') }}"></script>
<!-- CDN versions (disabled):
<script src="https://qz.glyphtree.com/api/latest/qz-tray.js"></script> <script src="https://qz.glyphtree.com/api/latest/qz-tray.js"></script>
<!-- Original CDN version (disabled): <script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script> --> <script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script>
-->
<script> <script>
// Simplified notification system // Simplified notification system
+1 -1
View File
@@ -237,7 +237,7 @@
<!-- Add html2canvas library for capturing preview as image --> <!-- Add html2canvas library for capturing preview as image -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<!-- PATCHED QZ Tray library - works with custom server using pairing key authentication --> <!-- PATCHED QZ Tray library - works with custom server using pairing key authentication -->
<script src="https://qz.glyphtree.com/api/latest/qz-tray.js"></script> <script src="{{ url_for('static', filename='js/qz-tray.js') }}"></script>
<script> <script>
// Store all orders data for searching (will be populated by API fetch on page load) // Store all orders data for searching (will be populated by API fetch on page load)
+1 -1
View File
@@ -230,7 +230,7 @@
<!-- Add html2canvas library for capturing preview as image --> <!-- Add html2canvas library for capturing preview as image -->
<script src="{{ url_for('static', filename='html2canvas.min.js') }}"></script> <script src="{{ url_for('static', filename='html2canvas.min.js') }}"></script>
<!-- PATCHED QZ Tray library - works with custom server using pairing key authentication --> <!-- PATCHED QZ Tray library - works with custom server using pairing key authentication -->
<script src="{{ url_for('static', filename='qz-tray.js') }}"></script> <script src="{{ url_for('static', filename='js/qz-tray.js') }}"></script>
<script> <script>
// Store all orders data for searching // Store all orders data for searching
View File
View File
+1 -1
View File
@@ -4,7 +4,7 @@
{% block extra_css %} {% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/fg_scan.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/fg_scan.css') }}">
<script src="https://cdn.jsdelivr.net/npm/qz-tray@2.1.0/qz-tray.js"></script> <script src="{{ url_for('static', filename='js/qz-tray.js') }}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
Regular → Executable
View File
+1 -1
View File
@@ -1,4 +1,4 @@
version: '3.8' #version: '3.8'
services: services:
# MariaDB Database Service # MariaDB Database Service
+260
View File
@@ -0,0 +1,260 @@
#!/usr/bin/env python3
"""
Add Example/Test Data for Labels Module
Creates sample orders in order_for_labels table for testing printing functionality via QZ Tray
"""
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from app.database import get_db
import logging
from datetime import datetime, timedelta
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def add_sample_labels_data():
"""Add sample order data for testing labels/printing functionality"""
try:
conn = get_db()
cursor = conn.cursor()
# Check if table exists
cursor.execute("SHOW TABLES LIKE 'order_for_labels'")
if not cursor.fetchone():
logger.error("order_for_labels table does not exist. Please run initialize_db.py first.")
return False
# Clear existing test data (optional - comment out if you want to keep existing data)
logger.info("Clearing existing test data...")
cursor.execute("DELETE FROM order_for_labels WHERE comanda_productie LIKE 'TEST%'")
# Sample test data for different scenarios
test_orders = [
{
'comanda_productie': 'TEST-ORD-001',
'cod_articol': 'ART001',
'descr_com_prod': 'Electronic Component Type A - High Performance',
'cantitate': 150,
'com_achiz_client': 'CLIENT-PO-2024-001',
'nr_linie_com_client': '01',
'customer_name': 'TechCorp Industries',
'customer_article_number': 'TC-COMP-A001',
'open_for_order': 1,
'line_number': 1,
'printed_labels': 0, # Not printed - will show in unprinted list
'data_livrara': '2024-03-15', # Fixed field name to match DB schema
'dimensiune': 'Standard-Large'
},
{
'comanda_productie': 'TEST-ORD-002',
'cod_articol': 'ART002',
'descr_com_prod': 'Mechanical Part B - Precision Machined',
'cantitate': 75,
'com_achiz_client': 'CLIENT-PO-2024-002',
'nr_linie_com_client': '02',
'customer_name': 'MechParts Ltd',
'customer_article_number': 'MP-PART-B002',
'open_for_order': 1,
'line_number': 2,
'printed_labels': 0, # Not printed
'data_livrara': '2024-03-18',
'dimensiune': 'Medium'
},
{
'comanda_productie': 'TEST-ORD-003',
'cod_articol': 'ART003',
'descr_com_prod': 'Plastic Housing C - UV Resistant',
'cantitate': 200,
'com_achiz_client': 'CLIENT-PO-2024-003',
'nr_linie_com_client': '03',
'customer_name': 'PlasticWorks Inc',
'customer_article_number': 'PW-HOUSE-C003',
'open_for_order': 1,
'line_number': 3,
'printed_labels': 1, # Already printed - will show in printed list
'data_livrara': '2024-03-20',
'dimensiune': 'Large'
},
{
'comanda_productie': 'TEST-ORD-004',
'cod_articol': 'ART004',
'descr_com_prod': 'Metal Bracket D - Galvanized Steel',
'cantitate': 300,
'com_achiz_client': 'CLIENT-PO-2024-004',
'nr_linie_com_client': '04',
'customer_name': 'MetalCraft Solutions',
'customer_article_number': 'MC-BRACK-D004',
'open_for_order': 1,
'line_number': 4,
'printed_labels': 0, # Not printed
'data_livrara': '2024-03-22',
'dimensiune': 'Small'
},
{
'comanda_productie': 'TEST-ORD-005',
'cod_articol': 'ART005',
'descr_com_prod': 'Circuit Board E - Multi-layer PCB',
'cantitate': 50,
'com_achiz_client': 'CLIENT-PO-2024-005',
'nr_linie_com_client': '05',
'customer_name': 'CircuitTech Systems',
'customer_article_number': 'CT-PCB-E005',
'open_for_order': 1,
'line_number': 5,
'printed_labels': 0, # Not printed
'data_livrara': '2024-03-25',
'dimensiune': 'Standard'
},
{
'comanda_productie': 'TEST-ORD-006',
'cod_articol': 'ART006',
'descr_com_prod': 'Rubber Gasket F - High Temperature',
'cantitate': 500,
'com_achiz_client': 'CLIENT-PO-2024-006',
'nr_linie_com_client': '06',
'customer_name': 'RubberPro Manufacturing',
'customer_article_number': 'RP-GASKET-F006',
'open_for_order': 1,
'line_number': 6,
'printed_labels': 1, # Already printed
'data_livrara': '2024-03-28',
'dimensiune': 'Extra-Small'
},
{
'comanda_productie': 'TEST-ORD-007',
'cod_articol': 'ART007',
'descr_com_prod': 'Glass Panel G - Tempered Safety Glass',
'cantitate': 25,
'com_achiz_client': 'CLIENT-PO-2024-007',
'nr_linie_com_client': '07',
'customer_name': 'GlassTech Innovations',
'customer_article_number': 'GT-PANEL-G007',
'open_for_order': 1,
'line_number': 7,
'printed_labels': 0, # Not printed - urgent order
'data_livrara': '2024-03-12', # Past due date for testing
'dimensiune': 'Extra-Large'
},
{
'comanda_productie': 'TEST-ORD-008',
'cod_articol': 'ART008',
'descr_com_prod': 'Fabric Cover H - Water-Resistant Canvas',
'cantitate': 120,
'com_achiz_client': 'CLIENT-PO-2024-008',
'nr_linie_com_client': '08',
'customer_name': 'FabricWorks Design',
'customer_article_number': 'FW-COVER-H008',
'open_for_order': 0, # Not open for order - for testing
'line_number': 8,
'printed_labels': 0,
'data_livrara': '2024-04-01',
'dimensiune': 'Custom'
}
]
# Insert test data
logger.info(f"Inserting {len(test_orders)} test orders...")
for order in test_orders:
cursor.execute("""
INSERT INTO order_for_labels (
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,
printed_labels, data_livrara, dimensiune
) VALUES (
%(comanda_productie)s, %(cod_articol)s, %(descr_com_prod)s, %(cantitate)s,
%(com_achiz_client)s, %(nr_linie_com_client)s, %(customer_name)s,
%(customer_article_number)s, %(open_for_order)s, %(line_number)s,
%(printed_labels)s, %(data_livrara)s, %(dimensiune)s
)
""", order)
conn.commit()
cursor.close()
conn.close()
logger.info("✅ Successfully added test data for labels module!")
logger.info("Test data summary:")
logger.info(f" - Total orders: {len(test_orders)}")
logger.info(f" - Unprinted orders: {sum(1 for o in test_orders if o['printed_labels'] == 0)}")
logger.info(f" - Printed orders: {sum(1 for o in test_orders if o['printed_labels'] == 1)}")
logger.info(" - Test order codes: TEST-ORD-001 through TEST-ORD-008")
logger.info("\nYou can now test:")
logger.info(" 1. Navigate to /labels/print-module")
logger.info(" 2. View unprinted orders (should show 6 orders)")
logger.info(" 3. Generate PDF labels and test QZ Tray printing")
logger.info(" 4. Mark orders as printed and verify status updates")
return True
except Exception as e:
logger.error(f"Error adding test data: {e}")
return False
def verify_test_data():
"""Verify that test data was added correctly"""
try:
conn = get_db()
cursor = conn.cursor()
# Count total test orders
cursor.execute("SELECT COUNT(*) FROM order_for_labels WHERE comanda_productie LIKE 'TEST%'")
total_count = cursor.fetchone()[0]
# Count unprinted test orders
cursor.execute("SELECT COUNT(*) FROM order_for_labels WHERE comanda_productie LIKE 'TEST%' AND printed_labels = 0")
unprinted_count = cursor.fetchone()[0]
# Count printed test orders
cursor.execute("SELECT COUNT(*) FROM order_for_labels WHERE comanda_productie LIKE 'TEST%' AND printed_labels = 1")
printed_count = cursor.fetchone()[0]
logger.info(f"\n📋 Test Data Verification:")
logger.info(f" - Total test orders: {total_count}")
logger.info(f" - Unprinted orders: {unprinted_count}")
logger.info(f" - Printed orders: {printed_count}")
# List some sample orders
cursor.execute("""
SELECT comanda_productie, descr_com_prod, cantitate, printed_labels, data_livrara
FROM order_for_labels
WHERE comanda_productie LIKE 'TEST%'
ORDER BY comanda_productie
LIMIT 5
""")
orders = cursor.fetchall()
if orders:
logger.info(f"\n🏷️ Sample Test Orders:")
for order in orders:
status = "✅ Printed" if order[3] else "⏳ Unprinted"
logger.info(f" - {order[0]}: {order[1][:50]}... (Qty: {order[2]}, {status})")
cursor.close()
conn.close()
return True
except Exception as e:
logger.error(f"Error verifying test data: {e}")
return False
if __name__ == "__main__":
logger.info("🏷️ Adding Labels Module Test Data...")
if add_sample_labels_data():
verify_test_data()
logger.info("\n🎉 Test data successfully added!")
logger.info("\nNext steps:")
logger.info("1. Start the application: docker compose up -d")
logger.info("2. Navigate to: http://localhost:8080/labels/print-module")
logger.info("3. Test QZ Tray printing functionality")
else:
logger.error("❌ Failed to add test data")
sys.exit(1)
@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""
Add Example/Test Data for Labels Module - Direct Database Version
Creates sample orders in order_for_labels table for testing printing functionality via QZ Tray
"""
import pymysql
import logging
from datetime import datetime
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Database connection parameters
DB_CONFIG = {
'host': 'localhost',
'port': 3306,
'user': 'quality_user',
'password': 'qualitypass',
'database': 'quality_db',
'charset': 'utf8mb4'
}
def add_sample_labels_data():
"""Add sample order data for testing labels/printing functionality"""
try:
# Connect to database
conn = pymysql.connect(**DB_CONFIG)
cursor = conn.cursor()
# Check if table exists
cursor.execute("SHOW TABLES LIKE 'order_for_labels'")
if not cursor.fetchone():
logger.error("order_for_labels table does not exist. Please run initialize_db.py first.")
return False
# Clear existing test data (optional - comment out if you want to keep existing data)
logger.info("Clearing existing test data...")
cursor.execute("DELETE FROM order_for_labels WHERE comanda_productie LIKE 'TEST%'")
# Sample test data for different scenarios
test_orders = [
('TEST-ORD-001', 'ART001', 'Electronic Component Type A - High Performance', 150, 'CLIENT-PO-2024-001', '01', 'TechCorp Industries', 'TC-COMP-A001', 1, 1, 0, '2024-03-15', 'Standard-Large'),
('TEST-ORD-002', 'ART002', 'Mechanical Part B - Precision Machined', 75, 'CLIENT-PO-2024-002', '02', 'MechParts Ltd', 'MP-PART-B002', 1, 2, 0, '2024-03-18', 'Medium'),
('TEST-ORD-003', 'ART003', 'Plastic Housing C - UV Resistant', 200, 'CLIENT-PO-2024-003', '03', 'PlasticWorks Inc', 'PW-HOUSE-C003', 1, 3, 1, '2024-03-20', 'Large'),
('TEST-ORD-004', 'ART004', 'Metal Bracket D - Galvanized Steel', 300, 'CLIENT-PO-2024-004', '04', 'MetalCraft Solutions', 'MC-BRACK-D004', 1, 4, 0, '2024-03-22', 'Small'),
('TEST-ORD-005', 'ART005', 'Circuit Board E - Multi-layer PCB', 50, 'CLIENT-PO-2024-005', '05', 'CircuitTech Systems', 'CT-PCB-E005', 1, 5, 0, '2024-03-25', 'Standard'),
('TEST-ORD-006', 'ART006', 'Rubber Gasket F - High Temperature', 500, 'CLIENT-PO-2024-006', '06', 'RubberPro Manufacturing', 'RP-GASKET-F006', 1, 6, 1, '2024-03-28', 'Extra-Small'),
('TEST-ORD-007', 'ART007', 'Glass Panel G - Tempered Safety Glass', 25, 'CLIENT-PO-2024-007', '07', 'GlassTech Innovations', 'GT-PANEL-G007', 1, 7, 0, '2024-03-12', 'Extra-Large'),
('TEST-ORD-008', 'ART008', 'Fabric Cover H - Water-Resistant Canvas', 120, 'CLIENT-PO-2024-008', '08', 'FabricWorks Design', 'FW-COVER-H008', 0, 8, 0, '2024-04-01', 'Custom'),
]
# Insert test data
logger.info(f"Inserting {len(test_orders)} test orders...")
cursor.executemany("""
INSERT INTO order_for_labels (
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,
printed_labels, data_livrara, dimensiune
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""", test_orders)
conn.commit()
cursor.close()
conn.close()
logger.info("✅ Successfully added test data for labels module!")
logger.info("Test data summary:")
logger.info(f" - Total orders: {len(test_orders)}")
logger.info(f" - Unprinted orders: {sum(1 for o in test_orders if o[10] == 0)}") # printed_labels is index 10
logger.info(f" - Printed orders: {sum(1 for o in test_orders if o[10] == 1)}")
logger.info(" - Test order codes: TEST-ORD-001 through TEST-ORD-008")
logger.info("\nYou can now test:")
logger.info(" 1. Navigate to /labels/print-module")
logger.info(" 2. View unprinted orders (should show 6 orders)")
logger.info(" 3. Generate PDF labels and test QZ Tray printing")
logger.info(" 4. Mark orders as printed and verify status updates")
return True
except Exception as e:
logger.error(f"Error adding test data: {e}")
return False
def verify_test_data():
"""Verify that test data was added correctly"""
try:
conn = pymysql.connect(**DB_CONFIG)
cursor = conn.cursor()
# Count total test orders
cursor.execute("SELECT COUNT(*) FROM order_for_labels WHERE comanda_productie LIKE 'TEST%'")
total_count = cursor.fetchone()[0]
# Count unprinted test orders
cursor.execute("SELECT COUNT(*) FROM order_for_labels WHERE comanda_productie LIKE 'TEST%' AND printed_labels = 0")
unprinted_count = cursor.fetchone()[0]
# Count printed test orders
cursor.execute("SELECT COUNT(*) FROM order_for_labels WHERE comanda_productie LIKE 'TEST%' AND printed_labels = 1")
printed_count = cursor.fetchone()[0]
logger.info(f"\n📋 Test Data Verification:")
logger.info(f" - Total test orders: {total_count}")
logger.info(f" - Unprinted orders: {unprinted_count}")
logger.info(f" - Printed orders: {printed_count}")
# List some sample orders
cursor.execute("""
SELECT comanda_productie, descr_com_prod, cantitate, printed_labels, data_livrara
FROM order_for_labels
WHERE comanda_productie LIKE 'TEST%'
ORDER BY comanda_productie
LIMIT 5
""")
orders = cursor.fetchall()
if orders:
logger.info(f"\n🏷️ Sample Test Orders:")
for order in orders:
status = "✅ Printed" if order[3] else "⏳ Unprinted"
desc = order[1][:50] + "..." if len(order[1]) > 50 else order[1]
logger.info(f" - {order[0]}: {desc} (Qty: {order[2]}, {status})")
cursor.close()
conn.close()
return True
except Exception as e:
logger.error(f"Error verifying test data: {e}")
return False
if __name__ == "__main__":
logger.info("🏷️ Adding Labels Module Test Data...")
if add_sample_labels_data():
verify_test_data()
logger.info("\n🎉 Test data successfully added!")
logger.info("\nNext steps:")
logger.info("1. Navigate to: http://localhost:8781/labels/print-module")
logger.info("2. Test QZ Tray printing functionality")
logger.info("3. Use test orders TEST-ORD-001 through TEST-ORD-008")
else:
logger.error("❌ Failed to add test data")
+2 -1
View File
@@ -9,4 +9,5 @@ Markdown==3.5.1
APScheduler==3.10.4 APScheduler==3.10.4
reportlab==4.0.7 reportlab==4.0.7
python-barcode==0.15.1 python-barcode==0.15.1
Flask-Session==0.8.0openpyxl==3.10.0 Flask-Session==0.8.0
openpyxl==3.1.5