uploadte to corect preview and print the labels for 1 row

This commit is contained in:
2025-09-20 17:31:20 +03:00
parent beeaa02c35
commit 2c01db9fea
6 changed files with 865 additions and 120 deletions

357
py_app/app/pdf_generator.py Normal file
View File

@@ -0,0 +1,357 @@
"""
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

View File

@@ -1308,14 +1308,155 @@ def view_orders():
@bp.route('/get_unprinted_orders', methods=['GET']) @bp.route('/get_unprinted_orders', methods=['GET'])
def get_unprinted_orders(): def get_unprinted_orders():
"""Get all rows from order_for_labels where printed != 1""" """Get all rows from order_for_labels where printed != 1"""
print(f"DEBUG: get_unprinted_orders called. Session role: {session.get('role')}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
print(f"DEBUG: Access denied for role: {session.get('role')}")
return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403
try:
print("DEBUG: Calling get_unprinted_orders_data()")
data = get_unprinted_orders_data()
print(f"DEBUG: Retrieved {len(data)} orders")
return jsonify(data)
except Exception as e:
print(f"DEBUG: Error in get_unprinted_orders: {e}")
import traceback
traceback.print_exc()
return jsonify({'error': str(e)}), 500
@bp.route('/generate_labels_pdf/<int:order_id>', methods=['POST'])
def generate_labels_pdf(order_id):
"""Generate PDF labels for a specific order"""
print(f"DEBUG: generate_labels_pdf called for order_id: {order_id}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
print(f"DEBUG: Access denied for role: {session.get('role')}")
return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403
try:
from .pdf_generator import generate_order_labels_pdf, update_order_printed_status
from .print_module import get_db_connection
from flask import make_response
# Get order data from database
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
SELECT id, 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,
printed_labels, created_at, updated_at
FROM order_for_labels
WHERE id = %s
""", (order_id,))
row = cursor.fetchone()
conn.close()
if not row:
return jsonify({'error': 'Order not found'}), 404
# Create order data dictionary
order_data = {
'id': row[0],
'comanda_productie': row[1],
'cod_articol': row[2],
'descr_com_prod': row[3],
'cantitate': row[4],
'data_livrare': row[5],
'dimensiune': row[6],
'com_achiz_client': row[7],
'nr_linie_com_client': row[8],
'customer_name': row[9],
'customer_article_number': row[10],
'open_for_order': row[11],
'line_number': row[12],
'printed_labels': row[13] if row[13] is not None else 0,
'created_at': row[14],
'updated_at': row[15]
}
print(f"DEBUG: Generating PDF for order {order_id} with quantity {order_data['cantitate']}")
# Generate PDF
pdf_buffer = generate_order_labels_pdf(order_id, order_data)
# Update printed status in database
update_success = update_order_printed_status(order_id)
if not update_success:
print(f"Warning: Could not update printed status for order {order_id}")
# Create response with PDF
response = make_response(pdf_buffer.getvalue())
response.headers['Content-Type'] = 'application/pdf'
response.headers['Content-Disposition'] = f'inline; filename="labels_order_{order_id}_{order_data["comanda_productie"]}.pdf"'
print(f"DEBUG: PDF generated successfully for order {order_id}")
return response
except Exception as e:
print(f"DEBUG: Error generating PDF for order {order_id}: {e}")
import traceback
traceback.print_exc()
return jsonify({'error': str(e)}), 500
@bp.route('/get_order_data/<int:order_id>', methods=['GET'])
def get_order_data(order_id):
"""Get specific order data for preview"""
print(f"DEBUG: get_order_data called for order_id: {order_id}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied'}), 403 return jsonify({'error': 'Access denied'}), 403
try: try:
data = get_unprinted_orders_data() from .print_module import get_db_connection
return jsonify(data)
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
SELECT id, 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,
printed_labels, created_at, updated_at
FROM order_for_labels
WHERE id = %s
""", (order_id,))
row = cursor.fetchone()
conn.close()
if not row:
return jsonify({'error': 'Order not found'}), 404
order_data = {
'id': row[0],
'comanda_productie': row[1],
'cod_articol': row[2],
'descr_com_prod': row[3],
'cantitate': row[4],
'data_livrare': str(row[5]) if row[5] else 'N/A',
'dimensiune': row[6],
'com_achiz_client': row[7],
'nr_linie_com_client': row[8],
'customer_name': row[9],
'customer_article_number': row[10],
'open_for_order': row[11],
'line_number': row[12],
'printed_labels': row[13] if row[13] is not None else 0,
'created_at': str(row[14]) if row[14] else 'N/A',
'updated_at': str(row[15]) if row[15] else 'N/A'
}
return jsonify(order_data)
except Exception as e: except Exception as e:
print(f"DEBUG: Error getting order data for {order_id}: {e}")
import traceback
traceback.print_exc()
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
@warehouse_bp.route('/create_locations', methods=['GET', 'POST']) @warehouse_bp.route('/create_locations', methods=['GET', 'POST'])

View File

@@ -235,14 +235,28 @@ table tbody tr.selected td {
</div> </div>
</div> </div>
</div> </div>
<div style="width: 100%; display: flex; justify-content: center; align-items: center; gap: 12px; margin-top: 18px;">
<label for="print-label-btn" style="font-size: 14px; font-weight: 500; color: var(--app-card-text); margin-bottom: 0;">Print selected order</label> <!-- PDF Information -->
<button id="print-label-btn" class="btn btn-primary" style="font-size: 14px; padding: 6px 24px;">Print</button> <div style="width: 100%; margin-top: 15px; padding: 0 15px; text-align: center;">
<div style="background: #e8f4f8; border: 1px solid #bee5eb; border-radius: 6px; padding: 12px; font-size: 11px; color: #0c5460;">
<strong>📄 PDF Label Generation</strong><br>
Labels will be generated as PDF files for universal printing compatibility.<br>
<small style="color: #6c757d;">Works with any browser and printer - no extensions needed!</small>
</div>
</div>
<!-- Print Button Section -->
<div style="width: 100%; display: flex; justify-content: center; align-items: center; gap: 12px; margin-top: 15px;">
<label for="print-label-btn" style="font-size: 14px; font-weight: 500; color: var(--app-card-text); margin-bottom: 0;">Generate PDF Labels (80x110mm)</label>
<button id="print-label-btn" class="btn btn-success" style="font-size: 14px; padding: 8px 28px; border-radius: 6px;">📄 Generate PDF</button>
</div>
<div style="width: 100%; text-align: center; margin-top: 8px; color: #6c757d; font-size: 12px;">
Creates sequential labels based on quantity (e.g., CP00000711-001 to CP00000711-063)
</div> </div>
<div style="width: 100%; text-align: center; margin-top: 12px;"> <div style="width: 100%; text-align: center; margin-top: 12px;">
<a href="{{ url_for('main.download_extension') }}" target="_blank" style="font-size: 12px; color: #007bff; text-decoration: none;"> <small style="font-size: 11px; color: #6c757d;">
🔗 Install Direct Print Extension <EFBFBD> PDF labels can be printed directly from your browser or saved for later use
</a> </small>
</div> </div>
</div> </div>
@@ -281,9 +295,24 @@ table tbody tr.selected td {
<script> <script>
document.getElementById('check-db-btn').addEventListener('click', function() { document.getElementById('check-db-btn').addEventListener('click', function() {
console.log('Check Database button clicked');
// Show loading state
const button = this;
const originalText = button.textContent;
button.textContent = 'Loading...';
button.disabled = true;
fetch('/get_unprinted_orders') fetch('/get_unprinted_orders')
.then(response => response.json()) .then(response => {
console.log('Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => { .then(data => {
console.log('Received data:', data);
const tbody = document.getElementById('unprinted-orders-table'); const tbody = document.getElementById('unprinted-orders-table');
tbody.innerHTML = ''; tbody.innerHTML = '';
data.forEach((order, index) => { data.forEach((order, index) => {
@@ -354,10 +383,10 @@ document.getElementById('check-db-btn').addEventListener('click', function() {
if (data.length > 0) { if (data.length > 0) {
updateLabelPreview(data[0]); updateLabelPreview(data[0]);
// Add fallback print functionality if extension is not available // Add fallback print functionality if extension is not available
addFallbackPrintHandler(); addPDFGenerationHandler();
// Auto-select first row // Auto-select first row
setTimeout(() => { setTimeout(() => {
const firstRow = document.querySelector('.print-module-table tbody tr'); const firstRow = document.querySelector('.print-module-table tbody tr');
if (firstRow) { if (firstRow) {
@@ -389,6 +418,20 @@ document.getElementById('check-db-btn').addEventListener('click', function() {
document.getElementById('prod-order-value').textContent = 'Error'; document.getElementById('prod-order-value').textContent = 'Error';
document.getElementById('barcode-text').textContent = 'Error'; document.getElementById('barcode-text').textContent = 'Error';
document.getElementById('vertical-barcode-text').textContent = '000000-00'; document.getElementById('vertical-barcode-text').textContent = '000000-00';
// Reset button state
button.textContent = originalText;
button.disabled = false;
})
.catch(error => {
console.error('Error fetching orders:', error);
// Show error message to user
alert('Failed to load orders from database. Error: ' + error.message);
// Reset button state
button.textContent = originalText;
button.disabled = false;
}); });
}); });
@@ -437,80 +480,177 @@ function updateLabelPreview(order) {
document.getElementById('barcode-text').textContent = prodOrder; document.getElementById('barcode-text').textContent = prodOrder;
} }
// Fallback print handler for when Chrome extension is not available // PDF Generation System - No printer setup needed
// Labels are generated as PDF files for universal compatibility
// Legacy function name - now handles PDF generation
function addFallbackPrintHandler() { function addFallbackPrintHandler() {
// Check if Chrome extension modified the button // Initialize PDF print button
setTimeout(() => { const printButton = document.getElementById('print-label-btn');
const printButton = document.getElementById('print-label-btn');
// If button text hasn't changed, extension is not active if (printButton) {
if (printButton && !printButton.innerHTML.includes('🖨️ Print Direct')) { // Update button text and appearance for PDF generation
console.log('Chrome extension not detected, adding fallback print handler'); printButton.innerHTML = '<27> Generate PDF Labels';
printButton.title = 'Generate PDF with multiple labels based on quantity';
// Add fallback event listener printButton.addEventListener('click', function(e) {
printButton.addEventListener('click', function(e) { e.preventDefault();
e.preventDefault();
const labelPreview = document.getElementById('label-preview'); // Get selected order
if (!labelPreview) { const selectedRow = document.querySelector('.print-module-table tbody tr.selected');
alert('Please select an order first.'); if (!selectedRow) {
return; alert('Please select an order first from the table below.');
return;
}
const orderId = selectedRow.dataset.orderId;
const quantityCell = selectedRow.querySelector('td:nth-child(5)'); // Cantitate column
const quantity = quantityCell ? parseInt(quantityCell.textContent) : 1;
const prodOrderCell = selectedRow.querySelector('td:nth-child(2)'); // Comanda Productie column
const prodOrder = prodOrderCell ? prodOrderCell.textContent.trim() : 'N/A';
if (!orderId) {
alert('Could not determine order ID. Please refresh and try again.');
return;
}
// Show loading state
const originalText = this.textContent;
this.textContent = 'Generating PDF...';
this.disabled = true;
console.log(`Generating PDF for order ${orderId} with ${quantity} labels`);
// Generate PDF
fetch(`/generate_labels_pdf/${orderId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
// Create download link for PDF
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `labels_${prodOrder}_${quantity}pcs.pdf`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
// Also open PDF in new tab for printing
const printWindow = window.open(url, '_blank');
printWindow.focus();
// Show success message
alert(`✅ PDF generated successfully!\n📊 Order: ${prodOrder}\n📦 Labels: ${quantity} pieces\n\nThe PDF has been downloaded and opened for printing.`);
// Refresh the orders table to reflect printed status
document.getElementById('check-db-btn').click();
})
.catch(error => {
console.error('Error generating PDF:', error);
alert('❌ Failed to generate PDF labels. Error: ' + error.message);
})
.finally(() => {
// Reset button state
this.textContent = originalText;
this.disabled = false;
});
});
}
}
// PDF generation handler
function addPDFGenerationHandler() {
const printButton = document.getElementById('print-label-btn');
if (printButton) {
printButton.addEventListener('click', function(e) {
e.preventDefault();
// Get selected order
const selectedRow = document.querySelector('.print-module-table tbody tr.selected');
if (!selectedRow) {
alert('Please select an order first from the table below.');
return;
}
const orderId = selectedRow.dataset.orderId;
const prodOrder = selectedRow.querySelector('td:nth-child(2)').textContent.trim();
const quantity = selectedRow.querySelector('td:nth-child(5)').textContent.trim();
if (!orderId) {
alert('Error: Could not determine order ID.');
return;
}
// Show loading state
const originalText = printButton.innerHTML;
printButton.innerHTML = '⏳ Generating PDF...';
printButton.disabled = true;
// Generate PDF
fetch(`/generate_labels_pdf/${orderId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.error || `HTTP ${response.status}`);
});
} }
// Create a new window for printing // Get filename from response headers or create default
const printWindow = window.open('', '_blank'); const contentDisposition = response.headers.get('Content-Disposition');
const printContent = ` let filename = `labels_${prodOrder}_qty${quantity}.pdf`;
<!DOCTYPE html> if (contentDisposition) {
<html> const matches = contentDisposition.match(/filename="?([^"]+)"?/);
<head> if (matches) filename = matches[1];
<title>Quality Recticel Label</title> }
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
}
.print-container {
width: 210mm;
height: 297mm;
margin: 0 auto;
}
.label-wrapper {
transform: scale(1.2);
transform-origin: top left;
}
@media print {
body { padding: 0; }
.print-container { margin: 0; }
}
</style>
</head>
<body>
<div class="print-container">
<div class="label-wrapper">
${labelPreview.outerHTML}
</div>
</div>
</body>
</html>
`;
printWindow.document.write(printContent); return response.blob().then(blob => ({ blob, filename }));
printWindow.document.close(); })
.then(({ blob, filename }) => {
// Create download link
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
// Wait for content to load, then print // Show success message
printWindow.onload = function() { alert(`✅ PDF generated successfully!\n\n📄 ${quantity} labels created for ${prodOrder}\n📋 Sequential numbering: ${prodOrder}-001 to ${prodOrder}-${String(quantity).padStart(3, '0')}\n📁 File: ${filename}\n\n➡️ The PDF is ready to print from your browser!`);
printWindow.focus();
printWindow.print(); // Refresh the orders table to show updated print status
printWindow.close(); setTimeout(() => {
}; document.getElementById('check-db-btn').click();
}, 1000);
})
.catch(error => {
console.error('PDF generation error:', error);
alert(`❌ Error generating PDF: ${error.message}\n\nPlease try again or contact support.`);
})
.finally(() => {
// Reset button state
printButton.innerHTML = originalText;
printButton.disabled = false;
}); });
});
// Update button text to indicate fallback mode }
printButton.innerHTML = '🖨️ Print (Browser)';
printButton.title = 'Print using browser dialog - Install Chrome Extension for direct printing';
}
}, 2000); // Wait 2 seconds for extension to load
} }
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -33,10 +33,10 @@ async function handlePrintLabel(printData, sendResponse) {
// Check if printing API is available // Check if printing API is available
if (!chrome.printing || !chrome.printing.getPrinters) { if (!chrome.printing || !chrome.printing.getPrinters) {
console.error('Chrome printing API not available'); console.error('Chrome printing API not available - browser may be Brave, Edge, or older Chrome');
sendResponse({ sendResponse({
success: false, success: false,
error: 'Chrome printing API not available. Please ensure you are using Chrome 85+ and the extension has proper permissions.' error: 'Direct printing API not available in this browser. The extension works best in Google Chrome 85+. Using fallback method instead.'
}); });
return; return;
} }
@@ -50,13 +50,21 @@ async function handlePrintLabel(printData, sendResponse) {
return; return;
} }
// Find default printer or use first available // Find selected printer or use default/first available
const defaultPrinter = printers.find(p => p.isDefault) || printers[0]; let selectedPrinter;
console.log('Using printer:', defaultPrinter); if (printData.printerId && printData.printerId !== 'default') {
selectedPrinter = printers.find(p => p.id === printData.printerId);
}
if (!selectedPrinter) {
selectedPrinter = printers.find(p => p.isDefault) || printers[0];
}
console.log('Using printer:', selectedPrinter);
// Create print job // Create print job
const printJob = { const printJob = {
printerId: defaultPrinter.id, printerId: selectedPrinter.id,
ticket: { ticket: {
version: '1.0', version: '1.0',
print: { print: {
@@ -100,7 +108,7 @@ async function handlePrintLabel(printData, sendResponse) {
lastPrint: { lastPrint: {
timestamp: Date.now(), timestamp: Date.now(),
jobId: result.jobId, jobId: result.jobId,
printer: defaultPrinter.displayName printer: selectedPrinter.displayName
} }
}); });
} else { } else {
@@ -120,7 +128,7 @@ async function getPrinters(sendResponse) {
if (!chrome.printing || !chrome.printing.getPrinters) { if (!chrome.printing || !chrome.printing.getPrinters) {
sendResponse({ sendResponse({
success: false, success: false,
error: 'Chrome printing API not available' error: 'Direct printing API not available in this browser (works in Chrome only)'
}); });
return; return;
} }
@@ -144,38 +152,108 @@ chrome.runtime.onInstalled.addListener((details) => {
}); });
}); });
// Fallback print method using tabs API // Fallback print method using tabs API (works in Brave, Edge, Chrome)
async function handleFallbackPrint(printData, sendResponse) { async function handleFallbackPrint(printData, sendResponse) {
try { try {
console.log('Using fallback print method'); console.log('Using fallback print method for Brave/Edge/Chrome');
// Create a new tab with the print content // Create enhanced HTML with better print styles
const enhancedHTML = `
<!DOCTYPE html>
<html>
<head>
<title>Quality Recticel Label</title>
<style>
@page {
margin: 10mm;
size: A4;
}
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background: white;
}
.print-container {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.label-wrapper {
transform: scale(1.5);
transform-origin: center;
page-break-inside: avoid;
}
@media print {
body { padding: 0; margin: 0; }
.print-container { min-height: auto; padding: 20px 0; }
.no-print { display: none !important; }
}
.print-instructions {
position: fixed;
top: 10px;
right: 10px;
background: #007bff;
color: white;
padding: 10px;
border-radius: 5px;
font-size: 14px;
z-index: 1000;
}
</style>
<script>
window.onload = function() {
// Auto-print after 1 second
setTimeout(() => {
window.print();
// Auto-close after printing (or after 10 seconds)
setTimeout(() => {
window.close();
}, 2000);
}, 1000);
};
// Handle print events
window.onbeforeprint = function() {
console.log('Print dialog opened');
};
window.onafterprint = function() {
console.log('Print dialog closed');
setTimeout(() => {
window.close();
}, 500);
};
</script>
</head>
<body>
<div class="print-instructions no-print">
🖨️ Printing will start automatically...<br>
<small>If dialog doesn't appear, press Ctrl+P</small>
</div>
<div class="print-container">
<div class="label-wrapper">
${printData.html.replace(/<!DOCTYPE html>|<html>|<\/html>|<head>.*?<\/head>|<body>|<\/body>/gi, '')}
</div>
</div>
</body>
</html>
`;
// Create a new tab with the enhanced print content
const tab = await chrome.tabs.create({ const tab = await chrome.tabs.create({
url: 'data:text/html,' + encodeURIComponent(printData.html), url: 'data:text/html;charset=utf-8,' + encodeURIComponent(enhancedHTML),
active: false active: true // Make active so user can see the print dialog
}); });
// Wait for tab to load, then print console.log('Created print tab:', tab.id);
setTimeout(async () => { sendResponse({success: true, method: 'fallback_tab', tabId: tab.id});
try {
await chrome.tabs.executeScript(tab.id, {
code: 'window.print();'
});
// Close the tab after printing
setTimeout(() => {
chrome.tabs.remove(tab.id);
}, 1000);
sendResponse({success: true, method: 'fallback'});
} catch (error) {
sendResponse({success: false, error: 'Fallback print failed: ' + error.message});
}
}, 1000);
} catch (error) { } catch (error) {
console.error('Fallback print error:', error); console.error('Fallback print error:', error);
sendResponse({success: false, error: error.message}); sendResponse({success: false, error: 'Browser print fallback failed: ' + error.message});
} }
} }

View File

@@ -30,6 +30,9 @@ function initializePrintExtension() {
} }
function addExtensionStatusIndicator() { function addExtensionStatusIndicator() {
// Detect browser type
const browserType = getBrowserType();
// Create status indicator element // Create status indicator element
const statusIndicator = document.createElement('div'); const statusIndicator = document.createElement('div');
statusIndicator.id = 'extension-status'; statusIndicator.id = 'extension-status';
@@ -37,24 +40,39 @@ function addExtensionStatusIndicator() {
position: fixed; position: fixed;
top: 10px; top: 10px;
right: 10px; right: 10px;
background: #28a745; background: ${browserType === 'chrome' ? '#28a745' : '#ffc107'};
color: white; color: ${browserType === 'chrome' ? 'white' : 'black'};
padding: 5px 10px; padding: 8px 12px;
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
z-index: 9999; z-index: 9999;
box-shadow: 0 2px 4px rgba(0,0,0,0.2); box-shadow: 0 2px 4px rgba(0,0,0,0.2);
max-width: 250px;
`; `;
statusIndicator.textContent = '🖨️ Print Extension Active';
if (browserType === 'chrome') {
statusIndicator.textContent = '🖨️ Print Extension Active (Direct Print Available)';
} else {
statusIndicator.innerHTML = `🖨️ Print Extension Active<br><small>${browserType.toUpperCase()} - Browser Print Mode</small>`;
}
document.body.appendChild(statusIndicator); document.body.appendChild(statusIndicator);
// Hide after 3 seconds // Hide after 4 seconds (longer for non-Chrome browsers)
setTimeout(() => { setTimeout(() => {
statusIndicator.style.opacity = '0'; statusIndicator.style.opacity = '0';
statusIndicator.style.transition = 'opacity 0.5s'; statusIndicator.style.transition = 'opacity 0.5s';
setTimeout(() => statusIndicator.remove(), 500); setTimeout(() => statusIndicator.remove(), 500);
}, 3000); }, browserType === 'chrome' ? 3000 : 5000);
}
// Detect browser type
function getBrowserType() {
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.includes('edg/')) return 'edge';
if (userAgent.includes('brave')) return 'brave';
if (userAgent.includes('chrome') && !userAgent.includes('edg/')) return 'chrome';
return 'chromium';
} }
function overridePrintButton() { function overridePrintButton() {
@@ -71,9 +89,15 @@ function overridePrintButton() {
// Add new event listener for extension printing // Add new event listener for extension printing
newPrintButton.addEventListener('click', handleExtensionPrint); newPrintButton.addEventListener('click', handleExtensionPrint);
// Update button text to indicate direct printing // Update button text based on browser capabilities
newPrintButton.innerHTML = '🖨️ Print Direct'; const browserType = getBrowserType();
newPrintButton.title = 'Print directly to default printer (Chrome Extension)'; if (browserType === 'chrome') {
newPrintButton.innerHTML = '🖨️ Print Direct';
newPrintButton.title = 'Print directly to default printer (Chrome Extension)';
} else {
newPrintButton.innerHTML = '🖨️ Print Enhanced';
newPrintButton.title = `Enhanced printing for ${browserType.toUpperCase()} browser - Opens optimized print dialog`;
}
console.log('Print button override complete'); console.log('Print button override complete');
} else { } else {
@@ -103,12 +127,16 @@ function handleExtensionPrint(event) {
// Create complete HTML for printing // Create complete HTML for printing
const printHTML = createPrintableHTML(labelPreview); const printHTML = createPrintableHTML(labelPreview);
// Get selected printer from dropdown
const selectedPrinterId = window.getSelectedPrinter ? window.getSelectedPrinter() : 'default';
// Try direct printing first, then fallback // Try direct printing first, then fallback
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
action: 'print_label', action: 'print_label',
data: { data: {
html: printHTML, html: printHTML,
timestamp: Date.now() timestamp: Date.now(),
printerId: selectedPrinterId
} }
}, (response) => { }, (response) => {
if (response && response.success) { if (response && response.success) {
@@ -122,7 +150,8 @@ function handleExtensionPrint(event) {
action: 'fallback_print', action: 'fallback_print',
data: { data: {
html: printHTML, html: printHTML,
timestamp: Date.now() timestamp: Date.now(),
printerId: selectedPrinterId
} }
}, handlePrintResponse); }, handlePrintResponse);
} }