it prints
This commit is contained in:
@@ -35,6 +35,71 @@
|
||||
background-color: #007bff !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* Print Progress Modal Styles */
|
||||
.print-progress-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.print-progress-content {
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
min-width: 400px;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.print-progress-content h3 {
|
||||
margin: 0 0 20px 0;
|
||||
color: #333;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
margin-bottom: 15px;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #007bff 0%, #0056b3 100%);
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.progress-details {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -44,6 +109,20 @@
|
||||
<div class="card scan-form-card" style="display: flex; flex-direction: column; justify-content: flex-start; align-items: center; min-height: 700px; width: 330px; flex-shrink: 0; position: relative; padding: 15px;">
|
||||
<div class="label-view-title" style="width: 100%; text-align: center; padding: 0 0 15px 0; font-size: 18px; font-weight: bold; letter-spacing: 0.5px;">Label View</div>
|
||||
|
||||
<!-- Add link to pairing key management -->
|
||||
<div style="width: 100%; text-align: center; margin-bottom: 10px;">
|
||||
<a href="{{ url_for('main.download_extension') }}" class="btn btn-info btn-sm" target="_blank">🔑 Manage Pairing Keys</a>
|
||||
</div>
|
||||
|
||||
<!-- Client/Printer selection dropdown -->
|
||||
<div style="width: 100%; text-align: center; margin-bottom: 10px;">
|
||||
<label for="client-select" style="font-weight: 600;">Select Printer/Client:</label>
|
||||
<select id="client-select" class="form-control form-control-sm" style="width: 80%; margin: 0 auto;"></select>
|
||||
</div>
|
||||
|
||||
<!-- Display pairing key for selected client -->
|
||||
<div id="pairing-key-display" style="width: 100%; text-align: center; margin-bottom: 15px; font-size: 13px; color: #007bff; font-family: monospace;"></div>
|
||||
|
||||
<!-- Label Preview Section -->
|
||||
<div id="label-preview" style="border: 1px solid #ddd; padding: 10px; position: relative; background: #fafafa; width: 301px; height: 434.7px;">
|
||||
<!-- Label content rectangle -->
|
||||
@@ -209,11 +288,19 @@
|
||||
<div style="font-size: 10px; color: #495057; margin-bottom: 10px; line-height: 1.3;">
|
||||
Professional printing solution • ZPL thermal labels • Direct hardware access
|
||||
</div>
|
||||
<a href="https://qz.io/download/" target="_blank" class="btn btn-info btn-sm" style="font-size: 10px; padding: 4px 12px; text-decoration: none;">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<button onclick="initializeQZTray()" class="btn btn-primary btn-sm" style="font-size: 10px; padding: 4px 12px;">
|
||||
🔄 Reconnect to QZ Tray
|
||||
</button>
|
||||
<button onclick="testQZConnection()" class="btn btn-info btn-sm" style="font-size: 10px; padding: 4px 12px;">
|
||||
🔍 Test Connection
|
||||
</button>
|
||||
</div>
|
||||
<a href="https://qz.io/download/" target="_blank" class="btn btn-secondary btn-sm" style="font-size: 10px; padding: 4px 12px; text-decoration: none;">
|
||||
📥 Download QZ Tray (Free)
|
||||
</a>
|
||||
<div style="font-size: 9px; color: #6c757d; margin-top: 8px; line-height: 1.2;">
|
||||
<strong>Setup:</strong> 1. Install QZ Tray → 2. Start the service → 3. Refresh this page
|
||||
<strong>Setup:</strong> 1. Install QZ Tray → 2. Start the service → 3. Click "Reconnect"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -253,10 +340,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Printing Progress Modal -->
|
||||
<div id="print-progress-modal" class="print-progress-modal" style="display: none;">
|
||||
<div class="print-progress-content">
|
||||
<h3>🖨️ Printing Labels</h3>
|
||||
<div class="progress-info">
|
||||
<span id="progress-text">Preparing to print...</span>
|
||||
</div>
|
||||
<div class="progress-bar-container">
|
||||
<div id="progress-bar" class="progress-bar"></div>
|
||||
</div>
|
||||
<div class="progress-details">
|
||||
<span id="progress-count">0 / 0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- QZ Tray JavaScript Library -->
|
||||
<!-- 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://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script>
|
||||
<!-- PATCHED QZ Tray library - works with custom server using pairing key authentication -->
|
||||
<script src="{{ url_for('static', filename='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>
|
||||
// Simplified notification system
|
||||
@@ -271,25 +376,31 @@ function showNotification(message, type = 'info') {
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
max-width: 350px;
|
||||
max-width: 450px;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
`;
|
||||
|
||||
// Convert newlines to <br> tags for proper display
|
||||
const formattedMessage = message.replace(/\n/g, '<br>');
|
||||
|
||||
notification.innerHTML = `
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<span style="flex: 1; padding-right: 10px;">${message}</span>
|
||||
<button type="button" onclick="this.parentElement.parentElement.remove()" style="background: none; border: none; font-size: 20px; cursor: pointer;">×</button>
|
||||
<div style="display: flex; align-items: flex-start; justify-content: space-between;">
|
||||
<span style="flex: 1; padding-right: 10px; white-space: pre-wrap; font-family: monospace; font-size: 12px;">${formattedMessage}</span>
|
||||
<button type="button" onclick="this.parentElement.parentElement.remove()" style="background: none; border: none; font-size: 20px; cursor: pointer; flex-shrink: 0;">×</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Longer timeout for error messages
|
||||
const timeout = type === 'error' ? 15000 : 5000;
|
||||
setTimeout(() => {
|
||||
if (notification.parentElement) {
|
||||
notification.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
// Database loading functionality
|
||||
@@ -404,6 +515,28 @@ document.getElementById('check-db-btn').addEventListener('click', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Fetch pairing keys and populate dropdown
|
||||
fetch('/get_pairing_keys')
|
||||
.then(response => response.json())
|
||||
.then(keys => {
|
||||
const select = document.getElementById('client-select');
|
||||
select.innerHTML = '';
|
||||
keys.forEach(key => {
|
||||
const option = document.createElement('option');
|
||||
option.value = key.pairing_key;
|
||||
option.textContent = key.printer_name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
// Show first key by default
|
||||
if (keys.length > 0) {
|
||||
document.getElementById('pairing-key-display').textContent = 'Pairing Key: ' + keys[0].pairing_key;
|
||||
}
|
||||
});
|
||||
// Update pairing key display on selection
|
||||
document.getElementById('client-select').addEventListener('change', function() {
|
||||
document.getElementById('pairing-key-display').textContent = 'Pairing Key: ' + this.value;
|
||||
});
|
||||
|
||||
// Update label preview with order data
|
||||
function updateLabelPreview(order) {
|
||||
const customerName = order.customer_name || 'N/A';
|
||||
@@ -472,15 +605,51 @@ let availablePrinters = [];
|
||||
// Initialize QZ Tray connection
|
||||
async function initializeQZTray() {
|
||||
try {
|
||||
// Check if QZ Tray is available
|
||||
console.log('🔍 Checking for QZ Tray...');
|
||||
|
||||
// Check if QZ Tray library is loaded
|
||||
if (typeof qz === 'undefined') {
|
||||
throw new Error('QZ Tray library not loaded');
|
||||
console.error('❌ QZ Tray library not loaded');
|
||||
const errorMsg = '❌ QZ Tray Library Not Loaded\n\n' +
|
||||
'The patched QZ Tray JavaScript library failed to load.\n' +
|
||||
'Check the browser console for errors and refresh the page.\n\n' +
|
||||
'Path: /static/qz-tray.js (patched version for pairing key auth)';
|
||||
document.getElementById('qztray-status').textContent = 'Library Error';
|
||||
document.getElementById('qztray-status').className = 'badge badge-danger';
|
||||
showNotification(errorMsg, 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('✅ QZ Tray library loaded');
|
||||
|
||||
// Using PATCHED qz-tray.js that works with our custom QZ Tray server
|
||||
// The patched library automatically skips certificate validation
|
||||
// Our custom server uses ONLY pairing key (HMAC) authentication
|
||||
console.log('🔒 Using patched qz-tray.js for pairing-key authentication...');
|
||||
|
||||
// No security setup needed - the patched library handles it
|
||||
// Original qz-tray.js required setCertificatePromise, but our patch bypasses it
|
||||
|
||||
console.log('✅ Ready to connect to custom QZ Tray server');
|
||||
console.log('🔌 Attempting to connect to QZ Tray on client PC...');
|
||||
console.log('📍 Will try: ws://localhost:8181 (insecure) then wss://localhost:8182 (secure)');
|
||||
|
||||
// Connect to QZ Tray
|
||||
// Set connection closed callback
|
||||
qz.websocket.setClosedCallbacks(function() {
|
||||
console.warn('⚠️ QZ Tray connection closed');
|
||||
document.getElementById('qztray-status').textContent = 'Disconnected';
|
||||
document.getElementById('qztray-status').className = 'badge badge-warning';
|
||||
});
|
||||
|
||||
// Connect to QZ Tray running on client PC
|
||||
console.log('⏳ Connecting...');
|
||||
await qz.websocket.connect();
|
||||
qzTray = qz;
|
||||
|
||||
const version = await qz.api.getVersion();
|
||||
console.log('✅ QZ Tray connected successfully');
|
||||
console.log('📋 QZ Tray Version:', version);
|
||||
|
||||
// Update status
|
||||
document.getElementById('qztray-status').textContent = 'Connected';
|
||||
document.getElementById('qztray-status').className = 'badge badge-success';
|
||||
@@ -488,20 +657,227 @@ async function initializeQZTray() {
|
||||
// Load available printers
|
||||
await loadQZTrayPrinters();
|
||||
|
||||
console.log('✅ QZ Tray connected successfully');
|
||||
showNotification('🖨️ QZ Tray connected successfully!', 'success');
|
||||
showNotification(`🖨️ QZ Tray v${version} connected successfully!`, 'success');
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ QZ Tray connection failed:', error);
|
||||
document.getElementById('qztray-status').textContent = 'Not Connected';
|
||||
console.error('Error details:', {
|
||||
message: error.message,
|
||||
type: typeof error,
|
||||
errorName: error.name,
|
||||
stack: error.stack
|
||||
});
|
||||
|
||||
// Detailed error messages based on actual error
|
||||
let errorMsg = '❌ Cannot Connect to QZ Tray\n\n';
|
||||
let statusText = 'Not Connected';
|
||||
|
||||
const errorStr = error.toString().toLowerCase();
|
||||
const messageStr = (error.message || '').toLowerCase();
|
||||
|
||||
if (messageStr.includes('unable to establish') ||
|
||||
errorStr.includes('unable to establish') ||
|
||||
messageStr.includes('failed to connect') ||
|
||||
errorStr.includes('websocket') ||
|
||||
messageStr.includes('econnrefused')) {
|
||||
|
||||
errorMsg += '🔌 Connection Refused\n\n';
|
||||
errorMsg += 'QZ Tray is not responding on this computer.\n\n';
|
||||
errorMsg += '✅ Steps to fix:\n';
|
||||
errorMsg += '1. Check if QZ Tray is installed on THIS computer\n';
|
||||
errorMsg += '2. Look for QZ Tray icon in system tray (bottom-right)\n';
|
||||
errorMsg += '3. If not running, start QZ Tray application\n';
|
||||
errorMsg += '4. If installed but not working, restart QZ Tray\n';
|
||||
errorMsg += '5. Download from: https://qz.io/download/\n\n';
|
||||
errorMsg += '🔍 Technical: Trying to connect to ports 8181/8182';
|
||||
statusText = 'Not Running';
|
||||
|
||||
} else if (messageStr.includes('certificate') ||
|
||||
errorStr.includes('certificate') ||
|
||||
messageStr.includes('security') ||
|
||||
messageStr.includes('ssl') ||
|
||||
messageStr.includes('tls')) {
|
||||
|
||||
errorMsg += '🔒 Certificate Security Issue\n\n';
|
||||
errorMsg += 'QZ Tray uses a self-signed certificate for security.\n\n';
|
||||
errorMsg += '✅ Steps to fix:\n';
|
||||
errorMsg += '1. Open new tab: https://localhost:8182\n';
|
||||
errorMsg += '2. Accept/Trust the security certificate\n';
|
||||
errorMsg += '3. Come back and click "Reconnect to QZ Tray"\n\n';
|
||||
errorMsg += '🔍 This is normal and safe for QZ Tray';
|
||||
statusText = 'Certificate Error';
|
||||
|
||||
} else {
|
||||
errorMsg += '⚠️ Unexpected Error\n\n';
|
||||
errorMsg += 'Error: ' + error.message + '\n\n';
|
||||
errorMsg += '🔍 Troubleshooting:\n';
|
||||
errorMsg += '1. Open browser console (F12) for details\n';
|
||||
errorMsg += '2. Click "Test Connection" for diagnostics\n';
|
||||
errorMsg += '3. Make sure QZ Tray is running\n';
|
||||
errorMsg += '4. Try restarting your browser';
|
||||
}
|
||||
|
||||
document.getElementById('qztray-status').textContent = statusText;
|
||||
document.getElementById('qztray-status').className = 'badge badge-danger';
|
||||
|
||||
showNotification('❌ QZ Tray not available. Please install and start QZ Tray.', 'error');
|
||||
showNotification(errorMsg, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Manual test connection function
|
||||
async function testQZConnection() {
|
||||
console.log('🔍 ========== QZ TRAY CONNECTION TEST ==========');
|
||||
|
||||
const statusElement = document.getElementById('qztray-status');
|
||||
const originalStatus = statusElement.textContent;
|
||||
statusElement.textContent = 'Testing...';
|
||||
statusElement.className = 'badge badge-warning';
|
||||
|
||||
let testResults = [];
|
||||
|
||||
try {
|
||||
// Test 1: Check if library is loaded
|
||||
console.log('Test 1: Checking library...');
|
||||
if (typeof qz === 'undefined') {
|
||||
testResults.push('❌ Test 1: Library NOT loaded from CDN');
|
||||
throw new Error('QZ Tray library not loaded from CDN');
|
||||
}
|
||||
testResults.push('✅ Test 1: Library loaded successfully');
|
||||
console.log('✅ Test 1 PASSED: Library loaded from local (patched version)');
|
||||
|
||||
// Using patched qz-tray.js - no security setup needed
|
||||
console.log('🔒 Using patched library for custom QZ Tray...');
|
||||
|
||||
// Test 2: Check if already connected
|
||||
console.log('Test 2: Checking existing connection...');
|
||||
if (qz.websocket.isActive()) {
|
||||
testResults.push('✅ Test 2: Already connected!');
|
||||
console.log('✅ Test 2 PASSED: Already connected');
|
||||
const version = await qz.api.getVersion();
|
||||
testResults.push(`✅ Version: ${version}`);
|
||||
console.log(`📋 QZ Tray Version: ${version}`);
|
||||
|
||||
const printers = await qz.printers.find();
|
||||
testResults.push(`✅ Found ${printers.length} printer(s)`);
|
||||
console.log(`🖨️ Printers found: ${printers.length}`);
|
||||
|
||||
showNotification(testResults.join('\n'), 'success');
|
||||
statusElement.textContent = 'Connected';
|
||||
statusElement.className = 'badge badge-success';
|
||||
console.log('========== TEST COMPLETED: ALL PASSED ==========');
|
||||
return;
|
||||
}
|
||||
testResults.push('⚠️ Test 2: Not currently connected');
|
||||
console.log('⚠️ Test 2: Not connected, will attempt connection...');
|
||||
|
||||
// Test 3: Try to connect
|
||||
console.log('Test 3: Attempting WebSocket connection...');
|
||||
testResults.push('🔌 Test 3: Connecting to QZ Tray...');
|
||||
console.log('🔌 Connecting to WebSocket...');
|
||||
|
||||
await qz.websocket.connect();
|
||||
testResults.push('✅ Test 3: WebSocket connected!');
|
||||
console.log('✅ Test 3 PASSED: WebSocket connected');
|
||||
|
||||
// Test 4: Get version
|
||||
console.log('Test 4: Getting QZ Tray version...');
|
||||
const version = await qz.api.getVersion();
|
||||
testResults.push(`✅ Test 4: QZ Tray v${version}`);
|
||||
console.log(`✅ Test 4 PASSED: Version ${version}`);
|
||||
|
||||
// Test 5: List printers
|
||||
console.log('Test 5: Fetching printer list...');
|
||||
const printers = await qz.printers.find();
|
||||
testResults.push(`✅ Test 5: Found ${printers.length} printer(s)`);
|
||||
console.log(`✅ Test 5 PASSED: ${printers.length} printers found`);
|
||||
|
||||
if (printers.length > 0) {
|
||||
console.log('📋 Available printers:', printers);
|
||||
testResults.push('📋 Printers: ' + printers.slice(0, 3).join(', ') + (printers.length > 3 ? '...' : ''));
|
||||
}
|
||||
|
||||
// Success!
|
||||
qzTray = qz;
|
||||
statusElement.textContent = 'Connected';
|
||||
statusElement.className = 'badge badge-success';
|
||||
|
||||
console.log('========== TEST COMPLETED: ALL PASSED ==========');
|
||||
showNotification(testResults.join('\n') + '\n\n✅ All tests passed!', 'success');
|
||||
|
||||
// Reload printers
|
||||
await loadQZTrayPrinters();
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ TEST FAILED:', error);
|
||||
console.error('Error type:', typeof error);
|
||||
console.error('Error name:', error.name);
|
||||
console.error('Error message:', error.message);
|
||||
console.error('Error stack:', error.stack);
|
||||
|
||||
statusElement.textContent = originalStatus;
|
||||
statusElement.className = 'badge badge-danger';
|
||||
|
||||
const errorStr = error.toString().toLowerCase();
|
||||
const messageStr = (error.message || '').toLowerCase();
|
||||
|
||||
let errorMsg = '❌ Connection Test Results:\n\n';
|
||||
errorMsg += testResults.join('\n') + '\n\n';
|
||||
|
||||
if (typeof qz === 'undefined') {
|
||||
errorMsg += '❌ FAILED: Library Load Error\n\n';
|
||||
errorMsg += '📚 The QZ Tray JavaScript library did not load.\n\n';
|
||||
errorMsg += 'Fix:\n';
|
||||
errorMsg += '• Check browser console for errors (F12)\n';
|
||||
errorMsg += '• Refresh the page (F5)\n';
|
||||
errorMsg += '• Check if library loaded:\n';
|
||||
errorMsg += ' /static/qz-tray.js (patched version)';
|
||||
|
||||
} else if (messageStr.includes('unable to establish') ||
|
||||
errorStr.includes('unable to establish') ||
|
||||
messageStr.includes('failed to connect') ||
|
||||
messageStr.includes('websocket') ||
|
||||
errorStr.includes('websocket error')) {
|
||||
|
||||
errorMsg += '❌ FAILED: Cannot Connect to QZ Tray\n\n';
|
||||
errorMsg += '🔌 QZ Tray is not running on this computer.\n\n';
|
||||
errorMsg += 'Fix:\n';
|
||||
errorMsg += '1. Check if QZ Tray is installed\n';
|
||||
errorMsg += '2. Look in system tray (bottom-right) for QZ icon\n';
|
||||
errorMsg += '3. If not there, launch QZ Tray app\n';
|
||||
errorMsg += '4. Download: https://qz.io/download/\n\n';
|
||||
errorMsg += '💡 QZ Tray must be running on THIS computer,\n';
|
||||
errorMsg += ' not on the server!';
|
||||
|
||||
} else if (messageStr.includes('certificate') ||
|
||||
errorStr.includes('certificate') ||
|
||||
messageStr.includes('security')) {
|
||||
|
||||
errorMsg += '❌ FAILED: Certificate Error\n\n';
|
||||
errorMsg += '🔒 Browser security blocking connection.\n\n';
|
||||
errorMsg += 'Fix:\n';
|
||||
errorMsg += '1. Open: https://localhost:8182\n';
|
||||
errorMsg += '2. Click "Advanced" or "Proceed"\n';
|
||||
errorMsg += '3. Accept the certificate\n';
|
||||
errorMsg += '4. Return here and test again\n\n';
|
||||
errorMsg += '💡 Self-signed certificates are normal for QZ Tray';
|
||||
|
||||
} else {
|
||||
errorMsg += '❌ FAILED: Unexpected Error\n\n';
|
||||
errorMsg += 'Error: ' + error.message + '\n\n';
|
||||
errorMsg += 'Next steps:\n';
|
||||
errorMsg += '1. Check browser console (F12)\n';
|
||||
errorMsg += '2. Verify QZ Tray is running\n';
|
||||
errorMsg += '3. Restart browser and QZ Tray\n';
|
||||
errorMsg += '4. Check firewall settings';
|
||||
}
|
||||
|
||||
console.log('========== TEST COMPLETED: FAILED ==========');
|
||||
showNotification(errorMsg, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Load available printers from QZ Tray
|
||||
async function loadQZTrayPrinters() {
|
||||
try {
|
||||
@@ -543,7 +919,7 @@ async function loadQZTrayPrinters() {
|
||||
// Generate PDF and send to thermal printer
|
||||
async function generatePDFAndPrint(selectedPrinter, orderData, pieceNumber, totalPieces) {
|
||||
try {
|
||||
console.log('📄 Generating PDF for thermal printing...');
|
||||
console.log(`📄 Generating PDF for thermal printing... (${pieceNumber}/${totalPieces})`);
|
||||
|
||||
// Prepare data for PDF generation
|
||||
const pdfData = {
|
||||
@@ -553,37 +929,44 @@ async function generatePDFAndPrint(selectedPrinter, orderData, pieceNumber, tota
|
||||
total_pieces: totalPieces
|
||||
};
|
||||
|
||||
// Generate PDF via server endpoint
|
||||
// Call backend to generate PDF
|
||||
const response = await fetch('/generate_label_pdf', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(pdfData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`PDF generation failed: ${response.statusText}`);
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to generate PDF');
|
||||
}
|
||||
|
||||
// Get PDF as array buffer and convert to base64
|
||||
const pdfArrayBuffer = await response.arrayBuffer();
|
||||
const pdfBase64 = arrayBufferToBase64(pdfArrayBuffer);
|
||||
// Get PDF as blob
|
||||
const pdfBlob = await response.blob();
|
||||
|
||||
console.log('🖨️ Sending PDF to thermal printer with correct sizing...');
|
||||
|
||||
// Create a more specific thermal printer configuration
|
||||
const config = qz.configs.create(selectedPrinter, {
|
||||
margins: { top: 0, right: 0, bottom: 0, left: 0 },
|
||||
size: {
|
||||
width: 3.15, // 80mm = 3.15 inches
|
||||
height: 4.33, // 110mm = 4.33 inches
|
||||
units: 'in'
|
||||
},
|
||||
orientation: 'portrait'
|
||||
// Convert blob to base64 for QZ Tray
|
||||
const pdfBase64 = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
// Remove data:application/pdf;base64, prefix
|
||||
const base64 = reader.result.split(',')[1];
|
||||
resolve(base64);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(pdfBlob);
|
||||
});
|
||||
|
||||
// Send PDF with explicit sizing for thermal labels - use raw base64
|
||||
console.log(`🖨️ Sending PDF to printer ${selectedPrinter}...`);
|
||||
|
||||
// Configure QZ Tray for PDF printing
|
||||
const config = qz.configs.create(selectedPrinter, {
|
||||
scaleContent: false,
|
||||
rasterize: false
|
||||
});
|
||||
|
||||
// Prepare PDF data for QZ Tray
|
||||
const data = [{
|
||||
type: 'pdf',
|
||||
format: 'base64',
|
||||
@@ -591,7 +974,7 @@ async function generatePDFAndPrint(selectedPrinter, orderData, pieceNumber, tota
|
||||
}];
|
||||
|
||||
await qz.print(config, data);
|
||||
console.log('✅ PDF sent to printer successfully');
|
||||
console.log(`✅ PDF sent to printer successfully (${pieceNumber}/${totalPieces})`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error generating/printing PDF:', error);
|
||||
@@ -895,6 +1278,11 @@ function generateHTMLLabel(orderData, pieceNumber, totalPieces) {
|
||||
|
||||
// Handle QZ Tray printing
|
||||
async function handleQZTrayPrint(selectedRow) {
|
||||
const modal = document.getElementById('print-progress-modal');
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
const progressText = document.getElementById('progress-text');
|
||||
const progressCount = document.getElementById('progress-count');
|
||||
|
||||
try {
|
||||
if (!qzTray) {
|
||||
await initializeQZTray();
|
||||
@@ -927,43 +1315,76 @@ async function handleQZTrayPrint(selectedRow) {
|
||||
const quantity = orderData.cantitate;
|
||||
console.log(`🖨️ Printing ${quantity} labels via QZ Tray to ${selectedPrinter}`);
|
||||
|
||||
const button = document.getElementById('print-label-btn');
|
||||
const originalText = button.textContent;
|
||||
button.textContent = `Printing 0/${quantity}...`;
|
||||
button.disabled = true;
|
||||
// Show progress modal
|
||||
modal.style.display = 'flex';
|
||||
progressText.textContent = 'Preparing to print...';
|
||||
progressBar.style.width = '0%';
|
||||
progressCount.textContent = `0 / ${quantity}`;
|
||||
|
||||
try {
|
||||
// Print each label sequentially
|
||||
for (let i = 1; i <= quantity; i++) {
|
||||
button.textContent = `Printing ${i}/${quantity}...`;
|
||||
progressText.textContent = `Printing label ${i} of ${quantity}...`;
|
||||
progressCount.textContent = `${i - 1} / ${quantity}`;
|
||||
progressBar.style.width = `${((i - 1) / quantity) * 100}%`;
|
||||
|
||||
// Generate PDF and send to printer
|
||||
await generatePDFAndPrint(selectedPrinter, orderData, i, quantity);
|
||||
|
||||
// Update progress after successful print
|
||||
progressCount.textContent = `${i} / ${quantity}`;
|
||||
progressBar.style.width = `${(i / quantity) * 100}%`;
|
||||
|
||||
// Small delay between labels for printer processing
|
||||
if (i < quantity) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
}
|
||||
|
||||
// All labels printed successfully
|
||||
progressText.textContent = '✅ All labels printed! Updating database...';
|
||||
|
||||
// Update database to mark order as printed
|
||||
try {
|
||||
const updateResponse = await fetch(`/update_printed_status/${orderData.id}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!updateResponse.ok) {
|
||||
console.error('Failed to update printed status in database');
|
||||
progressText.textContent = '⚠️ Labels printed but database update failed';
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
} else {
|
||||
progressText.textContent = '✅ Complete! Refreshing table...';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error('Database update error:', dbError);
|
||||
progressText.textContent = '⚠️ Labels printed but database update failed';
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
}
|
||||
|
||||
// Close modal
|
||||
modal.style.display = 'none';
|
||||
|
||||
// Show success notification
|
||||
showNotification(`✅ Successfully printed ${quantity} labels!`, 'success');
|
||||
|
||||
// Mark order as printed and refresh table
|
||||
setTimeout(() => {
|
||||
document.getElementById('check-db-btn').click();
|
||||
}, 1000);
|
||||
// Refresh table to show updated status
|
||||
document.getElementById('check-db-btn').click();
|
||||
|
||||
} catch (printError) {
|
||||
modal.style.display = 'none';
|
||||
throw new Error(`Print failed: ${printError.message}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
modal.style.display = 'none';
|
||||
console.error('QZ Tray print error:', error);
|
||||
showNotification('❌ QZ Tray print error: ' + error.message, 'error');
|
||||
} finally {
|
||||
const button = document.getElementById('print-label-btn');
|
||||
button.textContent = originalText;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1383
py_app/app/templates/print_module.html.backup
Normal file
1383
py_app/app/templates/print_module.html.backup
Normal file
File diff suppressed because it is too large
Load Diff
1374
py_app/app/templates/print_module.html.backup2
Normal file
1374
py_app/app/templates/print_module.html.backup2
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user