Feature Name: AI-Powered Supplier & Vendor Management System
Category: Procurement Intelligence (Phase 2)
Priority: High
Target Users: CFOs, Procurement Managers, Controllers, Category Managers
Dependencies: Procurement & Spend Control, Document & Contract Management V1, AI Invoice Processing
Transform supplier relationships from reactive invoice processing to proactive strategic management. By combining invoice data, contract intelligence, and AI analysis, Orcha provides a complete 360° view of every supplier relationship—enabling procurement teams to optimize costs, reduce risks, and leverage buying power.
The Breakthrough:
Value Proposition:
"Know everything about every supplier: what you're paying, what you should be paying, when to renegotiate, and how much you can save. All powered by AI that reads your contracts and analyzes your spend patterns."
What Companies Do Today:
Contract Chaos
Missed Savings Opportunities
Reactive Renewal Management
Limited Supplier Intelligence
Pain Points:
A comprehensive supplier intelligence platform that:
Centralized Supplier Profile:
┌────────────────────────────────────────────────────────────────┐
│ SUPPLIER: Acme Office Supplies GmbH │
├────────────────────────────────────────────────────────────────┤
│ │
│ 📊 OVERVIEW │
│ ├─ Annual Spend: €245,000 (Rank: #12 of 215 suppliers) │
│ ├─ Active Contracts: 2 (MSA + SaaS Subscription) │
│ ├─ Invoices YTD: 147 │
│ ├─ Payment Terms: Net 30 │
│ ├─ Primary Contact: Johann Schmidt (johann@acme.de) │
│ └─ Relationship Start: January 2019 (5 years) │
│ │
│ 🎯 PERFORMANCE SCORE: 87/100 │
│ ├─ On-Time Delivery: 94% (Target: 95%) │
│ ├─ Invoice Accuracy: 96% (Target: 98%) │
│ ├─ Contract Compliance: 92% (Target: 95%) │
│ ├─ Response Time: 4.2 hours avg (Target: 8 hours) │
│ └─ Trend: ↑ +3 points vs last quarter │
│ │
│ ⚠️ ALERTS (3) │
│ • Contract renewal in 67 days (Action required) │
│ • €12,450 from next volume discount tier │
│ • 3 invoices exceeded contract pricing this month │
│ │
│ 💰 SAVINGS OPPORTUNITIES (€18,500 identified) │
│ ├─ Volume consolidation: €8,500/year │
│ ├─ Early payment discount: €6,000/year │
│ ├─ Contract renegotiation: €4,000/year │
│ └─ Alternative sourcing: Evaluate 2 competitors │
│ │
│ [View Contracts] [View Invoices] [Contact Manager] │
│ │
└────────────────────────────────────────────────────────────────┘
Data Model:
CREATE TABLE suppliers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
-- Basic Info
name TEXT NOT NULL,
legal_name TEXT,
tax_id TEXT, -- VAT number / Tax ID
duns_number TEXT, -- D&B identifier
-- Contact
primary_contact_name TEXT,
primary_contact_email TEXT,
primary_contact_phone TEXT,
website TEXT,
-- Address
street_address TEXT,
city TEXT,
postal_code TEXT,
country TEXT DEFAULT 'DE',
-- Classification
category TEXT[], -- ['IT Services', 'Cloud Infrastructure']
tags TEXT[], -- Custom tags
tier TEXT, -- 'Strategic', 'Preferred', 'Approved', 'Restricted'
-- Financial
payment_terms TEXT, -- 'Net 30', 'Net 45', '2/10 Net 30'
currency TEXT DEFAULT 'EUR',
credit_limit DECIMAL(15,2),
-- Relationship
relationship_start_date DATE,
relationship_manager_id UUID REFERENCES users(id),
status TEXT DEFAULT 'active', -- 'active', 'inactive', 'blocked'
-- Performance (cached aggregates)
performance_score DECIMAL(5,2), -- 0-100
annual_spend_current DECIMAL(15,2),
annual_spend_prior DECIMAL(15,2),
invoice_count_ytd INTEGER,
-- Risk
risk_score DECIMAL(5,2), -- 0-100 (higher = riskier)
risk_factors JSONB, -- {'single_source': true, 'financial_risk': false}
-- Metadata
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
created_by UUID REFERENCES users(id),
updated_by UUID REFERENCES users(id)
);
CREATE INDEX idx_suppliers_tenant ON suppliers(tenant_id);
CREATE INDEX idx_suppliers_category ON suppliers USING GIN(category);
CREATE INDEX idx_suppliers_status ON suppliers(status);
CREATE INDEX idx_suppliers_tier ON suppliers(tier);
Automatically Extract Critical Clauses:
Upload & OCR:
(defn ingest-contract
"Process uploaded contract PDF"
[supplier-id file-data]
(let [;; 1. OCR + AI extraction
extracted-text (google-document-ai/extract-text file-data)
;; 2. Claude AI analyzes contract
contract-analysis (claude/analyze-contract
{:text extracted-text
:supplier-name (get-supplier-name supplier-id)
:extract-clauses [
:pricing
:volume-discounts
:payment-terms
:renewal-terms
:termination-clauses
:slas
:price-adjustment-clauses
:delivery-terms
:warranties
:liability-caps]})
;; 3. Store structured contract
contract-id (db/insert :contracts
{:supplier-id supplier-id
:file-url (upload-to-s3 file-data)
:contract-type (:type contract-analysis)
:effective-date (:effective-date contract-analysis)
:expiry-date (:expiry-date contract-analysis)
:status "active"})
;; 4. Store extracted clauses
(doseq [clause (:clauses contract-analysis)]
(db/insert :contract-clauses
{:contract-id contract-id
:clause-type (:type clause)
:clause-text (:text clause)
:extracted-values (:values clause)
:confidence-score (:confidence clause)}))]
{:contract-id contract-id
:clauses-extracted (count (:clauses contract-analysis))
:analysis contract-analysis}))
AI Prompt for Clause Extraction:
You are a procurement analyst extracting critical commercial terms from a supplier contract.
CONTRACT TEXT:
{{CONTRACT_TEXT}}
SUPPLIER: {{SUPPLIER_NAME}}
Extract the following clause types and provide structured data:
1. PRICING CLAUSES
- Base pricing (unit prices, rates)
- Volume discount tiers (thresholds and discount percentages)
- Early payment discounts (e.g., "2% if paid within 10 days")
- Price adjustment mechanisms (CPI indexation, annual increases)
- Most Favored Customer clauses
2. VOLUME DISCOUNTS
- Tier 1: Spend threshold + discount %
- Tier 2: Spend threshold + discount %
- Tier 3: Spend threshold + discount %
- Calculation period (monthly, quarterly, annually)
- Retroactive vs progressive discounts
3. RENEWAL TERMS
- Initial term length
- Renewal term length
- Auto-renewal clause (yes/no)
- Notice period for cancellation (days before expiry)
- Price increase caps on renewal
4. TERMINATION CLAUSES
- Termination for convenience (notice period)
- Termination for cause (conditions)
- Early termination fees
- Wind-down obligations
5. PAYMENT TERMS
- Standard payment terms (Net X days)
- Early payment discount options
- Late payment penalties
- Payment method requirements
6. SERVICE LEVEL AGREEMENTS (SLAs)
- Delivery time commitments
- Quality standards
- Response time requirements
- Penalties for non-performance
7. PRICE PROTECTION
- Price freeze periods
- Maximum annual increase (% cap)
- Price decrease triggers (e.g., raw material costs)
8. MINIMUM COMMITMENTS
- Minimum order quantities
- Minimum annual spend
- Consequences of not meeting minimums
Output as JSON with exact values extracted:
{
"pricing": {...},
"volume_discounts": [...],
"renewal_terms": {...},
"termination": {...},
"payment_terms": {...},
"slas": [...],
"price_protection": {...},
"minimum_commitments": {...}
}
Data Model:
CREATE TABLE contracts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
supplier_id UUID NOT NULL REFERENCES suppliers(id),
-- Document
contract_number TEXT UNIQUE,
contract_name TEXT NOT NULL,
contract_type TEXT NOT NULL, -- 'MSA', 'SaaS', 'Framework', 'Spot'
file_url TEXT NOT NULL, -- S3 URL to PDF
-- Dates
effective_date DATE NOT NULL,
expiry_date DATE,
renewal_date DATE,
notice_period_days INTEGER, -- Days before expiry to give notice
auto_renews BOOLEAN DEFAULT FALSE,
-- Financial
total_contract_value DECIMAL(15,2),
annual_value DECIMAL(15,2),
currency TEXT DEFAULT 'EUR',
-- Status
status TEXT DEFAULT 'active', -- 'draft', 'active', 'expired', 'terminated'
-- Tracking
next_review_date DATE,
renewal_alert_sent BOOLEAN DEFAULT FALSE,
-- Metadata
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
created_by UUID REFERENCES users(id)
);
CREATE TABLE contract_clauses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
contract_id UUID NOT NULL REFERENCES contracts(id),
-- Clause Type
clause_type TEXT NOT NULL, -- 'pricing', 'volume_discount', 'renewal', etc.
clause_category TEXT, -- 'commercial', 'legal', 'operational'
-- Content
clause_text TEXT NOT NULL, -- Original contract text
clause_summary TEXT, -- AI-generated summary
-- Extracted Structured Data
extracted_values JSONB NOT NULL,
/*
Example for volume_discount:
{
"tiers": [
{"threshold": 50000, "discount_pct": 5, "currency": "EUR"},
{"threshold": 100000, "discount_pct": 10, "currency": "EUR"},
{"threshold": 250000, "discount_pct": 15, "currency": "EUR"}
],
"period": "annual",
"type": "progressive"
}
*/
-- Importance & Confidence
importance_score DECIMAL(3,2), -- 0-1 (AI-scored criticality)
confidence_score DECIMAL(3,2), -- 0-1 (extraction confidence)
-- Monitoring
is_monitored BOOLEAN DEFAULT TRUE,
compliance_status TEXT, -- 'compliant', 'non-compliant', 'at-risk'
-- Metadata
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_clauses_contract ON contract_clauses(contract_id);
CREATE INDEX idx_clauses_type ON contract_clauses(clause_type);
CREATE INDEX idx_clauses_monitored ON contract_clauses(is_monitored) WHERE is_monitored = TRUE;
Monitor All Discount Types:
Real-Time Spend Aggregation:
(defn check-volume-discount-proximity
"Check how close supplier spend is to next discount tier"
[supplier-id]
(let [;; Get active volume discount clauses
discount-clauses (db/query
["SELECT * FROM contract_clauses
WHERE contract_id IN (
SELECT id FROM contracts
WHERE supplier_id = ?
AND status = 'active'
)
AND clause_type = 'volume_discount'"
supplier-id])
;; Calculate current spend (by period defined in contract)
current-spend (calculate-current-spend supplier-id)
;; For each tier, calculate proximity
proximity-alerts (for [clause discount-clauses
:let [tiers (get-in clause [:extracted-values :tiers])
period (get-in clause [:extracted-values :period])]]
(calculate-tier-proximity current-spend tiers period))]
(filter :alert proximity-alerts)))
(defn calculate-tier-proximity
[current-spend tiers period]
(let [;; Find next tier above current spend
next-tier (first (filter #(> (:threshold %) current-spend)
(sort-by :threshold tiers)))
;; Calculate distance
distance (when next-tier
(- (:threshold next-tier) current-spend))
;; Calculate % to next tier
progress-pct (when next-tier
(* 100 (/ current-spend (:threshold next-tier))))]
(when (and next-tier (< distance (* (:threshold next-tier) 0.15))) ;; Within 15%
{:alert true
:type :volume-discount-proximity
:current-spend current-spend
:next-threshold (:threshold next-tier)
:distance distance
:progress-pct progress-pct
:potential-discount-pct (:discount-pct next-tier)
:potential-savings (* current-spend (/ (:discount-pct next-tier) 100))
:message (format "You're €%,.0f from %d%% volume discount (%.1f%% there)"
distance
(:discount-pct next-tier)
progress-pct)})))
Alert Examples:
┌────────────────────────────────────────────────────────────────┐
│ 🎯 VOLUME DISCOUNT PROXIMITY ALERTS │
├────────────────────────────────────────────────────────────────┤
│ │
│ Acme Office Supplies │
│ Current Spend: €87,500 (Jan 1 - Dec 31, 2026) │
│ │
│ Next Tier: €100,000 (10% discount) │
│ Distance: €12,500 (87.5% there) │
│ Potential Savings: €10,000/year │
│ │
│ 💡 RECOMMENDATION: │
│ Consolidate orders to reach tier. Current pace: 3.2 months │
│ to threshold. Consider accelerating €12,500 in planned │
│ purchases to capture discount. │
│ │
│ [View Spend Breakdown] [Simulate Consolidation] │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ IT Services GmbH │
│ Current Spend: €232,000 (Q1-Q3 2026) │
│ │
│ Next Tier: €250,000 (15% discount) │
│ Distance: €18,000 (92.8% there) │
│ Potential Savings: €37,500/year │
│ │
│ ⚠️ URGENT: Only 2 months left in annual period! │
│ │
│ 💡 RECOMMENDATION: │
│ You'll likely cross threshold naturally. Lock in pricing │
│ now by pre-committing to €250K annual spend. │
│ │
│ [Negotiate Pre-Commitment] [View Forecast] │
│ │
└────────────────────────────────────────────────────────────────┘
Identify Uncaptured Discounts:
-- Query for available early payment discounts
SELECT
s.name as supplier_name,
COUNT(*) as eligible_invoices,
SUM(i.amount) as total_eligible_amount,
cc.extracted_values->>'discount_pct' as discount_pct,
cc.extracted_values->>'payment_days' as early_payment_days,
(SUM(i.amount) * (cc.extracted_values->>'discount_pct')::numeric / 100) as potential_savings,
AVG(i.payment_date - i.invoice_date) as avg_days_to_pay
FROM invoices i
JOIN suppliers s ON i.supplier_id = s.id
JOIN contracts c ON c.supplier_id = s.id AND c.status = 'active'
JOIN contract_clauses cc ON cc.contract_id = c.id
WHERE cc.clause_type = 'early_payment_discount'
AND i.status = 'approved'
AND i.payment_date IS NULL
AND (CURRENT_DATE - i.invoice_date) < (cc.extracted_values->>'payment_days')::integer
GROUP BY s.id, s.name, cc.extracted_values
ORDER BY potential_savings DESC;
Dashboard Widget:
┌────────────────────────────────────────────────────────────────┐
│ 💰 UNCAPTURED EARLY PAYMENT DISCOUNTS │
├────────────────────────────────────────────────────────────────┤
│ │
│ This Month: €8,450 available (23 invoices) │
│ YTD Captured: €34,200 (42% of available) │
│ YTD Missed: €47,300 (58% missed) │
│ │
│ TOP OPPORTUNITIES: │
│ │
│ 1. Office Supplies Co - €45,000 eligible │
│ • 2% discount for payment within 10 days │
│ • Potential: €900/invoice │
│ • Current avg payment: 28 days (too slow) │
│ [Enable Auto Early Payment] │
│ │
│ 2. IT Hardware GmbH - €32,000 eligible │
│ • 1.5% discount for payment within 7 days │
│ • Potential: €480/invoice │
│ • Current avg payment: 35 days │
│ [Review Cash Flow] │
│ │
└────────────────────────────────────────────────────────────────┘
Track Limited-Time Offers:
CREATE TABLE promotional_discounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
supplier_id UUID NOT NULL REFERENCES suppliers(id),
-- Offer Details
promotion_name TEXT NOT NULL,
discount_type TEXT NOT NULL, -- 'percentage', 'fixed_amount', 'tiered'
discount_value DECIMAL(10,2) NOT NULL,
-- Validity
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
-- Conditions
minimum_order_value DECIMAL(15,2),
applicable_categories TEXT[],
applicable_products TEXT[],
max_usage_count INTEGER,
current_usage_count INTEGER DEFAULT 0,
-- Tracking
estimated_savings DECIMAL(15,2),
actual_savings DECIMAL(15,2) DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
Predictive Renewal Intelligence:
90-Day Advance Warning System:
(defn generate-renewal-alerts
"Generate renewal alerts for contracts expiring in next 90 days"
[]
(let [contracts-expiring (db/query
["SELECT c.*, s.name as supplier_name,
c.expiry_date,
c.notice_period_days,
c.auto_renews,
(c.expiry_date - c.notice_period_days) as notice_deadline,
CURRENT_DATE as today
FROM contracts c
JOIN suppliers s ON c.supplier_id = s.id
WHERE c.status = 'active'
AND c.expiry_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '90 days'
AND c.renewal_alert_sent = FALSE
ORDER BY c.expiry_date"])]
(for [contract contracts-expiring
:let [days-to-expiry (days-between (:today contract) (:expiry-date contract))
days-to-notice (days-between (:today contract) (:notice-deadline contract))
urgency (calculate-urgency days-to-expiry days-to-notice)
spend-history (get-supplier-spend-history (:supplier-id contract))
performance (get-supplier-performance (:supplier-id contract))
market-intel (get-market-intelligence (:supplier-id contract))]]
{:contract-id (:id contract)
:supplier-name (:supplier-name contract)
:expiry-date (:expiry-date contract)
:days-to-expiry days-to-expiry
:days-to-notice days-to-notice
:urgency urgency
:auto-renews (:auto-renews contract)
:annual-spend (:annual-value contract)
:recommendation (generate-renewal-recommendation
contract
spend-history
performance
market-intel)})))
(defn generate-renewal-recommendation
[contract spend-history performance market-intel]
(let [spend-trend (:trend spend-history) ;; :increasing, :stable, :decreasing
perf-score (:score performance) ;; 0-100
market-rate (:competitive-rate market-intel) ;; comparison to market]
(cond
;; High value, good performance → Renew but negotiate better terms
(and (> (:annual-value contract) 100000)
(> perf-score 85))
{:action "renew-and-negotiate"
:priority "high"
:message "High-value supplier with strong performance. Leverage volume for better terms."
:negotiation-points ["Request 5-10% price reduction"
"Add volume discount tiers"
"Extend payment terms to Net 45"
"Lock pricing for 2 years"]}
;; Low performance → Consider alternatives
(< perf-score 70)
{:action "evaluate-alternatives"
:priority "medium"
:message "Performance below threshold. Evaluate competitive options."
:negotiation-points ["Address performance issues"
"Add SLA penalties"
"Shorten initial renewal term (6-12 months)"
"RFP to 2-3 alternatives"]}
;; Decreasing spend → Renegotiate or exit
(= spend-trend :decreasing)
{:action "renegotiate-or-exit"
:priority "low"
:message "Spend declining. Renegotiate for lower commitment or exit."
:negotiation-points ["Remove minimum commitment"
"Switch to pay-per-use"
"Negotiate exit without penalty"]}
;; Auto-renewal approaching notice deadline → Urgent decision
(and (:auto-renews contract)
(< (days-between (now) (:notice-deadline contract)) 14))
{:action "urgent-decision-required"
:priority "critical"
:message "Auto-renewal in 14 days! Decide: Renew, Renegotiate, or Cancel."
:negotiation-points ["Send cancellation notice immediately if not renewing"
"Request 30-day extension to evaluate"
"Negotiate better terms before renewal"]}
:else
{:action "standard-renewal"
:priority "medium"
:message "Standard renewal process. Review terms and confirm."
:negotiation-points ["CPI-linked price increases only"
"Maintain current terms"
"Add early exit clause"]})))
┌────────────────────────────────────────────────────────────────┐
│ 📅 CONTRACT RENEWAL DASHBOARD │
├────────────────────────────────────────────────────────────────┤
│ │
│ URGENT (Next 30 Days) - 4 Contracts │
│ │
│ 🔴 Microsoft 365 Business - Expires in 12 days │
│ Annual Value: €45,000 | Auto-Renews: YES │
│ Notice Deadline: PASSED (8 days ago) │
│ ⚠️ CRITICAL: Will auto-renew unless canceled by Jan 17 │
│ Performance: 92/100 | Spend Trend: ↑ +8% │
│ │
│ 💡 RECOMMENDATION: Renew but negotiate │
│ • Usage up 15% - justify volume discount │
│ • Lock 2-year pricing (avoid annual increases) │
│ • Add Microsoft Teams Phone (better bundle rate) │
│ │
│ [Cancel Renewal] [Negotiate Terms] [Auto-Renew] │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ 🟡 Office Supplies Framework - Expires in 28 days │
│ Annual Value: €85,000 | Auto-Renews: NO │
│ Notice Deadline: N/A │
│ Performance: 78/100 | Spend Trend: → Stable │
│ │
│ 💡 RECOMMENDATION: Evaluate alternatives │
│ • Performance below target (late deliveries: 12%) │
│ • Prices 8% above market average │
│ • RFP to 3 competitors (draft ready) │
│ │
│ [Send RFP] [Renegotiate] [Extend 90 Days] │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ UPCOMING (31-60 Days) - 7 Contracts │
│ FUTURE (61-90 Days) - 12 Contracts │
│ │
│ [View All] [Export Calendar] [Set Reminders] │
│ │
└────────────────────────────────────────────────────────────────┘
AI-Powered Negotiation Insights:
(defn generate-negotiation-intel
"Generate data-backed negotiation talking points"
[contract-id]
(let [contract (db/get-contract contract-id)
supplier-id (:supplier-id contract)
;; Historical spend analysis
spend-history (db/query
["SELECT
DATE_TRUNC('year', invoice_date) as year,
SUM(amount) as total_spend,
COUNT(*) as invoice_count,
AVG(amount) as avg_invoice_amount
FROM invoices
WHERE supplier_id = ?
GROUP BY DATE_TRUNC('year', invoice_date)
ORDER BY year DESC
LIMIT 3"
supplier-id])
;; Performance metrics
performance (calculate-supplier-performance supplier-id)
;; Market intelligence
market-data (get-market-benchmarks (:category contract))
;; Generate talking points
talking-points (generate-talking-points
contract
spend-history
performance
market-data)]
{:contract contract
:spend-history spend-history
:performance performance
:market-data market-data
:talking-points talking-points
:suggested-terms (generate-suggested-terms contract spend-history)}))
(defn generate-talking-points
[contract spend-history performance market-data]
(let [current-spend (-> spend-history first :total-spend)
prior-spend (-> spend-history second :total-spend)
spend-growth (/ (- current-spend prior-spend) prior-spend)]
[{:category "Volume Leverage"
:strength (if (> spend-growth 0.1) :high :medium)
:message (format "Our spend increased %.1f%% to €%,.0f this year. We expect continued growth and should receive volume pricing."
(* spend-growth 100)
current-spend)
:ask "Request 5-10% volume discount for spend > €100K"}
{:category "Performance Issues"
:strength (if (< (:on-time-pct performance) 0.95) :high :low)
:message (format "On-time delivery at %.1f%% (target: 95%%). This impacts our operations."
(* 100 (:on-time-pct performance)))
:ask "Add SLA penalties: 2% credit for each late delivery"}
{:category "Market Benchmarking"
:strength (if (> (:price-vs-market market-data) 1.05) :high :low)
:message (format "Your pricing is %.1f%% above market average for similar services."
(* 100 (- (:price-vs-market market-data) 1)))
:ask "Match market rates or justify premium with value-adds"}
{:category "Payment Terms"
:strength :medium
:message "We have strong payment history (98% on-time). Our cash position supports early payment."
:ask "Offer 2% discount for payment within 10 days"}
{:category "Contract Length"
:strength :medium
:message "Willing to commit to 2-year term for price certainty."
:ask "Lock pricing for 24 months (no annual increases)"}]))
Comprehensive Performance Metrics:
CREATE TABLE supplier_performance_metrics (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
supplier_id UUID NOT NULL REFERENCES suppliers(id),
measurement_period TEXT NOT NULL, -- '2026-Q1', '2026-01', etc.
-- Delivery Performance
on_time_delivery_pct DECIMAL(5,2), -- % of orders delivered on-time
early_delivery_pct DECIMAL(5,2),
late_delivery_pct DECIMAL(5,2),
avg_delivery_days DECIMAL(8,2),
-- Quality Metrics
invoice_accuracy_pct DECIMAL(5,2), -- % of invoices without errors
dispute_rate DECIMAL(5,2), -- Disputes per 100 invoices
rejection_rate DECIMAL(5,2), -- % of deliveries rejected
-- Compliance
contract_compliance_pct DECIMAL(5,2), -- % invoices compliant with contract
price_compliance_pct DECIMAL(5,2), -- % invoices at contract pricing
terms_compliance_pct DECIMAL(5,2), -- % invoices with correct payment terms
-- Responsiveness
avg_response_time_hours DECIMAL(8,2), -- Response to inquiries
avg_resolution_time_hours DECIMAL(8,2), -- Resolution of issues
-- Financial
total_spend DECIMAL(15,2),
invoice_count INTEGER,
avg_invoice_amount DECIMAL(15,2),
early_payment_discounts_captured DECIMAL(15,2),
-- Composite Score
overall_score DECIMAL(5,2), -- 0-100 weighted score
-- Metadata
calculated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_perf_supplier_period ON supplier_performance_metrics(supplier_id, measurement_period);
Performance Calculation:
(defn calculate-supplier-performance
"Calculate comprehensive performance score"
[supplier-id period]
(let [;; Raw metrics
delivery-metrics (calculate-delivery-metrics supplier-id period)
quality-metrics (calculate-quality-metrics supplier-id period)
compliance-metrics (calculate-compliance-metrics supplier-id period)
responsiveness-metrics (calculate-responsiveness-metrics supplier-id period)
;; Weighted scoring (0-100)
delivery-score (* 0.30 (:on-time-pct delivery-metrics))
quality-score (* 0.25 (:accuracy-pct quality-metrics))
compliance-score (* 0.25 (:compliance-pct compliance-metrics))
responsiveness-score (* 0.20 (:responsiveness-pct responsiveness-metrics))
overall-score (+ delivery-score quality-score
compliance-score responsiveness-score)]
{:period period
:delivery-metrics delivery-metrics
:quality-metrics quality-metrics
:compliance-metrics compliance-metrics
:responsiveness-metrics responsiveness-metrics
:overall-score overall-score
:rating (cond
(>= overall-score 90) "Excellent"
(>= overall-score 80) "Good"
(>= overall-score 70) "Acceptable"
(>= overall-score 60) "Needs Improvement"
:else "Unacceptable")
:trend (calculate-trend supplier-id overall-score)}))
┌────────────────────────────────────────────────────────────────┐
│ SUPPLIER PERFORMANCE: Acme Office Supplies │
├────────────────────────────────────────────────────────────────┤
│ │
│ OVERALL SCORE: 87/100 (Good) ↑ +3 vs last quarter │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 📦 DELIVERY PERFORMANCE (30% weight) - Score: 92/100 │
│ ├─ On-Time Delivery: 94.2% (Target: ≥95%) ⚠️ │
│ ├─ Average Delivery: 3.2 days (Target: ≤3 days) │
│ ├─ Late Deliveries: 8 orders (5.8%) │
│ └─ Trend: ↑ Improving (+2.1% vs Q3) │
│ │
│ 📋 QUALITY METRICS (25% weight) - Score: 88/100 │
│ ├─ Invoice Accuracy: 96.3% (Target: ≥98%) ⚠️ │
│ ├─ Disputes: 3 this quarter (2.2% rate) │
│ ├─ Common Errors: Missing PO number (5 invoices) │
│ └─ Trend: → Stable │
│ │
│ ✅ COMPLIANCE (25% weight) - Score: 91/100 │
│ ├─ Contract Compliance: 92.1% (Target: ≥95%) │
│ ├─ Pricing Compliance: 97.8% (3 invoices over contract) │
│ ├─ Terms Compliance: 88.4% (sent Net 45 instead of Net 30) │
│ └─ Trend: ↓ Declining (-1.2% vs Q3) ⚠️ │
│ │
│ ⚡ RESPONSIVENESS (20% weight) - Score: 78/100 │
│ ├─ Response Time: 4.2 hours (Target: ≤8 hours) ✅ │
│ ├─ Resolution Time: 18.5 hours (Target: ≤24 hours) ✅ │
│ ├─ Unresolved Issues: 1 open (12 days old) ⚠️ │
│ └─ Trend: ↑ Improving (+5% vs Q3) │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 🎯 ACTION ITEMS │
│ • Address 8 late deliveries (root cause: understaffing) │
│ • Fix invoice accuracy issues (train on PO requirements) │
│ • Review payment terms (using wrong Net 45 instead of 30) │
│ • Resolve 12-day-old open issue (escalate to account mgr) │
│ │
│ [View Detailed Metrics] [Export Report] [Schedule Review] │
│ │
└────────────────────────────────────────────────────────────────┘
Identify and Mitigate Supplier Risks:
(defn calculate-supplier-risk-score
"Calculate comprehensive risk score (0-100, higher = riskier)"
[supplier-id]
(let [;; Concentration risk
spend-concentration (calculate-spend-concentration supplier-id)
;; Single-source risk
single-source? (is-single-source? supplier-id)
;; Performance risk
performance-score (get-performance-score supplier-id)
;; Financial risk
financial-health (get-financial-health supplier-id)
;; Compliance risk
compliance-issues (count-compliance-issues supplier-id)
;; Geographic risk
geo-risk (calculate-geographic-risk supplier-id)
;; Weighted risk calculation
concentration-risk (* 0.25 spend-concentration) ;; 0-25 points
single-source-risk (if single-source? 20 0) ;; 0 or 20 points
performance-risk (* 0.20 (- 100 performance-score) 0.01) ;; 0-20 points
financial-risk (* 0.15 (- 100 financial-health) 0.01) ;; 0-15 points
compliance-risk (* 0.10 (min compliance-issues 10)) ;; 0-10 points max
geographic-risk (* 0.10 geo-risk) ;; 0-10 points
total-risk (+ concentration-risk single-source-risk performance-risk
financial-risk compliance-risk geographic-risk)]
{:total-risk-score total-risk
:risk-level (cond
(< total-risk 30) "Low"
(< total-risk 60) "Medium"
:else "High")
:risk-factors {:concentration concentration-risk
:single-source single-source-risk
:performance performance-risk
:financial financial-risk
:compliance compliance-risk
:geographic geographic-risk}
:mitigation-required (> total-risk 60)}))
┌────────────────────────────────────────────────────────────────┐
│ ⚠️ SUPPLIER RISK ANALYSIS │
├────────────────────────────────────────────────────────────────┤
│ │
│ HIGH RISK SUPPLIERS (3) │
│ │
│ 🔴 Critical Systems Inc - Risk Score: 78/100 │
│ Annual Spend: €450,000 (12% of total) │
│ │
│ Risk Factors: │
│ • Single-source supplier (no alternatives) ⚠️ │
│ • High spend concentration (12% of budget) │
│ • Recent financial issues (credit rating downgrade) │
│ • Geographic risk (single facility in one country) │
│ │
│ Mitigation Actions: │
│ ✅ Qualified 2 backup suppliers (in progress) │
│ ⏳ Negotiate 90-day inventory buffer │
│ ❌ Dual-sourcing strategy (not started) │
│ │
│ [View Mitigation Plan] [Assign Owner] [Set Review] │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ 🔴 Cloud Services AG - Risk Score: 72/100 │
│ Annual Spend: €280,000 (8% of total) │
│ │
│ Risk Factors: │
│ • Critical dependency (runs core infrastructure) │
│ • Long contract lock-in (24 months remaining) │
│ • Performance declining (81/100, was 92/100) │
│ • Price increases: 18% in last 12 months │
│ │
│ Mitigation Actions: │
│ ⏳ Performance improvement plan (30 days) │
│ ⏳ Evaluate migration to alternative provider │
│ ❌ Negotiate early exit clause │
│ │
│ [Escalate to Procurement] [RFP Alternative] [Vendor Call] │
│ │
└────────────────────────────────────────────────────────────────┘
AI-Powered Savings Recommendations:
(defn identify-savings-opportunities
"Analyze supplier relationships for cost optimization"
[supplier-id]
(let [supplier (db/get-supplier supplier-id)
contracts (db/get-active-contracts supplier-id)
invoices (db/get-invoices-last-12-months supplier-id)
performance (calculate-supplier-performance supplier-id)
market-data (get-market-benchmarks (:category supplier))]
(concat
;; Volume consolidation opportunities
(identify-volume-consolidation supplier invoices)
;; Price renegotiation opportunities
(identify-price-renegotiation supplier invoices market-data)
;; Early payment discount opportunities
(identify-early-payment-opportunities contracts invoices)
;; Alternative sourcing opportunities
(identify-alternative-sourcing supplier performance market-data)
;; Contract optimization opportunities
(identify-contract-optimization contracts invoices)
;; Waste reduction opportunities
(identify-waste-reduction supplier invoices))))
Savings Dashboard:
┌────────────────────────────────────────────────────────────────┐
│ 💰 SAVINGS OPPORTUNITIES - ALL SUPPLIERS │
├────────────────────────────────────────────────────────────────┤
│ │
│ Total Identified: €1,245,000/year across 47 opportunities │
│ │
│ BY CATEGORY: │
│ ├─ Volume Consolidation: €485,000 (39%) │
│ ├─ Price Renegotiation: €340,000 (27%) │
│ ├─ Early Payment Discounts: €220,000 (18%) │
│ ├─ Alternative Sourcing: €150,000 (12%) │
│ └─ Waste Reduction: €50,000 (4%) │
│ │
│ TOP 5 OPPORTUNITIES: │
│ │
│ 1. Consolidate IT Services vendors (currently 8) │
│ Potential Savings: €120,000/year │
│ Effort: Medium | Timeline: 3-6 months │
│ [View Analysis] [Start RFP] │
│ │
│ 2. Renegotiate Office Supplies contract │
│ Potential Savings: €85,000/year │
│ Reason: 12% above market, high volume leverage │
│ Effort: Low | Timeline: 1-2 months │
│ [Prepare Negotiation] [View Benchmarks] │
│ │
│ 3. Capture early payment discounts (10 suppliers) │
│ Potential Savings: €68,000/year │
│ Reason: Strong cash position, eligible discounts │
│ Effort: Low | Timeline: Immediate │
│ [Enable Auto-Payment] [Review Cash Flow] │
│ │
│ 4. Switch to alternative cloud provider │
│ Potential Savings: €45,000/year │
│ Reason: 22% cheaper for equivalent service │
│ Effort: High | Timeline: 6-12 months │
│ [Evaluate Alternatives] [Migration Plan] │
│ │
│ 5. Reduce SaaS license waste │
│ Potential Savings: €32,000/year │
│ Reason: 120 licenses, only 87 active users │
│ Effort: Low | Timeline: Immediate │
│ [Review Usage] [Downsize Plan] │
│ │
└────────────────────────────────────────────────────────────────┘
Annual Value:
Discount Optimization: €150-300K/year
Contract Renegotiation: €300-600K/year
Waste Reduction: €100-200K/year
Risk Mitigation: €50-150K/year
Total Value: €600K-1.25M/year
Implementation Cost:
Month 3:
Month 6:
Month 12:
Document Version: 1.0
Last Updated: January 5, 2026
Author: Orcha Product Team
Status: Ready for Development