Skip to content

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

  1. Real-time accuracy - inventory reflects physical stock within 15 minutes
  2. Complete traceability - every adjustment logged with reason and operator
  3. Zero negative inventory - system prevents adjustments resulting in negative stock
  4. 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

  1. Navigate to https://ops.protocolraw.co.uk
  2. Click 📦 Inventory tab
  3. Click ðŸâ€„ Refresh Inventory to load current stock levels

Step 2: Identify Batch

  1. Review current inventory table
  2. Identify batch requiring adjustment
  3. Note current kg_available value

Step 3: Execute Adjustment

  1. Click Manual Inventory Adjustment section
  2. Select batch from dropdown:
  3. Format: PR-YYMMDD-XXX (SKU) - XX.X kg available
  4. View batch status summary:
  5. Batch code
  6. SKU
  7. Location
  8. Current stock level
  9. Batch status (AVAILABLE / QA_HOLD)

Step 4: Enter Adjustment Details

  1. Adjustment Amount (kg):
  2. Negative for removal: -12 (remove 12kg damaged goods)
  3. Positive for addition: +8 (add 8kg found stock)
  4. System prevents adjustments resulting in negative inventory

  5. Reason for Adjustment:

  6. Required field - must explain why adjustment is needed
  7. 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"
  8. Your Name:

  9. Pre-filled with operator name (default: Anton)
  10. Change if adjustment performed by different team member

Step 5: Confirm & Submit

  1. Review adjustment summary:
  2. Batch: PR-241201-001
  3. Current: 48.0 kg
  4. Adjustment: -12.0 kg
  5. New: 36.0 kg
  6. Reason: "3 pouches damaged during pack day"

  7. Click ✅ Submit Adjustment

  8. System validates:
  9. Batch exists and is available
  10. New quantity >= 0
  11. Reason provided
  12. Operator name provided

  13. On success:

  14. Green confirmation message appears
  15. Shows before/after quantities
  16. Confirms logging to inventory_movements and ops_events
  17. Auto-refreshes inventory after 2 seconds

Step 6: Verify in Logs

  1. Scroll to 📋 Recent Adjustments section
  2. Click ðŸâ€„ Refresh Log
  3. Verify adjustment appears with:
  4. Timestamp
  5. SKU
  6. Change amount (red for negative, green for positive)
  7. Before/after quantities
  8. 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)

  1. Execute count:
  2. Print current inventory report from Operations Portal
  3. Physically weigh/count all batches in warehouse
  4. Record findings on count sheet with batch codes

  5. Reconcile discrepancies:

  6. Compare physical count vs. system inventory
  7. Investigate variances >1kg per batch
  8. Execute manual adjustments for confirmed discrepancies

  9. Document results:

  10. Count sheet scanned and uploaded to Google Drive
  11. Summary report in ops_events with variance analysis
  12. 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)

  1. Stock by Batch Status
  2. AVAILABLE vs. QA_HOLD split
  3. Aging analysis (days since production)
  4. Alert if >20% in QA_HOLD >7 days

  5. Daily Adjustment Volume

  6. Total kg adjusted (positive and negative)
  7. Adjustment count by reason category
  8. Alert if >50kg adjusted in single day

  9. Inventory Turnover

  10. FEFO adherence (oldest batch used first)
  11. Average age at allocation
  12. 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

  1. Operator Action:
  2. DO NOT OVERRIDE - system is enforcing data integrity
  3. Verify physical inventory with spot-check
  4. If physical stock is truly lower, first investigate:

    • Missing allocation? (order packed but not recorded)
    • Theft or misplacement?
    • Prior adjustment error?
  5. Resolution:

  6. Correct root cause (e.g., record missing dispatch)
  7. Re-attempt adjustment with corrected amount
  8. 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?

  1. Common Causes:
  2. Transport damage: Co-packer delivery issue → escalate to supplier
  3. Expiry disposal: Large batch past expiry → review rotation procedures
  4. Miscount correction: Prior receipt error → update receiving SOP

  5. Documentation:

  6. Photo evidence of damaged goods (if applicable)
  7. Email to co-packer (if supplier issue)
  8. 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

  1. 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 |

  2. Implementation:

  3. Update relevant SOP with process improvement
  4. Communicate change to team via Slack
  5. 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)

  1. Adjustment Analysis:
  2. Top 3 adjustment reasons (by volume)
  3. Adjustment trend (vs. prior month)
  4. Pattern identification (systematic issues)

  5. Compliance Status:

  6. FEFO adherence (%)
  7. Batch age distribution
  8. QA_HOLD duration (avg days)

  9. Action Items:

  10. Process improvements implemented
  11. Outstanding issues requiring escalation
  12. 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

  1. Operations Portal Demo (45 min):
  2. Navigate to Inventory tab
  3. Read current inventory display
  4. Execute sample adjustment (test batch)
  5. Review adjustment logs
  6. Understand error messages

  7. Process Walkthrough (30 min):

  8. Damaged goods scenario
  9. Miscount correction scenario
  10. Physical count reconciliation
  11. When to escalate

  12. Hands-On Practice (15 min):

  13. Execute 3 practice adjustments on test batch
  14. Verify logs update correctly
  15. Attempt negative inventory (should fail)
  16. 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

  1. Weight Scale Integration (Month 18+):
  2. Bluetooth scale at pack station
  3. Auto-calculate adjustment vs. expected weight
  4. Reduce manual entry errors

  5. AI Damage Detection (Month 24+):

  6. Camera system identifies torn pouches
  7. Auto-generates adjustment suggestion
  8. Operator confirms with one click

  9. Predictive Restocking (Month 18+):

  10. ML model predicts stockout date
  11. Auto-generates purchase order to co-packer
  12. 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.