updated to uplodad orders

This commit is contained in:
2025-09-15 21:14:48 +03:00
parent 8a7aa193dc
commit ceda2447a6
12 changed files with 1142 additions and 14 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,151 @@
#!/usr/bin/env python3
"""
Database script to add the printed_labels column to the order_for_labels table
This column will track whether labels have been printed for each order (boolean: 0=false, 1=true)
Default value: 0 (false)
"""
import sys
import os
import mariadb
from flask import Flask
# Add the app directory to the path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def get_db_connection():
"""Get database connection using settings from external_server.conf"""
# Go up two levels from this script to reach py_app directory, then to instance
app_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
settings_file = os.path.join(app_root, 'instance', '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 add_printed_labels_column():
"""
Adds the printed_labels column to the order_for_labels table after the line_number column
Column type: TINYINT(1) (boolean: 0=false, 1=true)
Default value: 0 (false)
"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Check if table exists
cursor.execute("SHOW TABLES LIKE 'order_for_labels'")
result = cursor.fetchone()
if not result:
print("❌ Table 'order_for_labels' does not exist. Please create it first.")
return False
# Check if column already exists
cursor.execute("SHOW COLUMNS FROM order_for_labels LIKE 'printed_labels'")
column_exists = cursor.fetchone()
if column_exists:
print(" Column 'printed_labels' already exists.")
# Show current structure
cursor.execute("DESCRIBE order_for_labels")
columns = cursor.fetchall()
print("\n📋 Current table structure:")
for col in columns:
null_info = 'NULL' if col[2] == 'YES' else 'NOT NULL'
default_info = f" DEFAULT {col[4]}" if col[4] else ""
print(f" 📌 {col[0]:<25} {col[1]:<20} {null_info}{default_info}")
else:
# Add the column after line_number
alter_table_sql = """
ALTER TABLE order_for_labels
ADD COLUMN printed_labels TINYINT(1) NOT NULL DEFAULT 0
COMMENT 'Boolean flag: 0=labels not printed, 1=labels printed'
AFTER line_number
"""
cursor.execute(alter_table_sql)
conn.commit()
print("✅ Column 'printed_labels' added successfully!")
# Show the updated structure
cursor.execute("DESCRIBE order_for_labels")
columns = cursor.fetchall()
print("\n📋 Updated table structure:")
for col in columns:
null_info = 'NULL' if col[2] == 'YES' else 'NOT NULL'
default_info = f" DEFAULT {col[4]}" if col[4] else ""
highlight = "🆕 " if col[0] == 'printed_labels' else " "
print(f"{highlight}{col[0]:<25} {col[1]:<20} {null_info}{default_info}")
# Show count of existing records that will have printed_labels = 0
cursor.execute("SELECT COUNT(*) FROM order_for_labels")
count = cursor.fetchone()[0]
if count > 0:
print(f"\n📊 {count} existing records now have printed_labels = 0 (false)")
conn.close()
except mariadb.Error as e:
print(f"❌ Database error: {e}")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
return True
def verify_column():
"""Verify the column was added correctly"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Test the column functionality
cursor.execute("SELECT COUNT(*) as total, SUM(printed_labels) as printed FROM order_for_labels")
result = cursor.fetchone()
if result:
total, printed = result
print(f"\n🔍 Verification:")
print(f" 📦 Total orders: {total}")
print(f" 🖨️ Printed orders: {printed or 0}")
print(f" 📄 Unprinted orders: {total - (printed or 0)}")
conn.close()
return True
except Exception as e:
print(f"❌ Verification failed: {e}")
return False
if __name__ == "__main__":
print("🔧 Adding printed_labels column to order_for_labels table...")
print("="*60)
success = add_printed_labels_column()
if success:
print("\n🔍 Verifying column addition...")
verify_column()
print("\n✅ Database modification completed successfully!")
print("\n📝 Column Details:")
print(" • Name: printed_labels")
print(" • Type: TINYINT(1) (boolean)")
print(" • Default: 0 (false - labels not printed)")
print(" • Values: 0 = not printed, 1 = printed")
print(" • Position: After line_number column")
else:
print("\n❌ Database modification failed!")
print("="*60)

View File

@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""
Database script to create the order_for_labels table
This table will store order information for label generation
"""
import sys
import os
import mariadb
from flask import Flask
# Add the app directory to the path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def get_db_connection():
"""Get database connection using settings from external_server.conf"""
# Go up two levels from this script to reach py_app directory, then to instance
app_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
settings_file = os.path.join(app_root, 'instance', '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 create_order_for_labels_table():
"""
Creates the order_for_labels table with the specified structure
"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# First check if table already exists
cursor.execute("SHOW TABLES LIKE 'order_for_labels'")
result = cursor.fetchone()
if result:
print("Table 'order_for_labels' already exists.")
# Show current structure
cursor.execute("DESCRIBE order_for_labels")
columns = cursor.fetchall()
print("\nCurrent table structure:")
for col in columns:
print(f" {col[0]} - {col[1]} {'NULL' if col[2] == 'YES' else 'NOT NULL'}")
else:
# Create the table
create_table_sql = """
CREATE TABLE order_for_labels (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'Unique identifier',
comanda_productie VARCHAR(15) NOT NULL COMMENT 'Production Order',
cod_articol VARCHAR(15) COMMENT 'Article Code',
descr_com_prod VARCHAR(50) NOT NULL COMMENT 'Production Order Description',
cantitate INT(3) NOT NULL COMMENT 'Quantity',
com_achiz_client VARCHAR(25) COMMENT 'Client Purchase Order',
nr_linie_com_client INT(3) COMMENT 'Client Order Line Number',
customer_name VARCHAR(50) COMMENT 'Customer Name',
customer_article_number VARCHAR(25) COMMENT 'Customer Article Number',
open_for_order VARCHAR(25) COMMENT 'Open for Order Status',
line_number INT(3) COMMENT 'Line Number',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'Record creation timestamp',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Record update timestamp'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Table for storing order information for label generation'
"""
cursor.execute(create_table_sql)
conn.commit()
print("✅ Table 'order_for_labels' created successfully!")
# Show the created structure
cursor.execute("DESCRIBE order_for_labels")
columns = cursor.fetchall()
print("\n📋 Table structure:")
for col in columns:
null_info = 'NULL' if col[2] == 'YES' else 'NOT NULL'
default_info = f" DEFAULT {col[4]}" if col[4] else ""
print(f" 📌 {col[0]:<25} {col[1]:<20} {null_info}{default_info}")
conn.close()
except mariadb.Error as e:
print(f"❌ Database error: {e}")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
return True
if __name__ == "__main__":
print("🏗️ Creating order_for_labels table...")
print("="*50)
success = create_order_for_labels_table()
if success:
print("\n✅ Database setup completed successfully!")
else:
print("\n❌ Database setup failed!")
print("="*50)

339
py_app/app/order_labels.py Normal file
View 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 []

View File

@@ -1002,6 +1002,28 @@ def generate_pdf():
return jsonify({'message': 'PDF generated successfully!', 'pdf_path': f'/static/label_templates/label_template.pdf'})
# Order Labels Upload Module Routes
@bp.route('/upload_orders', methods=['GET', 'POST'])
def upload_orders():
"""Route for uploading orders CSV files for label generation"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager']:
flash('Access denied: Warehouse management permissions required.')
return redirect(url_for('main.dashboard'))
from app.order_labels import upload_orders_handler
return upload_orders_handler()
@bp.route('/view_orders')
def view_orders():
"""Route for viewing uploaded orders"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager', 'warehouse_worker']:
flash('Access denied: Warehouse access required.')
return redirect(url_for('main.dashboard'))
from app.order_labels import get_orders_from_database
orders = get_orders_from_database(200) # Get last 200 orders
return render_template('view_orders.html', orders=orders)
@warehouse_bp.route('/create_locations', methods=['GET', 'POST'])
def create_locations():
from app.warehouse import create_locations_handler

View File

@@ -9,11 +9,11 @@
<!-- Row of evenly distributed cards -->
<div class="dashboard-container">
<!-- Card 1: Upload Data -->
<!-- Card 1: View Orders -->
<div class="dashboard-card">
<h3>Upload Data</h3>
<p>Upload data into the database for label management.</p>
<a href="{{ url_for('main.upload_data') }}" class="btn">Go to Upload Data</a>
<h3>View Orders</h3>
<p>View uploaded orders and manage label data for printing.</p>
<a href="{{ url_for('main.view_orders') }}" class="btn">View Orders</a>
</div>
<!-- Card 2: Launch Print Module -->

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block title %}Upload Data{% endblock %}
{% block content %}
<div class="upload-data-container">
<h1>Upload Data</h1>
<p>This page will allow users to upload data into the database.</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,244 @@
{% extends "base.html" %}
{% block title %}Upload Order Data for Labels{% endblock %}
{% block content %}
<div class="scan-container">
<!-- Upload Orders Card (first, fixed position) -->
<div class="card scan-form-card" style="margin-bottom: 24px;">
<h3>Upload Order Data for Labels</h3>
<form method="POST" enctype="multipart/form-data" class="form-centered" id="csv-upload-form">
<label for="csv_file">Choose CSV file:</label>
{% if not orders %}
<input type="file" name="csv_file" accept=".csv" required><br>
<button type="submit" class="btn">Upload & Preview</button>
{% else %}
<label style="font-weight: bold;">Selected file: {{ session['csv_filename'] if session['csv_filename'] else 'Unknown' }}</label><br>
<button type="button" class="btn" onclick="showPopupAndSubmit()">Save to Database</button>
{% endif %}
</form>
<!-- Popup Modal -->
<div id="popup-modal" class="popup" style="display:none; position:fixed; top:0; left:0; width:100vw; height:100vh; background:var(--app-overlay-bg, rgba(30,41,59,0.85)); z-index:9999; align-items:center; justify-content:center;">
<div class="popup-content" style="margin:auto; padding:32px; border-radius:8px; box-shadow:0 2px 8px #333; min-width:320px; max-width:400px; text-align:center;">
<h3 style="color:var(--app-label-text);">Saving orders to database...</h3>
</div>
</div>
<script>
function showPopupAndSubmit() {
document.getElementById('popup-modal').style.display = 'flex';
// Submit the form after showing popup
setTimeout(function() {
var form = document.getElementById('csv-upload-form');
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'save_to_database';
input.value = '1';
form.appendChild(input);
form.submit();
}, 500);
}
window.onload = function() {
if (window.location.hash === '#saved') {
document.getElementById('popup-modal').style.display = 'none';
}
}
</script>
</div>
<!-- Preview Table Card (expandable height, scrollable) -->
<div class="card scan-table-card" style="margin-bottom: 24px; max-height: 480px; overflow-y: auto;">
<h3>Preview Table</h3>
<table class="scan-table">
<thead>
<tr>
<th>Comanda Productie</th>
<th>Cod Articol</th>
<th>Descr. Com. Prod</th>
<th>Cantitate</th>
<th>Com.Achiz.Client</th>
<th>Nr. Linie com. Client</th>
<th>Customer Name</th>
<th>Customer Article Number</th>
<th>Open for order</th>
<th>Line</th>
</tr>
</thead>
<tbody>
{% if orders %}
{% for order in orders %}
<tr>
<td>{{ order.get('comanda_productie', '') }}</td>
<td>{{ order.get('cod_articol', '') }}</td>
<td>{{ order.get('descr_com_prod', '') }}</td>
<td>{{ order.get('cantitate', '') }}</td>
<td>{{ order.get('com_achiz_client', '') }}</td>
<td>{{ order.get('nr_linie_com_client', '') }}</td>
<td>{{ order.get('customer_name', '') }}</td>
<td>{{ order.get('customer_article_number', '') }}</td>
<td>{{ order.get('open_for_order', '') }}</td>
<td>{{ order.get('line_number', '') }}</td>
</tr>
{% endfor %}
{% else %}
<tr><td colspan="10" style="text-align:center;">No CSV file uploaded yet.</td></tr>
{% endif %}
</tbody>
</table>
</div>
{% if validation_errors or validation_warnings %}
<div class="card" style="margin-bottom: 24px;">
<h4>Validation Results</h4>
{% if validation_errors %}
<div style="color: #dc3545; margin-bottom: 16px;">
<strong>Errors found:</strong>
<ul>
{% for error in validation_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if validation_warnings %}
<div style="color: #ffc107;">
<strong>Warnings:</strong>
<ul>
{% for warning in validation_warnings %}
<li>{{ warning }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if report %}
<div class="card" style="margin-bottom: 24px;">
<h4>Import Report</h4>
<p>{{ report }}</p>
</div>
{% endif %}
</div>
<style>
/* Optimize table for better content fitting */
.scan-table {
font-size: 11px !important;
line-height: 1.2;
}
.scan-table th,
.scan-table td {
padding: 6px 4px !important;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.scan-table th {
font-size: 10px !important;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
}
/* Specific column width optimizations */
.scan-table th:nth-child(1), /* Comanda Productie */
.scan-table td:nth-child(1) {
max-width: 80px;
min-width: 80px;
}
.scan-table th:nth-child(2), /* Cod Articol */
.scan-table td:nth-child(2) {
max-width: 70px;
min-width: 70px;
}
.scan-table th:nth-child(3), /* Descr. Com. Prod */
.scan-table td:nth-child(3) {
max-width: 120px;
min-width: 120px;
}
.scan-table th:nth-child(4), /* Cantitate */
.scan-table td:nth-child(4) {
max-width: 60px;
min-width: 60px;
text-align: right;
}
.scan-table th:nth-child(5), /* Com.Achiz.Client */
.scan-table td:nth-child(5) {
max-width: 90px;
min-width: 90px;
}
.scan-table th:nth-child(6), /* Nr. Linie com. Client */
.scan-table td:nth-child(6) {
max-width: 60px;
min-width: 60px;
text-align: center;
}
.scan-table th:nth-child(7), /* Customer Name */
.scan-table td:nth-child(7) {
max-width: 100px;
min-width: 100px;
}
.scan-table th:nth-child(8), /* Customer Article Number */
.scan-table td:nth-child(8) {
max-width: 80px;
min-width: 80px;
}
.scan-table th:nth-child(9), /* Open for order */
.scan-table td:nth-child(9) {
max-width: 50px;
min-width: 50px;
text-align: center;
}
.scan-table th:nth-child(10), /* Line */
.scan-table td:nth-child(10) {
max-width: 40px;
min-width: 40px;
text-align: center;
}
/* Ensure table fits in container */
.scan-table-card {
overflow-x: auto;
}
.scan-table {
min-width: 800px;
width: 100%;
table-layout: fixed;
}
/* Add hover effect for better readability */
.scan-table tbody tr:hover td {
background-color: #f8f9fa !important;
position: relative;
}
/* Tooltip on hover for truncated text */
.scan-table td {
cursor: help;
}
.scan-table td:hover {
overflow: visible;
white-space: normal;
word-wrap: break-word;
z-index: 10;
position: relative;
background-color: #fff3cd !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
</style>
{% endblock %}

View File

@@ -0,0 +1,266 @@
{% extends "base.html" %}
{% block title %}View Uploaded Orders{% endblock %}
{% block content %}
<div class="scan-container">
<!-- Orders Actions Card (first, narrower) -->
<div class="card report-form-card">
<h3>List Uploaded Orders</h3>
<!-- Orders Actions -->
<div class="reports-grid">
<div class="form-centered">
<label class="report-description">View all uploaded orders in the current day for labels generation</label>
<button class="btn report-btn" onclick="window.location.reload()">Refresh Orders List</button>
</div>
<div class="form-centered">
<label class="report-description">View all uploaded orders for labels generation</label>
<button class="btn report-btn" onclick="window.location.reload()">Fetch Orders List</button>
</div>
<div class="form-centered">
<label class="report-description">Upload new CSV file with order data</label>
<button class="btn report-btn" onclick="location.href='/upload_orders'">Upload New Orders</button>
</div>
</div>
<!-- Separator -->
<div class="report-separator"></div>
<!-- Export Section -->
<div class="export-section">
<div class="form-centered last-buttons">
<label class="export-description">Export orders as:</label>
<div class="button-row">
<button class="btn export-btn" onclick="window.print()">Print Orders</button>
</div>
</div>
</div>
</div>
<!-- Orders Table Card (second, wider) -->
<div class="card report-table-card">
{% if orders %}
<h3>Uploaded Orders ({{ orders|length }} total)</h3>
{% else %}
<h3>No orders uploaded yet</h3>
{% endif %}
<div class="report-table-container">
{% if orders %}
<table class="scan-table">
<thead>
<tr>
<th>ID</th>
<th>Comanda Productie</th>
<th>Cod Articol</th>
<th>Descr. Com. Prod</th>
<th>Cantitate</th>
<th>Com.Achiz.Client</th>
<th>Nr. Linie</th>
<th>Customer Name</th>
<th>Customer Art. Nr.</th>
<th>Open Order</th>
<th>Line</th>
<th>Printed</th>
<th>Created</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td>{{ order.id }}</td>
<td><strong>{{ order.comanda_productie }}</strong></td>
<td>{{ order.cod_articol or '-' }}</td>
<td>{{ order.descr_com_prod }}</td>
<td style="text-align: right; font-weight: 600;">{{ order.cantitate }}</td>
<td>{{ order.com_achiz_client or '-' }}</td>
<td style="text-align: right;">{{ order.nr_linie_com_client or '-' }}</td>
<td>{{ order.customer_name or '-' }}</td>
<td>{{ order.customer_article_number or '-' }}</td>
<td>{{ order.open_for_order or '-' }}</td>
<td style="text-align: right;">{{ order.line_number or '-' }}</td>
<td style="text-align: center;">
{% if order.printed_labels == 1 %}
<span style="color: #28a745; font-weight: bold;">✓ Yes</span>
{% else %}
<span style="color: #dc3545;">✗ No</span>
{% endif %}
</td>
<td style="font-size: 11px; color: #6c757d;">
{% if order.created_at %}
{{ order.created_at.strftime('%Y-%m-%d %H:%M') }}
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div style="text-align: center; padding: 40px; color: #6c757d;">
<div style="font-size: 48px; margin-bottom: 20px;">📦</div>
<h4>No Orders Found</h4>
<p>Upload your first CSV file to see orders here.</p>
<button class="btn report-btn" onclick="location.href='/upload_orders'" style="margin-top: 20px;">
Upload Orders
</button>
</div>
{% endif %}
</div>
</div>
</div>
<style>
/* View Orders Table Optimization - Higher specificity to override base styles */
.report-table-card .scan-table {
font-size: 11px !important;
line-height: 1.3 !important;
}
.report-table-card .scan-table th {
font-size: 10px !important;
padding: 8px 6px !important;
font-weight: 600 !important;
text-transform: uppercase;
letter-spacing: 0.3px;
white-space: nowrap;
}
.report-table-card .scan-table td {
padding: 6px 6px !important;
font-size: 11px !important;
line-height: 1.2 !important;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
}
/* Specific column optimizations for better content fitting */
.report-table-card .scan-table th:nth-child(1), /* ID */
.report-table-card .scan-table td:nth-child(1) {
max-width: 50px !important;
min-width: 50px !important;
}
.report-table-card .scan-table th:nth-child(2), /* Comanda Productie */
.report-table-card .scan-table td:nth-child(2) {
max-width: 90px !important;
min-width: 90px !important;
}
.report-table-card .scan-table th:nth-child(3), /* Cod Articol */
.report-table-card .scan-table td:nth-child(3) {
max-width: 80px !important;
min-width: 80px !important;
}
.report-table-card .scan-table th:nth-child(4), /* Descr. Com. Prod */
.report-table-card .scan-table td:nth-child(4) {
max-width: 140px !important;
min-width: 140px !important;
}
.report-table-card .scan-table th:nth-child(5), /* Cantitate */
.report-table-card .scan-table td:nth-child(5) {
max-width: 70px !important;
min-width: 70px !important;
text-align: right !important;
}
.report-table-card .scan-table th:nth-child(6), /* Com.Achiz.Client */
.report-table-card .scan-table td:nth-child(6) {
max-width: 100px !important;
min-width: 100px !important;
}
.report-table-card .scan-table th:nth-child(7), /* Nr. Linie */
.report-table-card .scan-table td:nth-child(7) {
max-width: 60px !important;
min-width: 60px !important;
text-align: right !important;
}
.report-table-card .scan-table th:nth-child(8), /* Customer Name */
.report-table-card .scan-table td:nth-child(8) {
max-width: 120px !important;
min-width: 120px !important;
}
.report-table-card .scan-table th:nth-child(9), /* Customer Art. Nr. */
.report-table-card .scan-table td:nth-child(9) {
max-width: 90px !important;
min-width: 90px !important;
}
.report-table-card .scan-table th:nth-child(10), /* Open Order */
.report-table-card .scan-table td:nth-child(10) {
max-width: 70px !important;
min-width: 70px !important;
text-align: center !important;
}
.report-table-card .scan-table th:nth-child(11), /* Line */
.report-table-card .scan-table td:nth-child(11) {
max-width: 50px !important;
min-width: 50px !important;
text-align: right !important;
}
.report-table-card .scan-table th:nth-child(12), /* Printed */
.report-table-card .scan-table td:nth-child(12) {
max-width: 60px !important;
min-width: 60px !important;
text-align: center !important;
}
.report-table-card .scan-table th:nth-child(13), /* Created */
.report-table-card .scan-table td:nth-child(13) {
max-width: 110px !important;
min-width: 110px !important;
font-size: 10px !important;
}
/* Hover effects for better readability */
.report-table-card .scan-table tbody tr:hover {
background-color: #f8f9fa !important;
}
.report-table-card .scan-table tbody tr:hover td {
background-color: #f8f9fa !important;
}
/* Table container optimization */
.report-table-card .report-table-container {
overflow-x: auto !important;
}
.report-table-card .scan-table {
min-width: 1060px !important;
width: 100% !important;
table-layout: fixed !important;
}
/* Print styles */
@media print {
.report-form-card {
display: none !important;
}
.card {
box-shadow: none !important;
border: none !important;
}
.scan-table {
font-size: 9px !important;
}
.scan-table th,
.scan-table td {
padding: 3px 2px !important;
font-size: 9px !important;
}
}
</style>
{% endblock %}

6
sample_orders.csv Normal file
View File

@@ -0,0 +1,6 @@
Comanda Productie,Cod Articol,Descr. Com. Prod,Cantitate,Com.Achiz.Client,Nr. Linie com. Client,Customer Name,Customer Article Number,Open for order,Line
CP001234,ART001,Sample Production Order 1,100,CLIENT-PO-001,1,ABC Company Ltd,CUST-ART-001,Yes,1
CP001235,ART002,Sample Production Order 2,150,CLIENT-PO-002,2,XYZ Corporation,CUST-ART-002,Yes,1
CP001236,ART003,Sample Production Order 3,75,CLIENT-PO-003,1,DEF Industries,CUST-ART-003,No,2
CP001237,ART004,Sample Production Order 4,200,CLIENT-PO-004,3,GHI Manufacturing,CUST-ART-004,Yes,1
CP001238,ART005,Sample Production Order 5,50,CLIENT-PO-005,1,JKL Enterprises,CUST-ART-005,Yes,3
1 Comanda Productie Cod Articol Descr. Com. Prod Cantitate Com.Achiz.Client Nr. Linie com. Client Customer Name Customer Article Number Open for order Line
2 CP001234 ART001 Sample Production Order 1 100 CLIENT-PO-001 1 ABC Company Ltd CUST-ART-001 Yes 1
3 CP001235 ART002 Sample Production Order 2 150 CLIENT-PO-002 2 XYZ Corporation CUST-ART-002 Yes 1
4 CP001236 ART003 Sample Production Order 3 75 CLIENT-PO-003 1 DEF Industries CUST-ART-003 No 2
5 CP001237 ART004 Sample Production Order 4 200 CLIENT-PO-004 3 GHI Manufacturing CUST-ART-004 Yes 1
6 CP001238 ART005 Sample Production Order 5 50 CLIENT-PO-005 1 JKL Enterprises CUST-ART-005 Yes 3