Compare commits

...

2 Commits

Author SHA1 Message Date
Quality System Admin
d142129de6 updated structure in the table view 2025-10-26 19:30:12 +02:00
Quality System Admin
8cbede35d2 saved the new production order structure 2025-10-26 19:29:52 +02:00
9 changed files with 767 additions and 185 deletions

View File

@@ -464,3 +464,31 @@
192.168.0.132 - - [24/Oct/2025:19:45:59 +0300] "GET /static/logo_login.jpg HTTP/1.1" 200 0 "https://quality.moto-adv.com/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.52 Mobile Safari/537.36" 4653
192.168.0.132 - - [24/Oct/2025:19:45:59 +0300] "GET /static/script.js HTTP/1.1" 200 0 "https://quality.moto-adv.com/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.52 Mobile Safari/537.36" 1971
192.168.0.132 - - [24/Oct/2025:19:45:59 +0300] "GET /static/css/base.css HTTP/1.1" 200 0 "https://quality.moto-adv.com/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.52 Mobile Safari/537.36" 3201
192.168.0.132 - - [25/Oct/2025:03:24:51 +0300] "GET / HTTP/1.1" 200 1627 "-" "Mozilla/5.0 zgrab/0.x" 44791
192.168.0.132 - - [25/Oct/2025:03:24:56 +0300] "GET / HTTP/1.1" 200 1627 "http://quality.moto-adv.com/" "Mozilla/5.0 zgrab/0.x" 59615
192.168.0.132 - - [25/Oct/2025:05:02:20 +0300] "GET / HTTP/1.1" 200 1627 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" 59482
192.168.0.132 - - [25/Oct/2025:05:02:21 +0300] "GET /favicon.ico HTTP/1.1" 404 207 "https://quality.moto-adv.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" 2612
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

View File

@@ -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/<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 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)
);
-- =============================================

View File

@@ -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 == '':

View File

@@ -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;

View File

@@ -2,6 +2,10 @@
{% 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 %}
<div class="container-fluid">
<!-- Page Header -->
@@ -205,6 +209,43 @@
</div>
<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) {
background-color: #e7f3ff;
color: #0066cc;

View File

@@ -87,6 +87,11 @@
</h5>
<div class="d-flex align-items-center">
<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()">
<i class="fas fa-save"></i> Save All Changes
</button>
@@ -499,5 +504,46 @@ function saveRecord() {
function saveAllChanges() {
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>
{% endblock %}

View File

@@ -87,6 +87,11 @@
</h5>
<div class="d-flex align-items-center">
<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()">
<i class="fas fa-save"></i> Save All Changes
</button>
@@ -518,5 +523,46 @@ function saveRecord() {
function saveAllChanges() {
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>
{% endblock %}

View File

@@ -4,6 +4,47 @@
{% block extra_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 %}
{% block content %}
@@ -90,6 +131,11 @@
</h5>
<div class="d-flex align-items-center">
<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()">
<i class="fas fa-save"></i> Save All Changes
</button>
@@ -101,8 +147,9 @@
<thead class="table-dark">
<tr>
<th>Production Order</th>
<th>Opened for Order</th>
<th>Client Order-Line</th>
<th>Customer</th>
<th>Client Order</th>
<th>Article Code</th>
<th>Description</th>
<th>Quantity</th>
@@ -147,7 +194,7 @@
<!-- 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-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-header">
<h5 class="modal-title" id="editModalLabel">Edit Production Order</h5>
@@ -157,71 +204,94 @@
<form id="editForm">
<input type="hidden" id="editRecordId">
<!-- Production Order (Full Width) -->
<div class="row mb-3">
<div class="col-12">
<label for="editProductionOrder" class="form-label fw-bold">Production Order</label>
<input type="text" class="form-control form-control-lg" id="editProductionOrder" readonly>
</div>
</div>
<hr class="my-3">
<!-- Three Column Layout -->
<div class="row">
<div class="col-md-6 mb-3">
<label for="editProductionOrder" class="form-label">Production Order</label>
<input type="text" class="form-control" id="editProductionOrder" readonly>
<!-- Column 1 -->
<div class="col-md-4">
<div class="mb-3">
<label for="editOpenForOrderLine" class="form-label">Opened for Order-Line</label>
<input type="text" class="form-control" id="editOpenForOrderLine" readonly>
</div>
<div class="mb-3">
<label for="editCustomerCode" class="form-label">Customer Code</label>
<input type="text" class="form-control" id="editCustomerCode">
</div>
<div class="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>
<input type="date" class="form-control" id="editDeliveryDate">
</div>
</div>
<div class="col-md-6 mb-3">
<label for="editCustomerCode" class="form-label">Customer Code</label>
<input type="text" class="form-control" id="editCustomerCode">
<!-- 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>
<select class="form-control" id="editStatus">
<option value="PENDING">Pending</option>
<option value="IN_PROGRESS">In Progress</option>
<option value="FINISHED">Finished</option>
<option value="CANCELLED">Cancelled</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editCustomerName" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="editCustomerName">
<!-- Column 3 -->
<div class="col-md-4">
<div class="mb-3">
<label for="editMachine" class="form-label">Machine Code</label>
<input type="text" class="form-control" id="editMachine">
</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 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>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editArticleCode" class="form-label">Article Code</label>
<input type="text" class="form-control" id="editArticleCode">
</div>
<div class="col-md-6 mb-3">
<label for="editQuantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="editQuantity">
</div>
</div>
<div class="mb-3">
<label for="editDescription" class="form-label">Article Description</label>
<textarea class="form-control" id="editDescription" rows="2"></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editDeliveryDate" class="form-label">Delivery Date</label>
<input type="date" class="form-control" id="editDeliveryDate">
</div>
<div class="col-md-6 mb-3">
<label for="editStatus" class="form-label">Production Status</label>
<select class="form-control" id="editStatus">
<option value="PENDING">Pending</option>
<option value="IN_PROGRESS">In Progress</option>
<option value="FINISHED">Finished</option>
<option value="CANCELLED">Cancelled</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="editMachine" class="form-label">Machine Code</label>
<input type="text" class="form-control" id="editMachine">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="saveRecord()">
<i class="fas fa-save"></i> Save Changes
<div class="modal-footer d-flex justify-content-between">
<button type="button" class="btn btn-danger" onclick="deleteRecord()" style="min-width: 150px;">
<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
</button>
</div>
</div>
</div>
</div>
@@ -300,8 +370,9 @@ function renderTable(data) {
tbody.innerHTML = data.records.map((record, index) => `
<tr id="row-${record.id}">
<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.client_order || ''}</td>
<td>${record.article_code || ''}</td>
<td><small>${record.article_description || ''}</small></td>
<td>${record.quantity_requested || ''}</td>
@@ -371,9 +442,10 @@ function editRecord(recordId) {
// Populate the edit form
document.getElementById('editRecordId').value = record.id;
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('editCustomerName').value = record.customer_name || '';
document.getElementById('editClientOrder').value = record.client_order || '';
document.getElementById('editArticleCode').value = record.article_code || '';
document.getElementById('editDescription').value = record.article_description || '';
document.getElementById('editQuantity').value = record.quantity_requested || '';
@@ -382,8 +454,8 @@ function editRecord(recordId) {
document.getElementById('editMachine').value = record.machine_code || '';
// Explicitly enable all editable fields
const editableFields = ['editCustomerCode', 'editCustomerName', 'editClientOrder',
'editArticleCode', 'editDescription', 'editQuantity',
const editableFields = ['editCustomerCode',
'editCustomerName', 'editDescription', 'editQuantity',
'editDeliveryDate', 'editStatus', 'editMachine'];
editableFields.forEach(fieldId => {
const field = document.getElementById(fieldId);
@@ -403,6 +475,7 @@ function editRecord(recordId) {
// Show the modal with proper configuration
const modalElement = document.getElementById('editModal');
const modalDialog = modalElement.querySelector('.modal-dialog');
// Remove any existing modal instances to prevent conflicts
const existingModal = bootstrap.Modal.getInstance(modalElement);
@@ -416,8 +489,27 @@ function editRecord(recordId) {
focus: true
});
// Show the modal first
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
modalElement.addEventListener('shown.bs.modal', function () {
// Re-enable all fields after modal animation completes
@@ -445,9 +537,10 @@ function saveRecord() {
const recordId = document.getElementById('editRecordId').value;
const formData = {
open_for_order_line: document.getElementById('editOpenForOrderLine').value,
client_order_line: document.getElementById('editClientOrderLine').value,
customer_code: document.getElementById('editCustomerCode').value,
customer_name: document.getElementById('editCustomerName').value,
client_order: document.getElementById('editClientOrder').value,
article_code: document.getElementById('editArticleCode').value,
article_description: document.getElementById('editDescription').value,
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() {
document.getElementById('searchInput').value = '';
document.getElementById('statusFilter').value = '';
@@ -493,6 +619,47 @@ function saveAllChanges() {
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
document.getElementById('searchInput').addEventListener('input', function() {
clearTimeout(this.searchTimeout);