From b49a22832d3fc34b10a2da383d08142d4e1b05ee Mon Sep 17 00:00:00 2001 From: Scheianu Ionut Date: Mon, 22 Sep 2025 21:53:08 +0300 Subject: [PATCH] updated print module --- py_app/app/__pycache__/routes.cpython-312.pyc | Bin 77590 -> 83229 bytes py_app/app/routes.py | 145 +++++++++++++++++- py_app/app/templates/print_module.html | 42 ++--- windows_print_service/print_service.ps1 | 61 +++++++- 4 files changed, 223 insertions(+), 25 deletions(-) diff --git a/py_app/app/__pycache__/routes.cpython-312.pyc b/py_app/app/__pycache__/routes.cpython-312.pyc index dbbf39fcec5c3f5d909ee3a5abbb237f63813f9c..0e60eaab7e25b580ee10156a2898ddf8225637e4 100644 GIT binary patch delta 5403 zcmcIneQZ;*lYj~NA|}ufpy4YJ5?EpDmYF;U92_S+I|SV4 zdetT^OqC$>G-m?Vw9>XzR9d=818M(IwL)w8dM4?xGTK7bZQA~sflRH6#&*xMA5NfZ zyQy=c^Ul5J+}}C(oOACx@24Ng&i;thUe;(-1pd7?l|GcMoYY#;wKu9ytCFM0;yTJ$ z4x-ns=Omy&5X|@wh;uoX_~y5cBhRt#Us9dFq(VPeqSwv`jOdIMeU_3~(Y*a3A_(^1 zK(+$ehgY$`1%gspE9%l08xVV{%Y|x@BgMMVPUK9z=|WaSrxsjj6{HPqOuo|V zNWQ$0OcwvbSghlSK_ucJf>R}5++@TSmC_>2^StrLc9jIL$#Ds;D58s!k8H^peGrM3 znBqZ6A90XiN?t{Ab+l5{idKspr{;=8TjHv?W{K|xPVHg=HegX~nH!XDIkfX4{Mj#=#XYkAsq6 z%HjsDn4@r+m^cD^{1kZ1IM2xA#w8J?IRoB>>#Vc@%%P!X(O}`3*OPEYrkrsH5mWJc zJP~?_72s+6-D-1=|hjDJ$Sv+n_Zs;j>nwKEeaOTHWnW2`A zj7RjA?ZCMsIpQ2jrJcx-Ffp|pC8p=mrt8j-;sPa8>)RCNQFj<~ow$Vm$qq%}y$uvbbipw~QSYkqYn(?7;IU^J2 znASnbfJBVT*HI~MB9Y4pIPgD&@=*kGsal$%ujpRl3l7paA(qU4=ic}e74x}uvkB5t zC5E0^BjzPHgYQpxzm~_XoOP%xiT0Ew*Zxwaku6PDYEq4_6ntt6S$VQrJ#DeBu0S}z z`lA85XVWvZKg`gP047Y34Gcs>$LZ(@{cJGIjEqO<9%umQz5shPI1r#$&`XiA* zgxdC2&`P`xG6f?7whmS?Hb?)PjD1?GO8h1MKhc ze9W9j+8YUm59aU!tdD-I(emhXtyZDS5*jR_x5~9u(8UOSl+Z%yz<#cCDhAE~!~{g> zoSq?IDsXHdFbc7XEa-c4 zb-G!cVru|)49HPTJT@rz1i`k0s|C*sP75Dpe+LiXu&`0!LO^I3UI4FDxdX}s2e5At zZJezr{&rbV2%!(Mxg5Z;6s{mRdJ7cDU}~XwbhYr^lh1Yk7#mtp302w;_XFz&L#G6f z!7lh7u_eE|!IYf-j-rZPjVBijIsIjyPV6?kuuqP)&1CAD4S#=ilp}NZW(n#26J-(p zi$A3HzfUzT$_c&Yrp}qxIp=h)8@lT0-U(U8Qkpt8d2CuctDLj=Ce$BO8eU&D%}s}S zZSy?Uau+XhrTWWJ9c^}``X~G6OjQ%A45dA(KB0a|o1xSvRVP$0sqg4)saLS!#67vx zumMdd7AeAPKjWS{I31g_ubwkCPN)QI_w=^uc3!(`o@&6@mQ}N=IrG|yqO6=S)K4ff zy5f`LC&o{{aN>ojgY&wojM_5Md2;)S?Nf@Giucss+cxJkH5HpFp7HT5Thn#h_}cA% zXyPqB6Y7k?lxm$!w5Hl8+ou&XjyXfi1cmpkEjd*)wP{Y{$`VLfGrcdPHlEyhV&~M_ zd3E(CQapuqlv-ZznW>qv^V+6)syQp$gp?c5ZMv0Lmt`rUtTIE_XY8dJ`%@W5MaEGz z{e0TdcvqvaYbUlXmJpf}-q^%9Kf^b5B`(JKlD+flZ{Q7C=~R3&o-tJLC6&|P=Jkyk zlQUzh<~?h9YkS6O&ngLP<$Z#*YA3cVY6XJnRIHqNK3%c)Zjrpge4mgzEZKIMW&_ITZKotD<)m_| zW?t_W@SA4$&2Hn(Prs+{SZp91?u@%8O0cAG8!qmC%Y2dIZCf*rT3kVQm83(96K~XIRYb8d zTSOX-iv+1LW=rzhPJBTkKD}jdeAzZ0W}Voh2U+Wvhw*daY9EW-Lwrcorh9~p^xlFS zqIuRo+sA9$<|$vQM~8bdAHJ`C)7ieegm}x_X~d69(vt0Kq?b%);OjfrAW1xRRR!X!8fEuN@~T2FR4@F#COYd?>t~->w){)mMwY%1k*BfE&^))2Stb?^YL1KNLlwy6JLc4WR zUPbrR$@tn%VNj>styb_&+T998T0&xRT0!EOv|0+h4j|Gc%3h~5UE0=bm)N@sex-A`# z%E2#pAbZHf%52rqcTnMN|aG2iIEnEHAWJ< zaYjvqJQK1g8R+XSJ4qIMNs8#AZXl1exHeEu_p{`GVTY$Gq)G;aW?~E(cizOStmHVC z6*&tAY*DfxqC+f=kYVv|!9qcFWicq*EAI0&-Y{jdmmxMOkV)R2AmbkG@XY4cTD7u1 Z=M=rJ_XTPDOL4zv@u8@nvW&bx`v>7DTqOVi diff --git a/py_app/app/routes.py b/py_app/app/routes.py index fad8d52..d1ebedd 100644 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -1614,4 +1614,147 @@ def create_locations(): @warehouse_bp.route('/import_locations_csv', methods=['GET', 'POST']) def import_locations_csv(): from app.warehouse import import_locations_csv_handler - return import_locations_csv_handler() \ No newline at end of file + return import_locations_csv_handler() + +@bp.route('/print_labels_silent/', 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 \ No newline at end of file diff --git a/py_app/app/templates/print_module.html b/py_app/app/templates/print_module.html index 8f1b63e..7b4ade3 100644 --- a/py_app/app/templates/print_module.html +++ b/py_app/app/templates/print_module.html @@ -720,34 +720,25 @@ function updatePrintButtonForService(serviceAvailable) { } } -// Enhanced print function with Windows service support +// Enhanced print function with Windows service support - NEW SERVER-SIDE APPROACH async function printLabelsWithService(orderId, prodOrder, quantity) { console.log(`šŸ–Øļø printLabelsWithService called - Order: ${orderId}, Quantity: ${quantity}`); try { - // Generate PDF URL - const pdfUrl = `${window.location.origin}/generate_labels_pdf/${orderId}`; - console.log(`šŸ“„ PDF URL: ${pdfUrl}`); - // Get selected printer from dropdown const selectedPrinter = getSelectedPrinter(); console.log(`šŸ–Øļø Selected printer: ${selectedPrinter}`); - // Prepare print data for service + // Use new server-side endpoint that bypasses CORS const printData = { - pdf_url: pdfUrl, - printer_name: selectedPrinter, - copies: 1, - silent: true, - order_id: orderId, - quantity: quantity + printer_name: selectedPrinter }; console.log('šŸ“‹ Print request data:', printData); - console.log(`šŸ“” Sending to: ${PRINT_SERVICE_URL}/print/silent`); + console.log(`šŸ“” Sending to server endpoint: /print_labels_silent/${orderId}`); - // Send to Windows service - const response = await fetch(`${PRINT_SERVICE_URL}/print/silent`, { + // Send to Flask server which handles Windows service communication + const response = await fetch(`/print_labels_silent/${orderId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -755,10 +746,10 @@ async function printLabelsWithService(orderId, prodOrder, quantity) { body: JSON.stringify(printData) }); - console.log(`šŸ“Ø Service response status: ${response.status}`); + console.log(`šŸ“Ø Server response status: ${response.status}`); const result = await response.json(); - console.log('šŸ“‹ Service response data:', result); + console.log('šŸ“‹ Server response data:', result); if (response.ok && result.success) { // Success - labels printed silently @@ -771,13 +762,24 @@ async function printLabelsWithService(orderId, prodOrder, quantity) { await updatePrintedStatus(orderId); return true; + } else if (response.status === 503 && result.fallback === 'pdf_download') { + // Windows service not available - inform user and suggest fallback + console.warn('āš ļø Windows service not available, showing service setup info'); + + alert(`āš ļø Windows Print Service Not Available\n\n${result.error}\n\nšŸ“‹ To enable silent printing:\n1. Install the Windows Print Service\n2. Start the service: sc start QualityLabelPrinting\n3. Restart your browser\n\nšŸ’” For now, use the "Generate PDF" button for manual printing.`); + + // Mark service as unavailable for this session + printServiceAvailable = false; + updatePrintButtonForService(false); + + throw new Error('Windows Print Service not available'); } else { - console.error('āŒ Service returned error:', result); - throw new Error(result.error || `Print service failed with status ${response.status}`); + console.error('āŒ Server returned error:', result); + throw new Error(result.error || `Print operation failed with status ${response.status}`); } } catch (error) { - console.error('āŒ Windows service print error:', error); + console.error('āŒ Server-side print error:', error); throw error; } } diff --git a/windows_print_service/print_service.ps1 b/windows_print_service/print_service.ps1 index 3794ea4..aac6168 100644 --- a/windows_print_service/print_service.ps1 +++ b/windows_print_service/print_service.ps1 @@ -96,6 +96,51 @@ function Invoke-PrintPDF { } } +# Print local PDF file function +function Invoke-PrintLocalPDF { + param( + [string]$PdfPath, + [string]$PrinterName = "default", + [int]$Copies = 1 + ) + + try { + Write-ServiceLog "Local print request: File=$PdfPath, Printer=$PrinterName, Copies=$Copies" + + # Check if file exists + if (!(Test-Path $PdfPath)) { + throw "PDF file not found: $PdfPath" + } + + # Get default printer if needed + if ($PrinterName -eq "default" -or [string]::IsNullOrEmpty($PrinterName)) { + $defaultPrinter = Get-WmiObject -Class Win32_Printer | Where-Object { $_.Default -eq $true } + $PrinterName = $defaultPrinter.Name + } + + Write-ServiceLog "Using printer: $PrinterName" + + # Print using Windows shell + $printJob = Start-Process -FilePath $PdfPath -Verb Print -PassThru -WindowStyle Hidden + Start-Sleep -Seconds 2 + + Write-ServiceLog "Print job sent successfully to printer: $PrinterName" + return @{ + success = $true + message = "Print job sent successfully" + printer = $PrinterName + timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + } + } + catch { + Write-ServiceLog "Print error: $($_.Exception.Message)" + return @{ + success = $false + error = $_.Exception.Message + } + } +} + # HTTP Response function function Send-HttpResponse { param( @@ -179,7 +224,7 @@ function Start-PrintService { Send-HttpResponse -Context $context -Body ($printersData | ConvertTo-Json -Depth 3) } - "^/print/(pdf|silent)$" { + "^/print/(pdf|silent|file)$" { if ($method -eq "POST") { try { # Read request body @@ -190,8 +235,16 @@ function Start-PrintService { # Parse JSON $printData = $body | ConvertFrom-Json - # Print PDF - $result = Invoke-PrintPDF -PdfUrl $printData.pdf_url -PrinterName $printData.printer_name -Copies $printData.copies + # Print PDF - handle both URL and local file path + if ($printData.pdf_path -and (Test-Path $printData.pdf_path)) { + Write-ServiceLog "Using local PDF file: $($printData.pdf_path)" + $result = Invoke-PrintLocalPDF -PdfPath $printData.pdf_path -PrinterName $printData.printer_name -Copies $printData.copies + } elseif ($printData.pdf_url) { + Write-ServiceLog "Using PDF URL: $($printData.pdf_url)" + $result = Invoke-PrintPDF -PdfUrl $printData.pdf_url -PrinterName $printData.printer_name -Copies $printData.copies + } else { + throw "Either pdf_path or pdf_url must be provided" + } if ($result.success) { Send-HttpResponse -Context $context -Body ($result | ConvertTo-Json) @@ -219,7 +272,7 @@ function Start-PrintService { $errorResponse = @{ success = $false error = "Endpoint not found" - available_endpoints = @("/health", "/printers", "/print/pdf", "/print/silent") + available_endpoints = @("/health", "/printers", "/print/pdf", "/print/silent", "/print/file") } Send-HttpResponse -Context $context -StatusCode 404 -Body ($errorResponse | ConvertTo-Json) }