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

20 KiB

Quick Box Creation & Label Printing - Code Snippets Reference

Quick Reference - Key Code Files and Functions

1. Database Table Creation Code

File: app/warehouse.py

boxes_crates table:

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:

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

Global Variables:

// Global variables for scan-to-boxes feature
let scanToBoxesEnabled = false;
let currentCpCode = null;

Main Submit Function:

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:

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:

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:

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

FG Scan Route:

@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

Generate Box Number:

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:

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

Assign CP to Box Handler:

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

Search Box by Number:

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:

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:

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

Warehouse Box API Routes:

@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/ directory.