SOP-INV-01: Inventory Control & Manual Adjustments¶
Version: 1.1 FINAL
Effective Date: 2026-01-17
Owner: Operations Team
Replaces: SOP-INV-01-v1.0
Review Cycle: Quarterly
1. PURPOSE & SCOPE¶
1.1 Purpose¶
This SOP establishes the systematic process for: - Real-time inventory tracking and reconciliation - Manual inventory adjustments for damaged goods, miscounts, returns, and found stock - Complete audit trail maintenance for regulatory compliance - Prevention of negative inventory and stockouts
1.2 Scope¶
In Scope: - All batch inventory movements (receipts, allocations, adjustments, disposals) - Manual adjustment workflows for physical discrepancies - Automated inventory reconciliation via Operations Portal - Audit trail generation for food safety compliance
Out of Scope: - Initial batch receipt (covered by SOP-01) - Automated allocation to orders (covered by SOP-03) - Pack day inventory consumption (covered by SOP-PACK-01)
1.3 Key Principles¶
- Real-time accuracy - inventory reflects physical stock within 15 minutes
- Complete traceability - every adjustment logged with reason and operator
- Zero negative inventory - system prevents adjustments resulting in negative stock
- Audit-ready - full movement history available for regulatory inspection
2. ROLES & RESPONSIBILITIES¶
2.1 Operations Lead¶
- Primary: Execute manual inventory adjustments via Operations Portal
- Monitor: Review adjustment logs weekly for patterns (repeated damage, theft)
- Escalate: Report systematic issues (supplier quality, handling damage) to management
2.2 Ops & Data Lead (Month 6+)¶
- Oversight: Review all manual adjustments monthly
- Analysis: Identify root causes of recurring adjustments
- Optimization: Propose process improvements to reduce adjustment frequency
2.3 Founder (Pre-Month 6)¶
- Approval: Review all adjustments >20kg or suspicious patterns
- Audit: Spot-check physical inventory vs. system quarterly
- Compliance: Ensure audit trail meets UKAS/FEDIAF requirements
3. SYSTEM ARCHITECTURE¶
3.1 Database Schema¶
3.1.1 Core Inventory Table¶
Table: raw_ops.inventory
| Column | Type | Description |
|---|---|---|
inventory_id |
uuid | Primary key |
batch_id |
uuid | FK to batches table |
sku |
text | Product SKU (e.g., STARTER-8KG) |
location |
text | Storage location (default: WAREHOUSE) |
qty_available |
numeric(10,2) | Current available stock in kg |
qty_allocated |
numeric(10,2) | Reserved for orders (kg) |
qty_on_hold |
numeric(10,2) | QA hold inventory (kg) |
created_at |
timestamptz | Record creation timestamp |
updated_at |
timestamptz | Last modification timestamp |
Constraints:
- qty_available >= 0 (enforced by trigger)
- qty_allocated >= 0 (enforced by trigger)
- Unique constraint on (batch_id, sku, location)
3.1.2 Movement History Table¶
Table: raw_ops.inventory_movements
| Column | Type | Description |
|---|---|---|
movement_id |
uuid | Primary key |
inventory_id |
uuid | FK to inventory table |
movement_type |
text | RECEIPT / ALLOCATION / ADJUSTMENT / DISPOSAL |
qty_change |
numeric(10,2) | Quantity change (positive or negative) |
qty_before |
numeric(10,2) | Stock level before movement |
qty_after |
numeric(10,2) | Stock level after movement |
order_id |
uuid | Related order (if applicable) |
notes |
text | Reason/description of movement |
created_by |
text | Operator name/ID |
created_at |
timestamptz | Movement timestamp |
Indexes:
- idx_movements_inventory on inventory_id
- idx_movements_type on movement_type
- idx_movements_created on created_at DESC
3.2 Database Functions¶
3.2.1 Current Inventory Query¶
Function: get_current_inventory_for_reconciliation()
-- Returns real-time inventory with batch metadata for Operations Portal
CREATE OR REPLACE FUNCTION get_current_inventory_for_reconciliation()
RETURNS TABLE (
batch_code text,
sku text,
location text,
kg_available numeric,
batch_status text
) AS $$
BEGIN
RETURN QUERY
SELECT
b.batch_code,
i.sku,
i.location,
i.qty_available,
b.status::text as batch_status
FROM raw_ops.inventory i
JOIN raw_ops.batches b ON i.batch_id = b.batch_id
WHERE i.qty_available > 0
ORDER BY b.expiry_date ASC, b.batch_code ASC;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Access: Public schema, callable via anon/service role
Performance: <50ms for 100 batches
Purpose: Operations Portal "Current Inventory" display
3.2.2 Manual Adjustment Execution¶
Function: execute_manual_inventory_adjustment()
CREATE OR REPLACE FUNCTION execute_manual_inventory_adjustment(
p_batch_code text,
p_adjustment_kg numeric,
p_reason text,
p_adjusted_by text
) RETURNS jsonb AS $$
DECLARE
v_batch_id uuid;
v_inventory_id uuid;
v_current_qty numeric;
v_new_qty numeric;
v_movement_id uuid;
BEGIN
-- Validate batch exists
SELECT batch_id INTO v_batch_id
FROM raw_ops.batches
WHERE batch_code = p_batch_code;
IF v_batch_id IS NULL THEN
RAISE EXCEPTION 'Batch % not found', p_batch_code;
END IF;
-- Get current inventory record
SELECT inventory_id, qty_available INTO v_inventory_id, v_current_qty
FROM raw_ops.inventory
WHERE batch_id = v_batch_id
FOR UPDATE;
IF v_inventory_id IS NULL THEN
RAISE EXCEPTION 'Inventory record not found for batch %', p_batch_code;
END IF;
-- Calculate new quantity
v_new_qty := v_current_qty + p_adjustment_kg;
-- Prevent negative inventory
IF v_new_qty < 0 THEN
RAISE EXCEPTION 'Adjustment would result in negative inventory: current=% adjustment=% new=%',
v_current_qty, p_adjustment_kg, v_new_qty;
END IF;
-- Update inventory
UPDATE raw_ops.inventory
SET
qty_available = v_new_qty,
updated_at = NOW()
WHERE inventory_id = v_inventory_id;
-- Log movement
INSERT INTO raw_ops.inventory_movements (
movement_id,
inventory_id,
movement_type,
qty_change,
qty_before,
qty_after,
notes,
created_by,
created_at
) VALUES (
gen_random_uuid(),
v_inventory_id,
'ADJUSTMENT',
p_adjustment_kg,
v_current_qty,
v_new_qty,
p_reason,
p_adjusted_by,
NOW()
) RETURNING movement_id INTO v_movement_id;
-- Log to ops_events for monitoring
INSERT INTO raw_ops.ops_events (
event_id,
event_type,
severity,
message,
metadata,
created_at
) VALUES (
gen_random_uuid(),
'INVENTORY_ADJUSTMENT',
CASE
WHEN ABS(p_adjustment_kg) > 20 THEN 'WARNING'
ELSE 'INFO'
END,
format('Manual adjustment: %s kg for batch %s. Reason: %s',
p_adjustment_kg, p_batch_code, p_reason),
jsonb_build_object(
'batch_code', p_batch_code,
'adjustment_kg', p_adjustment_kg,
'qty_before', v_current_qty,
'qty_after', v_new_qty,
'adjusted_by', p_adjusted_by,
'movement_id', v_movement_id
),
NOW()
);
-- Return success
RETURN jsonb_build_object(
'success', true,
'batch_code', p_batch_code,
'adjustment_kg', p_adjustment_kg,
'qty_before', v_current_qty,
'qty_after', v_new_qty,
'movement_id', v_movement_id
);
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Adjustment failed: %', SQLERRM;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Access: Public schema, requires service_role key
Atomicity: Single transaction - all changes rollback on error
Validation: Prevents negative inventory, missing batches, invalid adjustments
Logging: Dual-write to inventory_movements + ops_events
4. OPERATIONAL PROCEDURES¶
4.1 Daily Inventory Reconciliation¶
Frequency: Daily at 6:00 AM (automated)
Purpose: Verify system inventory matches expected levels
Trigger: Scheduled Edge Function
Process:
1. Query current inventory via get_current_inventory_for_reconciliation()
2. Calculate expected levels:
- Starting inventory (yesterday's closing)
- Plus: New batch receipts (from SOP-01)
- Minus: Order allocations (from SOP-03)
- Minus: Manual adjustments (from SOP-INV-01)
3. Compare actual vs. expected:
- Variance <1kg = NORMAL (rounding tolerance)
- Variance 1-5kg = WARNING (investigate)
- Variance >5kg = CRITICAL (halt operations, physical count)
4. Log results to ops_events with variance details
5. Alert Operations Lead if WARNING or CRITICAL
Automation: Edge Function daily-inventory-reconciliation
Monitoring: Dashboard in Metabase (SLA: 100% daily execution)
4.2 Manual Inventory Adjustment Workflow¶
When to Adjust: - Damaged goods: Pouches torn, contaminated, or unsuitable for sale - Miscounts: Physical count differs from system during spot-check - Returns: Customer returns (rare, but must be logged) - Found stock: Discovery of previously unrecorded inventory - Disposal: Expired or recalled batches removed from inventory
Execution Steps:
Step 1: Access Operations Portal¶
- Navigate to
https://ops.protocolraw.co.uk - Click 📦 Inventory tab
- Click ðŸâ€„ Refresh Inventory to load current stock levels
Step 2: Identify Batch¶
- Review current inventory table
- Identify batch requiring adjustment
- Note current
kg_availablevalue
Step 3: Execute Adjustment¶
- Click Manual Inventory Adjustment section
- Select batch from dropdown:
- Format:
PR-YYMMDD-XXX (SKU) - XX.X kg available - View batch status summary:
- Batch code
- SKU
- Location
- Current stock level
- Batch status (AVAILABLE / QA_HOLD)
Step 4: Enter Adjustment Details¶
- Adjustment Amount (kg):
- Negative for removal:
-12(remove 12kg damaged goods) - Positive for addition:
+8(add 8kg found stock) -
System prevents adjustments resulting in negative inventory
-
Reason for Adjustment:
- Required field - must explain why adjustment is needed
-
Examples:
- "3 pouches damaged during pack day - torn packaging"
- "Physical count found 8kg unrecorded stock in freezer"
- "5kg returned by customer - unopened pouches"
- "Disposal of 20kg expired batch PR-241101-001"
-
Your Name:
- Pre-filled with operator name (default: Anton)
- Change if adjustment performed by different team member
Step 5: Confirm & Submit¶
- Review adjustment summary:
- Batch:
PR-241201-001 - Current:
48.0 kg - Adjustment:
-12.0 kg - New:
36.0 kg -
Reason: "3 pouches damaged during pack day"
-
Click ✅ Submit Adjustment
- System validates:
- Batch exists and is available
- New quantity >= 0
- Reason provided
-
Operator name provided
-
On success:
- Green confirmation message appears
- Shows before/after quantities
- Confirms logging to inventory_movements and ops_events
- Auto-refreshes inventory after 2 seconds
Step 6: Verify in Logs¶
- Scroll to 📋 Recent Adjustments section
- Click ðŸâ€„ Refresh Log
- Verify adjustment appears with:
- Timestamp
- SKU
- Change amount (red for negative, green for positive)
- Before/after quantities
- Reason text
SLA: - Adjustment execution: <5 seconds - Log visibility: Immediate (real-time) - Error notification: Instant via UI
4.3 Physical Inventory Count (Quarterly)¶
Frequency: Quarterly (every 13 weeks)
Owner: Operations Lead
Purpose: Independent verification of system accuracy
Process: 1. Schedule count: - Choose low-activity day (typically Monday) - Notify team 48 hours in advance - Freeze new batch receipts during count (2-3 hours)
- Execute count:
- Print current inventory report from Operations Portal
- Physically weigh/count all batches in warehouse
-
Record findings on count sheet with batch codes
-
Reconcile discrepancies:
- Compare physical count vs. system inventory
- Investigate variances >1kg per batch
-
Execute manual adjustments for confirmed discrepancies
-
Document results:
- Count sheet scanned and uploaded to Google Drive
- Summary report in ops_events with variance analysis
- Action items for recurring issues (e.g., storage improvements)
Compliance: Required for UKAS audit trail
Storage: Google Drive > Operations > Inventory Counts > YYYY-MM-DD_Count.pdf
5. MONITORING & ALERTS¶
5.1 Real-Time Monitoring¶
Metabase Dashboard: "Inventory Health"
Key Metrics: 1. Total Available Inventory (kg) - Current across all batches - Trend line (7-day, 30-day) - Alert if <100kg (1 week coverage)
- Stock by Batch Status
- AVAILABLE vs. QA_HOLD split
- Aging analysis (days since production)
-
Alert if >20% in QA_HOLD >7 days
-
Daily Adjustment Volume
- Total kg adjusted (positive and negative)
- Adjustment count by reason category
-
Alert if >50kg adjusted in single day
-
Inventory Turnover
- FEFO adherence (oldest batch used first)
- Average age at allocation
- Alert if batch >21 days remains unallocated
5.2 Automated Alerts¶
Monitoring Architecture: See SOP-MON-01: Monitoring & Alerting Architecture for implementation details.
Slack Channels: #ops-alerts (warning) / #ops-urgent (critical)
| Trigger | Severity | Channel | SLA |
|---|---|---|---|
| Adjustment >20kg | WARNING | #ops-alerts | Review within 4 hours |
| Negative inventory attempt | CRITICAL | #ops-urgent | Immediate investigation |
| Daily reconciliation variance >5kg | CRITICAL | #ops-urgent | Halt packing, physical count |
| Stock level below threshold | CRITICAL | #ops-urgent | Order new batch within 48h |
| Batch in QA_HOLD >7 days | WARNING | #ops-alerts | Chase lab results (see SOP-01) |
Stock Level Alert (SOP-MON-01)¶
The stock level alert runs natively within Supabase every 15 minutes:
Function: raw_ops.fn_check_stock_levels_v2()
Schedule: */15 * * * * (every 15 minutes)
Source: public.get_low_stock_alerts() function
Alert Fields: - SKU - Units Available - kg Available - Threshold Units
Check Recent Runs:
SELECT * FROM raw_ops.monitoring_runs
WHERE check_name = 'stock_levels'
ORDER BY run_at DESC LIMIT 5;
Other Alert Formats¶
âš ï¸Â INVENTORY ALERT: Large Adjustment
Batch: PR-241201-001
Amount: -24.0 kg
Reason: "Damaged during transport"
Adjusted by: Anton
New level: 72.0 kg
Action: Review supplier handling procedures
5.3 Weekly Review¶
Meeting: Operations Review (Fridays, 10:00 AM)
Attendees: Operations Lead, Founder (Month 1-6), Ops & Data Lead (Month 6+)
Agenda Items: 1. Review total adjustment volume (kg and count) 2. Analyze adjustment reasons - identify patterns: - Repeated damage → supplier quality issue - Frequent miscounts → process training needed - High returns → product/packaging issue 3. Review inventory turnover and FEFO compliance 4. Action items for process improvements
Documentation: Meeting notes in ops_events with WEEKLY_REVIEW type
6. COMPLIANCE & AUDIT TRAIL¶
6.1 Regulatory Requirements¶
UKAS Accreditation: - Complete traceability from batch → order → customer - Audit trail for all inventory movements - Retention: 2 years minimum (Protocol Raw standard: 5 years)
FEDIAF Guidelines: - Batch-level inventory control - Prevention of cross-contamination via location tracking - Documented procedures for damaged goods disposal
Food Safety Standards: - Immediate removal of contaminated stock - Segregation of QA_HOLD inventory - Documented approval for release to AVAILABLE
6.2 Audit Trail Query¶
Purpose: Generate complete movement history for regulatory inspection
SQL Query:
-- Full audit trail for specific batch
SELECT
im.created_at,
im.movement_type,
im.qty_change,
im.qty_before,
im.qty_after,
im.notes,
im.created_by,
o.order_number AS related_order
FROM raw_ops.inventory_movements im
JOIN raw_ops.inventory i ON im.inventory_id = i.inventory_id
JOIN raw_ops.batches b ON i.batch_id = b.batch_id
LEFT JOIN raw_ops.orders o ON im.order_id = o.order_id
WHERE b.batch_code = 'PR-241201-001'
ORDER BY im.created_at ASC;
Output Format:
| Timestamp | Type | Change | Before | After | Notes | By | Order |
|---------------------|------------|--------|--------|-------|------------------------|-------|----------|
| 2024-12-01 08:00:00 | RECEIPT | +120.0 | 0.0 | 120.0 | Initial batch receipt | Anton | NULL |
| 2024-12-02 10:15:00 | ALLOCATION | -12.0 | 120.0 | 108.0 | Order allocation | AUTO | PR-00123 |
| 2024-12-02 14:30:00 | ADJUSTMENT | -8.0 | 108.0 | 100.0 | 2 pouches damaged | Anton | NULL |
| 2024-12-03 09:00:00 | ALLOCATION | -12.0 | 100.0 | 88.0 | Order allocation | AUTO | PR-00124 |
Access: Ops & Data Lead, Founder (Month 1-6)
Frequency: On-demand for audits, monthly spot-checks
6.3 Data Retention¶
Storage Policy: - inventory table: Perpetual (current state only) - inventory_movements: 5 years rolling retention - ops_events (inventory type): 5 years rolling retention
Backup Schedule: - Daily: Supabase automatic backups (7-day retention) - Weekly: Manual export to Google Drive (CSV format) - Monthly: Full database snapshot to cold storage
Archive Location: Google Drive > Operations > Inventory Archives > YYYY-MM/
7. ESCALATION PROCEDURES¶
7.1 Negative Inventory Attempt¶
Trigger: User attempts adjustment that would result in qty_available < 0
Response: 1. System: Reject adjustment with error message:
âÂŒ Cannot adjust: Would result in negative inventory (-4.5 kg)
Current: 8.0 kg | Adjustment: -12.5 kg | New: -4.5 kg
- Operator Action:
- DO NOT OVERRIDE - system is enforcing data integrity
- Verify physical inventory with spot-check
-
If physical stock is truly lower, first investigate:
- Missing allocation? (order packed but not recorded)
- Theft or misplacement?
- Prior adjustment error?
-
Resolution:
- Correct root cause (e.g., record missing dispatch)
- Re-attempt adjustment with corrected amount
- Document incident in ops_events
Escalation: If negative inventory persists, halt operations and physical count all batches
7.2 Large Adjustment (>20kg)¶
Trigger: Single adjustment >20kg detected by ops_events monitoring
Response: 1. Automatic Alert: Slack notification to Operations Lead + Founder 2. Investigation Required: - What caused the large discrepancy? - When was last physical count for this batch? - Is this an isolated incident or pattern?
- Common Causes:
- Transport damage: Co-packer delivery issue → escalate to supplier
- Expiry disposal: Large batch past expiry → review rotation procedures
-
Miscount correction: Prior receipt error → update receiving SOP
-
Documentation:
- Photo evidence of damaged goods (if applicable)
- Email to co-packer (if supplier issue)
- Update to relevant SOP (if process gap identified)
SLA: Investigation within 4 hours, resolution within 24 hours
7.3 Systematic Adjustment Pattern¶
Trigger: >5 adjustments in 7 days OR >3 adjustments for same reason in 30 days
Response: 1. Weekly Review Agenda Item: - Present adjustment pattern to team - Root cause analysis - why is this recurring? - Process improvement proposal
-
Common Patterns & Solutions: | Pattern | Root Cause | Solution | |---------|------------|----------| | Daily damage adjustments | Poor handling procedures | Retrain pack day team on pouch handling | | Weekly miscounts | Inaccurate receipt recording | Implement weight verification at SOP-01 | | Monthly returns | Product quality issues | Quality review with co-packer |
-
Implementation:
- Update relevant SOP with process improvement
- Communicate change to team via Slack
- Monitor for 30 days to confirm pattern resolved
Owner: Ops & Data Lead (Month 6+), Founder (Month 1-6)
8. PERFORMANCE METRICS¶
8.1 Key Performance Indicators (KPIs)¶
| Metric | Target | Measurement | Review Frequency |
|---|---|---|---|
| Inventory Accuracy | >99% | (System qty / Physical qty) * 100 | Quarterly physical count |
| Adjustment Frequency | <5 per week | Count of manual adjustments | Weekly |
| Adjustment Volume | <2% of receipts | Total kg adjusted / Total kg received | Monthly |
| Reconciliation Variance | <1kg daily | Abs(Expected - Actual) | Daily automated |
| FEFO Compliance | 100% | Oldest batch allocated first | Daily automated |
| Stock Days Coverage | 7-14 days | Inventory kg / Avg daily usage | Daily automated |
8.2 Monthly Reporting¶
Report: "Inventory Health Report"
Distribution: Founder, Operations Lead, Ops & Data Lead
Format: PDF via email + Metabase dashboard
Contents: 1. Summary Metrics: - Opening inventory (kg) - Receipts (kg) - Allocations (kg) - Adjustments (kg, % of receipts) - Closing inventory (kg)
- Adjustment Analysis:
- Top 3 adjustment reasons (by volume)
- Adjustment trend (vs. prior month)
-
Pattern identification (systematic issues)
-
Compliance Status:
- FEFO adherence (%)
- Batch age distribution
-
QA_HOLD duration (avg days)
-
Action Items:
- Process improvements implemented
- Outstanding issues requiring escalation
- Next month's focus areas
Storage: Google Drive > Operations > Monthly Reports > YYYY-MM_Inventory_Health.pdf
9. SYSTEM MAINTENANCE¶
9.1 Database Performance¶
Monitoring:
- Query execution time for get_current_inventory_for_reconciliation() - Target: <50ms
- execute_manual_inventory_adjustment() transaction time - Target: <100ms
- Table size growth rate - Expected: ~100 rows/month in inventory_movements
Optimization: - Indexes: Review quarterly, add if queries >100ms - Vacuum: PostgreSQL auto-vacuum handles cleanup - Partitioning: Consider time-based partitioning of inventory_movements at 1M+ rows
Owner: Ops & Data Lead (Month 6+), Founder (Month 1-6)
9.2 Operations Portal Updates¶
Deployment Process:
1. Test changes in local environment
2. Deploy to ops.protocolraw.co.uk via Cloudflare Pages
3. Verify functionality in production (smoke test)
4. Notify team via Slack of new features/changes
Change Log: Maintained in Git commit history + version number in portal header
Rollback Plan: Cloudflare Pages maintains deployment history - revert to prior version in <5 minutes if critical bug
10. TRAINING & ONBOARDING¶
10.1 Operations Lead Training¶
Duration: 2 hours
Format: Live walkthrough + hands-on practice
Trainer: Founder (Month 1-6), Ops & Data Lead (Month 6+)
Curriculum: 1. System Overview (30 min): - Database architecture explanation - Inventory vs. inventory_movements tables - Real-time vs. historical data
- Operations Portal Demo (45 min):
- Navigate to Inventory tab
- Read current inventory display
- Execute sample adjustment (test batch)
- Review adjustment logs
-
Understand error messages
-
Process Walkthrough (30 min):
- Damaged goods scenario
- Miscount correction scenario
- Physical count reconciliation
-
When to escalate
-
Hands-On Practice (15 min):
- Execute 3 practice adjustments on test batch
- Verify logs update correctly
- Attempt negative inventory (should fail)
- Confirm understanding via Q&A
Assessment: Trainee must successfully execute 3 adjustments independently
Documentation: Training checklist signed and stored in Google Drive
10.2 Quick Reference Guide¶
1-Page Cheat Sheet for pack day team:
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
INVENTORY ADJUSTMENT QUICK GUIDE
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
📱 WHEN TO ADJUST:
✓ Pouches damaged/torn → Remove from inventory
✓ Physical count differs → Correct system
✓ Found unrecorded stock → Add to inventory
🚫 WHEN NOT TO ADJUST:
✗ Regular pack day usage → Automatic via allocation
✗ Order dispatch → Recorded in Fulfillment tab
✗ Batch receipt → Handled by SOP-01
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
STEP-BY-STEP PROCESS
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
1. Go to: ops.protocolraw.co.uk
2. Click: 📦 Inventory tab
3. Click: ðŸâ€„ Refresh Inventory
4. Select batch from dropdown
5. Enter amount:
• Negative to remove: -12
• Positive to add: +8
6. Enter reason (required!)
7. Confirm your name
8. Click: ✅ Submit Adjustment
9. Verify green success message
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
COMMON ADJUSTMENTS
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
DAMAGED GOODS:
Amount: -12 kg (e.g., 3 pouches × 4kg each)
Reason: "3 pouches damaged - torn during handling"
MISCOUNT:
Amount: +8 kg (found extra stock)
Reason: "Physical count found 8kg unrecorded in freezer"
RETURN:
Amount: +4 kg (customer returned 1 pouch)
Reason: "Customer return - unopened pouch"
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
ERRORS & WHAT THEY MEAN
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
âÂŒ "Negative inventory"
→ Adjustment too large. Check physical stock first.
âÂŒ "Batch not found"
→ Wrong batch code. Verify from inventory list.
âÂŒ "Reason required"
→ Must explain why adjusting. Be specific!
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
NEED HELP?
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
Slack: #ops-support
Email: ops@protocolraw.co.uk
Phone: [Operations Lead number]
ââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Âââ€Â
Distribution: Printed and laminated at pack day station + PDF in Google Drive
11. CONTINUOUS IMPROVEMENT¶
11.1 Monthly Process Review¶
Forum: Operations Review meeting (last Friday of month)
Review Questions: 1. Did we meet all KPI targets this month? 2. What adjustment patterns emerged? 3. Are there process gaps causing recurring adjustments? 4. Can any manual steps be automated? 5. Is the Operations Portal intuitive for new users?
Output: Action items logged in project management tool (Linear/Asana)
11.2 Automation Opportunities¶
Current State (Month 1-6): Manual adjustments via Operations Portal
Future Enhancements: 1. Photo Upload (Month 12+): - Attach photo evidence of damaged goods to adjustment - Stored in Supabase Storage, linked to movement_id - Improves audit trail quality
- Weight Scale Integration (Month 18+):
- Bluetooth scale at pack station
- Auto-calculate adjustment vs. expected weight
-
Reduce manual entry errors
-
AI Damage Detection (Month 24+):
- Camera system identifies torn pouches
- Auto-generates adjustment suggestion
-
Operator confirms with one click
-
Predictive Restocking (Month 18+):
- ML model predicts stockout date
- Auto-generates purchase order to co-packer
- Maintains optimal 10-14 day coverage
Prioritization: ROI analysis (time saved × labor cost) vs. implementation effort
11.3 Feedback Collection¶
Operator Feedback Form: Google Form embedded in Operations Portal
Questions: - How often do you use manual adjustments? (daily/weekly/monthly) - What's the most time-consuming part of the process? - Have you encountered any confusing error messages? - What additional features would help your workflow?
Review: Monthly, incorporated into process review agenda
12. APPENDICES¶
Appendix A: Glossary¶
| Term | Definition |
|---|---|
| Available Inventory | Stock cleared for allocation to customer orders (qty_available) |
| Allocated Inventory | Stock reserved for confirmed orders but not yet dispatched (qty_allocated) |
| QA Hold | Inventory pending lab test results, cannot be allocated (qty_on_hold) |
| FEFO | First Expired, First Out - allocation method prioritizing oldest batches |
| Movement Type | Category of inventory change: RECEIPT / ALLOCATION / ADJUSTMENT / DISPOSAL |
| Adjustment | Manual correction to inventory for physical discrepancies |
| Reconciliation | Process of comparing system inventory to physical stock |
| Audit Trail | Complete historical record of all inventory movements |
| Negative Inventory | Impossible state where qty_available < 0 (prevented by system) |
| Stock Days Coverage | Number of days until stockout at current usage rate |
Appendix B: Error Code Reference¶
| Code | Message | Cause | Resolution |
|---|---|---|---|
| INV-001 | Batch not found | Invalid batch_code | Verify batch exists in system |
| INV-002 | Negative inventory prevented | Adjustment too large | Reduce adjustment amount or investigate missing stock |
| INV-003 | Inventory record missing | Database integrity issue | Contact system administrator |
| INV-004 | Adjustment validation failed | Missing required field | Ensure reason and operator name provided |
| INV-005 | Concurrent update detected | Multiple users editing same batch | Retry adjustment after refresh |
Appendix C: Compliance Checklist¶
Quarterly Audit Preparation:
- [ ] Physical inventory count completed within last 90 days
- [ ] Count variance <2% for all batches
- [ ] All adjustments >10kg have documented reason
- [ ] No gaps in inventory_movements history
- [ ] Audit trail query runs successfully for all batches
- [ ] Backup files accessible in Google Drive archives
- [ ] KPI report generated for review period
- [ ] Escalated incidents have documented resolution
- [ ] Operations Portal access logs reviewed (who accessed when)
- [ ] Database performance metrics within targets
Sign-off: Operations Lead + Founder (Month 1-6) / Ops & Data Lead (Month 6+)
Appendix D: SQL Maintenance Queries¶
Identify Aged Inventory:
SELECT
b.batch_code,
b.production_date,
b.expiry_date,
i.qty_available,
CURRENT_DATE - b.production_date::date AS age_days
FROM raw_ops.inventory i
JOIN raw_ops.batches b ON i.batch_id = b.batch_id
WHERE i.qty_available > 0
AND CURRENT_DATE - b.production_date::date > 21
ORDER BY age_days DESC;
Adjustment Volume by Reason (Last 30 Days):
SELECT
CASE
WHEN notes ILIKE '%damage%' THEN 'Damaged Goods'
WHEN notes ILIKE '%miscount%' THEN 'Miscount'
WHEN notes ILIKE '%return%' THEN 'Customer Return'
WHEN notes ILIKE '%found%' THEN 'Found Stock'
ELSE 'Other'
END AS reason_category,
COUNT(*) AS adjustment_count,
SUM(ABS(qty_change)) AS total_kg_adjusted
FROM raw_ops.inventory_movements
WHERE movement_type = 'ADJUSTMENT'
AND created_at > CURRENT_DATE - INTERVAL '30 days'
GROUP BY reason_category
ORDER BY total_kg_adjusted DESC;
Inventory Turnover Rate:
SELECT
b.batch_code,
b.production_date,
MIN(im.created_at)::date AS first_allocation_date,
(MIN(im.created_at)::date - b.production_date::date) AS days_to_first_use
FROM raw_ops.batches b
JOIN raw_ops.inventory i ON b.batch_id = i.batch_id
JOIN raw_ops.inventory_movements im ON i.inventory_id = im.inventory_id
WHERE im.movement_type = 'ALLOCATION'
AND b.production_date > CURRENT_DATE - INTERVAL '90 days'
GROUP BY b.batch_id, b.batch_code, b.production_date
ORDER BY days_to_first_use DESC;
DOCUMENT CONTROL¶
Version History:
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 DRAFT | 2024-11-10 | Anton | Initial draft based on operations requirements |
| 0.9 REVIEW | 2024-11-12 | Anton | Incorporated Operations Portal v3.0 integration |
| 1.0 FINAL | 2024-11-13 | Anton | Production-ready, all testing complete |
| 1.1 FINAL | 2026-01-17 | Anton | Stock level alerting migrated to SOP-MON-01 native monitoring |
Approval:
| Role | Name | Signature | Date |
|---|---|---|---|
| Founder | Anton | _____ | 2026-01-17 |
| Operations Lead | TBD | _____ | [Upon hire] |
Next Review Date: 2026-04-17 (Quarterly)
Document Location:
- Master: Google Drive > Operations > SOPs > [SOP-INV-01](SOP-INV-01_Inventory_Control_v1_1_FINAL.md)_v1_1_FINAL.md
- Public: ops.protocolraw.co.uk/docs/[sop-inv-01](SOP-INV-01_Inventory_Control_v1_1_FINAL.md)
- Monitoring: See SOP-MON-01 for alerting architecture
END OF DOCUMENT
Protocol Raw - Verified safe, batch by batch.