Files
quality_app-v2/documentation/BOXES_CODE_SNIPPETS.md
Quality App Developer b15cc93b9d FG Scan form validation improvements with warehouse module updates
- Fixed 3 JavaScript syntax errors in fg_scan.html (lines 951, 840-950, 1175-1215)
- Restored form field validation with proper null safety checks
- Re-enabled auto-advance between form fields
- Re-enabled CP code auto-complete with hyphen detection
- Updated validation error messages with clear format specifications and examples
- Added autocomplete='off' to all input fields
- Removed auto-prefix correction feature
- Updated warehouse routes and modules for box assignment workflow
- Added/improved database initialization scripts
- Updated requirements.txt dependencies

Format specifications implemented:
- Operator Code: OP + 2 digits (example: OP01, OP99)
- CP Code: CP + 8 digits + hyphen + 4 digits (example: CP00000000-0001)
- OC1/OC2 Codes: OC + 2 digits (example: OC01, OC99)
- Defect Code: 3 digits only
2026-01-30 10:50:06 +02:00

606 lines
20 KiB
Markdown

# Quick Box Creation & Label Printing - Code Snippets Reference
## Quick Reference - Key Code Files and Functions
### 1. Database Table Creation Code
**File:** [app/warehouse.py](warehouse.py#L32-L62)
**boxes_crates table:**
```python
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}")
```
**box_contents table:**
```python
def ensure_box_contents_table():
"""Ensure box_contents table exists for tracking CP codes in boxes"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SHOW TABLES LIKE 'box_contents'")
result = cursor.fetchone()
if not result:
cursor.execute('''
CREATE TABLE IF NOT EXISTS box_contents (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
box_id BIGINT NOT NULL,
cp_code VARCHAR(15) NOT NULL,
scan_id BIGINT,
scanned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
scanned_by VARCHAR(100),
FOREIGN KEY (box_id) REFERENCES boxes_crates(id) ON DELETE CASCADE,
INDEX idx_box_id (box_id),
INDEX idx_cp_code (cp_code)
)
''')
conn.commit()
print("box_contents table created successfully")
conn.close()
except Exception as e:
print(f"Error ensuring box_contents table: {e}")
```
---
### 2. Frontend JavaScript Implementation
**File:** [templates/fg_scan.html](templates/fg_scan.html#L10-L200)
**Global Variables:**
```javascript
// Global variables for scan-to-boxes feature
let scanToBoxesEnabled = false;
let currentCpCode = null;
```
**Main Submit Function:**
```javascript
async function submitScanWithBoxAssignment() {
const form = document.getElementById('fg-scan-form');
const formData = new FormData(form);
console.log('=== submitScanWithBoxAssignment called ===');
try {
const response = await fetch(window.location.href, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
body: formData
});
console.log('Response status:', response.status);
if (response.ok) {
currentCpCode = formData.get('cp_code');
const defectCode = formData.get('defect_code') || '000';
console.log('CP Code:', currentCpCode);
console.log('Defect Code:', defectCode);
showNotification('✅ Scan recorded successfully!', 'success');
// Only show box modal if quality code is 000
if (defectCode === '000' || defectCode === '0') {
console.log('Should show box modal');
showBoxModal(currentCpCode);
} else {
console.log('Defect code not 000, reloading page');
setTimeout(() => window.location.reload(), 1000);
}
// Clear form fields (except operator code)
document.getElementById('cp_code').value = '';
document.getElementById('oc1_code').value = '';
document.getElementById('oc2_code').value = '';
document.getElementById('defect_code').value = '';
} else {
console.error('Response not OK');
showNotification('❌ Scan submission failed', 'error');
}
} catch (error) {
console.error('Error in submitScanWithBoxAssignment:', error);
showNotification('❌ Error: ' + error.message, 'error');
}
}
```
**Show Modal Function:**
```javascript
function showBoxModal(cpCode) {
document.getElementById('modal-cp-code').textContent = cpCode;
document.getElementById('box-assignment-modal').style.display = 'block';
document.getElementById('scan-box-input').value = '';
document.getElementById('scan-box-input').focus();
}
```
**Assign CP to Box Function:**
```javascript
async function assignCpToBox(boxNumber) {
const response = await fetch('/warehouse/assign_cp_to_box', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
box_number: boxNumber,
cp_code: currentCpCode
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to assign CP to box');
}
return await response.json();
}
```
**Notification Helper:**
```javascript
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : type === 'warning' ? '#ff9800' : '#2196F3'};
color: white;
padding: 15px 20px;
border-radius: 5px;
z-index: 10001;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
font-weight: bold;
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 4000);
}
```
---
### 3. Backend Routes
**File:** [app/routes.py](routes.py#L1020-L1090)
**FG Scan Route:**
```python
@bp.route('/fg_scan', methods=['GET', 'POST'])
@requires_quality_module
def fg_scan():
# Ensure scanfg_orders table exists
ensure_scanfg_orders_table()
if request.method == 'POST':
# Handle form submission
operator_code = request.form.get('operator_code')
cp_code = request.form.get('cp_code')
oc1_code = request.form.get('oc1_code')
oc2_code = request.form.get('oc2_code')
defect_code = request.form.get('defect_code')
date = request.form.get('date')
time = request.form.get('time')
try:
# Connect to the database
with db_connection_context() as conn:
cursor = conn.cursor()
# Always insert a new entry
insert_query = """
INSERT INTO scanfg_orders
(operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time)
VALUES (%s, %s, %s, %s, %s, %s, %s)
"""
cursor.execute(insert_query,
(operator_code, cp_code, oc1_code, oc2_code, defect_code, date, time))
conn.commit()
# Get the quantities from the newly inserted row
cp_base_code = cp_code[:10]
cursor.execute("""
SELECT approved_quantity, rejected_quantity
FROM scanfg_orders
WHERE CP_full_code = %s
""", (cp_code,))
result = cursor.fetchone()
approved_count = result[0] if result else 0
rejected_count = result[1] if result else 0
# Flash appropriate message
if int(defect_code) == 0:
flash(f'✅ APPROVED scan recorded for {cp_code}. Total approved: {approved_count}')
else:
flash(f'❌ REJECTED scan recorded for {cp_code} (defect: {defect_code}). Total rejected: {rejected_count}')
except mariadb.Error as e:
print(f"Error saving finish goods scan data: {e}")
flash(f"Error saving scan data: {e}")
# Check if this is an AJAX request (for scan-to-boxes feature)
if request.headers.get('X-Requested-With') == 'XMLHttpRequest' or \
request.accept_mimetypes.best == 'application/json':
# For AJAX requests, return JSON response without redirect
return jsonify({'success': True, 'message': 'Scan recorded successfully'})
# For normal form submissions, redirect to prevent form resubmission
return redirect(url_for('main.fg_scan'))
# Fetch the latest scan data for display
scan_data = []
try:
with db_connection_context() as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT Id, operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time,
approved_quantity, rejected_quantity
FROM scanfg_orders
ORDER BY Id DESC
LIMIT 15
""")
raw_scan_data = cursor.fetchall()
# Apply formatting to scan data
scan_data = [[format_cell_data(cell) for cell in row] for row in raw_scan_data]
except mariadb.Error as e:
print(f"Error fetching finish goods scan data: {e}")
flash(f"Error fetching scan data: {e}")
return render_template('fg_scan.html', scan_data=scan_data)
```
---
### 4. Box Management Functions
**File:** [app/warehouse.py](warehouse.py#L155-L210)
**Generate Box Number:**
```python
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)
```
**Add Box:**
```python
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}"
```
---
### 5. CP to Box Assignment
**File:** [app/warehouse.py](warehouse.py#L619-L658)
**Assign CP to Box Handler:**
```python
def assign_cp_to_box_handler():
"""Handle assigning CP code to a box"""
from flask import request, jsonify, session
import json
try:
# Ensure box_contents table exists
ensure_box_contents_table()
data = json.loads(request.data)
box_number = data.get('box_number')
cp_code = data.get('cp_code')
scanned_by = session.get('user', 'Unknown')
if not box_number or not cp_code:
return jsonify({'success': False, 'error': 'Missing box_number or cp_code'}), 400
conn = get_db_connection()
cursor = conn.cursor()
# Find the box by number
cursor.execute("SELECT id FROM boxes_crates WHERE box_number = %s", (box_number,))
box = cursor.fetchone()
if not box:
conn.close()
return jsonify({'success': False, 'error': f'Box {box_number} not found'}), 404
box_id = box[0]
# Insert into box_contents
cursor.execute("""
INSERT INTO box_contents (box_id, cp_code, scanned_by)
VALUES (%s, %s, %s)
""", (box_id, cp_code, scanned_by))
conn.commit()
conn.close()
return jsonify({
'success': True,
'message': f'CP {cp_code} assigned to box {box_number}'
}), 200
except Exception as e:
import traceback
print(f"Error in assign_cp_to_box_handler: {e}")
print(traceback.format_exc())
return jsonify({'success': False, 'error': str(e)}), 500
```
---
### 6. Box Search and Management
**File:** [app/warehouse.py](warehouse.py#L740-L830)
**Search Box by Number:**
```python
def search_box_by_number(box_number):
"""
Search for a box by box number and return its details including location
Returns:
tuple: (success: bool, data: dict, status_code: int)
"""
try:
if not box_number:
return False, {'message': 'Box number is required'}, 400
conn = get_db_connection()
cursor = conn.cursor()
# Search for the box and get its location info
cursor.execute("""
SELECT
b.id,
b.box_number,
b.status,
b.location_id,
w.location_code
FROM boxes_crates b
LEFT JOIN warehouse_locations w ON b.location_id = w.id
WHERE b.box_number = %s
""", (box_number,))
result = cursor.fetchone()
conn.close()
if result:
return True, {
'box': {
'id': result[0],
'box_number': result[1],
'status': result[2],
'location_id': result[3],
'location_code': result[4]
}
}, 200
else:
return False, {'message': f'Box "{box_number}" not found in the system'}, 404
except Exception as e:
return False, {'message': f'Error searching for box: {str(e)}'}, 500
```
**Assign Box to Location:**
```python
def assign_box_to_location(box_id, location_code):
"""Assign a box to a warehouse location"""
try:
if not box_id or not location_code:
return False, {'message': 'Box ID and location code are required'}, 400
conn = get_db_connection()
cursor = conn.cursor()
# Check if location exists
cursor.execute("SELECT id FROM warehouse_locations WHERE location_code = %s", (location_code,))
location_result = cursor.fetchone()
if not location_result:
conn.close()
return False, {'message': f'Location "{location_code}" not found in the system'}, 404
location_id = location_result[0]
# Update box location
cursor.execute("""
UPDATE boxes_crates
SET location_id = %s, updated_at = NOW()
WHERE id = %s
""", (location_id, box_id))
conn.commit()
conn.close()
return True, {'message': f'Box successfully assigned to location "{location_code}"'}, 200
except Exception as e:
return False, {'message': f'Error assigning box to location: {str(e)}'}, 500
```
**Change Box Status:**
```python
def change_box_status(box_id, new_status):
"""Change the status of a box (open/closed)"""
try:
if not box_id:
return False, {'message': 'Box ID is required'}, 400
if new_status not in ['open', 'closed']:
return False, {'message': 'Invalid status. Must be "open" or "closed"'}, 400
conn = get_db_connection()
cursor = conn.cursor()
# Get box number for response message
cursor.execute("SELECT box_number FROM boxes_crates WHERE id = %s", (box_id,))
box_result = cursor.fetchone()
if not box_result:
conn.close()
return False, {'message': 'Box not found'}, 404
box_number = box_result[0]
# Update box status
cursor.execute("""
UPDATE boxes_crates
SET status = %s, updated_at = NOW()
WHERE id = %s
""", (new_status, box_id))
conn.commit()
conn.close()
return True, {'message': f'Box "{box_number}" status changed to "{new_status}"'}, 200
except Exception as e:
return False, {'message': f'Error changing box status: {str(e)}'}, 500
```
---
### 7. API Routes
**File:** [app/routes.py](routes.py#L5657-L5717)
**Warehouse Box API Routes:**
```python
@bp.route('/api/warehouse/box/search', methods=['POST'])
@requires_warehouse_module
def api_search_box():
"""Search for a box by box number"""
data = request.get_json()
box_number = data.get('box_number', '').strip()
success, response_data, status_code = search_box_by_number(box_number)
return jsonify({
'success': success,
**response_data
}), status_code
@bp.route('/api/warehouse/box/assign-location', methods=['POST'])
@requires_warehouse_module
def api_assign_box_to_location():
"""Assign a box to a warehouse location (only if box is closed)"""
data = request.get_json()
box_id = data.get('box_id')
location_code = data.get('location_code', '').strip()
# Additional check: verify box is closed before assigning
if box_id:
try:
with db_connection_context() as conn:
cursor = conn.cursor()
cursor.execute("SELECT status FROM boxes_crates WHERE id = %s", (box_id,))
result = cursor.fetchone()
if result and result[0] == 'open':
return jsonify({
'success': False,
'message': 'Cannot assign an open box to a location. Please close the box first.'
}), 400
except Exception as e:
pass # Continue to the main function
success, response_data, status_code = assign_box_to_location(box_id, location_code)
return jsonify({
'success': success,
**response_data
}), status_code
@bp.route('/api/warehouse/box/change-status', methods=['POST'])
@requires_warehouse_module
def api_change_box_status():
"""Change the status of a box (open/closed)"""
data = request.get_json()
box_id = data.get('box_id')
new_status = data.get('new_status', '').strip()
success, response_data, status_code = change_box_status(box_id, new_status)
return jsonify({
'success': success,
**response_data
}), status_code
```
---
## Summary of Key Code Locations
| Feature | File | Lines | Function |
|---------|------|-------|----------|
| Create boxes_crates table | warehouse.py | 32-45 | `ensure_boxes_crates_table()` |
| Create box_contents table | warehouse.py | 47-62 | `ensure_box_contents_table()` |
| Generate box numbers | warehouse.py | 155-165 | `generate_box_number()` |
| Add new box | warehouse.py | 167-183 | `add_box()` |
| Assign CP to box | warehouse.py | 619-658 | `assign_cp_to_box_handler()` |
| Search box | warehouse.py | 740-785 | `search_box_by_number()` |
| Assign box to location | warehouse.py | 787-830 | `assign_box_to_location()` |
| Change box status | warehouse.py | 906-965 | `change_box_status()` |
| FG Scan route | routes.py | 1020-1090 | `fg_scan()` |
| Warehouse API routes | routes.py | 5657-5717 | Multiple API endpoints |
| Frontend JS | fg_scan.html | 10-200 | Multiple JS functions |
All code snippets are from [/srv/quality_app/py_app/app/](../py_app/app/) directory.