updated to uplodad orders
This commit is contained in:
339
py_app/app/order_labels.py
Normal file
339
py_app/app/order_labels.py
Normal file
@@ -0,0 +1,339 @@
|
||||
"""
|
||||
Order Labels Module - Handles CSV upload and processing for order label generation
|
||||
Author: Auto-generated module for order management
|
||||
"""
|
||||
|
||||
import mariadb
|
||||
from flask import current_app, request, render_template, session, redirect, url_for, flash
|
||||
import csv
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
|
||||
def get_db_connection():
|
||||
"""Get database connection using external server configuration"""
|
||||
settings_file = current_app.instance_path + '/external_server.conf'
|
||||
settings = {}
|
||||
with open(settings_file, 'r') as f:
|
||||
for line in f:
|
||||
key, value = line.strip().split('=', 1)
|
||||
settings[key] = value
|
||||
return mariadb.connect(
|
||||
user=settings['username'],
|
||||
password=settings['password'],
|
||||
host=settings['server_domain'],
|
||||
port=int(settings['port']),
|
||||
database=settings['database_name']
|
||||
)
|
||||
|
||||
def validate_order_row(row_data):
|
||||
"""
|
||||
Validate a single order row for required fields and data types
|
||||
Required fields: Comanda Productie, Cantitate, Descr. Com. Prod
|
||||
"""
|
||||
errors = []
|
||||
warnings = []
|
||||
|
||||
# Check required fields
|
||||
if not row_data.get('comanda_productie', '').strip():
|
||||
errors.append("Comanda Productie is required")
|
||||
|
||||
if not row_data.get('descr_com_prod', '').strip():
|
||||
errors.append("Descr. Com. Prod is required")
|
||||
|
||||
# Validate Cantitate (quantity) - must be integer
|
||||
cantitate_str = row_data.get('cantitate', '').strip()
|
||||
if not cantitate_str:
|
||||
errors.append("Cantitate is required")
|
||||
else:
|
||||
try:
|
||||
cantitate = int(cantitate_str)
|
||||
if cantitate <= 0:
|
||||
errors.append("Cantitate must be a positive number")
|
||||
elif cantitate > 999: # INT(3) limit
|
||||
warnings.append("Cantitate exceeds 999 (will be truncated)")
|
||||
except ValueError:
|
||||
errors.append("Cantitate must be a valid number")
|
||||
|
||||
# Validate numeric fields (optional but must be valid if provided)
|
||||
for field, max_val in [('nr_linie_com_client', 999), ('line_number', 999)]:
|
||||
value = row_data.get(field, '').strip()
|
||||
if value:
|
||||
try:
|
||||
num_val = int(value)
|
||||
if num_val < 0:
|
||||
warnings.append(f"{field} should be positive")
|
||||
elif num_val > max_val:
|
||||
warnings.append(f"{field} exceeds {max_val} (will be truncated)")
|
||||
except ValueError:
|
||||
errors.append(f"{field} must be a valid number")
|
||||
|
||||
# Validate string length limits
|
||||
field_limits = {
|
||||
'comanda_productie': 15,
|
||||
'cod_articol': 15,
|
||||
'descr_com_prod': 50,
|
||||
'com_achiz_client': 25,
|
||||
'customer_name': 50,
|
||||
'customer_article_number': 25,
|
||||
'open_for_order': 25
|
||||
}
|
||||
|
||||
for field, max_len in field_limits.items():
|
||||
value = row_data.get(field, '').strip()
|
||||
if value and len(value) > max_len:
|
||||
warnings.append(f"{field} exceeds {max_len} characters (will be truncated)")
|
||||
|
||||
return errors, warnings
|
||||
|
||||
def add_order_to_database(order_data):
|
||||
"""
|
||||
Add a single order to the order_for_labels table
|
||||
Returns (success: bool, message: str)
|
||||
"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Prepare data with proper types and limits
|
||||
insert_data = {
|
||||
'comanda_productie': order_data.get('comanda_productie', '').strip()[:15],
|
||||
'cod_articol': order_data.get('cod_articol', '').strip()[:15] or None,
|
||||
'descr_com_prod': order_data.get('descr_com_prod', '').strip()[:50],
|
||||
'cantitate': int(order_data.get('cantitate', 0)),
|
||||
'com_achiz_client': order_data.get('com_achiz_client', '').strip()[:25] or None,
|
||||
'nr_linie_com_client': int(order_data.get('nr_linie_com_client', 0)) if order_data.get('nr_linie_com_client', '').strip() else None,
|
||||
'customer_name': order_data.get('customer_name', '').strip()[:50] or None,
|
||||
'customer_article_number': order_data.get('customer_article_number', '').strip()[:25] or None,
|
||||
'open_for_order': order_data.get('open_for_order', '').strip()[:25] or None,
|
||||
'line_number': int(order_data.get('line_number', 0)) if order_data.get('line_number', '').strip() else None
|
||||
}
|
||||
|
||||
sql = """
|
||||
INSERT INTO order_for_labels
|
||||
(comanda_productie, cod_articol, descr_com_prod, cantitate,
|
||||
com_achiz_client, nr_linie_com_client, customer_name,
|
||||
customer_article_number, open_for_order, line_number)
|
||||
VALUES (%(comanda_productie)s, %(cod_articol)s, %(descr_com_prod)s, %(cantitate)s,
|
||||
%(com_achiz_client)s, %(nr_linie_com_client)s, %(customer_name)s,
|
||||
%(customer_article_number)s, %(open_for_order)s, %(line_number)s)
|
||||
"""
|
||||
|
||||
cursor.execute(sql, insert_data)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return True, f"Successfully added order {insert_data['comanda_productie']}"
|
||||
|
||||
except mariadb.Error as e:
|
||||
return False, f"Database error: {str(e)}"
|
||||
except ValueError as e:
|
||||
return False, f"Data validation error: {str(e)}"
|
||||
except Exception as e:
|
||||
return False, f"Unexpected error: {str(e)}"
|
||||
|
||||
def process_csv_file(file_path):
|
||||
"""
|
||||
Process uploaded CSV file and return parsed data with validation
|
||||
Returns: (orders_data: list, validation_errors: list, validation_warnings: list)
|
||||
"""
|
||||
orders_data = []
|
||||
all_errors = []
|
||||
all_warnings = []
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
# Try to detect the CSV dialect
|
||||
sample = f.read(1024)
|
||||
f.seek(0)
|
||||
|
||||
# Create CSV reader
|
||||
reader = csv.DictReader(f)
|
||||
|
||||
# Map possible column names (case-insensitive)
|
||||
column_mapping = {
|
||||
'comanda productie': 'comanda_productie',
|
||||
'cod articol': 'cod_articol',
|
||||
'descr. com. prod': 'descr_com_prod',
|
||||
'cantitate': 'cantitate',
|
||||
'com.achiz.client': 'com_achiz_client',
|
||||
'nr. linie com. client': 'nr_linie_com_client',
|
||||
'customer name': 'customer_name',
|
||||
'customer article number': 'customer_article_number',
|
||||
'open for order': 'open_for_order',
|
||||
'line': 'line_number'
|
||||
}
|
||||
|
||||
for row_num, row in enumerate(reader, start=2): # Start at 2 (row 1 is header)
|
||||
# Normalize column names and create order data
|
||||
normalized_row = {}
|
||||
for col_name, col_value in row.items():
|
||||
if col_name:
|
||||
col_key = col_name.lower().strip()
|
||||
mapped_key = column_mapping.get(col_key, col_key.replace(' ', '_').replace('.', '_'))
|
||||
normalized_row[mapped_key] = col_value.strip() if col_value else ''
|
||||
|
||||
# Validate the row
|
||||
errors, warnings = validate_order_row(normalized_row)
|
||||
|
||||
if errors:
|
||||
all_errors.extend([f"Row {row_num}: {error}" for error in errors])
|
||||
else:
|
||||
# Only add valid rows
|
||||
orders_data.append(normalized_row)
|
||||
|
||||
if warnings:
|
||||
all_warnings.extend([f"Row {row_num}: {warning}" for warning in warnings])
|
||||
|
||||
except UnicodeDecodeError:
|
||||
# Try different encodings
|
||||
try:
|
||||
with open(file_path, 'r', encoding='latin-1') as f:
|
||||
reader = csv.DictReader(f)
|
||||
# ... repeat the same processing logic
|
||||
except Exception as e:
|
||||
all_errors.append(f"File encoding error: {str(e)}")
|
||||
except Exception as e:
|
||||
all_errors.append(f"File processing error: {str(e)}")
|
||||
|
||||
return orders_data, all_errors, all_warnings
|
||||
|
||||
def upload_orders_handler():
|
||||
"""
|
||||
Main handler for the upload orders functionality
|
||||
Handles both CSV upload/preview and database insertion
|
||||
"""
|
||||
report = None
|
||||
orders_data = []
|
||||
validation_errors = []
|
||||
validation_warnings = []
|
||||
temp_dir = tempfile.gettempdir()
|
||||
|
||||
if request.method == 'POST':
|
||||
# Handle file upload
|
||||
file = request.files.get('csv_file')
|
||||
if file and file.filename.endswith(('.csv', '.CSV')):
|
||||
try:
|
||||
# Save uploaded file
|
||||
temp_path = os.path.join(temp_dir, file.filename)
|
||||
file.save(temp_path)
|
||||
|
||||
# Store file info in session
|
||||
session['csv_filename'] = file.filename
|
||||
session['orders_csv_filepath'] = temp_path
|
||||
|
||||
# Process the CSV file
|
||||
orders_data, validation_errors, validation_warnings = process_csv_file(temp_path)
|
||||
|
||||
# Store processed data in session
|
||||
session['orders_csv_data'] = orders_data
|
||||
session['orders_validation_errors'] = validation_errors
|
||||
session['orders_validation_warnings'] = validation_warnings
|
||||
|
||||
flash(f"📁 File '{file.filename}' uploaded and processed successfully!", "info")
|
||||
|
||||
if validation_errors:
|
||||
flash(f"⚠️ Found {len(validation_errors)} validation errors. Please fix them before importing.", "warning")
|
||||
|
||||
if validation_warnings:
|
||||
flash(f"ℹ️ Found {len(validation_warnings)} warnings. Data will be adjusted automatically.", "info")
|
||||
|
||||
except Exception as e:
|
||||
flash(f"❌ Error processing file: {str(e)}", "error")
|
||||
|
||||
# Handle database insertion
|
||||
elif request.form.get('save_to_database') and 'orders_csv_data' in session:
|
||||
orders_data = session['orders_csv_data']
|
||||
|
||||
if not orders_data:
|
||||
flash("❌ No valid data to save to database.", "error")
|
||||
else:
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
failed_orders = []
|
||||
|
||||
for order in orders_data:
|
||||
success, message = add_order_to_database(order)
|
||||
if success:
|
||||
success_count += 1
|
||||
else:
|
||||
failed_count += 1
|
||||
failed_orders.append(f"{order.get('comanda_productie', 'Unknown')}: {message}")
|
||||
|
||||
# Create report
|
||||
report = f"✅ Successfully imported {success_count} orders."
|
||||
if failed_count > 0:
|
||||
report += f" ❌ {failed_count} orders failed to import."
|
||||
for failure in failed_orders[:5]: # Show first 5 failures
|
||||
report += f"<br>• {failure}"
|
||||
if len(failed_orders) > 5:
|
||||
report += f"<br>• ... and {len(failed_orders) - 5} more failures."
|
||||
|
||||
# Clear session data after successful import
|
||||
if success_count > 0:
|
||||
session.pop('orders_csv_data', None)
|
||||
session.pop('csv_filename', None)
|
||||
session.pop('orders_csv_filepath', None)
|
||||
session.pop('orders_validation_errors', None)
|
||||
session.pop('orders_validation_warnings', None)
|
||||
|
||||
flash(report, "success" if failed_count == 0 else "warning")
|
||||
|
||||
return redirect(url_for('main.upload_orders') + '#imported')
|
||||
|
||||
# Load data from session if available
|
||||
elif 'orders_csv_data' in session:
|
||||
orders_data = session['orders_csv_data']
|
||||
validation_errors = session.get('orders_validation_errors', [])
|
||||
validation_warnings = session.get('orders_validation_warnings', [])
|
||||
|
||||
return render_template('upload_orders.html',
|
||||
orders=orders_data,
|
||||
validation_errors=validation_errors,
|
||||
validation_warnings=validation_warnings,
|
||||
report=report)
|
||||
|
||||
def get_orders_from_database(limit=100):
|
||||
"""
|
||||
Retrieve orders from the database for display
|
||||
Returns list of order dictionaries
|
||||
"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("""
|
||||
SELECT id, comanda_productie, cod_articol, descr_com_prod, cantitate,
|
||||
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
|
||||
ORDER BY created_at DESC
|
||||
LIMIT %s
|
||||
""", (limit,))
|
||||
|
||||
orders = []
|
||||
for row in cursor.fetchall():
|
||||
orders.append({
|
||||
'id': row[0],
|
||||
'comanda_productie': row[1],
|
||||
'cod_articol': row[2],
|
||||
'descr_com_prod': row[3],
|
||||
'cantitate': row[4],
|
||||
'com_achiz_client': row[5],
|
||||
'nr_linie_com_client': row[6],
|
||||
'customer_name': row[7],
|
||||
'customer_article_number': row[8],
|
||||
'open_for_order': row[9],
|
||||
'line_number': row[10],
|
||||
'printed_labels': row[11],
|
||||
'created_at': row[12],
|
||||
'updated_at': row[13]
|
||||
})
|
||||
|
||||
conn.close()
|
||||
return orders
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error retrieving orders: {e}")
|
||||
return []
|
||||
Reference in New Issue
Block a user