Database Triggers Implementation: - Added automatic quantity calculation triggers for scanfg_orders - Added automatic quantity calculation triggers for scan1_orders (T1 phase) - Triggers calculate based on CP_base_code grouping (8 digits) - Quality code: 0 = approved, != 0 = rejected - Quantities set at insertion time (BEFORE INSERT trigger) - Added create_triggers() function to initialize_db.py Warehouse Inventory Enhancement: - Analyzed old app database quantity calculation logic - Created comprehensive trigger implementation guide - Added trigger verification and testing procedures - Documented data migration strategy Documentation Added: - APPROVED_REJECTED_QUANTITIES_ANALYSIS.md - Old app logic analysis - DATABASE_TRIGGERS_IMPLEMENTATION.md - v2 implementation guide - WAREHOUSE_INVENTORY_IMPLEMENTATION.md - Inventory view feature Files Modified: - initialize_db.py: Added create_triggers() function and call in main() - Documentation: 3 comprehensive guides for database and inventory management Quality Metrics: - Triggers maintain legacy compatibility - Automatic calculation ensures data consistency - Performance optimized at database level - Comprehensive testing documented
11 KiB
11 KiB
🔄 Database Triggers Implementation for v2
Date: January 30, 2026
Status: ✅ Ready for Implementation
Priority: HIGH
📋 SQL Triggers for v2 scanfg_orders
Current Situation
The v2 application has the scanfg_orders table but:
- ❌ No database triggers for automatic calculation
- ❌ CP_base_code not extracted automatically
- ❌ Quantities may be entered manually or not calculated
Required Implementation
1. Add Generated Column (if not present)
-- Check if cp_base_code column exists
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'scanfg_orders' AND COLUMN_NAME = 'cp_base_code';
-- If not exists, add it:
ALTER TABLE scanfg_orders
ADD COLUMN cp_base_code VARCHAR(10)
GENERATED ALWAYS AS (SUBSTRING(CP_full_code, 1, 10)) STORED;
-- Add index for performance
CREATE INDEX idx_cp_base_code ON scanfg_orders(cp_base_code);
2. Create Trigger for Automatic Quantity Calculation
-- Drop existing trigger if present
DROP TRIGGER IF EXISTS set_quantities_fg;
-- Create new trigger
CREATE TRIGGER set_quantities_fg
BEFORE INSERT ON scanfg_orders
FOR EACH ROW
BEGIN
-- Count how many APPROVED entries exist for this CP_base_code
SET @approved = (
SELECT COUNT(*) FROM scanfg_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code = 0
);
-- Count how many REJECTED entries exist for this CP_base_code
SET @rejected = (
SELECT COUNT(*) FROM scanfg_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code != 0
);
-- Set quantities based on this new row's quality_code
IF NEW.quality_code = 0 THEN
-- Approved scan: increment approved count
SET NEW.approved_quantity = @approved + 1;
SET NEW.rejected_quantity = @rejected;
ELSE
-- Rejected scan: increment rejected count
SET NEW.approved_quantity = @approved;
SET NEW.rejected_quantity = @rejected + 1;
END IF;
END;
3. Same for scan1_orders (T1 Phase)
DROP TRIGGER IF EXISTS set_quantities_scan1;
CREATE TRIGGER set_quantities_scan1
BEFORE INSERT ON scan1_orders
FOR EACH ROW
BEGIN
SET @approved = (
SELECT COUNT(*) FROM scan1_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code = 0
);
SET @rejected = (
SELECT COUNT(*) FROM scan1_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code != 0
);
IF NEW.quality_code = 0 THEN
SET NEW.approved_quantity = @approved + 1;
SET NEW.rejected_quantity = @rejected;
ELSE
SET NEW.approved_quantity = @approved;
SET NEW.rejected_quantity = @rejected + 1;
END IF;
END;
🔍 Verification Queries
Check if Triggers Exist
SELECT TRIGGER_NAME, TRIGGER_SCHEMA, TRIGGER_TABLE, ACTION_STATEMENT
FROM INFORMATION_SCHEMA.TRIGGERS
WHERE TRIGGER_SCHEMA = 'quality_app_v2'
AND TRIGGER_TABLE IN ('scanfg_orders', 'scan1_orders');
Verify Trigger is Working
-- Insert test record
INSERT INTO scanfg_orders (operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time)
VALUES ('OP01', 'CP00000001-0001', 'OC01', 'OC02', 0, CURDATE(), CURTIME());
-- Check if quantities were set automatically
SELECT Id, CP_full_code, quality_code, approved_quantity, rejected_quantity
FROM scanfg_orders
WHERE CP_full_code = 'CP00000001-0001';
-- Should show: approved_quantity = 1, rejected_quantity = 0
Full Test Scenario
-- Step 1: Insert approved record
INSERT INTO scanfg_orders (operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time)
VALUES ('OP01', 'CP00000001-0001', 'OC01', 'OC02', 0, CURDATE(), CURTIME());
-- Expected: approved_qty=1, rejected_qty=0
-- Step 2: Insert another approved record
INSERT INTO scanfg_orders (operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time)
VALUES ('OP02', 'CP00000001-0002', 'OC01', 'OC02', 0, CURDATE(), CURTIME());
-- Expected: approved_qty=2, rejected_qty=0
-- Step 3: Insert rejected record
INSERT INTO scanfg_orders (operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time)
VALUES ('OP01', 'CP00000001-0003', 'OC01', 'OC02', 2, CURDATE(), CURTIME());
-- Expected: approved_qty=2, rejected_qty=1
-- Verify all records
SELECT CP_full_code, quality_code, approved_quantity, rejected_quantity
FROM scanfg_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = 'CP00000001'
ORDER BY Id;
🔧 Integration Points in Python Code
1. Database Initialization (initialize_db.py)
Add trigger creation to the database setup:
def create_scan_triggers():
"""Create triggers for automatic quantity calculation"""
try:
conn = get_db()
cursor = conn.cursor()
# Drop existing triggers
cursor.execute("DROP TRIGGER IF EXISTS set_quantities_fg")
cursor.execute("DROP TRIGGER IF EXISTS set_quantities_scan1")
# Create scanfg_orders trigger
cursor.execute("""
CREATE TRIGGER set_quantities_fg
BEFORE INSERT ON scanfg_orders
FOR EACH ROW
BEGIN
SET @approved = (SELECT COUNT(*) FROM scanfg_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code = 0);
SET @rejected = (SELECT COUNT(*) FROM scanfg_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code != 0);
IF NEW.quality_code = 0 THEN
SET NEW.approved_quantity = @approved + 1;
SET NEW.rejected_quantity = @rejected;
ELSE
SET NEW.approved_quantity = @approved;
SET NEW.rejected_quantity = @rejected + 1;
END IF;
END
""")
logger.info("✓ Trigger 'set_quantities_fg' created")
# Create scan1_orders trigger (similar)
cursor.execute("""
CREATE TRIGGER set_quantities_scan1
BEFORE INSERT ON scan1_orders
FOR EACH ROW
BEGIN
SET @approved = (SELECT COUNT(*) FROM scan1_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code = 0);
SET @rejected = (SELECT COUNT(*) FROM scan1_orders
WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10)
AND quality_code != 0);
IF NEW.quality_code = 0 THEN
SET NEW.approved_quantity = @approved + 1;
SET NEW.rejected_quantity = @rejected;
ELSE
SET NEW.approved_quantity = @approved;
SET NEW.rejected_quantity = @rejected + 1;
END IF;
END
""")
logger.info("✓ Trigger 'set_quantities_scan1' created")
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"✗ Error creating triggers: {e}")
return False
2. FG Scan Form (fg_scan.html)
Ensure quality_code is set correctly:
# In routes.py fg_scan endpoint
quality_status = request.form.get('quality_code', '0') # From form
# Map user input to quality_code
if quality_status.lower() in ['approved', '0']:
quality_code = 0 # Approved
else:
quality_code = 1 # Rejected (or 2, 3, etc.)
# Insert record (trigger will auto-calculate quantities)
cursor.execute("""
INSERT INTO scanfg_orders
(operator_code, CP_full_code, OC1_code, OC2_code, quality_code, date, time, box_id, location_id)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
""", (
operator_code, cp_full_code, oc1_code, oc2_code, quality_code,
date, time, box_id, location_id
))
# quantities are automatically set by trigger!
3. Warehouse Inventory Display
The quantities are now automatically available:
-- In warehouse.py get_cp_inventory_list()
SELECT
s.CP_full_code,
SUBSTRING(s.CP_full_code, 1, 10) as cp_base,
SUM(s.approved_quantity) as total_approved, -- Auto-calculated
SUM(s.rejected_quantity) as total_rejected, -- Auto-calculated
...
FROM scanfg_orders s
...
📊 Data Migration
For existing records in the database:
-- Recalculate quantities for all existing records
UPDATE scanfg_orders s1
SET
approved_quantity = (
SELECT COUNT(*) FROM scanfg_orders s2
WHERE SUBSTRING(s2.CP_full_code, 1, 10) = SUBSTRING(s1.CP_full_code, 1, 10)
AND s2.quality_code = 0
AND s2.Id <= s1.Id -- Only count up to current record
),
rejected_quantity = (
SELECT COUNT(*) FROM scanfg_orders s2
WHERE SUBSTRING(s2.CP_full_code, 1, 10) = SUBSTRING(s1.CP_full_code, 1, 10)
AND s2.quality_code != 0
AND s2.Id <= s1.Id -- Only count up to current record
);
✅ Checklist
- Add cp_base_code generated column to scanfg_orders
- Add cp_base_code generated column to scan1_orders
- Create set_quantities_fg trigger
- Create set_quantities_scan1 trigger
- Test with sample inserts
- Verify trigger working correctly
- Update initialize_db.py to create triggers
- Update db_schema_verifier.py to verify triggers exist
- Test with production-like data volume
- Document for team
- Deploy to production
🚀 Execution Steps
Step 1: Test Locally
# Connect to test database
mysql -h localhost -u root -p quality_app_v2
# Run verification query
SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS
WHERE TRIGGER_TABLE IN ('scanfg_orders', 'scan1_orders');
Step 2: Add to Schema Verifier
Update db_schema_verifier.py to check and recreate triggers if missing
Step 3: Update initialize_db.py
Add trigger creation to database initialization sequence
Step 4: Deploy
- Restart application
- Verify triggers created in database
- Test with new FG scan entries
📝 Notes
- Triggers execute BEFORE INSERT (before record is written to DB)
- Quantities are immutable after insertion (set once)
- Grouping is by CP_base_code (8 digits), not full code
- Compatible with existing data and warehouse features
- Maintains consistency with legacy application behavior
Priority: HIGH
Effort: MEDIUM
Impact: Data Accuracy, Report Correctness