diff --git a/logs/access.log b/logs/access.log index 8180038..c453032 100644 --- a/logs/access.log +++ b/logs/access.log @@ -471,3 +471,24 @@ 192.168.0.132 - - [25/Oct/2025:11:57:37 +0300] "GET / HTTP/1.1" 200 1627 "-" "-" 46568 192.168.0.132 - - [25/Oct/2025:12:15:43 +0300] "GET / HTTP/1.1" 200 1627 "-" "-" 47085 192.168.0.132 - - [26/Oct/2025:09:28:48 +0200] "GET /robots.txt HTTP/1.1" 404 207 "http://quality.moto-adv.com/robots.txt" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36; compatible; OAI-SearchBot/1.0; robots.txt; +https://openai.com/searchbot" 2129 +192.168.0.132 - - [26/Oct/2025:15:05:56 +0200] "GET / HTTP/1.1" 200 1627 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1703 +192.168.0.132 - - [26/Oct/2025:15:05:56 +0200] "GET //wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1929 +192.168.0.132 - - [26/Oct/2025:15:05:56 +0200] "GET //xmlrpc.php?rsd HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1466 +192.168.0.132 - - [26/Oct/2025:15:05:57 +0200] "GET / HTTP/1.1" 200 1627 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1741 +192.168.0.132 - - [26/Oct/2025:15:05:57 +0200] "GET //blog/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1441 +192.168.0.132 - - [26/Oct/2025:15:05:57 +0200] "GET //web/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1362 +192.168.0.132 - - [26/Oct/2025:15:05:57 +0200] "GET //wordpress/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1372 +192.168.0.132 - - [26/Oct/2025:15:05:57 +0200] "GET //website/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 2574 +192.168.0.132 - - [26/Oct/2025:15:05:58 +0200] "GET //wp/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 2042 +192.168.0.132 - - [26/Oct/2025:15:05:58 +0200] "GET //news/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1445 +192.168.0.132 - - [26/Oct/2025:15:05:58 +0200] "GET //2018/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1362 +192.168.0.132 - - [26/Oct/2025:15:05:58 +0200] "GET //2019/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1347 +192.168.0.132 - - [26/Oct/2025:15:05:58 +0200] "GET //shop/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1358 +192.168.0.132 - - [26/Oct/2025:15:05:59 +0200] "GET //wp1/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1448 +192.168.0.132 - - [26/Oct/2025:15:05:59 +0200] "GET //test/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1419 +192.168.0.132 - - [26/Oct/2025:15:05:59 +0200] "GET //media/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1365 +192.168.0.132 - - [26/Oct/2025:15:05:59 +0200] "GET //wp2/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1365 +192.168.0.132 - - [26/Oct/2025:15:06:00 +0200] "GET //site/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1385 +192.168.0.132 - - [26/Oct/2025:15:06:00 +0200] "GET //cms/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1375 +192.168.0.132 - - [26/Oct/2025:15:06:00 +0200] "GET //sito/wp-includes/wlwmanifest.xml HTTP/1.1" 404 207 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 1433 +192.168.0.132 - - [26/Oct/2025:17:11:18 +0200] "GET / HTTP/1.1" 200 1627 "http://172.67.151.21:80/" "-" 1691 diff --git a/py_app/app/daily_mirror.py b/py_app/app/daily_mirror.py index c74bf98..2c54de2 100644 --- a/py_app/app/daily_mirror.py +++ b/py_app/app/daily_mirror.py @@ -906,8 +906,8 @@ def api_get_production_data(): params = [] if search: - where_conditions.append("(production_order LIKE %s OR customer_code LIKE %s OR article_code LIKE %s)") - params.extend([f"%{search}%", f"%{search}%", f"%{search}%"]) + where_conditions.append("(production_order LIKE %s OR open_for_order_line LIKE %s OR client_order_line LIKE %s OR customer_code LIKE %s OR article_code LIKE %s)") + params.extend([f"%{search}%", f"%{search}%", f"%{search}%", f"%{search}%", f"%{search}%"]) if filter_status: where_conditions.append("production_status = %s") @@ -927,10 +927,10 @@ def api_get_production_data(): # Get paginated data offset = (page - 1) * per_page data_query = f""" - SELECT id, production_order, customer_code, customer_name, client_order, - article_code, article_description, quantity_requested, delivery_date, - production_status, end_of_quilting, end_of_sewing, machine_code, - data_planificare + SELECT id, production_order, open_for_order_line, client_order_line, + customer_code, customer_name, article_code, article_description, + quantity_requested, delivery_date, production_status, + end_of_quilting, end_of_sewing, machine_code, data_planificare FROM dm_production_orders {where_clause} ORDER BY data_planificare DESC, production_order LIMIT %s OFFSET %s @@ -942,18 +942,19 @@ def api_get_production_data(): records.append({ 'id': row[0], 'production_order': row[1], - 'customer_code': row[2], - 'customer_name': row[3], - 'client_order': row[4], - 'article_code': row[5], - 'article_description': row[6], - 'quantity_requested': row[7], - 'delivery_date': str(row[8]) if row[8] else None, - 'production_status': row[9], - 'end_of_quilting': str(row[10]) if row[10] else None, - 'end_of_sewing': str(row[11]) if row[11] else None, - 'machine_code': row[12], - 'data_planificare': str(row[13]) if row[13] else None + 'open_for_order_line': row[2], + 'client_order_line': row[3], + 'customer_code': row[4], + 'customer_name': row[5], + 'article_code': row[6], + 'article_description': row[7], + 'quantity_requested': row[8], + 'delivery_date': str(row[9]) if row[9] else None, + 'production_status': row[10], + 'end_of_quilting': str(row[11]) if row[11] else None, + 'end_of_sewing': str(row[12]) if row[12] else None, + 'machine_code': row[13], + 'data_planificare': str(row[14]) if row[14] else None }) dm_db.disconnect() @@ -986,7 +987,8 @@ def api_update_production_data(record_id): update_query = """ UPDATE dm_production_orders SET - customer_code = %s, customer_name = %s, client_order = %s, + open_for_order_line = %s, client_order_line = %s, + customer_code = %s, customer_name = %s, article_code = %s, article_description = %s, quantity_requested = %s, delivery_date = %s, production_status = %s, machine_code = %s, updated_at = CURRENT_TIMESTAMP @@ -994,9 +996,10 @@ def api_update_production_data(record_id): """ cursor.execute(update_query, ( + data.get('open_for_order_line'), + data.get('client_order_line'), data.get('customer_code'), data.get('customer_name'), - data.get('client_order'), data.get('article_code'), data.get('article_description'), data.get('quantity_requested'), @@ -1013,4 +1016,106 @@ def api_update_production_data(record_id): except Exception as e: print(f"Error updating production data: {e}") + return jsonify({'error': str(e)}), 500 + + +@daily_mirror_bp.route('/api/tune/production_data/', methods=['DELETE']) +def api_delete_production_data(record_id): + """API endpoint to delete a production record""" + access_check = check_daily_mirror_api_access() + if access_check: + return access_check + + try: + dm_db = DailyMirrorDatabase() + dm_db.connect() + cursor = dm_db.connection.cursor() + + # Delete the record + delete_query = "DELETE FROM dm_production_orders WHERE id = ?" + cursor.execute(delete_query, (record_id,)) + + if cursor.rowcount == 0: + dm_db.disconnect() + return jsonify({'error': 'Record not found'}), 404 + + dm_db.connection.commit() + dm_db.disconnect() + + return jsonify({'success': True, 'message': 'Production record deleted successfully'}) + + except Exception as e: + print(f"Error deleting production data: {e}") + return jsonify({'error': str(e)}), 500 + + +@daily_mirror_bp.route('/clear_production_orders', methods=['POST']) +def clear_production_orders(): + """Delete all rows from the Daily Mirror production orders table""" + # Check access + access_check = check_daily_mirror_api_access() + if access_check: + return access_check + + try: + dm_db = DailyMirrorDatabase() + dm_db.connect() + result = dm_db.clear_production_orders() + dm_db.disconnect() + + if result: + return jsonify({'success': True, 'message': 'All production orders deleted successfully.'}) + else: + return jsonify({'success': False, 'message': 'Error deleting production orders.'}), 500 + + except Exception as e: + print(f"Error clearing production orders: {e}") + return jsonify({'error': str(e)}), 500 + + +@daily_mirror_bp.route('/clear_orders', methods=['POST']) +def clear_orders(): + """Delete all rows from the Daily Mirror orders table""" + # Check access + access_check = check_daily_mirror_api_access() + if access_check: + return access_check + + try: + dm_db = DailyMirrorDatabase() + dm_db.connect() + result = dm_db.clear_orders() + dm_db.disconnect() + + if result: + return jsonify({'success': True, 'message': 'All orders deleted successfully.'}) + else: + return jsonify({'success': False, 'message': 'Error deleting orders.'}), 500 + + except Exception as e: + print(f"Error clearing orders: {e}") + return jsonify({'error': str(e)}), 500 + + +@daily_mirror_bp.route('/clear_delivery', methods=['POST']) +def clear_delivery(): + """Delete all rows from the Daily Mirror delivery table""" + # Check access + access_check = check_daily_mirror_api_access() + if access_check: + return access_check + + try: + dm_db = DailyMirrorDatabase() + dm_db.connect() + result = dm_db.clear_delivery() + dm_db.disconnect() + + if result: + return jsonify({'success': True, 'message': 'All delivery records deleted successfully.'}) + else: + return jsonify({'success': False, 'message': 'Error deleting delivery records.'}), 500 + + except Exception as e: + print(f"Error clearing delivery records: {e}") return jsonify({'error': str(e)}), 500 \ No newline at end of file diff --git a/py_app/app/daily_mirror_database_schema.sql b/py_app/app/daily_mirror_database_schema.sql index 99db030..934de24 100644 --- a/py_app/app/daily_mirror_database_schema.sql +++ b/py_app/app/daily_mirror_database_schema.sql @@ -36,59 +36,83 @@ CREATE TABLE IF NOT EXISTS dm_orders ( -- PRODUCTION DATA TABLES -- ============================================= --- Production Orders Table (from Comenzi Productie) +-- Production Orders Table (from Comenzi Productie - Production orders Data sheet) CREATE TABLE IF NOT EXISTS dm_production_orders ( id INT AUTO_INCREMENT PRIMARY KEY, + + -- Primary Identifiers production_order VARCHAR(50) UNIQUE NOT NULL, - order_id VARCHAR(50), + open_for_order_line VARCHAR(100), -- Concatenated: "Opened for Order" + "-" + "Linia" + client_order_line VARCHAR(100), -- Concatenated: "Com. Achiz. Client" + "-" + "Nr. linie com. client" + + -- Customer Information customer_code VARCHAR(50), customer_name VARCHAR(255), - client_order VARCHAR(100), - article_code VARCHAR(50), + + -- Article Information + article_code VARCHAR(100), article_description TEXT, quantity_requested INT, - delivery_date DATE, - production_status VARCHAR(50), + unit_of_measure VARCHAR(20), + + -- Dates + delivery_date DATE, -- SO Duedate + opening_date DATE, -- Data Deschiderii + closing_date DATE, -- Data Inchiderii + data_planificare DATE, -- Data Planific. + + -- Production Status + production_status VARCHAR(50), -- Status (Inchis, etc.) + + -- Machine Information + machine_code VARCHAR(50), -- Masina cusut + machine_type VARCHAR(50), -- Tip masina + machine_number VARCHAR(20), -- Machine Number -- Production Timeline - end_of_quilting DATETIME, - end_of_sewing DATETIME, - data_deschiderii DATE, - data_planificare DATE, + end_of_quilting DATE, -- End of Quilting + end_of_sewing DATE, -- End of Sewing - -- Quality Control Stages - t1_status DECIMAL(3,1), - t1_registration_date DATETIME, - t1_operator_name VARCHAR(100), - t2_status DECIMAL(3,1), - t2_registration_date DATETIME, - t2_operator_name VARCHAR(100), - t3_status DECIMAL(3,1), - t3_registration_date DATETIME, - t3_operator_name VARCHAR(100), + -- Quality Control Phase T1 (Prepared) + phase_t1_prepared VARCHAR(50), -- Faza pregatit(T1) + t1_operator_name VARCHAR(100), -- Nume complet T1 + t1_registration_date DATETIME, -- Data inregistrare T1 - -- Machine and Production Details - machine_code VARCHAR(50), - machine_type VARCHAR(50), - machine_number VARCHAR(20), - classification VARCHAR(100), - design_number INT, - needle_position INT, - total_norm_time DECIMAL(8,2), - model_lb2 VARCHAR(255), + -- Quality Control Phase T2 (Cut/Quilted) + phase_t2_cut VARCHAR(50), -- Faza taiat/matlasat(T2) + t2_operator_name VARCHAR(100), -- Nume complet T2 + t2_registration_date DATETIME, -- Data inregistrare T2 + -- Quality Control Phase T3 (Sewing) + phase_t3_sewing VARCHAR(50), -- Faza cusut(T3) + t3_operator_name VARCHAR(100), -- Nume complet T3 + t3_registration_date DATETIME, -- Data inregistrare T3 + + -- Additional Information + design_number INT, -- Design number + classification VARCHAR(100), -- Clasificare + model_description VARCHAR(255), -- Descriere Model + model_lb2 VARCHAR(255), -- Model Lb2 + needle_position DECIMAL(5,1), -- Needle Position + needle_row VARCHAR(50), -- Needle row + priority INT DEFAULT 0, -- Prioritate executie + + -- Metadata created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + -- Indexes for Performance INDEX idx_production_order (production_order), - INDEX idx_order_id (order_id), + INDEX idx_open_for_order_line (open_for_order_line), + INDEX idx_client_order_line (client_order_line), INDEX idx_customer (customer_code), INDEX idx_article (article_code), INDEX idx_delivery_date (delivery_date), INDEX idx_status (production_status), INDEX idx_machine (machine_code), INDEX idx_quilting_date (end_of_quilting), - INDEX idx_sewing_date (end_of_sewing) + INDEX idx_sewing_date (end_of_sewing), + INDEX idx_data_planificare (data_planificare) ); -- ============================================= diff --git a/py_app/app/daily_mirror_db_setup.py b/py_app/app/daily_mirror_db_setup.py index f888c52..59b2d15 100644 --- a/py_app/app/daily_mirror_db_setup.py +++ b/py_app/app/daily_mirror_db_setup.py @@ -94,50 +94,52 @@ class DailyMirrorDatabase: return False def import_production_data(self, file_path): - """Import production data from Excel file (Comenzi Productie format)""" + """Import production data from Excel file (Production orders Data sheet OR DataSheet)""" try: - # The correct data is in the first sheet (DataSheet) + # Read from "Production orders Data" sheet (new format) or "DataSheet" (old format) df = None sheet_used = None - # Get available sheets - excel_file = pd.ExcelFile(file_path) - logger.info(f"Available sheets: {excel_file.sheet_names}") + # Try different engines (openpyxl for .xlsx, pyxlsb for .xlsb) + engines_to_try = ['openpyxl', 'pyxlsb'] - # Try DataSheet first (where the actual production data is), then fallback options - sheet_attempts = [ - ('DataSheet', 'openpyxl'), - ('DataSheet', 'xlrd'), - (0, 'openpyxl'), - (0, 'xlrd'), - ('Sheet1', 'openpyxl'), # fallback to Sheet1 if DataSheet fails - (1, 'openpyxl') - ] + # Try different sheet names (new format first, then old format) + sheet_names_to_try = ['Production orders Data', 'DataSheet'] - for sheet_name, engine in sheet_attempts: - try: - logger.info(f"Trying to read sheet '{sheet_name}' with engine '{engine}'") - df = pd.read_excel(file_path, sheet_name=sheet_name, engine=engine, header=0) - sheet_used = f"{sheet_name} (engine: {engine})" - logger.info(f"Successfully read from sheet: {sheet_used}") + for engine in engines_to_try: + if df is not None: break + + try: + logger.info(f"Trying to read Excel file with engine: {engine}") + excel_file = pd.ExcelFile(file_path, engine=engine) + logger.info(f"Available sheets: {excel_file.sheet_names}") + + # Try each sheet name + for sheet_name in sheet_names_to_try: + if sheet_name in excel_file.sheet_names: + try: + logger.info(f"Reading sheet '{sheet_name}'") + df = pd.read_excel(file_path, sheet_name=sheet_name, engine=engine, header=0) + sheet_used = f"{sheet_name} (engine: {engine})" + logger.info(f"Successfully read from sheet: {sheet_used}") + break + except Exception as sheet_error: + logger.warning(f"Failed to read sheet '{sheet_name}': {sheet_error}") + continue + + if df is not None: + break + except Exception as e: - logger.warning(f"Failed to read sheet {sheet_name} with {engine}: {e}") + logger.warning(f"Failed with engine {engine}: {e}") continue - # If all engines fail on DataSheet, try a different approach if df is None: - try: - logger.info("Trying alternative method: reading without specifying engine") - df = pd.read_excel(file_path, sheet_name='DataSheet') - sheet_used = "DataSheet (default engine)" - logger.info("Successfully read with default engine") - except Exception as e: - logger.error(f"Failed with default engine: {e}") - raise Exception("Could not read the DataSheet from the Excel file. The file may be corrupted.") + raise Exception("Could not read Excel file. Please ensure it has a 'Production orders Data' or 'DataSheet' sheet.") logger.info(f"Loaded production data from {sheet_used}: {len(df)} rows, {len(df.columns)} columns") - logger.info(f"Available columns: {list(df.columns)}") + logger.info(f"First 5 column names: {list(df.columns)[:5]}") cursor = self.connection.cursor() success_count = 0 @@ -145,60 +147,116 @@ class DailyMirrorDatabase: updated_count = 0 error_count = 0 - # Prepare insert statement + # Prepare insert statement with new schema insert_sql = """ INSERT INTO dm_production_orders ( - production_order, customer_code, client_order, article_code, - article_description, quantity_requested, delivery_date, production_status, - end_of_quilting, end_of_sewing, t1_status, t1_registration_date, t1_operator_name, - t2_status, t2_registration_date, t2_operator_name, t3_status, t3_registration_date, - t3_operator_name, machine_code, machine_type, classification, total_norm_time, - data_deschiderii, model_lb2, data_planificare, machine_number, design_number, needle_position - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + production_order, open_for_order_line, client_order_line, + customer_code, customer_name, article_code, article_description, + quantity_requested, unit_of_measure, delivery_date, opening_date, + closing_date, data_planificare, production_status, + machine_code, machine_type, machine_number, + end_of_quilting, end_of_sewing, + phase_t1_prepared, t1_operator_name, t1_registration_date, + phase_t2_cut, t2_operator_name, t2_registration_date, + phase_t3_sewing, t3_operator_name, t3_registration_date, + design_number, classification, model_description, model_lb2, + needle_position, needle_row, priority + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE + open_for_order_line = VALUES(open_for_order_line), + client_order_line = VALUES(client_order_line), customer_code = VALUES(customer_code), - client_order = VALUES(client_order), + customer_name = VALUES(customer_name), article_code = VALUES(article_code), article_description = VALUES(article_description), quantity_requested = VALUES(quantity_requested), delivery_date = VALUES(delivery_date), production_status = VALUES(production_status), + machine_code = VALUES(machine_code), + end_of_quilting = VALUES(end_of_quilting), + end_of_sewing = VALUES(end_of_sewing), + phase_t1_prepared = VALUES(phase_t1_prepared), + t1_operator_name = VALUES(t1_operator_name), + t1_registration_date = VALUES(t1_registration_date), + phase_t2_cut = VALUES(phase_t2_cut), + t2_operator_name = VALUES(t2_operator_name), + t2_registration_date = VALUES(t2_registration_date), + phase_t3_sewing = VALUES(phase_t3_sewing), + t3_operator_name = VALUES(t3_operator_name), + t3_registration_date = VALUES(t3_registration_date), updated_at = CURRENT_TIMESTAMP """ for index, row in df.iterrows(): try: + # Create concatenated fields with dash separator + opened_for_order = str(row.get('Opened for Order', '')).strip() if pd.notna(row.get('Opened for Order')) else '' + linia = str(row.get('Linia', '')).strip() if pd.notna(row.get('Linia')) else '' + open_for_order_line = f"{opened_for_order}-{linia}" if opened_for_order and linia else '' + + com_achiz_client = str(row.get('Com. Achiz. Client', '')).strip() if pd.notna(row.get('Com. Achiz. Client')) else '' + nr_linie_com_client = str(row.get('Nr. linie com. client', '')).strip() if pd.notna(row.get('Nr. linie com. client')) else '' + client_order_line = f"{com_achiz_client}-{nr_linie_com_client}" if com_achiz_client and nr_linie_com_client else '' + + # Helper function to safely get numeric values + def safe_int(value, default=None): + if pd.isna(value) or value == '': + return default + try: + return int(float(value)) + except (ValueError, TypeError): + return default + + def safe_float(value, default=None): + if pd.isna(value) or value == '': + return default + try: + return float(value) + except (ValueError, TypeError): + return default + + def safe_str(value, default=''): + if pd.isna(value): + return default + return str(value).strip() + # Prepare data tuple data = ( - row.get('Comanda Productie', ''), - row.get('Customer', ''), - row.get('Comanda client', ''), - row.get('Cod Articol', ''), - row.get('Descriere', ''), - row.get('Cantitate ceruta', 0), - self._parse_date(row.get('Delivery date')), - row.get('Status', ''), - self._parse_datetime(row.get('End of Quilting')), - self._parse_datetime(row.get('End of sewing')), - row.get('T1', 0), - self._parse_datetime(row.get('Data inregistrare T1')), - row.get('Numele Complet T1', ''), - row.get('T2', 0), - self._parse_datetime(row.get('Data inregistrare T2')), - row.get('Numele Complet T2', ''), - row.get('T3', 0), - self._parse_datetime(row.get('Data inregistrare T3')), - row.get('Numele Complet T3', ''), - row.get('Masina Cusut ', ''), - row.get('Tip Masina', ''), - row.get('Clasificare', ''), - row.get('Timp normat total', 0), - self._parse_date(row.get('Data Deschiderii')), - row.get('Model Lb2', ''), - self._parse_date(row.get('Data Planific.')), - row.get('Numar masina', ''), - row.get('Design nr', 0), - row.get('Needle position', 0) + safe_str(row.get('Comanda Productie')), # production_order + open_for_order_line, # open_for_order_line (concatenated) + client_order_line, # client_order_line (concatenated) + safe_str(row.get('Cod. Client')), # customer_code + safe_str(row.get('Customer Name')), # customer_name + safe_str(row.get('Cod Articol')), # article_code + safe_str(row.get('Descr. Articol.1')), # article_description + safe_int(row.get('Cantitate Com. Prod.'), 0), # quantity_requested + safe_str(row.get('U.M.')), # unit_of_measure + self._parse_date(row.get('SO Duedate')), # delivery_date + self._parse_date(row.get('Data Deschiderii')), # opening_date + self._parse_date(row.get('Data Inchiderii')), # closing_date + self._parse_date(row.get('Data Planific.')), # data_planificare + safe_str(row.get('Status')), # production_status + safe_str(row.get('Masina cusut')), # machine_code + safe_str(row.get('Tip masina')), # machine_type + safe_str(row.get('Machine Number')), # machine_number + self._parse_date(row.get('End of Quilting')), # end_of_quilting + self._parse_date(row.get('End of Sewing')), # end_of_sewing + safe_str(row.get('T2')), # phase_t1_prepared (using T2 column) + safe_str(row.get('Nume complet T2')), # t1_operator_name + self._parse_datetime(row.get('Data inregistrare T2')), # t1_registration_date + safe_str(row.get('T1')), # phase_t2_cut (using T1 column) + safe_str(row.get('Nume complet T1')), # t2_operator_name + self._parse_datetime(row.get('Data inregistrare T1')), # t2_registration_date + safe_str(row.get('T3')), # phase_t3_sewing (using T3 column) + safe_str(row.get('Nume complet T3')), # t3_operator_name + self._parse_datetime(row.get('Data inregistrare T3')), # t3_registration_date + safe_int(row.get('Design number')), # design_number + safe_str(row.get('Clasificare')), # classification + safe_str(row.get('Descriere Model')), # model_description + safe_str(row.get('Model Lb2')), # model_lb2 + safe_float(row.get('Needle Position')), # needle_position + safe_str(row.get('Needle row')), # needle_row + safe_int(row.get('Prioritate executie'), 0) # priority ) cursor.execute(insert_sql, data) @@ -217,11 +275,17 @@ class DailyMirrorDatabase: except Exception as row_error: logger.warning(f"Error processing row {index}: {row_error}") + # Log first few values of problematic row + try: + row_sample = {k: v for k, v in list(row.items())[:5]} + logger.warning(f"Row data sample: {row_sample}") + except: + pass error_count += 1 continue self.connection.commit() - logger.info(f"Production data import completed: {success_count} successful, {error_count} failed") + logger.info(f"Production data import completed: {success_count} successful ({created_count} created, {updated_count} updated), {error_count} failed") return { 'success_count': success_count, @@ -233,6 +297,8 @@ class DailyMirrorDatabase: except Exception as e: logger.error(f"Error importing production data: {e}") + import traceback + logger.error(traceback.format_exc()) return None def import_orders_data(self, file_path): @@ -688,6 +754,30 @@ class DailyMirrorDatabase: logger.error(f"Error deleting production orders: {e}") return False + def clear_orders(self): + """Delete all rows from the Daily Mirror orders table""" + try: + cursor = self.connection.cursor() + cursor.execute("DELETE FROM dm_orders") + self.connection.commit() + logger.info("All orders deleted from dm_orders table.") + return True + except Exception as e: + logger.error(f"Error deleting orders: {e}") + return False + + def clear_delivery(self): + """Delete all rows from the Daily Mirror delivery table""" + try: + cursor = self.connection.cursor() + cursor.execute("DELETE FROM dm_deliveries") + self.connection.commit() + logger.info("All delivery records deleted from dm_deliveries table.") + return True + except Exception as e: + logger.error(f"Error deleting delivery records: {e}") + return False + def _parse_date(self, date_value): """Parse date with better null handling""" if pd.isna(date_value) or date_value == 'nan' or date_value is None or date_value == '': diff --git a/py_app/app/static/css/daily_mirror_tune.css b/py_app/app/static/css/daily_mirror_tune.css index e7485a8..429de07 100644 --- a/py_app/app/static/css/daily_mirror_tune.css +++ b/py_app/app/static/css/daily_mirror_tune.css @@ -1,6 +1,41 @@ /* Daily Mirror Tune Pages - Modal Styles */ /* Fixes for editable modals across tune/production, tune/orders, and tune/delivery pages */ +/* Force modal width to be extra large (20% wider than standard 1200px) */ +#editModal .modal-dialog { + max-width: 1440px !important; +} + +/* Modal footer button spacing and sizing */ +#editModal .modal-footer { + display: flex; + justify-content: space-between; + align-items: center; +} + +#editModal .modal-footer .btn { + min-width: 100px; +} + +#editModal .modal-footer .btn-danger { + min-width: 150px; + background-color: #dc3545 !important; + border-color: #dc3545 !important; +} + +#editModal .modal-footer .btn-danger:hover { + background-color: #bb2d3b !important; + border-color: #b02a37 !important; +} + +#editModal .modal-footer .btn-primary { + min-width: 150px; +} + +#editModal .modal-footer .btn-secondary { + margin-right: 10px; +} + /* Force Bootstrap modal to have proper z-index */ #editModal.modal { z-index: 9999 !important; diff --git a/py_app/app/templates/daily_mirror_build_database.html b/py_app/app/templates/daily_mirror_build_database.html index e524b6e..e69e239 100644 --- a/py_app/app/templates/daily_mirror_build_database.html +++ b/py_app/app/templates/daily_mirror_build_database.html @@ -2,6 +2,10 @@ {% block title %}Build Database - Daily Mirror{% endblock %} +{% block extra_css %} + +{% endblock %} + {% block content %}
@@ -205,6 +209,43 @@
{% endblock %} {% block content %} @@ -90,6 +131,11 @@
+ {% if session.get('role') == 'superadmin' %} + + {% endif %} @@ -101,8 +147,9 @@ Production Order + Opened for Order + Client Order-Line Customer - Client Order Article Code Description Quantity @@ -147,7 +194,7 @@