From ceda2447a6d35999d0419d2297c2b4efb1fabca3 Mon Sep 17 00:00:00 2001 From: Scheianu Ionut Date: Mon, 15 Sep 2025 21:14:48 +0300 Subject: [PATCH] updated to uplodad orders --- instance/users.db | Bin 16384 -> 0 bytes .../__pycache__/order_labels.cpython-312.pyc | Bin 0 -> 15011 bytes py_app/app/__pycache__/routes.cpython-312.pyc | Bin 52083 -> 53335 bytes .../add_printed_labels_column.py | 151 ++++++++ .../create_order_for_labels_table.py | 110 ++++++ py_app/app/order_labels.py | 339 ++++++++++++++++++ py_app/app/routes.py | 22 ++ py_app/app/templates/main_page_etichete.html | 8 +- py_app/app/templates/upload_data.html | 10 - py_app/app/templates/upload_orders.html | 244 +++++++++++++ py_app/app/templates/view_orders.html | 266 ++++++++++++++ sample_orders.csv | 6 + 12 files changed, 1142 insertions(+), 14 deletions(-) delete mode 100644 instance/users.db create mode 100644 py_app/app/__pycache__/order_labels.cpython-312.pyc create mode 100644 py_app/app/db_create_scripts/add_printed_labels_column.py create mode 100644 py_app/app/db_create_scripts/create_order_for_labels_table.py create mode 100644 py_app/app/order_labels.py delete mode 100644 py_app/app/templates/upload_data.html create mode 100644 py_app/app/templates/upload_orders.html create mode 100644 py_app/app/templates/view_orders.html create mode 100644 sample_orders.csv diff --git a/instance/users.db b/instance/users.db deleted file mode 100644 index fa85049ba526e8018e009ece720d7aee20970d16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI(%SyvQ6b9fKQ*VVfZY0~|&SDY7b-^m56x*mZ1-mL^Glf8!)Le>Napi;fAU=RE zBNSaaNxfAHZl(N(Ooj<(W;ow26LNXc^b$pbFdE8)_SpvGoE;D`#tI)zcX@Dq&z^lo zHvTILY8pJA$10VN=kr-0eUPRaMWd++Ffj%d+_Wa=vC1>UHzm(#-t) z5p5UgM|dtZY)71kHl4Q{t$Mplr=m;sj&s(q_3W0go$AbtY*%u`l|vo7anTWEpE+cA zn$6iX+1J&?JztHLKTV&X=c>3Ld85P&gFNkPuS#WU#j07Hd4a2LKWdk066WJxUQBN< z?+gvSmSLB4IXBqJqbl7UNuH0Wms2ys!@#myI{F9ZZ2009U<00Izz00bZa0SG_< z0t+KhGPc&gPYb5s|0m3xEKGqY1OgC%00bZa0SG_<0uX=z1Rwx`OkkZG7N7nH@D8e~ BkF5Xz diff --git a/py_app/app/__pycache__/order_labels.cpython-312.pyc b/py_app/app/__pycache__/order_labels.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..767ed3277929ac241750fc6fd0cfd48c3da57aad GIT binary patch literal 15011 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 Tc!wlgo?KG2k|pdffZ6{8G0Qb% literal 0 HcmV?d00001 diff --git a/py_app/app/__pycache__/routes.cpython-312.pyc b/py_app/app/__pycache__/routes.cpython-312.pyc index f3b6de2d6639f5ed36d70c4d0bff70b6a18a7565..702debeaf34d8001cedd33278bd54033c9aa2371 100644 GIT binary patch delta 1134 zcmZ{jO-vI(6vublWp}0YLj$D(#sNa4Cbohfq$Cj0NTLQX5+zZjEZylx><7*kiG|u~ zP|zreu2ByhYV?4C0|Y~iM;&_0WBs{Y?n9VMRVdL#tpYL&&klH|8Pa zSl8DiTuJ@mxUqFZSEiI7nQ^b_niQrrh77@gOFL8)JN_sF?yY!1Wdm8_3%&6tA zMJX&xT%K%Abi^%5`uL`XqpP>Hl;%X5MCpB5)tD_w@88t$bTdhorAd@7mMd1e{s52# BP;meN delta 211 zcmcb$Byj%=Gkjj2MW7(#Od=iW%8`V#-GpbF#z$iMIokIb_;*L%cO%Y3H zS|h%iX|e-{2umfSX6)u895K5l2OqCvt6^kdD5{=({kS4q9gz8!dGf#GlelX^qKzP; zVe;$~s!B~j<|k$bhGIUTg$)c(S*&hyt6%2UyuhMaWDFG2^xORC#0ExPkP$^7#YJX7 pQLt2zA&~Wp!zMRBr8FniuBaO*3q%aX&YMr3`ozNIz&PnbH~ 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) \ No newline at end of file diff --git a/py_app/app/db_create_scripts/create_order_for_labels_table.py b/py_app/app/db_create_scripts/create_order_for_labels_table.py new file mode 100644 index 0000000..fc21e80 --- /dev/null +++ b/py_app/app/db_create_scripts/create_order_for_labels_table.py @@ -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) \ No newline at end of file diff --git a/py_app/app/order_labels.py b/py_app/app/order_labels.py new file mode 100644 index 0000000..78a3ff3 --- /dev/null +++ b/py_app/app/order_labels.py @@ -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"
• {failure}" + if len(failed_orders) > 5: + report += f"
• ... 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 [] \ No newline at end of file diff --git a/py_app/app/routes.py b/py_app/app/routes.py index d72e5fb..1fc519b 100644 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -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 diff --git a/py_app/app/templates/main_page_etichete.html b/py_app/app/templates/main_page_etichete.html index e2c55fd..311e1fd 100644 --- a/py_app/app/templates/main_page_etichete.html +++ b/py_app/app/templates/main_page_etichete.html @@ -9,11 +9,11 @@
- +
-

Upload Data

-

Upload data into the database for label management.

- Go to Upload Data +

View Orders

+

View uploaded orders and manage label data for printing.

+ View Orders
diff --git a/py_app/app/templates/upload_data.html b/py_app/app/templates/upload_data.html deleted file mode 100644 index 7e46a4e..0000000 --- a/py_app/app/templates/upload_data.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Upload Data{% endblock %} - -{% block content %} -
-

Upload Data

-

This page will allow users to upload data into the database.

-
-{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/upload_orders.html b/py_app/app/templates/upload_orders.html new file mode 100644 index 0000000..d5b72d4 --- /dev/null +++ b/py_app/app/templates/upload_orders.html @@ -0,0 +1,244 @@ +{% extends "base.html" %} +{% block title %}Upload Order Data for Labels{% endblock %} +{% block content %} +
+ +
+

Upload Order Data for Labels

+
+ + {% if not orders %} +
+ + {% else %} +
+ + {% endif %} +
+ + + + + +
+ + +
+

Preview Table

+ + + + + + + + + + + + + + + + + {% if orders %} + {% for order in orders %} + + + + + + + + + + + + + {% endfor %} + {% else %} + + {% endif %} + +
Comanda ProductieCod ArticolDescr. Com. ProdCantitateCom.Achiz.ClientNr. Linie com. ClientCustomer NameCustomer Article NumberOpen for orderLine
{{ order.get('comanda_productie', '') }}{{ order.get('cod_articol', '') }}{{ order.get('descr_com_prod', '') }}{{ order.get('cantitate', '') }}{{ order.get('com_achiz_client', '') }}{{ order.get('nr_linie_com_client', '') }}{{ order.get('customer_name', '') }}{{ order.get('customer_article_number', '') }}{{ order.get('open_for_order', '') }}{{ order.get('line_number', '') }}
No CSV file uploaded yet.
+
+ + {% if validation_errors or validation_warnings %} +
+

Validation Results

+ + {% if validation_errors %} +
+ Errors found: +
    + {% for error in validation_errors %} +
  • {{ error }}
  • + {% endfor %} +
+
+ {% endif %} + + {% if validation_warnings %} +
+ Warnings: +
    + {% for warning in validation_warnings %} +
  • {{ warning }}
  • + {% endfor %} +
+
+ {% endif %} +
+ {% endif %} + + {% if report %} +
+

Import Report

+

{{ report }}

+
+ {% endif %} +
+ + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/view_orders.html b/py_app/app/templates/view_orders.html new file mode 100644 index 0000000..18c5544 --- /dev/null +++ b/py_app/app/templates/view_orders.html @@ -0,0 +1,266 @@ +{% extends "base.html" %} +{% block title %}View Uploaded Orders{% endblock %} +{% block content %} +
+ +
+

List Uploaded Orders

+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+ +
+ +
+
+
+
+ + +
+ {% if orders %} +

Uploaded Orders ({{ orders|length }} total)

+ {% else %} +

No orders uploaded yet

+ {% endif %} + +
+ {% if orders %} + + + + + + + + + + + + + + + + + + + + {% for order in orders %} + + + + + + + + + + + + + + + + {% endfor %} + +
IDComanda ProductieCod ArticolDescr. Com. ProdCantitateCom.Achiz.ClientNr. LinieCustomer NameCustomer Art. Nr.Open OrderLinePrintedCreated
{{ order.id }}{{ order.comanda_productie }}{{ order.cod_articol or '-' }}{{ order.descr_com_prod }}{{ order.cantitate }}{{ order.com_achiz_client or '-' }}{{ order.nr_linie_com_client or '-' }}{{ order.customer_name or '-' }}{{ order.customer_article_number or '-' }}{{ order.open_for_order or '-' }}{{ order.line_number or '-' }} + {% if order.printed_labels == 1 %} + āœ“ Yes + {% else %} + āœ— No + {% endif %} + + {% if order.created_at %} + {{ order.created_at.strftime('%Y-%m-%d %H:%M') }} + {% else %} + - + {% endif %} +
+ {% else %} +
+
šŸ“¦
+

No Orders Found

+

Upload your first CSV file to see orders here.

+ +
+ {% endif %} +
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/sample_orders.csv b/sample_orders.csv new file mode 100644 index 0000000..8480e6a --- /dev/null +++ b/sample_orders.csv @@ -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 \ No newline at end of file