saved
This commit is contained in:
Binary file not shown.
@@ -2038,6 +2038,33 @@ def generate_labels_pdf(order_id, paper_saving_mode='true'):
|
||||
traceback.print_exc()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/update_printed_status/<int:order_id>', methods=['POST'])
|
||||
def update_printed_status(order_id):
|
||||
"""Update printed status for direct printing (without PDF generation)"""
|
||||
print(f"DEBUG: update_printed_status 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 update_order_printed_status
|
||||
|
||||
# Update printed status in database
|
||||
update_success = update_order_printed_status(order_id)
|
||||
|
||||
if update_success:
|
||||
print(f"DEBUG: Successfully updated printed status for order {order_id}")
|
||||
return jsonify({'success': True, 'message': f'Order {order_id} marked as printed'})
|
||||
else:
|
||||
print(f"WARNING: Could not update printed status for order {order_id}")
|
||||
return jsonify({'error': 'Could not update printed status'}), 500
|
||||
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error in update_printed_status: {e}")
|
||||
return jsonify({'error': 'Internal server error'}), 500
|
||||
|
||||
@bp.route('/get_order_data/<int:order_id>', methods=['GET'])
|
||||
def get_order_data(order_id):
|
||||
"""Get specific order data for preview"""
|
||||
|
||||
@@ -40,8 +40,10 @@
|
||||
{% block content %}
|
||||
<div class="scan-container" style="display: flex; flex-direction: row; gap: 20px; width: 100%; align-items: flex-start;">
|
||||
<!-- Label Preview Card -->
|
||||
<div class="card scan-form-card" style="display: flex; flex-direction: column; justify-content: flex-start; align-items: center; min-height: 480px; width: 330px; flex-shrink: 0; position: relative;">
|
||||
<div class="label-view-title" style="width: 100%; text-align: center; padding: 18px 0 0 0; font-size: 18px; font-weight: bold; letter-spacing: 0.5px;">Label View</div>
|
||||
<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>
|
||||
|
||||
<!-- 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 -->
|
||||
<div id="label-content" style="position: absolute; top: 65.7px; left: 11.34px; width: 227.4px; height: 321.3px; border: 2px solid #333; background: white;">
|
||||
@@ -150,12 +152,60 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="print-label-btn" class="btn btn-success" style="margin-top: 15px;">Generate PDF</button>
|
||||
</div>
|
||||
|
||||
<!-- Data Preview Card -->
|
||||
<!-- Print Options and Button Section - Inside the Card -->
|
||||
<div style="width: 100%; margin-top: 20px;">
|
||||
<!-- Print Method Selection -->
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 8px; display: block;">
|
||||
🖨️ Print Method:
|
||||
</label>
|
||||
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="radio" name="printMethod" id="directPrint" value="direct" checked>
|
||||
<label class="form-check-label" for="directPrint" style="font-size: 11px; line-height: 1.3;">
|
||||
<strong>Direct Print</strong><br>
|
||||
<span class="text-muted">Print directly to thermal label printer</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="radio" name="printMethod" id="pdfGenerate" value="pdf">
|
||||
<label class="form-check-label" for="pdfGenerate" style="font-size: 11px; line-height: 1.3;">
|
||||
<strong>Generate PDF</strong><br>
|
||||
<span class="text-muted">Create PDF for manual printing</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Printer Selection -->
|
||||
<div id="printerSelection" style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 5px; display: block;">
|
||||
Select Printer:
|
||||
</label>
|
||||
<select id="printerSelect" class="form-control form-control-sm" style="font-size: 11px; padding: 4px 8px;">
|
||||
<option value="default">Default Printer</option>
|
||||
<option value="Epson TM-T20">Epson TM-T20</option>
|
||||
<option value="Citizen CTS-310">Citizen CTS-310</option>
|
||||
<option value="custom">Other Printer...</option>
|
||||
</select>
|
||||
<small class="text-muted" style="font-size: 10px;">Choose your thermal label printer</small>
|
||||
</div>
|
||||
|
||||
<!-- Print Button -->
|
||||
<div style="width: 100%; text-align: center; margin-bottom: 15px;">
|
||||
<button id="print-label-btn" class="btn btn-success" style="font-size: 14px; padding: 10px 30px; border-radius: 6px; font-weight: 600;">
|
||||
🖨️ Print Labels
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Print Information -->
|
||||
<div style="width: 100%; text-align: center; color: #6c757d; font-size: 11px; line-height: 1.4;">
|
||||
<div style="margin-bottom: 5px;">Creates sequential labels based on quantity</div>
|
||||
<small>(e.g., CP00000711-001 to CP00000711-063)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- Data Preview Card -->
|
||||
<div class="card scan-table-card" style="min-height: 700px; width: calc(100% - 350px); margin: 0;">
|
||||
<h3>Data Preview (Unprinted Orders)</h3>
|
||||
<button id="check-db-btn" class="btn btn-primary mb-3">Check Database</button>
|
||||
@@ -352,9 +402,13 @@ function addPDFGenerationHandler() {
|
||||
const printButton = document.getElementById('print-label-btn');
|
||||
|
||||
if (printButton) {
|
||||
// Update button text and appearance for PDF generation
|
||||
printButton.innerHTML = '📄 Generate PDF Labels';
|
||||
printButton.title = 'Generate PDF with multiple labels based on quantity';
|
||||
// Update button text and appearance based on print method
|
||||
updatePrintButtonText();
|
||||
|
||||
// Add event listeners for radio buttons
|
||||
document.querySelectorAll('input[name="printMethod"]').forEach(radio => {
|
||||
radio.addEventListener('change', updatePrintButtonText);
|
||||
});
|
||||
|
||||
printButton.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
@@ -366,97 +420,218 @@ function addPDFGenerationHandler() {
|
||||
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';
|
||||
// Get print method
|
||||
const printMethod = document.querySelector('input[name="printMethod"]:checked').value;
|
||||
|
||||
if (!orderId) {
|
||||
alert('Could not determine order ID. Please refresh and try again.');
|
||||
return;
|
||||
if (printMethod === 'direct') {
|
||||
handleDirectPrint(selectedRow);
|
||||
} else {
|
||||
handlePDFGeneration(selectedRow);
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
const originalText = this.textContent;
|
||||
this.textContent = 'Generating PDF...';
|
||||
this.disabled = true;
|
||||
|
||||
console.log(`Generating PDF for order ${orderId} with ${quantity} labels`);
|
||||
|
||||
// Always use paper-saving mode (optimized for thermal label printers)
|
||||
const paperSavingMode = 'true';
|
||||
|
||||
console.log(`Using paper-saving mode for optimal label printing`);
|
||||
|
||||
// Generate PDF with paper-saving mode enabled
|
||||
fetch(`/generate_labels_pdf/${orderId}/${paperSavingMode}`, {
|
||||
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 blob URL for PDF
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
// Create download link for PDF
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `labels_${prodOrder}_${quantity}pcs.pdf`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
|
||||
// Also open PDF in new tab for printing
|
||||
const printWindow = window.open(url, '_blank');
|
||||
if (printWindow) {
|
||||
printWindow.focus();
|
||||
|
||||
// Wait for PDF to load, then show print dialog and cleanup
|
||||
setTimeout(() => {
|
||||
printWindow.print();
|
||||
|
||||
// Clean up blob URL after print dialog is shown
|
||||
setTimeout(() => {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 2000);
|
||||
}, 1500);
|
||||
} else {
|
||||
// If popup was blocked, clean up immediately
|
||||
setTimeout(() => {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Show success message
|
||||
alert(`✅ PDF generated successfully!\n📊 Order: ${prodOrder}\n📦 Labels: ${quantity} pieces\n\nThe PDF has been downloaded and opened for printing.\n\n📋 The order has been marked as printed and removed from the unprinted orders list.`);
|
||||
|
||||
// Refresh the orders table to reflect printed status
|
||||
// This will automatically hide the printed order from the unprinted orders list
|
||||
setTimeout(() => {
|
||||
refreshUnprintedOrdersTable();
|
||||
}, 1000);
|
||||
})
|
||||
.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;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updatePrintButtonText() {
|
||||
const printButton = document.getElementById('print-label-btn');
|
||||
const printMethod = document.querySelector('input[name="printMethod"]:checked').value;
|
||||
const printerSelection = document.getElementById('printerSelection');
|
||||
|
||||
if (printMethod === 'direct') {
|
||||
printButton.innerHTML = '🖨️ Print Directly';
|
||||
printButton.title = 'Print directly to thermal label printer';
|
||||
printerSelection.style.display = 'block';
|
||||
} else {
|
||||
printButton.innerHTML = '📄 Generate PDF';
|
||||
printButton.title = 'Generate PDF with multiple labels based on quantity';
|
||||
printerSelection.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function handleDirectPrint(selectedRow) {
|
||||
const orderId = selectedRow.dataset.orderId;
|
||||
const quantityCell = selectedRow.querySelector('td:nth-child(5)');
|
||||
const quantity = quantityCell ? parseInt(quantityCell.textContent) : 1;
|
||||
const prodOrderCell = selectedRow.querySelector('td:nth-child(2)');
|
||||
const prodOrder = prodOrderCell ? prodOrderCell.textContent.trim() : 'N/A';
|
||||
|
||||
// Get selected printer
|
||||
const printerSelect = document.getElementById('printerSelect');
|
||||
let printerName = printerSelect.value;
|
||||
|
||||
if (printerName === 'custom') {
|
||||
printerName = prompt('Enter your printer name:');
|
||||
if (!printerName) return;
|
||||
} else if (printerName === 'default') {
|
||||
printerName = 'default';
|
||||
}
|
||||
|
||||
console.log(`Direct printing to: ${printerName}`);
|
||||
|
||||
// Extract order data from selected row
|
||||
const cells = selectedRow.querySelectorAll('td');
|
||||
const orderData = {
|
||||
order_id: parseInt(orderId),
|
||||
comanda_productie: cells[1].textContent.trim(),
|
||||
cod_articol: cells[2].textContent.trim(),
|
||||
descr_com_prod: cells[3].textContent.trim(),
|
||||
cantitate: parseInt(cells[4].textContent.trim()),
|
||||
data_livrare: cells[5].textContent.trim(),
|
||||
dimensiune: cells[6].textContent.trim(),
|
||||
com_achiz_client: cells[7].textContent.trim(),
|
||||
nr_linie_com_client: cells[8].textContent.trim(),
|
||||
customer_name: cells[9].textContent.trim(),
|
||||
customer_article_number: cells[10].textContent.trim()
|
||||
};
|
||||
|
||||
// Print each label individually
|
||||
for (let i = 1; i <= quantity; i++) {
|
||||
const labelData = {
|
||||
...orderData,
|
||||
sequential_number: `${orderData.comanda_productie}-${i.toString().padStart(3, '0')}`,
|
||||
current_label: i,
|
||||
total_labels: quantity
|
||||
};
|
||||
|
||||
// Encode data as base64 JSON
|
||||
const jsonString = JSON.stringify(labelData);
|
||||
const base64Data = btoa(unescape(encodeURIComponent(jsonString)));
|
||||
|
||||
// Create custom protocol URL
|
||||
const printUrl = `recticel-print://${encodeURIComponent(printerName)}/${base64Data}`;
|
||||
|
||||
console.log(`Printing label ${i}/${quantity}: ${printUrl.substring(0, 100)}...`);
|
||||
|
||||
// Trigger direct print via custom protocol
|
||||
try {
|
||||
window.location.href = printUrl;
|
||||
} catch (error) {
|
||||
console.error(`Failed to print label ${i}:`, error);
|
||||
alert(`❌ Failed to print label ${i}/${quantity}. Please check if the print service is installed.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Small delay between labels to prevent overwhelming the printer
|
||||
if (i < quantity) {
|
||||
setTimeout(() => {}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Show success message
|
||||
setTimeout(() => {
|
||||
alert(`✅ Sent ${quantity} labels to printer: ${printerName}\n📊 Order: ${prodOrder}`);
|
||||
|
||||
// Update database status and refresh table
|
||||
updatePrintedStatus(orderId);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function handlePDFGeneration(selectedRow) {
|
||||
const orderId = selectedRow.dataset.orderId;
|
||||
const quantityCell = selectedRow.querySelector('td:nth-child(5)');
|
||||
const quantity = quantityCell ? parseInt(quantityCell.textContent) : 1;
|
||||
const prodOrderCell = selectedRow.querySelector('td:nth-child(2)');
|
||||
const prodOrder = prodOrderCell ? prodOrderCell.textContent.trim() : 'N/A';
|
||||
|
||||
const button = document.getElementById('print-label-btn');
|
||||
const originalText = button.textContent;
|
||||
button.textContent = 'Generating PDF...';
|
||||
button.disabled = true;
|
||||
|
||||
console.log(`Generating PDF for order ${orderId} with ${quantity} labels`);
|
||||
|
||||
// Always use paper-saving mode (optimized for thermal label printers)
|
||||
const paperSavingMode = 'true';
|
||||
|
||||
console.log(`Using paper-saving mode for optimal label printing`);
|
||||
|
||||
// Generate PDF with paper-saving mode enabled
|
||||
fetch(`/generate_labels_pdf/${orderId}/${paperSavingMode}`, {
|
||||
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 blob URL for PDF
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
// Create download link for PDF
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `labels_${prodOrder}_${quantity}pcs.pdf`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
|
||||
// Also open PDF in new tab for printing
|
||||
const printWindow = window.open(url, '_blank');
|
||||
if (printWindow) {
|
||||
printWindow.focus();
|
||||
|
||||
// Wait for PDF to load, then show print dialog and cleanup
|
||||
setTimeout(() => {
|
||||
printWindow.print();
|
||||
|
||||
// Clean up blob URL after print dialog is shown
|
||||
setTimeout(() => {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 2000);
|
||||
}, 1500);
|
||||
} else {
|
||||
// If popup was blocked, clean up immediately
|
||||
setTimeout(() => {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Show success message
|
||||
alert(`✅ PDF generated successfully!\n📊 Order: ${prodOrder}\n📦 Labels: ${quantity} pieces\n\nThe PDF has been downloaded and opened for printing.\n\n📋 The order has been marked as printed and removed from the unprinted orders list.`);
|
||||
|
||||
// Refresh the orders table to reflect printed status
|
||||
// This will automatically hide the printed order from the unprinted orders list
|
||||
setTimeout(() => {
|
||||
refreshUnprintedOrdersTable();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error generating PDF:', error);
|
||||
alert('❌ Failed to generate PDF labels. Error: ' + error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
// Reset button state
|
||||
button.textContent = originalText;
|
||||
button.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function updatePrintedStatus(orderId) {
|
||||
// Call backend to update printed status for direct printing
|
||||
fetch(`/update_printed_status/${orderId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Updated printed status:', data);
|
||||
// Refresh table
|
||||
setTimeout(() => {
|
||||
refreshUnprintedOrdersTable();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating printed status:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to refresh the unprinted orders table
|
||||
function refreshUnprintedOrdersTable() {
|
||||
console.log('Refreshing unprinted orders table...');
|
||||
|
||||
Reference in New Issue
Block a user