updated to silent print
This commit is contained in:
74
py_app/PRINT_SERVICE_SETUP.md
Normal file
74
py_app/PRINT_SERVICE_SETUP.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Windows Print Service Network Configuration Guide
|
||||
|
||||
## Current Issue
|
||||
Your Flask server (Linux) cannot connect to Windows Print Service because they're on different machines.
|
||||
|
||||
## Solution Options
|
||||
|
||||
### Option 1: Same Machine Setup
|
||||
If both Flask and Windows service are on the same machine:
|
||||
```python
|
||||
WINDOWS_PRINT_SERVICE_URL = "http://localhost:8765"
|
||||
```
|
||||
|
||||
### Option 2: Different Machines (Recommended)
|
||||
If Flask server (Linux) and Windows service are on different machines:
|
||||
|
||||
1. **Find Windows Machine IP Address:**
|
||||
```cmd
|
||||
# On Windows machine, run:
|
||||
ipconfig
|
||||
# Look for IPv4 Address, e.g., 192.168.1.100
|
||||
```
|
||||
|
||||
2. **Update Configuration:**
|
||||
```python
|
||||
# In /app/print_config.py, change:
|
||||
WINDOWS_PRINT_SERVICE_URL = "http://192.168.1.100:8765"
|
||||
```
|
||||
|
||||
3. **Test Connection:**
|
||||
```bash
|
||||
# From Linux machine, test:
|
||||
curl http://192.168.1.100:8765/health
|
||||
```
|
||||
|
||||
### Option 3: Windows Firewall Configuration
|
||||
Ensure Windows allows incoming connections on port 8765:
|
||||
|
||||
1. **Windows Firewall Settings:**
|
||||
- Control Panel → System and Security → Windows Defender Firewall
|
||||
- Advanced Settings → Inbound Rules → New Rule
|
||||
- Port → TCP → 8765 → Allow the connection
|
||||
|
||||
2. **Test from Linux:**
|
||||
```bash
|
||||
telnet [WINDOWS_IP] 8765
|
||||
```
|
||||
|
||||
### Option 4: Port Forwarding/Tunneling
|
||||
If direct connection isn't possible, set up SSH tunnel or port forwarding.
|
||||
|
||||
## Quick Fix for Testing
|
||||
|
||||
1. **Get Windows IP Address**
|
||||
2. **Update print_config.py with the IP**
|
||||
3. **Restart Flask server**
|
||||
4. **Test printing**
|
||||
|
||||
## Current Status
|
||||
- ✅ Windows Print Service: Running (localhost:8765/health works)
|
||||
- ❌ Network Connection: Flask can't reach Windows service
|
||||
- 🔧 Fix Needed: Update WINDOWS_PRINT_SERVICE_URL with correct IP
|
||||
|
||||
## Example Commands
|
||||
|
||||
```bash
|
||||
# 1. Find Windows IP (run on Windows):
|
||||
# ipconfig | findstr IPv4
|
||||
|
||||
# 2. Test connection (run on Linux):
|
||||
# curl http://[WINDOWS_IP]:8765/health
|
||||
|
||||
# 3. Update config and restart Flask
|
||||
```
|
||||
125
py_app/analyze_pdf_dimensions.py
Normal file
125
py_app/analyze_pdf_dimensions.py
Normal file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PDF Label Dimension Analysis Tool
|
||||
Analyzes the generated PDF to verify exact dimensions and content positioning
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
|
||||
|
||||
from app.pdf_generator import LabelPDFGenerator, mm_to_points
|
||||
from reportlab.lib.units import mm
|
||||
|
||||
def analyze_pdf_dimensions():
|
||||
"""Analyze the PDF dimensions in detail"""
|
||||
print("=== PDF Label Dimension Analysis ===\n")
|
||||
|
||||
generator = LabelPDFGenerator()
|
||||
|
||||
# Label dimensions
|
||||
print("1. LABEL PHYSICAL DIMENSIONS:")
|
||||
print(f" Width: {generator.label_width/mm:.1f}mm ({generator.label_width:.2f} points)")
|
||||
print(f" Height: {generator.label_height/mm:.1f}mm ({generator.label_height:.2f} points)")
|
||||
print(f" ✓ Should be exactly 80.0mm × 110.0mm")
|
||||
|
||||
# Content area
|
||||
print(f"\n2. CONTENT AREA DIMENSIONS:")
|
||||
print(f" Content Width: {generator.content_width/mm:.1f}mm")
|
||||
print(f" Content Height: {generator.content_height/mm:.1f}mm")
|
||||
print(f" Left margin: {generator.content_x/mm:.1f}mm")
|
||||
print(f" Bottom margin: {generator.content_y/mm:.1f}mm")
|
||||
print(f" Right margin: {(generator.label_width - generator.content_x - generator.content_width)/mm:.1f}mm")
|
||||
print(f" Top margin: {(generator.label_height - generator.content_y - generator.content_height)/mm:.1f}mm")
|
||||
|
||||
# Row analysis
|
||||
print(f"\n3. ROW LAYOUT:")
|
||||
total_rows = 9
|
||||
standard_rows = 8 # 7 standard + 1 double
|
||||
double_rows = 1
|
||||
|
||||
standard_row_height_mm = generator.row_height/mm
|
||||
double_row_height_mm = generator.double_row_height/mm
|
||||
|
||||
total_content_used = (standard_rows * standard_row_height_mm + double_rows * double_row_height_mm)
|
||||
expected_content_height = generator.content_height/mm
|
||||
|
||||
print(f" Standard row height: {standard_row_height_mm:.1f}mm")
|
||||
print(f" Double row height: {double_row_height_mm:.1f}mm")
|
||||
print(f" Total rows: {total_rows} (8 standard + 1 double)")
|
||||
print(f" Content height used: {total_content_used:.1f}mm")
|
||||
print(f" Content area available: {expected_content_height:.1f}mm")
|
||||
print(f" Remaining space: {expected_content_height - total_content_used:.1f}mm")
|
||||
|
||||
# Barcode area
|
||||
print(f"\n4. BARCODE AREA:")
|
||||
barcode_bottom_margin = generator.content_y/mm
|
||||
print(f" Bottom barcode space: {barcode_bottom_margin:.1f}mm (should be ~12-15mm)")
|
||||
|
||||
top_margin = (generator.label_height - generator.content_y - generator.content_height)/mm
|
||||
print(f" Top margin space: {top_margin:.1f}mm")
|
||||
|
||||
# Check for potential issues
|
||||
print(f"\n5. POTENTIAL ISSUES ANALYSIS:")
|
||||
|
||||
# Check if content is too high
|
||||
if total_content_used > expected_content_height:
|
||||
print(f" ⚠️ ISSUE: Content ({total_content_used:.1f}mm) exceeds available space ({expected_content_height:.1f}mm)")
|
||||
else:
|
||||
print(f" ✓ Content fits within available space")
|
||||
|
||||
# Check margins
|
||||
if barcode_bottom_margin < 10:
|
||||
print(f" ⚠️ WARNING: Bottom margin ({barcode_bottom_margin:.1f}mm) may be too small for barcode")
|
||||
else:
|
||||
print(f" ✓ Bottom margin adequate for barcode")
|
||||
|
||||
if top_margin < 3:
|
||||
print(f" ⚠️ WARNING: Top margin ({top_margin:.1f}mm) very small")
|
||||
else:
|
||||
print(f" ✓ Top margin adequate")
|
||||
|
||||
# Aspect ratio check
|
||||
aspect_ratio = generator.label_height / generator.label_width
|
||||
expected_ratio = 110.0 / 80.0 # 1.375
|
||||
|
||||
print(f"\n6. ASPECT RATIO CHECK:")
|
||||
print(f" Current ratio: {aspect_ratio:.3f}")
|
||||
print(f" Expected ratio: {expected_ratio:.3f} (110/80)")
|
||||
|
||||
if abs(aspect_ratio - expected_ratio) < 0.001:
|
||||
print(f" ✓ Aspect ratio is correct")
|
||||
else:
|
||||
print(f" ⚠️ Aspect ratio mismatch!")
|
||||
|
||||
# Browser display notes
|
||||
print(f"\n7. BROWSER DISPLAY CONSIDERATIONS:")
|
||||
print(f" • Browser zoom affects apparent size")
|
||||
print(f" • PDF viewer scaling can change appearance")
|
||||
print(f" • Monitor DPI affects screen dimensions")
|
||||
print(f" • Print preview scaling may alter appearance")
|
||||
print(f"\n 🔧 SOLUTION: Always verify with physical printout at 100% scale")
|
||||
|
||||
return {
|
||||
'width_mm': generator.label_width/mm,
|
||||
'height_mm': generator.label_height/mm,
|
||||
'content_height_mm': expected_content_height,
|
||||
'content_used_mm': total_content_used,
|
||||
'aspect_ratio': aspect_ratio,
|
||||
'expected_ratio': expected_ratio
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
results = analyze_pdf_dimensions()
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
if (abs(results['width_mm'] - 80.0) < 0.1 and
|
||||
abs(results['height_mm'] - 110.0) < 0.1 and
|
||||
abs(results['aspect_ratio'] - results['expected_ratio']) < 0.001):
|
||||
print("✅ PDF dimensions are CORRECT: 80mm × 110mm")
|
||||
print("If labels appear too large, check:")
|
||||
print(" • Browser zoom level (should be 100%)")
|
||||
print(" • PDF viewer scaling settings")
|
||||
print(" • Print scaling (should be 'Actual size' or 100%)")
|
||||
else:
|
||||
print("❌ PDF dimensions need adjustment")
|
||||
Binary file not shown.
16
py_app/app/print_config.py
Normal file
16
py_app/app/print_config.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Windows Print Service Configuration
|
||||
# Configure the Windows Print Service connection for cross-platform printing
|
||||
|
||||
# Default Windows Print Service URL
|
||||
# Change this to the IP address of your Windows machine if running on different systems
|
||||
# Examples:
|
||||
# Same machine: http://localhost:8765
|
||||
# Different machine: http://192.168.1.100:8765
|
||||
# Domain name: http://printserver.company.local:8765
|
||||
WINDOWS_PRINT_SERVICE_URL = "http://192.168.1.XXX:8765" # ← REPLACE XXX WITH YOUR WINDOWS IP
|
||||
|
||||
# Print service connection timeout (seconds)
|
||||
PRINT_SERVICE_TIMEOUT = 30
|
||||
|
||||
# Enable print service connection (set to False to always use PDF fallback)
|
||||
PRINT_SERVICE_ENABLED = True
|
||||
@@ -1616,145 +1616,13 @@ def import_locations_csv():
|
||||
from app.warehouse import import_locations_csv_handler
|
||||
return import_locations_csv_handler()
|
||||
|
||||
@bp.route('/print_labels_silent/<int:order_id>', methods=['POST'])
|
||||
def print_labels_silent(order_id):
|
||||
"""Generate PDF and send directly to Windows Print Service - bypasses CORS"""
|
||||
print(f"DEBUG: print_labels_silent called for order_id: {order_id}")
|
||||
|
||||
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
|
||||
return jsonify({'error': 'Access denied'}), 403
|
||||
|
||||
try:
|
||||
import requests
|
||||
import tempfile
|
||||
import os
|
||||
from .pdf_generator import generate_order_labels_pdf, update_order_printed_status
|
||||
from .print_module import get_db_connection
|
||||
|
||||
# Get printer name from request
|
||||
data = request.get_json() or {}
|
||||
printer_name = data.get('printer_name', 'default')
|
||||
|
||||
print(f"DEBUG: Using printer: {printer_name}")
|
||||
|
||||
# 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,))
|
||||
|
||||
order_data = cursor.fetchone()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if not order_data:
|
||||
return jsonify({'error': 'Order not found'}), 404
|
||||
|
||||
# Convert to dictionary for easier access
|
||||
columns = ['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']
|
||||
order_dict = dict(zip(columns, order_data))
|
||||
|
||||
print(f"DEBUG: Order data: {order_dict}")
|
||||
|
||||
# Generate PDF content in memory
|
||||
pdf_content = generate_order_labels_pdf(order_dict)
|
||||
|
||||
# Save PDF to a location accessible by Windows service
|
||||
pdf_dir = r"C:\temp\quality_labels"
|
||||
if not os.path.exists(pdf_dir):
|
||||
os.makedirs(pdf_dir, exist_ok=True)
|
||||
|
||||
pdf_filename = f"order_{order_id}_{order_dict['comanda_productie']}.pdf"
|
||||
pdf_path = os.path.join(pdf_dir, pdf_filename)
|
||||
|
||||
# Write PDF to file
|
||||
with open(pdf_path, 'wb') as pdf_file:
|
||||
pdf_file.write(pdf_content)
|
||||
|
||||
print(f"DEBUG: Created PDF file: {pdf_path}")
|
||||
|
||||
try:
|
||||
# Send to Windows Print Service with local file path
|
||||
print_service_url = 'http://localhost:8765'
|
||||
|
||||
# Create the print request with local file path
|
||||
print_data = {
|
||||
'pdf_path': pdf_path,
|
||||
'printer_name': printer_name,
|
||||
'copies': 1,
|
||||
'silent': True,
|
||||
'order_id': order_id,
|
||||
'quantity': order_dict['cantitate']
|
||||
}
|
||||
|
||||
print(f"DEBUG: Sending print request to {print_service_url}/print/silent")
|
||||
|
||||
# Send request to Windows service
|
||||
response = requests.post(
|
||||
f'{print_service_url}/print/silent',
|
||||
json=print_data,
|
||||
headers={'Content-Type': 'application/json'},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
print(f"DEBUG: Print service response: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"DEBUG: Print result: {result}")
|
||||
|
||||
if result.get('success'):
|
||||
# Update order status
|
||||
update_order_printed_status(order_id)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Labels printed successfully',
|
||||
'printer': printer_name,
|
||||
'order': order_dict['comanda_productie'],
|
||||
'quantity': order_dict['cantitate']
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': result.get('error', 'Print service returned error')
|
||||
}), 500
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Print service returned status {response.status_code}'
|
||||
}), 500
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"DEBUG: Print service connection error: {e}")
|
||||
# Fallback: Return the PDF for manual printing
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Windows Print Service not available: {str(e)}',
|
||||
'fallback': 'pdf_download',
|
||||
'pdf_path': pdf_path
|
||||
}), 503
|
||||
|
||||
finally:
|
||||
# Clean up PDF file
|
||||
try:
|
||||
os.unlink(pdf_path)
|
||||
print(f"DEBUG: Cleaned up PDF file: {pdf_path}")
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error cleaning up PDF file: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error in print_labels_silent: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
# NOTE for frontend/extension developers:
|
||||
# To print labels, call the Chrome extension and pass the PDF URL:
|
||||
# /generate_labels_pdf/<order_id>
|
||||
# The extension should POST to http://localhost:8765/print/silent with:
|
||||
# {
|
||||
# "pdf_url": "https://your-linux-server/generate_labels_pdf/15",
|
||||
# "printer_name": "default",
|
||||
# "copies": 1
|
||||
# }
|
||||
@@ -8,6 +8,17 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Inserted custom CSS from user */
|
||||
.card.scan-table-card table.print-module-table.scan-table thead th {
|
||||
border-bottom: 2e6 !important;
|
||||
background-color: #f8f9fa !important;
|
||||
padding: 0.25rem 0.4rem !important;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 10px !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
/* Enhanced table styling to match view_orders.html with higher specificity */
|
||||
.card.scan-table-card table.print-module-table.scan-table {
|
||||
width: 100% !important;
|
||||
@@ -849,77 +860,101 @@ async function updatePrintedStatus(orderId) {
|
||||
}
|
||||
|
||||
// PDF generation handler
|
||||
// Helper to get extension ID injected by content script
|
||||
function getInjectedExtensionId() {
|
||||
const el = document.getElementById('chrome-extension-id');
|
||||
if (el) return el.getAttribute('data-extension-id');
|
||||
return null;
|
||||
}
|
||||
|
||||
function addPDFGenerationHandler() {
|
||||
const printButton = document.getElementById('print-label-btn');
|
||||
|
||||
if (printButton) {
|
||||
printButton.addEventListener('click', async 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;
|
||||
const originalColor = printButton.style.background;
|
||||
printButton.innerHTML = '⏳ Processing...';
|
||||
printButton.disabled = true;
|
||||
|
||||
if (!printButton) return;
|
||||
|
||||
printButton.addEventListener('click', async 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;
|
||||
const originalColor = printButton.style.background;
|
||||
printButton.innerHTML = '⏳ Processing...';
|
||||
printButton.disabled = true;
|
||||
|
||||
try {
|
||||
// Step 1: Generate PDF and get its URL
|
||||
const pdfResponse = await fetch(`/generate_labels_pdf/${orderId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
if (!pdfResponse.ok) throw new Error('Failed to generate PDF');
|
||||
// Try to get the PDF URL from the response (assume server returns a URL or we can construct it)
|
||||
// If not, fallback to download
|
||||
let pdfUrl = '';
|
||||
try {
|
||||
let success = false;
|
||||
|
||||
console.log(`🖨️ Print operation started - Service available: ${printServiceAvailable}`);
|
||||
|
||||
// Try Windows service first if available
|
||||
if (printServiceAvailable) {
|
||||
console.log('🚀 Attempting silent print via Windows service...');
|
||||
try {
|
||||
success = await printLabelsWithService(orderId, prodOrder, quantity);
|
||||
console.log(`✅ Windows service print result: ${success}`);
|
||||
} catch (serviceError) {
|
||||
console.error('❌ Windows service failed:', serviceError);
|
||||
console.warn('🔄 Falling back to PDF download mode');
|
||||
printServiceAvailable = false; // Mark as unavailable for this session
|
||||
updatePrintButtonForService(false);
|
||||
}
|
||||
} else {
|
||||
console.log('📄 Service not available, using PDF download mode');
|
||||
}
|
||||
|
||||
// Fallback to PDF download if service failed or unavailable
|
||||
if (!success) {
|
||||
console.log('📥 Generating PDF for download...');
|
||||
success = await downloadPDFLabels(orderId, prodOrder, quantity);
|
||||
console.log(`📄 PDF download result: ${success}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Print operation failed:', error);
|
||||
alert(`❌ Print operation failed: ${error.message}\n\nPlease check:\n• Windows Print Service is running\n• Chrome extension is installed\n• Network connectivity\n• PDF generation permissions`);
|
||||
} finally {
|
||||
// Reset button state
|
||||
printButton.innerHTML = originalText;
|
||||
printButton.style.background = originalColor;
|
||||
printButton.disabled = false;
|
||||
|
||||
// Recheck service availability for next operation
|
||||
setTimeout(checkPrintServiceAvailability, 2000);
|
||||
const data = await pdfResponse.json();
|
||||
pdfUrl = data.pdf_url || '';
|
||||
} catch {
|
||||
// If not JSON, fallback to constructing the URL
|
||||
pdfUrl = `/static/generated_labels/labels_${prodOrder}_${quantity}pcs.pdf`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Step 2: Prepare print job for Chrome extension
|
||||
const selectedPrinter = getSelectedPrinter();
|
||||
const printJob = {
|
||||
pdfUrl: window.location.origin + pdfUrl,
|
||||
printer: selectedPrinter,
|
||||
orderId: orderId,
|
||||
prodOrder: prodOrder,
|
||||
quantity: quantity
|
||||
};
|
||||
|
||||
// Step 3: Get extension ID from injected DOM
|
||||
const extensionId = getInjectedExtensionId();
|
||||
|
||||
// Step 4: Send message to Chrome extension
|
||||
if (window.chrome && window.chrome.runtime && window.chrome.runtime.sendMessage && extensionId) {
|
||||
window.chrome.runtime.sendMessage(
|
||||
extensionId,
|
||||
{ action: 'print_pdf', ...printJob },
|
||||
function(response) {
|
||||
if (response && response.success) {
|
||||
alert('✅ Labels sent to printer!\nOrder: ' + prodOrder + '\nQuantity: ' + quantity + '\nPrinter: ' + selectedPrinter);
|
||||
updatePrintedStatus(orderId);
|
||||
} else {
|
||||
alert('❌ Failed to print via extension. PDF will be downloaded.');
|
||||
downloadPDFLabels(orderId, prodOrder, quantity);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Fallback: Download PDF
|
||||
alert('ℹ️ Chrome extension not detected or extension ID not injected. PDF will be downloaded.');
|
||||
await downloadPDFLabels(orderId, prodOrder, quantity);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Print operation failed:', error);
|
||||
alert('❌ Print operation failed: ' + error.message);
|
||||
} finally {
|
||||
printButton.innerHTML = originalText;
|
||||
printButton.style.background = originalColor;
|
||||
printButton.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -5,4 +5,5 @@ gunicorn
|
||||
flask-sqlalchemy
|
||||
pyodbc
|
||||
mariadb
|
||||
reportlab
|
||||
reportlab
|
||||
requests
|
||||
161
py_app/test_print_connection.py
Normal file
161
py_app/test_print_connection.py
Normal file
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Windows Print Service Connection Tester
|
||||
Tests connectivity between Flask server and Windows Print Service
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
|
||||
|
||||
import requests
|
||||
import time
|
||||
from app.print_config import WINDOWS_PRINT_SERVICE_URL, PRINT_SERVICE_TIMEOUT
|
||||
|
||||
def test_print_service_connection():
|
||||
"""Test connection to Windows Print Service"""
|
||||
print("=== Windows Print Service Connection Test ===\n")
|
||||
|
||||
print(f"🔍 Testing connection to: {WINDOWS_PRINT_SERVICE_URL}")
|
||||
print(f"⏱️ Timeout: {PRINT_SERVICE_TIMEOUT} seconds")
|
||||
print(f"🌐 From: Flask server (Linux)")
|
||||
print(f"🎯 To: Windows Print Service")
|
||||
print()
|
||||
|
||||
# Test 1: Health check
|
||||
print("1️⃣ Testing /health endpoint...")
|
||||
try:
|
||||
start_time = time.time()
|
||||
response = requests.get(
|
||||
f"{WINDOWS_PRINT_SERVICE_URL}/health",
|
||||
timeout=PRINT_SERVICE_TIMEOUT
|
||||
)
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f" ✅ SUCCESS (Status: {response.status_code}, Time: {elapsed:.2f}s)")
|
||||
print(f" 📋 Service: {data.get('service', 'Unknown')}")
|
||||
print(f" 🖥️ Platform: {data.get('platform', 'Unknown')}")
|
||||
print(f" 📅 Timestamp: {data.get('timestamp', 'Unknown')}")
|
||||
health_ok = True
|
||||
else:
|
||||
print(f" ❌ FAILED (Status: {response.status_code})")
|
||||
print(f" 📝 Response: {response.text}")
|
||||
health_ok = False
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f" ❌ CONNECTION REFUSED")
|
||||
print(f" 🔧 Error: {str(e)}")
|
||||
print(f" 💡 This usually means:")
|
||||
print(f" • Windows service is not running")
|
||||
print(f" • Wrong IP address in configuration")
|
||||
print(f" • Firewall blocking connection")
|
||||
print(f" • Network routing issue")
|
||||
health_ok = False
|
||||
except Exception as e:
|
||||
print(f" ❌ ERROR: {str(e)}")
|
||||
health_ok = False
|
||||
|
||||
print()
|
||||
|
||||
# Test 2: Printers endpoint (only if health check passed)
|
||||
if health_ok:
|
||||
print("2️⃣ Testing /printers endpoint...")
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{WINDOWS_PRINT_SERVICE_URL}/printers",
|
||||
timeout=PRINT_SERVICE_TIMEOUT
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
printers = data.get('printers', [])
|
||||
print(f" ✅ SUCCESS - Found {len(printers)} printer(s)")
|
||||
for i, printer in enumerate(printers[:3]): # Show first 3
|
||||
name = printer.get('name', 'Unknown') if isinstance(printer, dict) else printer
|
||||
print(f" 🖨️ {i+1}. {name}")
|
||||
if len(printers) > 3:
|
||||
print(f" ... and {len(printers)-3} more")
|
||||
else:
|
||||
print(f" ❌ FAILED (Status: {response.status_code})")
|
||||
except Exception as e:
|
||||
print(f" ❌ ERROR: {str(e)}")
|
||||
|
||||
print()
|
||||
|
||||
# Test 3: Network diagnostics
|
||||
print("3️⃣ Network Diagnostics...")
|
||||
|
||||
# Parse URL to get host and port
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
parsed = urlparse(WINDOWS_PRINT_SERVICE_URL)
|
||||
host = parsed.hostname
|
||||
port = parsed.port or 8765
|
||||
|
||||
print(f" 🌐 Target Host: {host}")
|
||||
print(f" 🔌 Target Port: {port}")
|
||||
|
||||
# Test DNS resolution
|
||||
try:
|
||||
import socket
|
||||
ip = socket.gethostbyname(host)
|
||||
print(f" 🔍 DNS Resolution: {host} → {ip}")
|
||||
dns_ok = True
|
||||
except Exception as e:
|
||||
print(f" ❌ DNS Resolution failed: {e}")
|
||||
dns_ok = False
|
||||
|
||||
# Test raw TCP connection
|
||||
if dns_ok:
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(5)
|
||||
result = sock.connect_ex((host, port))
|
||||
sock.close()
|
||||
|
||||
if result == 0:
|
||||
print(f" ✅ TCP Connection: Port {port} is open")
|
||||
else:
|
||||
print(f" ❌ TCP Connection: Port {port} is closed or filtered")
|
||||
print(f" 💡 Check Windows Firewall and service status")
|
||||
except Exception as e:
|
||||
print(f" ❌ TCP Test error: {e}")
|
||||
except Exception as e:
|
||||
print(f" ❌ URL parsing error: {e}")
|
||||
|
||||
print()
|
||||
|
||||
# Final recommendations
|
||||
print("🔧 TROUBLESHOOTING RECOMMENDATIONS:")
|
||||
print()
|
||||
|
||||
if not health_ok:
|
||||
print("❌ CONNECTION FAILED - Try these steps:")
|
||||
print("1. Verify Windows Print Service is running:")
|
||||
print(" • Windows: sc query QualityLabelPrinting")
|
||||
print(" • Or: Get-Service QualityLabelPrinting")
|
||||
print()
|
||||
print("2. Check Windows machine IP address:")
|
||||
print(" • Windows: ipconfig | findstr IPv4")
|
||||
print(" • Update WINDOWS_PRINT_SERVICE_URL in print_config.py")
|
||||
print()
|
||||
print("3. Test firewall:")
|
||||
print(" • Windows: Allow port 8765 in Windows Firewall")
|
||||
print(" • Test: telnet [WINDOWS_IP] 8765")
|
||||
print()
|
||||
print("4. If same machine, verify service binding:")
|
||||
print(" • Service should bind to 0.0.0.0:8765 (not 127.0.0.1)")
|
||||
else:
|
||||
print("✅ CONNECTION SUCCESS - Print service should work!")
|
||||
print("• Service is reachable and responding")
|
||||
print("• Ready for label printing")
|
||||
print("• Try printing labels from the web interface")
|
||||
|
||||
print(f"\n📝 Current Configuration:")
|
||||
print(f" Service URL: {WINDOWS_PRINT_SERVICE_URL}")
|
||||
print(f" Timeout: {PRINT_SERVICE_TIMEOUT}s")
|
||||
|
||||
return health_ok
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_print_service_connection()
|
||||
Reference in New Issue
Block a user