updated structure in the table view

This commit is contained in:
Quality System Admin
2025-10-26 19:30:12 +02:00
parent 8cbede35d2
commit d142129de6
9 changed files with 760 additions and 185 deletions

View File

@@ -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: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 - - [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: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

View File

@@ -906,8 +906,8 @@ def api_get_production_data():
params = [] params = []
if search: if search:
where_conditions.append("(production_order LIKE %s OR customer_code LIKE %s OR article_code LIKE %s)") 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}%"]) params.extend([f"%{search}%", f"%{search}%", f"%{search}%", f"%{search}%", f"%{search}%"])
if filter_status: if filter_status:
where_conditions.append("production_status = %s") where_conditions.append("production_status = %s")
@@ -927,10 +927,10 @@ def api_get_production_data():
# Get paginated data # Get paginated data
offset = (page - 1) * per_page offset = (page - 1) * per_page
data_query = f""" data_query = f"""
SELECT id, production_order, customer_code, customer_name, client_order, SELECT id, production_order, open_for_order_line, client_order_line,
article_code, article_description, quantity_requested, delivery_date, customer_code, customer_name, article_code, article_description,
production_status, end_of_quilting, end_of_sewing, machine_code, quantity_requested, delivery_date, production_status,
data_planificare end_of_quilting, end_of_sewing, machine_code, data_planificare
FROM dm_production_orders {where_clause} FROM dm_production_orders {where_clause}
ORDER BY data_planificare DESC, production_order ORDER BY data_planificare DESC, production_order
LIMIT %s OFFSET %s LIMIT %s OFFSET %s
@@ -942,18 +942,19 @@ def api_get_production_data():
records.append({ records.append({
'id': row[0], 'id': row[0],
'production_order': row[1], 'production_order': row[1],
'customer_code': row[2], 'open_for_order_line': row[2],
'customer_name': row[3], 'client_order_line': row[3],
'client_order': row[4], 'customer_code': row[4],
'article_code': row[5], 'customer_name': row[5],
'article_description': row[6], 'article_code': row[6],
'quantity_requested': row[7], 'article_description': row[7],
'delivery_date': str(row[8]) if row[8] else None, 'quantity_requested': row[8],
'production_status': row[9], 'delivery_date': str(row[9]) if row[9] else None,
'end_of_quilting': str(row[10]) if row[10] else None, 'production_status': row[10],
'end_of_sewing': str(row[11]) if row[11] else None, 'end_of_quilting': str(row[11]) if row[11] else None,
'machine_code': row[12], 'end_of_sewing': str(row[12]) if row[12] else None,
'data_planificare': str(row[13]) if row[13] else None 'machine_code': row[13],
'data_planificare': str(row[14]) if row[14] else None
}) })
dm_db.disconnect() dm_db.disconnect()
@@ -986,7 +987,8 @@ def api_update_production_data(record_id):
update_query = """ update_query = """
UPDATE dm_production_orders SET 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, article_code = %s, article_description = %s, quantity_requested = %s,
delivery_date = %s, production_status = %s, machine_code = %s, delivery_date = %s, production_status = %s, machine_code = %s,
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
@@ -994,9 +996,10 @@ def api_update_production_data(record_id):
""" """
cursor.execute(update_query, ( cursor.execute(update_query, (
data.get('open_for_order_line'),
data.get('client_order_line'),
data.get('customer_code'), data.get('customer_code'),
data.get('customer_name'), data.get('customer_name'),
data.get('client_order'),
data.get('article_code'), data.get('article_code'),
data.get('article_description'), data.get('article_description'),
data.get('quantity_requested'), data.get('quantity_requested'),
@@ -1014,3 +1017,105 @@ def api_update_production_data(record_id):
except Exception as e: except Exception as e:
print(f"Error updating production data: {e}") print(f"Error updating production data: {e}")
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
@daily_mirror_bp.route('/api/tune/production_data/<int:record_id>', 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

View File

@@ -36,59 +36,83 @@ CREATE TABLE IF NOT EXISTS dm_orders (
-- PRODUCTION DATA TABLES -- 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 ( CREATE TABLE IF NOT EXISTS dm_production_orders (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
-- Primary Identifiers
production_order VARCHAR(50) UNIQUE NOT NULL, 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_code VARCHAR(50),
customer_name VARCHAR(255), customer_name VARCHAR(255),
client_order VARCHAR(100),
article_code VARCHAR(50), -- Article Information
article_code VARCHAR(100),
article_description TEXT, article_description TEXT,
quantity_requested INT, quantity_requested INT,
delivery_date DATE, unit_of_measure VARCHAR(20),
production_status VARCHAR(50),
-- 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 -- Production Timeline
end_of_quilting DATETIME, end_of_quilting DATE, -- End of Quilting
end_of_sewing DATETIME, end_of_sewing DATE, -- End of Sewing
data_deschiderii DATE,
data_planificare DATE,
-- Quality Control Stages -- Quality Control Phase T1 (Prepared)
t1_status DECIMAL(3,1), phase_t1_prepared VARCHAR(50), -- Faza pregatit(T1)
t1_registration_date DATETIME, t1_operator_name VARCHAR(100), -- Nume complet T1
t1_operator_name VARCHAR(100), t1_registration_date DATETIME, -- Data inregistrare T1
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),
-- Machine and Production Details -- Quality Control Phase T2 (Cut/Quilted)
machine_code VARCHAR(50), phase_t2_cut VARCHAR(50), -- Faza taiat/matlasat(T2)
machine_type VARCHAR(50), t2_operator_name VARCHAR(100), -- Nume complet T2
machine_number VARCHAR(20), t2_registration_date DATETIME, -- Data inregistrare T2
classification VARCHAR(100),
design_number INT,
needle_position INT,
total_norm_time DECIMAL(8,2),
model_lb2 VARCHAR(255),
-- 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, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- Indexes for Performance
INDEX idx_production_order (production_order), 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_customer (customer_code),
INDEX idx_article (article_code), INDEX idx_article (article_code),
INDEX idx_delivery_date (delivery_date), INDEX idx_delivery_date (delivery_date),
INDEX idx_status (production_status), INDEX idx_status (production_status),
INDEX idx_machine (machine_code), INDEX idx_machine (machine_code),
INDEX idx_quilting_date (end_of_quilting), 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)
); );
-- ============================================= -- =============================================

View File

@@ -94,50 +94,52 @@ class DailyMirrorDatabase:
return False return False
def import_production_data(self, file_path): 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: try:
# The correct data is in the first sheet (DataSheet) # Read from "Production orders Data" sheet (new format) or "DataSheet" (old format)
df = None df = None
sheet_used = None sheet_used = None
# Get available sheets # Try different engines (openpyxl for .xlsx, pyxlsb for .xlsb)
excel_file = pd.ExcelFile(file_path) engines_to_try = ['openpyxl', 'pyxlsb']
# Try different sheet names (new format first, then old format)
sheet_names_to_try = ['Production orders Data', 'DataSheet']
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}") logger.info(f"Available sheets: {excel_file.sheet_names}")
# Try DataSheet first (where the actual production data is), then fallback options # Try each sheet name
sheet_attempts = [ for sheet_name in sheet_names_to_try:
('DataSheet', 'openpyxl'), if sheet_name in excel_file.sheet_names:
('DataSheet', 'xlrd'),
(0, 'openpyxl'),
(0, 'xlrd'),
('Sheet1', 'openpyxl'), # fallback to Sheet1 if DataSheet fails
(1, 'openpyxl')
]
for sheet_name, engine in sheet_attempts:
try: try:
logger.info(f"Trying to read sheet '{sheet_name}' with engine '{engine}'") logger.info(f"Reading sheet '{sheet_name}'")
df = pd.read_excel(file_path, sheet_name=sheet_name, engine=engine, header=0) df = pd.read_excel(file_path, sheet_name=sheet_name, engine=engine, header=0)
sheet_used = f"{sheet_name} (engine: {engine})" sheet_used = f"{sheet_name} (engine: {engine})"
logger.info(f"Successfully read from sheet: {sheet_used}") logger.info(f"Successfully read from sheet: {sheet_used}")
break break
except Exception as e: except Exception as sheet_error:
logger.warning(f"Failed to read sheet {sheet_name} with {engine}: {e}") logger.warning(f"Failed to read sheet '{sheet_name}': {sheet_error}")
continue continue
# If all engines fail on DataSheet, try a different approach if df is not None:
if df is None: break
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: except Exception as e:
logger.error(f"Failed with default engine: {e}") logger.warning(f"Failed with engine {engine}: {e}")
raise Exception("Could not read the DataSheet from the Excel file. The file may be corrupted.") continue
if df is None:
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"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() cursor = self.connection.cursor()
success_count = 0 success_count = 0
@@ -145,60 +147,116 @@ class DailyMirrorDatabase:
updated_count = 0 updated_count = 0
error_count = 0 error_count = 0
# Prepare insert statement # Prepare insert statement with new schema
insert_sql = """ insert_sql = """
INSERT INTO dm_production_orders ( INSERT INTO dm_production_orders (
production_order, customer_code, client_order, article_code, production_order, open_for_order_line, client_order_line,
article_description, quantity_requested, delivery_date, production_status, customer_code, customer_name, article_code, article_description,
end_of_quilting, end_of_sewing, t1_status, t1_registration_date, t1_operator_name, quantity_requested, unit_of_measure, delivery_date, opening_date,
t2_status, t2_registration_date, t2_operator_name, t3_status, t3_registration_date, closing_date, data_planificare, production_status,
t3_operator_name, machine_code, machine_type, classification, total_norm_time, machine_code, machine_type, machine_number,
data_deschiderii, model_lb2, data_planificare, machine_number, design_number, needle_position end_of_quilting, end_of_sewing,
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 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 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), customer_code = VALUES(customer_code),
client_order = VALUES(client_order), customer_name = VALUES(customer_name),
article_code = VALUES(article_code), article_code = VALUES(article_code),
article_description = VALUES(article_description), article_description = VALUES(article_description),
quantity_requested = VALUES(quantity_requested), quantity_requested = VALUES(quantity_requested),
delivery_date = VALUES(delivery_date), delivery_date = VALUES(delivery_date),
production_status = VALUES(production_status), 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 updated_at = CURRENT_TIMESTAMP
""" """
for index, row in df.iterrows(): for index, row in df.iterrows():
try: 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 # Prepare data tuple
data = ( data = (
row.get('Comanda Productie', ''), safe_str(row.get('Comanda Productie')), # production_order
row.get('Customer', ''), open_for_order_line, # open_for_order_line (concatenated)
row.get('Comanda client', ''), client_order_line, # client_order_line (concatenated)
row.get('Cod Articol', ''), safe_str(row.get('Cod. Client')), # customer_code
row.get('Descriere', ''), safe_str(row.get('Customer Name')), # customer_name
row.get('Cantitate ceruta', 0), safe_str(row.get('Cod Articol')), # article_code
self._parse_date(row.get('Delivery date')), safe_str(row.get('Descr. Articol.1')), # article_description
row.get('Status', ''), safe_int(row.get('Cantitate Com. Prod.'), 0), # quantity_requested
self._parse_datetime(row.get('End of Quilting')), safe_str(row.get('U.M.')), # unit_of_measure
self._parse_datetime(row.get('End of sewing')), self._parse_date(row.get('SO Duedate')), # delivery_date
row.get('T1', 0), self._parse_date(row.get('Data Deschiderii')), # opening_date
self._parse_datetime(row.get('Data inregistrare T1')), self._parse_date(row.get('Data Inchiderii')), # closing_date
row.get('Numele Complet T1', ''), self._parse_date(row.get('Data Planific.')), # data_planificare
row.get('T2', 0), safe_str(row.get('Status')), # production_status
self._parse_datetime(row.get('Data inregistrare T2')), safe_str(row.get('Masina cusut')), # machine_code
row.get('Numele Complet T2', ''), safe_str(row.get('Tip masina')), # machine_type
row.get('T3', 0), safe_str(row.get('Machine Number')), # machine_number
self._parse_datetime(row.get('Data inregistrare T3')), self._parse_date(row.get('End of Quilting')), # end_of_quilting
row.get('Numele Complet T3', ''), self._parse_date(row.get('End of Sewing')), # end_of_sewing
row.get('Masina Cusut ', ''), safe_str(row.get('T2')), # phase_t1_prepared (using T2 column)
row.get('Tip Masina', ''), safe_str(row.get('Nume complet T2')), # t1_operator_name
row.get('Clasificare', ''), self._parse_datetime(row.get('Data inregistrare T2')), # t1_registration_date
row.get('Timp normat total', 0), safe_str(row.get('T1')), # phase_t2_cut (using T1 column)
self._parse_date(row.get('Data Deschiderii')), safe_str(row.get('Nume complet T1')), # t2_operator_name
row.get('Model Lb2', ''), self._parse_datetime(row.get('Data inregistrare T1')), # t2_registration_date
self._parse_date(row.get('Data Planific.')), safe_str(row.get('T3')), # phase_t3_sewing (using T3 column)
row.get('Numar masina', ''), safe_str(row.get('Nume complet T3')), # t3_operator_name
row.get('Design nr', 0), self._parse_datetime(row.get('Data inregistrare T3')), # t3_registration_date
row.get('Needle position', 0) 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) cursor.execute(insert_sql, data)
@@ -217,11 +275,17 @@ class DailyMirrorDatabase:
except Exception as row_error: except Exception as row_error:
logger.warning(f"Error processing row {index}: {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 error_count += 1
continue continue
self.connection.commit() 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 { return {
'success_count': success_count, 'success_count': success_count,
@@ -233,6 +297,8 @@ class DailyMirrorDatabase:
except Exception as e: except Exception as e:
logger.error(f"Error importing production data: {e}") logger.error(f"Error importing production data: {e}")
import traceback
logger.error(traceback.format_exc())
return None return None
def import_orders_data(self, file_path): def import_orders_data(self, file_path):
@@ -688,6 +754,30 @@ class DailyMirrorDatabase:
logger.error(f"Error deleting production orders: {e}") logger.error(f"Error deleting production orders: {e}")
return False 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): def _parse_date(self, date_value):
"""Parse date with better null handling""" """Parse date with better null handling"""
if pd.isna(date_value) or date_value == 'nan' or date_value is None or date_value == '': if pd.isna(date_value) or date_value == 'nan' or date_value is None or date_value == '':

View File

@@ -1,6 +1,41 @@
/* Daily Mirror Tune Pages - Modal Styles */ /* Daily Mirror Tune Pages - Modal Styles */
/* Fixes for editable modals across tune/production, tune/orders, and tune/delivery pages */ /* 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 */ /* Force Bootstrap modal to have proper z-index */
#editModal.modal { #editModal.modal {
z-index: 9999 !important; z-index: 9999 !important;

View File

@@ -2,6 +2,10 @@
{% block title %}Build Database - Daily Mirror{% endblock %} {% block title %}Build Database - Daily Mirror{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='daily_mirror_tune.css') }}">
{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">
<!-- Page Header --> <!-- Page Header -->
@@ -205,6 +209,43 @@
</div> </div>
<style> <style>
/* Additional modal fixes specific to this page */
#uploadResultModal {
z-index: 10000 !important;
pointer-events: auto !important;
}
#uploadResultModal .modal-dialog {
pointer-events: auto !important;
z-index: 10001 !important;
}
#uploadResultModal .modal-content {
pointer-events: auto !important;
background-color: white !important;
}
#uploadResultModal .modal-backdrop {
z-index: 9998 !important;
}
#uploadResultModal .btn-close,
#uploadResultModal .modal-footer button {
pointer-events: auto !important;
cursor: pointer !important;
opacity: 1 !important;
}
/* Ensure modal can be dismissed */
.modal-backdrop.show {
pointer-events: auto !important;
}
/* Dark mode modal fixes */
body.dark-mode #uploadResultModal .modal-content {
background-color: #2d3748 !important;
}
.accordion-button:not(.collapsed) { .accordion-button:not(.collapsed) {
background-color: #e7f3ff; background-color: #e7f3ff;
color: #0066cc; color: #0066cc;

View File

@@ -87,6 +87,11 @@
</h5> </h5>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span id="recordsInfo" class="text-muted me-3"></span> <span id="recordsInfo" class="text-muted me-3"></span>
{% if session.get('role') == 'superadmin' %}
<button class="btn btn-danger btn-sm me-2" onclick="clearAllDelivery()" id="clearAllBtn">
<i class="fas fa-trash-alt"></i> Clear All Delivery
</button>
{% endif %}
<button class="btn btn-success btn-sm" onclick="saveAllChanges()"> <button class="btn btn-success btn-sm" onclick="saveAllChanges()">
<i class="fas fa-save"></i> Save All Changes <i class="fas fa-save"></i> Save All Changes
</button> </button>
@@ -499,5 +504,46 @@ function saveRecord() {
function saveAllChanges() { function saveAllChanges() {
alert('Save All Changes functionality will be implemented for bulk operations.'); alert('Save All Changes functionality will be implemented for bulk operations.');
} }
function clearAllDelivery() {
if (!confirm('⚠️ WARNING: This will permanently delete ALL delivery records from the database!\n\nAre you absolutely sure you want to continue?')) {
return;
}
// Second confirmation for safety
if (!confirm('This action CANNOT be undone! All delivery data will be lost.\n\nClick OK to proceed with deletion.')) {
return;
}
const clearBtn = document.getElementById('clearAllBtn');
const originalText = clearBtn.innerHTML;
clearBtn.disabled = true;
clearBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Deleting...';
fetch('/daily_mirror/clear_delivery', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('✅ ' + data.message);
// Reload the page to show empty table
loadDeliveryData(1);
} else {
alert('❌ Error: ' + (data.message || 'Failed to clear delivery records'));
}
})
.catch(error => {
console.error('Error:', error);
alert('❌ Error clearing delivery records: ' + error.message);
})
.finally(() => {
clearBtn.disabled = false;
clearBtn.innerHTML = originalText;
});
}
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -87,6 +87,11 @@
</h5> </h5>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span id="recordsInfo" class="text-muted me-3"></span> <span id="recordsInfo" class="text-muted me-3"></span>
{% if session.get('role') == 'superadmin' %}
<button class="btn btn-danger btn-sm me-2" onclick="clearAllOrders()" id="clearAllBtn">
<i class="fas fa-trash-alt"></i> Clear All Orders
</button>
{% endif %}
<button class="btn btn-success btn-sm" onclick="saveAllChanges()"> <button class="btn btn-success btn-sm" onclick="saveAllChanges()">
<i class="fas fa-save"></i> Save All Changes <i class="fas fa-save"></i> Save All Changes
</button> </button>
@@ -518,5 +523,46 @@ function saveRecord() {
function saveAllChanges() { function saveAllChanges() {
alert('Save All Changes functionality will be implemented for bulk operations.'); alert('Save All Changes functionality will be implemented for bulk operations.');
} }
function clearAllOrders() {
if (!confirm('⚠️ WARNING: This will permanently delete ALL customer orders from the database!\n\nAre you absolutely sure you want to continue?')) {
return;
}
// Second confirmation for safety
if (!confirm('This action CANNOT be undone! All customer order data will be lost.\n\nClick OK to proceed with deletion.')) {
return;
}
const clearBtn = document.getElementById('clearAllBtn');
const originalText = clearBtn.innerHTML;
clearBtn.disabled = true;
clearBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Deleting...';
fetch('/daily_mirror/clear_orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('✅ ' + data.message);
// Reload the page to show empty table
loadOrdersData(1);
} else {
alert('❌ Error: ' + (data.message || 'Failed to clear orders'));
}
})
.catch(error => {
console.error('Error:', error);
alert('❌ Error clearing orders: ' + error.message);
})
.finally(() => {
clearBtn.disabled = false;
clearBtn.innerHTML = originalText;
});
}
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -4,6 +4,47 @@
{% block extra_css %} {% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/daily_mirror_tune.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/daily_mirror_tune.css') }}">
<style>
/* Force modal width - using viewport width for maximum responsiveness */
.modal#editModal .modal-dialog {
max-width: 95vw !important;
width: 95vw !important;
margin: 1.75rem auto !important;
}
.modal#editModal .modal-dialog.modal-xl {
max-width: 95vw !important;
width: 95vw !important;
}
/* Override ALL Bootstrap media queries */
@media (min-width: 576px) {
.modal#editModal .modal-dialog {
max-width: 95vw !important;
width: 95vw !important;
}
}
@media (min-width: 992px) {
.modal#editModal .modal-dialog {
max-width: 95vw !important;
width: 95vw !important;
}
}
@media (min-width: 1200px) {
.modal#editModal .modal-dialog,
.modal#editModal .modal-dialog.modal-xl {
max-width: 95vw !important;
width: 95vw !important;
}
}
/* Ensure pointer events work */
.modal#editModal .modal-dialog {
pointer-events: auto !important;
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@@ -90,6 +131,11 @@
</h5> </h5>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span id="recordsInfo" class="text-muted me-3"></span> <span id="recordsInfo" class="text-muted me-3"></span>
{% if session.get('role') == 'superadmin' %}
<button class="btn btn-danger btn-sm me-2" onclick="clearAllProductionOrders()" id="clearAllBtn">
<i class="fas fa-trash-alt"></i> Clear All Orders
</button>
{% endif %}
<button class="btn btn-success btn-sm" onclick="saveAllChanges()"> <button class="btn btn-success btn-sm" onclick="saveAllChanges()">
<i class="fas fa-save"></i> Save All Changes <i class="fas fa-save"></i> Save All Changes
</button> </button>
@@ -101,8 +147,9 @@
<thead class="table-dark"> <thead class="table-dark">
<tr> <tr>
<th>Production Order</th> <th>Production Order</th>
<th>Opened for Order</th>
<th>Client Order-Line</th>
<th>Customer</th> <th>Customer</th>
<th>Client Order</th>
<th>Article Code</th> <th>Article Code</th>
<th>Description</th> <th>Description</th>
<th>Quantity</th> <th>Quantity</th>
@@ -147,7 +194,7 @@
<!-- Edit Modal --> <!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true" data-bs-backdrop="true" data-bs-keyboard="true"> <div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true" data-bs-backdrop="true" data-bs-keyboard="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable"> <div class="modal-dialog modal-xl modal-dialog-scrollable" style="max-width: 95vw !important; width: 95vw !important;">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="editModalLabel">Edit Production Order</h5> <h5 class="modal-title" id="editModalLabel">Edit Production Order</h5>
@@ -157,50 +204,59 @@
<form id="editForm"> <form id="editForm">
<input type="hidden" id="editRecordId"> <input type="hidden" id="editRecordId">
<div class="row"> <!-- Production Order (Full Width) -->
<div class="col-md-6 mb-3"> <div class="row mb-3">
<label for="editProductionOrder" class="form-label">Production Order</label> <div class="col-12">
<input type="text" class="form-control" id="editProductionOrder" readonly> <label for="editProductionOrder" class="form-label fw-bold">Production Order</label>
</div> <input type="text" class="form-control form-control-lg" id="editProductionOrder" readonly>
<div class="col-md-6 mb-3">
<label for="editCustomerCode" class="form-label">Customer Code</label>
<input type="text" class="form-control" id="editCustomerCode">
</div> </div>
</div> </div>
<div class="row"> <hr class="my-3">
<div class="col-md-6 mb-3">
<label for="editCustomerName" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="editCustomerName">
</div>
<div class="col-md-6 mb-3">
<label for="editClientOrder" class="form-label">Client Order</label>
<input type="text" class="form-control" id="editClientOrder">
</div>
</div>
<!-- Three Column Layout -->
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <!-- Column 1 -->
<label for="editArticleCode" class="form-label">Article Code</label> <div class="col-md-4">
<input type="text" class="form-control" id="editArticleCode"> <div class="mb-3">
</div> <label for="editOpenForOrderLine" class="form-label">Opened for Order-Line</label>
<div class="col-md-6 mb-3"> <input type="text" class="form-control" id="editOpenForOrderLine" readonly>
<label for="editQuantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="editQuantity">
</div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="editDescription" class="form-label">Article Description</label> <label for="editCustomerCode" class="form-label">Customer Code</label>
<textarea class="form-control" id="editDescription" rows="2"></textarea> <input type="text" class="form-control" id="editCustomerCode">
</div> </div>
<div class="row"> <div class="mb-3">
<div class="col-md-6 mb-3"> <label for="editArticleCode" class="form-label">Article Code</label>
<input type="text" class="form-control" id="editArticleCode" readonly>
</div>
<div class="mb-3">
<label for="editDeliveryDate" class="form-label">Delivery Date</label> <label for="editDeliveryDate" class="form-label">Delivery Date</label>
<input type="date" class="form-control" id="editDeliveryDate"> <input type="date" class="form-control" id="editDeliveryDate">
</div> </div>
<div class="col-md-6 mb-3"> </div>
<!-- Column 2 -->
<div class="col-md-4">
<div class="mb-3">
<label for="editClientOrderLine" class="form-label">Client Order-Line</label>
<input type="text" class="form-control" id="editClientOrderLine" readonly>
</div>
<div class="mb-3">
<label for="editCustomerName" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="editCustomerName">
</div>
<div class="mb-3">
<label for="editQuantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="editQuantity">
</div>
<div class="mb-3">
<label for="editStatus" class="form-label">Production Status</label> <label for="editStatus" class="form-label">Production Status</label>
<select class="form-control" id="editStatus"> <select class="form-control" id="editStatus">
<option value="PENDING">Pending</option> <option value="PENDING">Pending</option>
@@ -211,20 +267,34 @@
</div> </div>
</div> </div>
<!-- Column 3 -->
<div class="col-md-4">
<div class="mb-3"> <div class="mb-3">
<label for="editMachine" class="form-label">Machine Code</label> <label for="editMachine" class="form-label">Machine Code</label>
<input type="text" class="form-control" id="editMachine"> <input type="text" class="form-control" id="editMachine">
</div> </div>
<div class="mb-3">
<label for="editDescription" class="form-label">Article Description</label>
<textarea class="form-control" id="editDescription" rows="6"></textarea>
</div>
</div>
</div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer d-flex justify-content-between">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> <button type="button" class="btn btn-danger" onclick="deleteRecord()" style="min-width: 150px;">
<button type="button" class="btn btn-primary" onclick="saveRecord()"> <i class="fas fa-trash"></i> Delete Record
</button>
<div>
<button type="button" class="btn btn-secondary me-2" data-bs-dismiss="modal" style="min-width: 100px;">Cancel</button>
<button type="button" class="btn btn-primary" onclick="saveRecord()" style="min-width: 150px;">
<i class="fas fa-save"></i> Save Changes <i class="fas fa-save"></i> Save Changes
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
<script> <script>
@@ -300,8 +370,9 @@ function renderTable(data) {
tbody.innerHTML = data.records.map((record, index) => ` tbody.innerHTML = data.records.map((record, index) => `
<tr id="row-${record.id}"> <tr id="row-${record.id}">
<td><strong>${record.production_order}</strong></td> <td><strong>${record.production_order}</strong></td>
<td>${record.open_for_order_line || ''}</td>
<td>${record.client_order_line || ''}</td>
<td>${record.customer_code}<br><small class="text-muted">${record.customer_name || ''}</small></td> <td>${record.customer_code}<br><small class="text-muted">${record.customer_name || ''}</small></td>
<td>${record.client_order || ''}</td>
<td>${record.article_code || ''}</td> <td>${record.article_code || ''}</td>
<td><small>${record.article_description || ''}</small></td> <td><small>${record.article_description || ''}</small></td>
<td>${record.quantity_requested || ''}</td> <td>${record.quantity_requested || ''}</td>
@@ -371,9 +442,10 @@ function editRecord(recordId) {
// Populate the edit form // Populate the edit form
document.getElementById('editRecordId').value = record.id; document.getElementById('editRecordId').value = record.id;
document.getElementById('editProductionOrder').value = record.production_order; document.getElementById('editProductionOrder').value = record.production_order;
document.getElementById('editOpenForOrderLine').value = record.open_for_order_line || '';
document.getElementById('editClientOrderLine').value = record.client_order_line || '';
document.getElementById('editCustomerCode').value = record.customer_code || ''; document.getElementById('editCustomerCode').value = record.customer_code || '';
document.getElementById('editCustomerName').value = record.customer_name || ''; document.getElementById('editCustomerName').value = record.customer_name || '';
document.getElementById('editClientOrder').value = record.client_order || '';
document.getElementById('editArticleCode').value = record.article_code || ''; document.getElementById('editArticleCode').value = record.article_code || '';
document.getElementById('editDescription').value = record.article_description || ''; document.getElementById('editDescription').value = record.article_description || '';
document.getElementById('editQuantity').value = record.quantity_requested || ''; document.getElementById('editQuantity').value = record.quantity_requested || '';
@@ -382,8 +454,8 @@ function editRecord(recordId) {
document.getElementById('editMachine').value = record.machine_code || ''; document.getElementById('editMachine').value = record.machine_code || '';
// Explicitly enable all editable fields // Explicitly enable all editable fields
const editableFields = ['editCustomerCode', 'editCustomerName', 'editClientOrder', const editableFields = ['editCustomerCode',
'editArticleCode', 'editDescription', 'editQuantity', 'editCustomerName', 'editDescription', 'editQuantity',
'editDeliveryDate', 'editStatus', 'editMachine']; 'editDeliveryDate', 'editStatus', 'editMachine'];
editableFields.forEach(fieldId => { editableFields.forEach(fieldId => {
const field = document.getElementById(fieldId); const field = document.getElementById(fieldId);
@@ -403,6 +475,7 @@ function editRecord(recordId) {
// Show the modal with proper configuration // Show the modal with proper configuration
const modalElement = document.getElementById('editModal'); const modalElement = document.getElementById('editModal');
const modalDialog = modalElement.querySelector('.modal-dialog');
// Remove any existing modal instances to prevent conflicts // Remove any existing modal instances to prevent conflicts
const existingModal = bootstrap.Modal.getInstance(modalElement); const existingModal = bootstrap.Modal.getInstance(modalElement);
@@ -416,8 +489,27 @@ function editRecord(recordId) {
focus: true focus: true
}); });
// Show the modal first
modal.show(); modal.show();
// Force the modal dialog width AFTER modal is shown - using 95% of viewport width
setTimeout(() => {
if (modalDialog) {
console.log('Applying width after modal shown');
modalDialog.style.setProperty('max-width', '95vw', 'important');
modalDialog.style.setProperty('width', '95vw', 'important');
modalDialog.style.setProperty('margin', '1.75rem auto', 'important');
// Also apply to modal content for good measure
const modalContent = modalDialog.querySelector('.modal-content');
if (modalContent) {
modalContent.style.setProperty('width', '100%', 'important');
}
console.log('Modal dialog computed width:', window.getComputedStyle(modalDialog).width);
}
}, 100);
// Ensure form inputs are focusable and interactive after modal is shown // Ensure form inputs are focusable and interactive after modal is shown
modalElement.addEventListener('shown.bs.modal', function () { modalElement.addEventListener('shown.bs.modal', function () {
// Re-enable all fields after modal animation completes // Re-enable all fields after modal animation completes
@@ -445,9 +537,10 @@ function saveRecord() {
const recordId = document.getElementById('editRecordId').value; const recordId = document.getElementById('editRecordId').value;
const formData = { const formData = {
open_for_order_line: document.getElementById('editOpenForOrderLine').value,
client_order_line: document.getElementById('editClientOrderLine').value,
customer_code: document.getElementById('editCustomerCode').value, customer_code: document.getElementById('editCustomerCode').value,
customer_name: document.getElementById('editCustomerName').value, customer_name: document.getElementById('editCustomerName').value,
client_order: document.getElementById('editClientOrder').value,
article_code: document.getElementById('editArticleCode').value, article_code: document.getElementById('editArticleCode').value,
article_description: document.getElementById('editDescription').value, article_description: document.getElementById('editDescription').value,
quantity_requested: parseInt(document.getElementById('editQuantity').value) || 0, quantity_requested: parseInt(document.getElementById('editQuantity').value) || 0,
@@ -482,6 +575,39 @@ function saveRecord() {
}); });
} }
function deleteRecord() {
const recordId = document.getElementById('editRecordId').value;
const productionOrder = document.getElementById('editProductionOrder').value;
if (!confirm(`⚠️ Are you sure you want to delete production order "${productionOrder}"?\n\nThis action cannot be undone.`)) {
return;
}
fetch(`/daily_mirror/api/tune/production_data/${recordId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
// Close modal and reload data
const modal = bootstrap.Modal.getInstance(document.getElementById('editModal'));
modal.hide();
alert('Record deleted successfully!');
loadProductionData(currentPage);
})
.catch(error => {
console.error('Error deleting record:', error);
alert('Error deleting record: ' + error.message);
});
}
function clearFilters() { function clearFilters() {
document.getElementById('searchInput').value = ''; document.getElementById('searchInput').value = '';
document.getElementById('statusFilter').value = ''; document.getElementById('statusFilter').value = '';
@@ -493,6 +619,47 @@ function saveAllChanges() {
alert('Bulk save functionality will be implemented in a future update!'); alert('Bulk save functionality will be implemented in a future update!');
} }
function clearAllProductionOrders() {
if (!confirm('⚠️ WARNING: This will permanently delete ALL production orders from the database!\n\nAre you absolutely sure you want to continue?')) {
return;
}
// Second confirmation for safety
if (!confirm('This action CANNOT be undone! All production order data will be lost.\n\nClick OK to proceed with deletion.')) {
return;
}
const clearBtn = document.getElementById('clearAllBtn');
const originalText = clearBtn.innerHTML;
clearBtn.disabled = true;
clearBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Deleting...';
fetch('/daily_mirror/clear_production_orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('✅ ' + data.message);
// Reload the page to show empty table
loadProductionData(1);
} else {
alert('❌ Error: ' + (data.message || 'Failed to clear production orders'));
}
})
.catch(error => {
console.error('Error:', error);
alert('❌ Error clearing production orders: ' + error.message);
})
.finally(() => {
clearBtn.disabled = false;
clearBtn.innerHTML = originalText;
});
}
// Add event listeners for real-time filtering // Add event listeners for real-time filtering
document.getElementById('searchInput').addEventListener('input', function() { document.getElementById('searchInput').addEventListener('input', function() {
clearTimeout(this.searchTimeout); clearTimeout(this.searchTimeout);