- 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
398 lines
14 KiB
Markdown
398 lines
14 KiB
Markdown
# Old App FG Scan Box Assignment Workflow Reference
|
||
|
||
## Overview
|
||
The old app (`/srv/quality_app/py_app/app/templates/fg_scan.html`) implements a sophisticated **two-stage scanning and box assignment workflow** that provides users with options to either create new boxes or assign scans to existing boxes.
|
||
|
||
## Workflow Trigger
|
||
|
||
**When does the modal appear?**
|
||
- User fills in all scan fields (Operator Code, CP Code, OC1, OC2, Defect Code)
|
||
- User scans defect code (3 digits)
|
||
- The checkbox **"Enable Scan to Boxes"** must be ENABLED
|
||
- Defect code must be **'000'** (good quality only)
|
||
|
||
**Code Reference (Lines 730-750):**
|
||
```javascript
|
||
if (scanToBoxesEnabled && this.value === '000') {
|
||
console.log('Auto-submit: Scan-to-boxes enabled, calling submitScanWithBoxAssignment');
|
||
submitScanWithBoxAssignment(); // <-- Triggers modal instead of normal form submit
|
||
} else {
|
||
console.log('Auto-submit: Normal form submission');
|
||
form.submit(); // <-- Normal database insert, page reloads
|
||
}
|
||
```
|
||
|
||
## Step-by-Step Workflow
|
||
|
||
### Step 1: Scan Entry & Validation
|
||
**File: fg_scan.html, Lines 1-400**
|
||
|
||
User interface with scan input form:
|
||
```html
|
||
<label for="operator_code">Quality Operator Code:</label>
|
||
<label for="cp_code">CP Code:</label>
|
||
<label for="oc1_code">OC1 Code:</label>
|
||
<label for="oc2_code">OC2 Code:</label>
|
||
<label for="defect_code">Defect Code:</label>
|
||
|
||
<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #ddd;">
|
||
<label style="display: flex; align-items: center; justify-content: center; gap: 10px; cursor: pointer;">
|
||
<span style="font-weight: bold;">Enable Scan to Boxes:</span>
|
||
<input type="checkbox" id="scan-to-boxes-toggle" style="width: 20px; height: 20px; cursor: pointer;">
|
||
</label>
|
||
</div>
|
||
```
|
||
|
||
**Key validation rules:**
|
||
- Operator code: Must start with `OP`
|
||
- CP code: Must start with `CP`
|
||
- OC1 code: Must start with `OC`
|
||
- OC2 code: Must start with `OC`
|
||
- Defect code: Must be 3 digits (000-999)
|
||
|
||
### Step 2: Form Submission - POST to Database
|
||
**File: fg_scan.html, Lines 25-65**
|
||
|
||
Before showing modal, the scan is saved to database:
|
||
```javascript
|
||
async function submitScanWithBoxAssignment() {
|
||
const form = document.getElementById('fg-scan-form');
|
||
const formData = new FormData(form);
|
||
|
||
try {
|
||
const response = await fetch(window.location.href, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
},
|
||
body: formData // <-- Posts all scan data to database
|
||
});
|
||
|
||
if (response.ok) {
|
||
currentCpCode = formData.get('cp_code');
|
||
const defectCode = formData.get('defect_code') || '000';
|
||
|
||
// ✅ Scan saved to database
|
||
showNotification('✅ Scan recorded successfully!', 'success');
|
||
|
||
// Only show modal if quality code is 000
|
||
if (defectCode === '000' || defectCode === '0') {
|
||
console.log('Should show box modal');
|
||
showBoxModal(currentCpCode); // <-- MODAL APPEARS HERE
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Error:', error);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Step 3: Modal Presentation
|
||
**File: fg_scan.html, Lines 1150-1250**
|
||
|
||
Modal HTML structure:
|
||
```html
|
||
<!-- Box Assignment Popup Modal -->
|
||
<div id="box-assignment-modal" class="box-modal" style="display: none;">
|
||
<div class="box-modal-content">
|
||
<div class="box-modal-header">
|
||
<h3>Assign to Box</h3>
|
||
<span class="box-modal-close" onclick="closeBoxModal()">×</span>
|
||
</div>
|
||
<div class="box-modal-body">
|
||
<p>CP Code: <strong id="modal-cp-code"></strong></p>
|
||
|
||
<!-- Option 1: Quick Box Creation -->
|
||
<div style="margin: 20px 0; padding: 15px; background: #f0f8ff; border-radius: 5px;">
|
||
<button type="button" id="quick-box-create-btn" class="btn" style="width: 100%; background: #28a745; color: white;">
|
||
📦 Quick Box Label Creation
|
||
</button>
|
||
<p style="font-size: 0.85em; color: #666; margin-top: 8px; text-align: center;">
|
||
Creates new box and prints label immediately
|
||
</p>
|
||
</div>
|
||
|
||
<div style="text-align: center; margin: 15px 0; color: #999;">— OR —</div>
|
||
|
||
<!-- Option 2: Scan Existing Box -->
|
||
<div style="margin: 20px 0;">
|
||
<label style="font-weight: bold;">Scan Box Number:</label>
|
||
<input type="text" id="scan-box-input" placeholder="Scan or enter box number"
|
||
style="width: 100%; padding: 8px; font-size: 1em; margin-top: 5px;">
|
||
<p style="font-size: 0.85em; color: #666; margin-top: 5px;">
|
||
Scan an existing box label to assign this CP code to that box
|
||
</p>
|
||
</div>
|
||
|
||
<div class="box-modal-buttons" style="margin-top: 20px;">
|
||
<button type="button" class="btn" onclick="closeBoxModal()" style="background: #6c757d;">Skip</button>
|
||
<button type="button" id="assign-to-box-btn" class="btn" style="background: #007bff;">Assign to Box</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
## Three User Choices in Modal
|
||
|
||
### Choice 1: Create New Box (Green Button)
|
||
**Code: Lines 1005-1095**
|
||
|
||
```javascript
|
||
document.getElementById('quick-box-create-btn').addEventListener('click', async function() {
|
||
this.disabled = true;
|
||
this.textContent = 'Creating box...';
|
||
|
||
try {
|
||
// Step 1: Create box in database
|
||
const createResponse = await fetch('/warehouse/create_box', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({})
|
||
});
|
||
|
||
const result = await createResponse.json();
|
||
|
||
if (result.success && result.box_number) {
|
||
const boxNumber = result.box_number;
|
||
|
||
// Step 2: Generate PDF label
|
||
const formData = new FormData();
|
||
formData.append('box_number', boxNumber);
|
||
|
||
const pdfResponse = await fetch('/generate_box_label_pdf', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
// Step 3: Print label using QZ Tray
|
||
const config = window.qz.configs.create(printer, {
|
||
scaleContent: false,
|
||
rasterize: false,
|
||
size: { width: 80, height: 50 },
|
||
units: 'mm',
|
||
margins: { top: 0, right: 0, bottom: 0, left: 0 }
|
||
});
|
||
|
||
const printData = [{
|
||
type: 'pdf',
|
||
format: 'base64',
|
||
data: pdfBase64
|
||
}];
|
||
|
||
await window.qz.print(config, printData);
|
||
|
||
showNotification(`✅ Box ${boxNumber} created and label printed!`, 'success');
|
||
|
||
// Step 4: Focus input to scan the newly created box
|
||
document.getElementById('scan-box-input').value = '';
|
||
document.getElementById('scan-box-input').focus();
|
||
document.getElementById('scan-box-input').placeholder = 'Scan the printed label now...';
|
||
}
|
||
} catch (error) {
|
||
console.error('Error creating box:', error);
|
||
showNotification('❌ Error creating box: ' + error.message, 'error');
|
||
}
|
||
});
|
||
```
|
||
|
||
**Process:**
|
||
1. ✅ Create empty box in database via `/warehouse/create_box` endpoint
|
||
2. ✅ Generate PDF label via `/generate_box_label_pdf` endpoint
|
||
3. ✅ Print label immediately using QZ Tray (thermal printer)
|
||
4. ✅ Focus input field to scan the newly printed label
|
||
5. ✅ Modal stays open for box assignment
|
||
|
||
### Choice 2: Scan Existing Box (Assign Button - Blue)
|
||
**Code: Lines 1100-1120**
|
||
|
||
```javascript
|
||
document.getElementById('assign-to-box-btn').addEventListener('click', async function() {
|
||
const boxNumber = document.getElementById('scan-box-input').value.trim();
|
||
|
||
if (!boxNumber) {
|
||
showNotification('⚠️ Please scan or enter a box number', 'warning');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await assignCpToBox(boxNumber); // POST to /warehouse/assign_cp_to_box
|
||
|
||
showNotification(`✅ CP ${currentCpCode} assigned to box ${boxNumber}`, 'success');
|
||
|
||
setTimeout(() => closeBoxModal(), 1000);
|
||
} catch (error) {
|
||
showNotification('❌ Error: ' + error.message, 'error');
|
||
}
|
||
});
|
||
|
||
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();
|
||
}
|
||
```
|
||
|
||
**Process:**
|
||
1. User scans existing box number (or enters manually)
|
||
2. Click "Assign to Box" button
|
||
3. POST to `/warehouse/assign_cp_to_box` with:
|
||
- `box_number`: The scanned/entered box ID
|
||
- `cp_code`: The current CP code from the scan
|
||
4. ✅ CP assigned to box in database
|
||
5. Modal closes and page reloads
|
||
6. Ready for next scan
|
||
|
||
### Choice 3: Skip (Gray Button)
|
||
**Code: Lines 1110-1115**
|
||
|
||
```javascript
|
||
<button type="button" class="btn" onclick="closeBoxModal()" style="background: #6c757d;">Skip</button>
|
||
```
|
||
|
||
**Process:**
|
||
1. User clicks "Skip"
|
||
2. Modal closes via `closeBoxModal()`
|
||
3. Page reloads
|
||
4. Scan is already in database (saved in Step 2)
|
||
5. No box assignment for this scan
|
||
6. Ready for next scan
|
||
|
||
## Toggle Checkbox - Enable/Disable Feature
|
||
|
||
**Code: Lines 310-360**
|
||
|
||
```javascript
|
||
const toggleElement = document.getElementById('scan-to-boxes-toggle');
|
||
if (toggleElement) {
|
||
toggleElement.checked = scanToBoxesEnabled;
|
||
toggleElement.addEventListener('change', function() {
|
||
scanToBoxesEnabled = this.checked;
|
||
localStorage.setItem('scan_to_boxes_enabled', this.checked);
|
||
|
||
// Connect or disconnect QZ Tray based on toggle state
|
||
if (scanToBoxesEnabled) {
|
||
// Connect to printer for label printing
|
||
window.qz.websocket.connect().then(() => {
|
||
showNotification('✅ QZ Tray connected for box label printing', 'success');
|
||
});
|
||
} else {
|
||
// Disconnect printer connection
|
||
window.qz.websocket.disconnect().then(() => {
|
||
showNotification('ℹ️ QZ Tray disconnected', 'info');
|
||
});
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
**Behavior:**
|
||
- When **CHECKED**: Modal appears after good quality scans (defect code 000)
|
||
- When **UNCHECKED**: Normal database insert, no modal, page auto-reloads
|
||
- Preference saved to browser localStorage for persistence
|
||
|
||
## Database Endpoints Required
|
||
|
||
For this workflow to function, the backend must have these endpoints:
|
||
|
||
1. **POST `/warehouse/create_box`**
|
||
- Creates empty box in `boxes_crates` table
|
||
- Returns: `{ success: true, box_number: "BOX-12345" }`
|
||
|
||
2. **POST `/warehouse/assign_cp_to_box`**
|
||
- Creates record linking CP to box (likely in `box_contents` table)
|
||
- Accepts: `{ box_number: "BOX-12345", cp_code: "CP-123456" }`
|
||
- Returns: `{ success: true, message: "..." }`
|
||
|
||
3. **POST `/generate_box_label_pdf`**
|
||
- Generates PDF label for box number
|
||
- Accepts: `FormData` with `box_number`
|
||
- Returns: PDF blob
|
||
|
||
4. **POST `/fg_scan` (or current page)**
|
||
- Original form POST
|
||
- Inserts scan record with all fields
|
||
- Returns: JSON response for AJAX handling
|
||
|
||
## Key Features to Implement in Quality App V2
|
||
|
||
### 1. Toggle Checkbox (Already Have)
|
||
✅ Present in quality_app-v2 fg_scan.html
|
||
|
||
### 2. Modal Structure (Need to Review/Update)
|
||
The modal should include:
|
||
- Current CP code display
|
||
- Option 1: Create box + print label
|
||
- Option 2: Scan/enter existing box
|
||
- Option 3: Skip assignment
|
||
- Close button
|
||
|
||
### 3. QZ Tray Integration (Already Have)
|
||
✅ PDF printing capability already implemented
|
||
|
||
### 4. Database Endpoints (CRITICAL)
|
||
Need to verify these exist in quality_app-v2:
|
||
- [ ] `/warehouse/create_box` - Create empty box
|
||
- [ ] `/warehouse/assign_cp_to_box` - Link CP to box
|
||
- [ ] `/generate_box_label_pdf` - PDF label generation
|
||
|
||
### 5. Database Tables (VERIFIED ✅)
|
||
All required tables now in init_db.py:
|
||
- ✅ `boxes_crates` - Box master records
|
||
- ✅ `box_contents` - CP to box assignments
|
||
- ✅ `scanfg_orders` - Scan records (with location_id, box_id columns)
|
||
|
||
## HTML Form Flow Comparison
|
||
|
||
### Old App (`/srv/quality_app`)
|
||
1. Checkbox enabled → Defect code 000 → Modal appears
|
||
2. User has 3 choices (Create/Scan/Skip)
|
||
3. Create box → Print label → Scan it → Assign
|
||
4. OR Scan existing box → Assign
|
||
5. OR Skip
|
||
|
||
### New App (`/srv/quality_app-v2`)
|
||
**CURRENT STATE:** Likely same structure, needs verification
|
||
- [ ] Check if modal exists in current templates
|
||
- [ ] Verify all three choice buttons present
|
||
- [ ] Verify QZ Tray integration working
|
||
- [ ] Verify backend endpoints exist
|
||
|
||
## Testing Checklist
|
||
|
||
- [ ] Checkbox toggles feature on/off
|
||
- [ ] Checkbox persists in localStorage
|
||
- [ ] QZ Tray connects when checkbox enabled
|
||
- [ ] Defect code 000 triggers modal
|
||
- [ ] Other defect codes skip modal
|
||
- [ ] "Create Box" button creates box + prints label
|
||
- [ ] "Assign to Box" button assigns CP to existing box
|
||
- [ ] "Skip" button closes modal, reload page
|
||
- [ ] Modal has CP code displayed
|
||
- [ ] Modal can be closed by X button
|
||
- [ ] Modal can be closed by clicking outside
|
||
|
||
## File Reference
|
||
- **Old App HTML:** `/srv/quality_app/py_app/app/templates/fg_scan.html` (1242 lines)
|
||
- **Key JS Functions:** Lines 15-120 (submitScanWithBoxAssignment, showBoxModal, assignCpToBox)
|
||
- **Modal HTML:** Lines 1150-1250
|
||
- **Toggle Code:** Lines 310-360
|
||
|
||
## Next Steps
|
||
1. Review new app's fg_scan.html for same structure
|
||
2. Verify backend endpoints exist for box operations
|
||
3. Test the entire workflow (especially QZ Tray printing)
|
||
4. Update if needed to match old app's three-choice model
|