uploadte to corect preview and print the labels for 1 row
This commit is contained in:
Binary file not shown.
357
py_app/app/pdf_generator.py
Normal file
357
py_app/app/pdf_generator.py
Normal 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
|
||||||
@@ -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'])
|
||||||
|
|||||||
@@ -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 %}
|
||||||
@@ -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});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user