Purchase Requests & Approval Workflows Feature Specification

Overview

This feature enables organizations to:

  1. Submit Purchase Requests - Employees request purchases with AI-assisted data collection
  2. Define Approval Workflows - Admins configure routing rules based on amount, supplier, cost center, etc.
  3. Execute Approvals - Via Slack, Email, or Web with full budget visibility
  4. Monitor & Track - Company-wide dashboard with role-based access control

The system handles both purchase requests (before purchase) and invoice approvals (after receipt).


Part A: Purchase Requests

A.1 Request Submission Flow

Employees submit purchase requests through Orcha. The AI ensures complete information before routing to approval.

Submission Channels:

A.2 AI-Assisted Data Collection

When an employee initiates a purchase request, the LLM validates completeness and asks clarifying questions before submission.

Required Fields:

Field Description AI Prompt if Missing
description What is being purchased "What exactly do you need to purchase?"
amount Estimated cost "What is the estimated cost?"
supplier Vendor name "Which supplier/vendor is this from?"
justification Business reason "Why do you need this? How does it benefit the team/project?"
cost_center Budget allocation "Which cost center should this be charged to?"
department Requesting department (Auto-detected from user profile, confirm if ambiguous)
urgency Timeline "When do you need this by? Is this urgent?"

Optional Fields (AI may prompt based on context):

Field When AI Asks
project If multiple active projects in department
alternatives_considered For high-value requests (>€10k)
recurring If description suggests subscription
existing_contract If supplier already has relationship

Example AI Conversation:

Employee: I need to buy some new laptops for the team

Orcha: I can help you submit a purchase request for laptops. Let me gather some details:

1. How many laptops do you need, and what specifications?
2. What is the estimated cost per laptop?
3. Which team members are these for?
4. When do you need them by?
5. Which cost center should this be charged to?

Employee: 5 MacBook Pros for the new engineers starting next month.
         Around €2,500 each. Cost center is CC-ENG-001.

Orcha: Got it. I'll create a purchase request:
- 5x MacBook Pro laptops
- Total: €12,500
- Cost Center: CC-ENG-001 (Engineering)
- Needed by: Next month (new hire onboarding)

One more question: Are there any specific specs required (RAM, storage),
or should I note "standard developer configuration"?

Employee: 32GB RAM, 512GB SSD

Orcha: ✓ Purchase request PR-2026-0089 created and sent for approval.
       Approver: Thomas Weber (Engineering Director)
       Estimated approval time: 24-48 hours

       I'll notify you when there's an update.

A.3 Purchase Request States

draft → pending_approval → approved → ordered → received → closed
                        ↘ rejected (workflow ends, must create new PR)
                        ↘ needs_info → (updated) → pending_approval
State Description
draft Employee is still entering details
pending_approval Awaiting approver action
needs_info Approver requested more information
approved Ready to order
rejected Denied (with reason). Workflow ends. Requester must create new PR if needed
ordered PO issued to supplier
received Goods/services received
closed Complete (invoice matched)

A.4 Recurring Purchases

For purchases with a contract period (e.g., annual SaaS license):

Example:

Jan 2026: PR approved for "Figma Annual License - €1,200/year"
          Contract period: Jan 2026 - Dec 2026

Feb-Dec 2026: Monthly invoices (~€100/month) auto-matched, no approval needed

Dec 2026: Renewal notification sent
Jan 2027: New PR required for "Figma Annual License - Renewal 2027"
          Goes through approval workflow again

The system tracks:


Part B: Budget Visibility for Approvers

B.1 Budget Context Display

When an approver reviews a request, they see comprehensive budget information to make informed decisions.

Budget Summary Card:

┌─────────────────────────────────────────────────────────────────┐
│ Budget: CC-ENG-001 (Engineering) - Q1 2026                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Allocated Budget:                           €150,000.00        │
│  ─────────────────────────────────────────────────────────      │
│  Invoices Received (paid/pending):          - €67,500.00        │
│  Approved PRs (not yet invoiced):           - €23,200.00        │
│  ─────────────────────────────────────────────────────────      │
│  Committed Total:                             €90,700.00        │
│  ─────────────────────────────────────────────────────────      │
│  Available Budget:                            €59,300.00        │
│                                                                 │
│  THIS REQUEST:                              - €12,500.00        │
│  ─────────────────────────────────────────────────────────      │
│  Remaining After Approval:                    €46,800.00   ✓    │
│                                                                 │
│  Pending PRs (not yet approved):            (€18,400.00)        │
│  If all pending approved:                     €28,400.00        │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│  ℹ️  Previous Period (Q4 2025):                                 │
│      Unused budget that expired: €12,300.00                     │
│      (No rollover - for reference only)                         │
└─────────────────────────────────────────────────────────────────┘

Note: Budget periods are strict (no rollover). The previous period's unused amount is shown for context only—it does not increase the current period's available budget.

When Budget is Not Configured:

If no budget has been defined for the cost center, show a placeholder that encourages configuration:

┌─────────────────────────────────────────────────────────────────┐
│ Budget: CC-ENG-001 (Engineering) - Q1 2026                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ⚠️  No budget configured for this cost center                  │
│                                                                 │
│  Without a budget, Orcha cannot show:                           │
│  • Available funds remaining                                    │
│  • Budget impact of this request                                │
│  • Warnings when spending exceeds allocation                    │
│                                                                 │
│  To enable budget tracking:                                     │
│  → Settings > Budgets > Upload Budget (Excel)                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Budget Upload:

B.2 Budget Calculation Components

Committed Spend:

Committed = Invoices Received + Approved PRs (not invoiced)
Component Description Source
Invoices Received Invoices posted to this cost center Invoice table, status = received/paid
Approved PRs Purchase requests approved but no invoice yet PR table, status = approved/ordered

Projected Spend:

Projected = Committed + Pending PRs (including this request)
Component Description Source
Pending PRs Purchase requests awaiting approval PR table, status = pending_approval

B.3 Budget Warnings

The system displays warnings based on budget impact:

Condition Warning Level Display
Remaining > 20% of budget None Green checkmark
Remaining 10-20% of budget Caution Yellow warning
Remaining < 10% of budget Warning Orange alert
Would exceed budget Critical Red block with explanation
Would exceed + pending PRs also exceed Critical+ Red with note about queue

Example Warning (Budget Exceeded):

┌─────────────────────────────────────────────────────────────────┐
│ ⚠️  BUDGET ALERT: This request would exceed the Q1 budget      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Available Budget:                             €8,500.00        │
│  This Request:                                €12,500.00        │
│  ─────────────────────────────────────────────────────────      │
│  Over Budget By:                               €4,000.00        │
│  Percentage Over:                                  47%          │
│                                                                 │
│  Options:                                                       │
│  • Approve with budget exception (requires Finance Director)    │
│  • Request budget increase                                      │
│  • Defer to Q2                                                  │
│  • Reject with explanation                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

B.4 Time Period Handling

Budgets can be defined at different granularities:

Period Calculation Window
Monthly Current calendar month
Quarterly Current quarter (Q1=Jan-Mar, etc.)
Annual Current fiscal year
Project-based Project start to end date

The system automatically selects the appropriate period based on cost center configuration.


Part C: Approval Workflows

C.1 Condition System

Workflows are triggered by conditions on document attributes. All conditions in a workflow must match (AND logic). Multiple workflows can exist; priority determines which one applies.

Supported Condition Fields:

Field Operators Example
amount <, <=, >, >=, between amount >= 10000 AND amount < 50000
supplier.country =, in, not_in supplier.country IN (DE, AT, CH)
supplier.legal_type =, in supplier.legal_type = GmbH
supplier.name contains, starts_with, = supplier.name contains "Consulting"
cost_center =, in, starts_with cost_center IN (CC-1001, CC-1002)
department =, in department = Engineering
document_type = document_type = invoice
currency =, in currency = EUR
has_po_match = has_po_match = false (invoice without PO)
budget_impact =, > budget_impact > 0.8 (>80% of remaining)

Condition Examples:

Workflow: "High-Value DACH Region"
Priority: 100
Conditions:
  - amount >= 25000
  - supplier.country IN (DE, AT, CH)

Workflow: "GmbH Supplier Standard"
Priority: 50
Conditions:
  - supplier.legal_type = GmbH
  - amount < 25000

Workflow: "Engineering Cost Center"
Priority: 75
Conditions:
  - cost_center starts_with "CC-ENG"
  - amount >= 5000

Workflow: "Budget Exception Required"
Priority: 200
Conditions:
  - budget_impact > 1.0  (would exceed budget)

C.2 Approval Steps

Each workflow contains ordered steps. A step defines:

Approver Types:

Type Description Example
user Specific person anna.schmidt@company.com
role Anyone with role Finance Director
dynamic:requester_manager Requester's direct manager Resolved at runtime
dynamic:cost_center_owner Owner of the document's cost center Resolved at runtime
dynamic:budget_owner Budget owner for category Resolved at runtime
dynamic:department_head Head of requester's department Resolved at runtime

Step Configuration Example:

steps:
  - step: 1
    name: "Department Head Approval"
    approvers:
      - type: dynamic:department_head
    timeout_hours: 24
    on_timeout: remind

  - step: 2
    name: "Finance Review"
    approvers:
      - type: role
        value: "Finance Controller"
      - type: role
        value: "Finance Director"
    require_all: false  # Any one can approve
    timeout_hours: 48
    on_timeout: escalate
    escalate_to:
      - type: user
        value: "cfo@company.com"

  - step: 3
    name: "Executive Sign-off"
    approvers:
      - type: user
        value: "cfo@company.com"
      - type: user
        value: "ceo@company.com"
    require_all: true  # Both must approve
    timeout_hours: 72

C.3 Priority & Conflict Resolution

The Problem: A single purchase request might match multiple workflows. For example:

If a €15,000 request from a German supplier comes in, it matches all three workflows. Which one applies?

The Solution: Priority-based matching. Users assign explicit priority values (1-1000). Higher priority = evaluated first. The first matching workflow wins.

Workflow Selection Algorithm:

  1. Gather all active workflows for the document type
  2. Sort by priority descending (highest first)
  3. For each workflow (in priority order), evaluate all its conditions
  4. Return the first workflow where all conditions match
  5. If no workflow matches, use default (single-step manager approval)

Example:

Workflow Priority Conditions Approvers
C: High-Value German Supplier 100 amount > €10k AND country = DE Finance Director + Regional Manager
A: High-Value General 50 amount > €10k Finance Director
B: German Supplier 30 country = DE Regional Manager

For a €15,000 request from Germany:

For a €15,000 request from France:

UI for Priority Management:


Part D: AI Workflow Selection

When a document enters the system (via OCR/extraction, manual entry, or purchase request), the AI automatically selects the appropriate workflow.

D.1 Selection Process

Document/Request Received
       │
       ▼
┌─────────────────┐
│ Extract/Validate│  (supplier, amount, cost center, etc.)
│     Fields      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Calculate Budget│  (committed, projected, impact %)
│     Impact      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Match Workflows │  (evaluate conditions, respect priority)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Resolve Dynamic │  (lookup requester manager, budget owner)
│    Approvers    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Create Approval │  (request with step 1 active)
│    Request      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Send to First   │  (via configured channels)
│    Approvers    │
└─────────────────┘

D.2 AI Enhancement

For edge cases where rule-based matching is insufficient, the AI can:


Part E: Integration Channels

E.1 Slack Integration

Setup:

Approval Request Message (with Budget Context):

┌─────────────────────────────────────────────────────────────────┐
│ 📋 Approval Request #PR-2026-0089                               │
├─────────────────────────────────────────────────────────────────┤
│ Type: Purchase Request                                          │
│ Description: 5x MacBook Pro laptops (32GB, 512GB)               │
│ Supplier: Apple Inc.                                            │
│ Amount: €12,500.00                                              │
│ Cost Center: CC-ENG-001 (Engineering)                           │
│ Requester: Sarah Mueller                                        │
│ Justification: New hire onboarding - 5 engineers starting Feb   │
├─────────────────────────────────────────────────────────────────┤
│ 💰 Budget Impact (Q1 2026):                                     │
│    Available: €59,300 → After approval: €46,800 (31% remaining) │
│    Status: ✓ Within budget                                      │
├─────────────────────────────────────────────────────────────────┤
│ Workflow: Engineering >€10k (Step 1 of 2)                       │
│ Your Action Required                                            │
├─────────────────────────────────────────────────────────────────┤
│ [✓ Approve]  [✗ Reject]  [❓ Ask Question]  [📄 View Details]   │
└─────────────────────────────────────────────────────────────────┘

Query Commands: Users can ask Orcha via Slack:

E.2 Email Integration

Approval Email:

Emails contain the same complete information as Slack so approvers can make fast, informed decisions directly from email:

Subject: [Action Required] Approval Request PR-2026-0089 - €12,500

┌─────────────────────────────────────────────────────────────────┐
│ 📋 Approval Request #PR-2026-0089                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│ REQUEST DETAILS                                                 │
│ ───────────────────────────────────────────────────────────     │
│ Type:           Purchase Request                                │
│ Description:    5x MacBook Pro laptops (32GB RAM, 512GB SSD)    │
│ Supplier:       Apple Inc. (USA)                                │
│ Amount:         €12,500.00                                      │
│ Cost Center:    CC-ENG-001 (Engineering)                        │
│ Requester:      Sarah Mueller                                   │
│ Needed By:      February 2026                                   │
│                                                                 │
│ JUSTIFICATION                                                   │
│ ───────────────────────────────────────────────────────────     │
│ New hire onboarding - 5 engineers starting in February.         │
│ Standard developer configuration for the team.                  │
│                                                                 │
│ 💰 BUDGET IMPACT (Q1 2026)                                      │
│ ───────────────────────────────────────────────────────────     │
│ Available Budget:     €59,300.00                                │
│ This Request:        -€12,500.00                                │
│ After Approval:       €46,800.00 (31% remaining) ✓              │
│                                                                 │
│ Previous Period (Q4 2025): €12,300 unused (no rollover)         │
│                                                                 │
│ WORKFLOW                                                        │
│ ───────────────────────────────────────────────────────────     │
│ Engineering >€10k (Step 1 of 2)                                 │
│ Next Step: Finance Director approval                            │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│    [ ✓ APPROVE ]      [ ✗ REJECT ]      [ ❓ ASK QUESTION ]     │
│                                                                 │
│    (One-click buttons - no login required)                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Email Features:

Query via Email: Users can email approvals@company.getorcha.com:

E.3 Orcha Web UI

Full approval management in the web application (see Monitoring Dashboard section).


Part F: Access Control & Visibility

F.1 Roles

Role Permissions
employee Submit PRs, view own requests, view approvals assigned to self
approver All employee permissions + approve/reject assigned items
budget_viewer View budget status for assigned cost centers
finance_admin View all approvals in assigned cost centers/departments
workflow_admin Create/edit/delete workflows, view all workflow analytics
org_admin Full access to all approvals, all workflows, all analytics

F.2 Visibility Rules

Document Visibility: A user can see an approval request if ANY of these apply:

Budget Visibility:

F.3 Query Access Control

When a user asks "What are all open approvals?":

Response Example (non-admin):

You have 3 pending approvals:
1. PR-2026-0089 - €12,500 - MacBook Pros (awaiting your approval)
2. PR-2026-0092 - €3,200 - Office Supplies (you submitted, pending manager)
3. INV-2026-0051 - €8,900 - Cloud Services (awaiting your approval)

Response Example (org-admin):

Company-wide pending approvals: 47 total

By Age:
- < 24 hours: 23
- 24-48 hours: 15
- > 48 hours: 9 (attention needed)

Budget Alerts:
- 3 requests would exceed their cost center budget
- 5 requests would use >80% of remaining budget

Top pending by amount:
1. PR-2026-0078 - €125,000 - Enterprise Software (Step 2/3, waiting CFO)
2. PR-2026-0095 - €89,000 - Hardware (Step 1/2, waiting Dept Head)
...

Part G: Monitoring Dashboard

G.1 Company Overview (Admin View)

Summary Cards:

Pending Approvals Table:

Type ID Description Amount Requester Budget Impact Current Step Waiting Since
PR PR-2026-0089 MacBook Pros €12,500 S. Mueller 21% of remaining 1/2 2 days
INV INV-2026-0042 SaaS License €8,500 T. Weber 14% of remaining 1/1 4 hours
PR PR-2026-0091 Server Hardware €45,000 A. Schmidt ⚠️ Exceeds budget 2/3 1 day

Filters:

G.2 Budget Overview

Cost Center Budget Summary:

Cost Center Q1 Budget Committed Pending Available Status
CC-ENG-001 €150,000 €90,700 €18,400 €59,300 ✓ OK
CC-MKT-001 €80,000 €72,500 €12,000 €7,500 ⚠️ 9% left
CC-OPS-001 €50,000 €48,200 €8,500 €1,800 🔴 Over if pending approved

G.3 Activity Feed

Real-time feed of approval actions:

10:42 - M. Schmidt approved PR-2026-0089 (€12,500) via Slack
10:38 - T. Weber submitted PR-2026-0115 (€8,200)
10:15 - A. Hoffmann rejected INV-2026-0048 (€3,100) - "Missing documentation"
09:55 - System escalated PR-2026-0039 to CFO (48h timeout)
09:30 - Budget alert: CC-MKT-001 at 91% utilization

Part H: Data Model

Core Tables

-- Purchase Request
CREATE TABLE purchase_request (
    request_id        UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    org_id            UUID NOT NULL REFERENCES organization(org_id),
    request_number    TEXT NOT NULL UNIQUE,  -- PR-2026-0001
    status            TEXT NOT NULL DEFAULT 'draft'
                      CHECK (status IN ('draft', 'pending_approval', 'needs_info',
                                        'approved', 'rejected', 'ordered', 'received', 'closed')),
    requester_id      UUID NOT NULL REFERENCES app_user(user_id),
    description       TEXT NOT NULL,
    justification     TEXT,
    supplier_id       UUID REFERENCES supplier(supplier_id),
    supplier_name     TEXT,  -- Denormalized for display
    estimated_amount  NUMERIC(15,2) NOT NULL,  -- Always in EUR (converted at submission)
    original_amount   NUMERIC(15,2),          -- Original amount if submitted in different currency
    original_currency TEXT,                    -- Original currency code (null if EUR)
    exchange_rate     NUMERIC(10,6),          -- Rate used for conversion
    cost_center       TEXT NOT NULL,
    department        TEXT,
    project           TEXT,
    urgency           TEXT CHECK (urgency IN ('low', 'normal', 'high', 'critical')),
    needed_by         DATE,
    -- Recurring purchase fields
    is_recurring      BOOLEAN DEFAULT false,
    contract_start    DATE,  -- Start of contract period
    contract_end      DATE,  -- End of contract period (triggers renewal reminder)
    renewal_reminder_sent BOOLEAN DEFAULT false,
    -- Metadata
    metadata          JSONB,  -- Additional fields, attachments, etc.
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    submitted_at      TIMESTAMPTZ,
    approved_at       TIMESTAMPTZ,
    closed_at         TIMESTAMPTZ
);

-- Budget Definition
CREATE TABLE budget (
    budget_id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    org_id            UUID NOT NULL REFERENCES organization(org_id),
    cost_center       TEXT NOT NULL,
    period_type       TEXT NOT NULL CHECK (period_type IN ('monthly', 'quarterly', 'annual', 'project')),
    period_start      DATE NOT NULL,
    period_end        DATE NOT NULL,
    allocated_amount  NUMERIC(15,2) NOT NULL,
    currency          TEXT NOT NULL DEFAULT 'EUR',
    owner_id          UUID REFERENCES app_user(user_id),
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (org_id, cost_center, period_start, period_end)
);

-- Supplier Master Data (Hybrid: auto-created from documents, requires verification)
CREATE TABLE supplier (
    supplier_id       UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    org_id            UUID NOT NULL REFERENCES organization(org_id),
    name              TEXT NOT NULL,
    legal_type        TEXT,  -- GmbH, AG, Ltd, Inc, etc.
    country           TEXT,  -- ISO country code
    tax_id            TEXT,
    address           JSONB,
    payment_terms     TEXT,
    is_preferred      BOOLEAN DEFAULT false,
    is_verified       BOOLEAN DEFAULT false,  -- Admin must verify before trusted
    verified_by       UUID REFERENCES app_user(user_id),
    verified_at       TIMESTAMPTZ,
    source            TEXT,  -- 'manual', 'invoice_extraction', 'purchase_request'
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (org_id, name, country)
);

-- Workflow definition
CREATE TABLE approval_workflow (
    workflow_id       UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    org_id            UUID NOT NULL REFERENCES organization(org_id),
    name              TEXT NOT NULL,
    description       TEXT,
    document_type     TEXT NOT NULL CHECK (document_type IN ('invoice', 'purchase_request')),
    priority          INTEGER NOT NULL DEFAULT 50,
    active            BOOLEAN NOT NULL DEFAULT true,
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by        UUID REFERENCES app_user(user_id)
);

-- Workflow conditions (multiple per workflow, AND logic)
CREATE TABLE approval_workflow_condition (
    condition_id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    workflow_id       UUID NOT NULL REFERENCES approval_workflow(workflow_id) ON DELETE CASCADE,
    field             TEXT NOT NULL,
    operator          TEXT NOT NULL,
    value             JSONB NOT NULL,
    sort_order        INTEGER NOT NULL DEFAULT 0
);

-- Workflow steps (ordered)
CREATE TABLE approval_workflow_step (
    step_id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    workflow_id       UUID NOT NULL REFERENCES approval_workflow(workflow_id) ON DELETE CASCADE,
    step_number       INTEGER NOT NULL,
    name              TEXT NOT NULL,
    require_all       BOOLEAN NOT NULL DEFAULT false,
    timeout_hours     INTEGER,
    on_timeout        TEXT CHECK (on_timeout IN ('remind', 'escalate', 'auto_approve')),
    escalate_to_step  UUID REFERENCES approval_workflow_step(step_id),
    UNIQUE (workflow_id, step_number)
);

-- Approvers per step
CREATE TABLE approval_step_approver (
    approver_id       UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    step_id           UUID NOT NULL REFERENCES approval_workflow_step(step_id) ON DELETE CASCADE,
    approver_type     TEXT NOT NULL,
    approver_value    TEXT,
    sort_order        INTEGER NOT NULL DEFAULT 0
);

-- Approval request (tracks approval progress for any document)
CREATE TABLE approval_request (
    request_id        UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    org_id            UUID NOT NULL REFERENCES organization(org_id),
    document_type     TEXT NOT NULL,
    document_id       UUID NOT NULL,  -- Reference to purchase_request or invoice
    workflow_id       UUID REFERENCES approval_workflow(workflow_id),
    status            TEXT NOT NULL DEFAULT 'pending'
                      CHECK (status IN ('pending', 'approved', 'rejected', 'cancelled', 'expired')),
    current_step      INTEGER NOT NULL DEFAULT 1,
    budget_snapshot   JSONB,  -- Snapshot of budget state at time of request
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    completed_at      TIMESTAMPTZ
);

-- Individual approval actions
CREATE TABLE approval_action (
    action_id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    request_id        UUID NOT NULL REFERENCES approval_request(request_id),
    step_number       INTEGER NOT NULL,
    approver_id       UUID NOT NULL REFERENCES app_user(user_id),
    action            TEXT NOT NULL CHECK (action IN ('approve', 'reject', 'delegate', 'request_info')),
    comment           TEXT,
    channel           TEXT,
    delegate_to       UUID REFERENCES app_user(user_id),
    acted_at          TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Indexes
CREATE INDEX idx_purchase_request_org_status ON purchase_request(org_id, status);
CREATE INDEX idx_purchase_request_cost_center ON purchase_request(cost_center, status);
CREATE INDEX idx_budget_org_cost_center ON budget(org_id, cost_center, period_start);
CREATE INDEX idx_approval_request_document ON approval_request(document_type, document_id);

Budget Calculation Query

-- Get budget status for a cost center
WITH budget_period AS (
    SELECT budget_id, allocated_amount
    FROM budget
    WHERE org_id = :org_id
      AND cost_center = :cost_center
      AND :date BETWEEN period_start AND period_end
),
invoices_received AS (
    SELECT COALESCE(SUM(amount), 0) as total
    FROM invoice
    WHERE org_id = :org_id
      AND cost_center = :cost_center
      AND status IN ('received', 'paid', 'pending_payment')
      AND invoice_date BETWEEN :period_start AND :period_end
),
approved_prs AS (
    SELECT COALESCE(SUM(estimated_amount), 0) as total
    FROM purchase_request
    WHERE org_id = :org_id
      AND cost_center = :cost_center
      AND status IN ('approved', 'ordered')
      AND approved_at BETWEEN :period_start AND :period_end
),
pending_prs AS (
    SELECT COALESCE(SUM(estimated_amount), 0) as total
    FROM purchase_request
    WHERE org_id = :org_id
      AND cost_center = :cost_center
      AND status = 'pending_approval'
)
SELECT
    bp.allocated_amount,
    ir.total as invoices_received,
    ap.total as approved_prs_not_invoiced,
    (ir.total + ap.total) as committed,
    (bp.allocated_amount - ir.total - ap.total) as available,
    pp.total as pending_prs
FROM budget_period bp, invoices_received ir, approved_prs ap, pending_prs pp;

Part I: Implementation Phases

Phase 1: Database & Core Logic

Phase 2: Purchase Request Submission

Phase 3: Budget Visibility

Phase 4: Workflow Editor UI

Phase 5: Access Control

Phase 6: Slack Integration

Phase 7: Enhanced Monitoring


Part J: Design Decisions

These decisions have been confirmed:

Decision Choice Details
Supplier Management Hybrid Auto-create from documents, require admin verification before "trusted"
Budget Periods Strict, no rollover Budgets are strict per month/quarter. However, show previous period's unused amount to approver for context
PO-Invoice Matching Asymmetric, zero tolerance on overage Invoice ≤ PR: OK. Invoice > PR (any amount): Flag for review
Partial Approvals Not supported Approve or reject only. Requester must resubmit with different amount
After Rejection Workflow ends Requester must create a new PR from scratch (rejected PR is closed)
Recurring Purchases Contract-period based Approved for the contract period (e.g., 12-month license). No re-approval needed within period. Renewal/extension requires new approval
Delegation Not supported Approvers must act themselves. No delegation to others. Escalation to next step is the only alternative
Cost Center Matching Exact match only No wildcard/hierarchy support. Must list each cost center explicitly in workflow conditions
Multi-Currency Convert at submission Convert to base currency (EUR) when PR is submitted. Budget calculations use EUR
Audit Retention Configurable No specific requirement now. Make retention period configurable per organization

Part K: Future Considerations

These items are out of scope for initial implementation but noted for future:

  1. Cost Center Hierarchy: Currently exact match only. Could add wildcard support (CC-ENG-* matches CC-ENG-001) if needed.

  2. Multi-Currency Display: Currently all converted to EUR. Could show original currency alongside EUR conversion.

  3. Approval Delegation: Currently not supported. Could add delegation with constraints (same department, vacation coverage, etc.)

  4. Partial Approvals: Currently not supported. Could allow approvers to reduce amounts.

  5. Mobile App: Slack/Email work on mobile, but dedicated app could improve experience.