Add boxes/crates management system to warehouse module

- Created boxes_crates table with 8-digit auto-generated box numbers
- Added manage_boxes route and full CRUD operations
- Implemented box status tracking (open/closed)
- Added location assignment functionality
- Integrated QZ Tray printing with barcode labels
- Created manage_boxes.html with table, preview, and print features
- Added 'Manage Boxes/Crates' card to warehouse main page
- Boxes can be created without location and assigned later
- Includes delete functionality for admin/management roles
- Added box statistics display
This commit is contained in:
ske087
2025-12-07 00:31:41 +02:00
parent f8209e0e0a
commit c7f5203aa7
5 changed files with 827 additions and 2 deletions

View File

@@ -44,6 +44,30 @@ def ensure_warehouse_locations_table():
except Exception as e:
print(f"Error ensuring warehouse_locations table: {e}")
def ensure_boxes_crates_table():
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SHOW TABLES LIKE 'boxes_crates'")
result = cursor.fetchone()
if not result:
cursor.execute('''
CREATE TABLE IF NOT EXISTS boxes_crates (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
box_number VARCHAR(8) NOT NULL UNIQUE,
status ENUM('open', 'closed') DEFAULT 'open',
location_id BIGINT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by VARCHAR(100),
FOREIGN KEY (location_id) REFERENCES warehouse_locations(id) ON DELETE SET NULL
)
''')
conn.commit()
conn.close()
except Exception as e:
print(f"Error ensuring boxes_crates table: {e}")
# Add warehouse-specific functions below
def add_location(location_code, size, description):
conn = get_db_connection()
@@ -268,9 +292,158 @@ def update_location(location_id, location_code, size, description):
except Exception as e:
print(f"Error updating location: {e}")
return {"success": False, "error": str(e)}
print(f"Error updating location: {e}")
# ============================================================================
# Boxes/Crates Functions
# ============================================================================
def generate_box_number():
"""Generate next box number with 8 digits (00000001, 00000002, etc.)"""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT MAX(CAST(box_number AS UNSIGNED)) FROM boxes_crates")
result = cursor.fetchone()
conn.close()
if result and result[0]:
next_number = int(result[0]) + 1
else:
next_number = 1
return str(next_number).zfill(8)
def add_box(location_id=None, created_by=None):
"""Add a new box/crate"""
conn = get_db_connection()
cursor = conn.cursor()
box_number = generate_box_number()
try:
cursor.execute(
"INSERT INTO boxes_crates (box_number, status, location_id, created_by) VALUES (%s, %s, %s, %s)",
(box_number, 'open', location_id if location_id else None, created_by)
)
conn.commit()
conn.close()
return f"Box {box_number} created successfully"
except Exception as e:
conn.close()
return f"Error creating box: {e}"
def get_boxes():
"""Get all boxes with location information"""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
SELECT
b.id,
b.box_number,
b.status,
l.location_code,
b.created_at,
b.updated_at,
b.created_by,
b.location_id
FROM boxes_crates b
LEFT JOIN warehouse_locations l ON b.location_id = l.id
ORDER BY b.id DESC
""")
boxes = cursor.fetchall()
conn.close()
return boxes
def update_box_status(box_id, status):
"""Update box status (open/closed)"""
conn = get_db_connection()
cursor = conn.cursor()
try:
cursor.execute(
"UPDATE boxes_crates SET status = %s WHERE id = %s",
(status, box_id)
)
conn.commit()
conn.close()
return {"success": True, "message": f"Box status updated to {status}"}
except Exception as e:
conn.close()
return {"success": False, "error": str(e)}
def update_box_location(box_id, location_id):
"""Update box location"""
conn = get_db_connection()
cursor = conn.cursor()
try:
cursor.execute(
"UPDATE boxes_crates SET location_id = %s WHERE id = %s",
(location_id if location_id else None, box_id)
)
conn.commit()
conn.close()
return {"success": True, "message": "Box location updated"}
except Exception as e:
conn.close()
return {"success": False, "error": str(e)}
def delete_boxes_by_ids(ids_str):
"""Delete boxes by comma-separated IDs"""
ids = [id.strip() for id in ids_str.split(',') if id.strip().isdigit()]
if not ids:
return "No valid IDs provided."
conn = get_db_connection()
cursor = conn.cursor()
deleted = 0
for id in ids:
cursor.execute("DELETE FROM boxes_crates WHERE id = %s", (id,))
if cursor.rowcount:
deleted += 1
conn.commit()
conn.close()
return f"Deleted {deleted} box(es)."
def manage_boxes_handler():
"""Handler for boxes/crates management page"""
try:
# Ensure table exists
ensure_boxes_crates_table()
ensure_warehouse_locations_table()
if request.method == "POST":
action = request.form.get("action")
if action == "delete_boxes":
ids_str = request.form.get("delete_ids", "")
message = delete_boxes_by_ids(ids_str)
session['flash_message'] = message
elif action == "add_box":
created_by = session.get('user', 'Unknown')
message = add_box(None, created_by) # Create box without location
session['flash_message'] = message
elif action == "update_status":
box_id = request.form.get("box_id")
new_status = request.form.get("new_status")
message = update_box_status(box_id, new_status)
session['flash_message'] = message
elif action == "update_location":
box_id = request.form.get("box_id")
new_location_id = request.form.get("new_location_id")
message = update_box_location(box_id, new_location_id)
session['flash_message'] = message
return redirect(url_for('warehouse.manage_boxes'))
# Get flash message from session if any
message = session.pop('flash_message', None)
boxes = get_boxes()
locations = get_locations()
return render_template("manage_boxes.html", boxes=boxes, locations=locations, message=message)
except Exception as e:
import traceback
error_trace = traceback.format_exc()
print(f"Error in manage_boxes_handler: {e}")
print(error_trace)
return f"<h1>Error loading boxes management</h1><pre>{error_trace}</pre>", 500
def delete_location_by_id(location_id):
"""Delete a warehouse location by ID"""
try: