Compare commits
2 Commits
87469ecb8e
...
d142129de6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d142129de6 | ||
|
|
8cbede35d2 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
-- =============================================
|
||||
|
||||
@@ -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 == '':
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user