navidocs/intelligence/session-2/accounting-integration-spec.md
Claude d250dc334e
Session 2: Complete technical architecture from 11 Haiku agents
All 11 agents (S2-H01 through S2-H09 + S2-H03A + S2-H07A) have completed
their technical specifications:

- S2-H01: NaviDocs codebase architecture analysis
- S2-H02: Inventory tracking system (€15K-€50K value recovery)
- S2-H03: Maintenance log & reminder system
- S2-H04: Camera & Home Assistant integration
- S2-H05: Contact management system
- S2-H06: Accounting module & receipt OCR integration
- S2-H07: Impeccable search UX (Meilisearch facets)
- S2-H08: WhatsApp Business API + AI agent integration
- S2-H09: Document versioning with IF.TTT compliance
- S2-H03A: VAT/tax jurisdiction tracking & compliance
- S2-H07A: Multi-calendar system (4 calendar types)

Total: ~15,600 lines of technical specifications
Status: Ready for S2-H10 synthesis (awaiting Session 1 completion)
IF.bus: All inter-agent communications documented
2025-11-13 01:57:25 +00:00

65 KiB

S2-H06: Receipt/Invoice Upload + Accounting Module Integration Specification

Agent Identity: S2-H06 Assigned to: Receipt/Invoice Upload + Accounting Module Integration Document Status: Complete Specification Session: Cloud Session 2 - Technical Integration Architecture Date: 2025-11-13


Executive Summary

This specification defines the architectural integration of receipt/invoice upload and multi-user expense tracking for NaviDocs. The system enables boat owners and captains to capture, process, and manage expenses with OCR-powered automation, reimbursement workflows, and full IF.TTT compliance for audit traceability.

Key Components:

  • Multi-user expense tracking (owner, captain, crew)
  • Receipt/invoice OCR workflow (photo → extraction → review → save)
  • Accounting module integration (Spliit fork for MIT-licensed flexibility)
  • Reimbursement workflow with owner approval
  • WhatsApp AI integration for expense logging
  • Multi-currency support (€, $, £)
  • Export to Excel/CSV for accountants
  • Full IF.TTT audit trail compliance

1. Multi-User Expense Tracking Design

1.1 User Roles & Expense Categories

Owner Expenditure

  • Payment Methods: Cash, card, bank transfers
  • Typical Expenses: Marina fees, insurance, major upgrades, professional services, fuel, haul-outs
  • Approval Flow: Direct entry (owner logs own expenses) + approval of captain reimbursements
  • Budget Authority: Can set monthly/annual budgets and approve work roadmap expenses
  • Tax Implications: Tracks VAT status, business vs. personal expenses (for charter/rental boats)

Captain/Crew Expenditure

  • Payment Methods: Boat card (pre-approved limit), boat cash, personal funds (for reimbursement)
  • Typical Expenses: Provisions, fuel, minor repairs, supplies, crew meals, dockage during captain-only periods
  • Approval Flow: Submit receipt → OCR extraction → owner approves/rejects → mark paid
  • Documentation: Must provide receipt photo for all expenses
  • Reimbursement Tracking: Separate accounting for personal cash advances vs. company card charges

1.2 Expense Categories (Hierarchical)

Root Categories:
├── Fuel & Provisioning
│   ├── Fuel
│   ├── Diesel
│   ├── LPG
│   ├── Provisions (food/water)
│   └── Beverage supplies
├── Marina & Mooring
│   ├── Marina fees
│   ├── Mooring buoy rental
│   ├── Harbor dues
│   └── Fuel dock (if separate)
├── Maintenance & Repairs
│   ├── Engine maintenance
│   ├── Electronics
│   ├── Hull & Structure
│   ├── Deck & Rigging
│   ├── Safety equipment
│   └── Professional services (surveyor, mechanic)
├── Insurance
│   ├── Hull insurance
│   ├── Liability insurance
│   ├── Crew insurance
│   └── Document insurance
├── Professional Upgrades
│   ├── Electronics upgrades
│   ├── Safety system upgrades
│   ├── Interior renovations
│   └── Engine upgrades
├── Operating Expenses
│   ├── Crew salaries/tips
│   ├── Supplies & consumables
│   ├── Cleaning & laundry
│   ├── Documentation & permits
│   └── Crew meals
├── Miscellaneous
│   ├── Tips & gratuities
│   ├── Small tools
│   ├── Spare parts inventory
│   └── Other

Currency: € (EUR), $ (USD), £ (GBP) - auto-converted to primary boat currency

1.3 Expense Model Schema

CREATE TABLE expenses (
  id VARCHAR(36) PRIMARY KEY,
  boat_id VARCHAR(36) NOT NULL,

  -- User/Role Information
  created_by_user_id VARCHAR(36) NOT NULL,
  created_by_role ENUM('owner', 'captain', 'crew') NOT NULL,

  -- Financial Data
  amount DECIMAL(12, 2) NOT NULL,
  currency ENUM('EUR', 'USD', 'GBP', 'OTHER') DEFAULT 'EUR',
  amount_in_primary_currency DECIMAL(12, 2) DEFAULT NULL,
  exchange_rate DECIMAL(10, 6) DEFAULT 1.0,

  -- Expense Details
  vendor_name VARCHAR(255),
  vendor_id VARCHAR(36) DEFAULT NULL,  -- Link to boat_contacts if recurring vendor

  category_id VARCHAR(36) NOT NULL,
  category_name VARCHAR(100),  -- Denormalized for quick access

  purchase_date DATE NOT NULL,
  description VARCHAR(500),

  -- Reimbursement Tracking
  payment_method ENUM('cash', 'card', 'bank_transfer', 'personal_cash', 'boat_cash') NOT NULL,
  reimbursement_status ENUM('pending', 'approved', 'rejected', 'paid', 'partial', 'pending_captain_approval') DEFAULT 'pending',
  reimbursement_requested_at TIMESTAMP,
  reimbursement_approved_by_user_id VARCHAR(36),
  reimbursement_approved_at TIMESTAMP,
  reimbursement_amount DECIMAL(12, 2),
  reimbursement_date DATE,

  -- VAT/Tax
  vat_amount DECIMAL(10, 2),
  vat_percentage DECIMAL(5, 2),
  tax_deductible BOOLEAN DEFAULT TRUE,

  -- OCR & Receipt Data
  receipt_image_url VARCHAR(2048),
  receipt_image_hash VARCHAR(64),  -- SHA-256 for IF.TTT
  ocr_extracted BOOLEAN DEFAULT FALSE,
  ocr_vendor VARCHAR(255),
  ocr_amount DECIMAL(12, 2),
  ocr_date DATE,
  ocr_vat DECIMAL(10, 2),
  ocr_raw_text LONGTEXT,
  ocr_confidence DECIMAL(3, 2),

  -- Linking to Other Modules
  maintenance_log_id VARCHAR(36) DEFAULT NULL,
  inventory_item_id VARCHAR(36) DEFAULT NULL,
  calendar_event_id VARCHAR(36) DEFAULT NULL,

  -- IF.TTT Compliance
  content_hash VARCHAR(64),  -- SHA-256 of all expense data
  ed25519_signature VARCHAR(128),  -- Ed25519 signature of creator
  citation_id VARCHAR(255),  -- if://receipt/navidocs/boat-123/expense-456

  -- Timestamps & Tracking
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP,

  -- Audit
  audit_log LONGTEXT,  -- JSON array of all changes

  INDEX idx_boat_id (boat_id),
  INDEX idx_created_by (created_by_user_id),
  INDEX idx_category (category_id),
  INDEX idx_purchase_date (purchase_date),
  INDEX idx_reimbursement_status (reimbursement_status),
  INDEX idx_payment_method (payment_method),
  FOREIGN KEY (boat_id) REFERENCES boats(id),
  FOREIGN KEY (created_by_user_id) REFERENCES users(id),
  FOREIGN KEY (category_id) REFERENCES expense_categories(id),
  FOREIGN KEY (maintenance_log_id) REFERENCES maintenance_log(id),
  FOREIGN KEY (inventory_item_id) REFERENCES boat_inventory(id),
  FOREIGN KEY (calendar_event_id) REFERENCES calendar_events(id)
);

-- Reimbursement Tracking (for captain expenses paid from personal funds)
CREATE TABLE reimbursement_requests (
  id VARCHAR(36) PRIMARY KEY,
  boat_id VARCHAR(36) NOT NULL,

  -- Request Details
  captain_user_id VARCHAR(36) NOT NULL,
  owner_user_id VARCHAR(36) NOT NULL,

  -- Aggregation
  total_requested DECIMAL(12, 2) NOT NULL,
  total_approved DECIMAL(12, 2) DEFAULT 0,
  total_paid DECIMAL(12, 2) DEFAULT 0,
  currency ENUM('EUR', 'USD', 'GBP') DEFAULT 'EUR',

  -- Workflow
  request_date TIMESTAMP NOT NULL,
  request_status ENUM('draft', 'submitted', 'owner_reviewing', 'approved', 'rejected', 'in_payment', 'paid', 'partial') DEFAULT 'draft',
  owner_approved_at TIMESTAMP,
  owner_notes VARCHAR(500),

  -- Attached Expenses (many-to-many)
  expense_ids JSON,  -- Array of expense IDs included in request

  -- Payment Tracking
  payment_method ENUM('bank_transfer', 'cash', 'card', 'other'),
  payment_date TIMESTAMP,
  payment_reference VARCHAR(100),

  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  FOREIGN KEY (boat_id) REFERENCES boats(id),
  FOREIGN KEY (captain_user_id) REFERENCES users(id),
  FOREIGN KEY (owner_user_id) REFERENCES users(id),
  INDEX idx_boat (boat_id),
  INDEX idx_status (request_status),
  INDEX idx_captain (captain_user_id)
);

-- Expense Categories (Customizable)
CREATE TABLE expense_categories (
  id VARCHAR(36) PRIMARY KEY,
  boat_id VARCHAR(36) NOT NULL,

  name VARCHAR(100) NOT NULL,
  description VARCHAR(500),
  parent_category_id VARCHAR(36),  -- For hierarchical categories

  -- Defaults for this category
  is_taxable BOOLEAN DEFAULT TRUE,
  average_vat_percentage DECIMAL(5, 2),
  default_payment_method VARCHAR(50),

  -- System vs. Custom
  is_system_default BOOLEAN DEFAULT FALSE,

  color_hex VARCHAR(7),  -- For UI display
  icon VARCHAR(50),

  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP,

  FOREIGN KEY (boat_id) REFERENCES boats(id),
  FOREIGN KEY (parent_category_id) REFERENCES expense_categories(id),
  INDEX idx_boat (boat_id),
  UNIQUE KEY unique_name_per_boat (boat_id, name)
);

-- Multi-Currency Exchange Rate History
CREATE TABLE exchange_rates (
  id VARCHAR(36) PRIMARY KEY,
  from_currency ENUM('EUR', 'USD', 'GBP', 'OTHER'),
  to_currency ENUM('EUR', 'USD', 'GBP', 'OTHER'),
  rate DECIMAL(10, 6),
  date DATE,
  source VARCHAR(100),  -- 'ECB', 'OER', 'manual', etc.

  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_currencies (from_currency, to_currency, date)
);

2. Receipt/Invoice OCR Workflow

2.1 OCR Processing Pipeline

User Flow:
┌─────────────────────────────────────────────────────────────┐
│ 1. Mobile Upload (Camera or Gallery)                         │
│    - Capture receipt photo or select from gallery            │
│    - Auto-crop to receipt area (smart phone features)       │
│    - Compression for bandwidth (JPEG, max 5MB)              │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 2. Image Storage & Hashing (IF.TTT)                          │
│    - Store original image: /boat-{boatId}/receipts/         │
│    - Generate SHA-256 hash of image file                    │
│    - Generate Ed25519 signature (uploader identity)         │
│    - Create citation_id: if://receipt/navidocs/boat-Y/Z    │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 3. OCR Processing (Parallel)                                 │
│    a) Tesseract (local, fast, offline)                      │
│       - Extracts raw text from image                        │
│       - Returns text block bounding boxes                   │
│    b) Google Vision API (cloud, accurate)                   │
│       - Full document analysis                              │
│       - Structured text detection                           │
│       - Response: { items, subtotal, tax, total, date }    │
│    c) Custom boat-specific patterns                         │
│       - Marina receipt patterns (fee breakdown)             │
│       - Fuel receipt patterns (type, quantity, price)       │
│       - Parts supplier patterns (item codes)                │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 4. Structured Data Extraction                                │
│    - Vendor name (from header/footer)                       │
│    - Amount (total, subtotal, tax)                          │
│    - Date (receipt date, transaction date)                  │
│    - VAT/Tax percentage                                     │
│    - Currency                                               │
│    - Line items (if detailed receipt)                       │
│    - Confidence scores per field                            │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 5. Category Suggestion (ML/Rules)                            │
│    - Pattern match against vendor database                   │
│    - Keyword matching (e.g., "Diesel" → Fuel category)     │
│    - User history (captain usually buys provisions → suggest)│
│    - Context (if linked to maintenance log, suggest related │
│    - Present 1st/2nd/3rd category suggestions              │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 6. Review & Confirmation (User Interface)                    │
│    ┌────────────────────────────────────────────────┐       │
│    │ Receipt Image (small preview)                  │       │
│    ├────────────────────────────────────────────────┤       │
│    │ Vendor: [extracted] ← editable text field     │       │
│    │ Date:   [extracted] ← date picker             │       │
│    │ Amount: [extracted] [Currency dropdown]       │       │
│    │ VAT:    [extracted] ← numeric field           │       │
│    │ Category: [1st suggestion] ▼ (other options) │       │
│    │ Description: [optional text]                  │       │
│    │ Link to: □ Maintenance log  □ Inventory      │       │
│    │          □ Calendar event                     │       │
│    │ [Cancel]  [Save]  [Reuse as Template]        │       │
│    └────────────────────────────────────────────────┘       │
│    - OCR confidence shown as visual indicator               │
│    - User can edit any field before saving                  │
│    - Option to reuse as expense template                    │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 7. Expense Creation & IF.TTT Signing                         │
│    - Create expense record with extracted data              │
│    - Calculate content_hash (SHA-256 of all fields)         │
│    - Generate ed25519_signature from user's private key    │
│    - Generate citation_id link                              │
│    - Store audit log: who, when, what was edited           │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────┐
│ 8. Workflow Routing                                          │
│    IF (creator_role == 'owner'):                            │
│      - Mark as 'recorded' (no approval needed)              │
│      - Optional: link to maintenance/inventory             │
│    IF (creator_role == 'captain' OR 'crew'):               │
│      - Mark as 'pending_captain_approval'                  │
│      - Wait for captain approval before owner approval     │
│      - Notify captain: "New expense request from [crew]"   │
│    IF (captain role):                                       │
│      - Mark as 'pending' (awaiting owner approval)         │
│      - Notify owner: "New reimbursement request"           │
│      - Captain can modify until approval                    │
│    ELSE:                                                     │
│      - Auto-approve if preset rule matches                 │
└──────────────────┬──────────────────────────────────────────┘
                   │
└──────────────────▼──────────────────────────────────────────┘
                   Done!

2.2 OCR API Integration

Primary Provider: Google Cloud Vision API

// google-vision-integration.js

const vision = require('@google-cloud/vision');

async function extractReceiptData(imagePath) {
  const client = new vision.ImageAnnotatorClient({
    keyFilename: process.env.GOOGLE_VISION_KEY_FILE
  });

  const request = {
    image: { source: { filename: imagePath } },
  };

  const [result] = await client.documentTextDetection(request);
  const fullTextAnnotation = result.fullTextAnnotation;

  // Parse text detection results
  return {
    rawText: fullTextAnnotation.text,
    blocks: fullTextAnnotation.pages[0].blocks,
    textAnnotations: result.textAnnotations
  };
}

async function parseReceiptStructure(rawText, blocks) {
  // Custom parsing rules for boat receipts
  const parsed = {
    vendor: null,
    date: null,
    amount: null,
    vat: null,
    currency: 'EUR',
    lineItems: [],
    confidence: {}
  };

  // Pattern matching for common vendors
  const vendorPatterns = {
    marina: /marina|port|harbour|mooring|dock/i,
    fuel: /diesel|fuel|gasolina|essence|petrol|lpg|gas/i,
    maintenance: /maintenance|repair|service|technician|mechanic/i,
    supplies: /supply|chandlery|provisions|grocery|supermarket/i
  };

  // Date extraction (multiple formats)
  const dateRegex = /(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}|\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2})/;
  const dateMatch = rawText.match(dateRegex);
  if (dateMatch) {
    parsed.date = normalizeDate(dateMatch[1]);
    parsed.confidence.date = 0.95;
  }

  // Amount extraction (€, $, £, etc.)
  const amountRegex = /[€$£¥]?\s*(\d+[\.\,]\d{2})/;
  const amounts = rawText.matchAll(amountRegex);
  const amountArray = Array.from(amounts);

  if (amountArray.length > 0) {
    // Take largest amount (usually total)
    const total = Math.max(...amountArray.map(m => parseFloat(m[1].replace(',', '.'))));
    parsed.amount = total;
    parsed.confidence.amount = 0.90;
  }

  // VAT extraction
  const vatRegex = /VAT|TVA|IVA|GST|TAX[:\s]*(\d+[\.\,]\d{2})/i;
  const vatMatch = rawText.match(vatRegex);
  if (vatMatch) {
    parsed.vat = parseFloat(vatMatch[1].replace(',', '.'));
    parsed.confidence.vat = 0.85;
  }

  return parsed;
}

async function categorizeExpense(vendor, amount, rawText, boatId) {
  // Rule-based categorization
  const rulesDb = await loadCategoryRules(boatId);

  // Check vendor database first
  const vendorMatch = await db.query(
    'SELECT category_id FROM vendor_categories WHERE boat_id = ? AND vendor ILIKE ?',
    [boatId, vendor]
  );

  if (vendorMatch) {
    return {
      category: vendorMatch.category_id,
      confidence: 0.99,
      reason: 'vendor_history'
    };
  }

  // Pattern matching
  const patterns = {
    fuel: /diesel|fuel|gasoline|petrol|lpg|fuel_dock/i,
    provisions: /provisions|food|grocery|supermarket|market/i,
    maintenance: /maintenance|repair|service|mechanic|technician/i,
    marina: /marina|port|harbour|mooring|dock|berth/i,
    insurance: /insurance|assurance|coasec/i
  };

  for (const [category, pattern] of Object.entries(patterns)) {
    if (pattern.test(vendor) || pattern.test(rawText)) {
      return {
        category,
        confidence: 0.75,
        reason: 'pattern_match'
      };
    }
  }

  // Fallback: user-provided category
  return {
    category: 'miscellaneous',
    confidence: 0.40,
    reason: 'fallback'
  };
}

Fallback Provider: Tesseract (Local)

// tesseract-fallback.js

const Tesseract = require('tesseract.js');

async function extractWithTesseract(imagePath) {
  const { data: { text } } = await Tesseract.recognize(
    imagePath,
    ['eng', 'fra', 'deu', 'ita', 'spa'],  // Yacht typically multilingual
    {
      logger: m => console.log('Tesseract:', m.status, m.progress)
    }
  );

  return text;
}

3. Accounting Module Architecture

3.1 Integration Strategy: Fork vs. Library

Recommended Approach: Spliit Fork (MIT License)

Rationale:

  • Boat-specific features (owner/captain/crew roles not in original Spliit)
  • Full control over expense data model
  • Direct integration with IF.TTT compliance (signatures, hashes)
  • Customizable workflows (reimbursement approval chains)
  • Multi-currency support (Spliit has this; we enhance)
  • Receipt scanning core feature (matches our OCR pipeline)
  • PWA mobile support (critical for boat operations)
  • MIT license allows commercial use, modifications

Spliit Source: https://github.com/pmihaylov/spliit (2.3K stars, active)

Fork & Customize Process:

# 1. Fork Spliit repository
git clone https://github.com/[navidocs-org]/spliit-navidocs.git
cd spliit-navidocs

# 2. Merge NaviDocs-specific branches
git remote add upstream https://github.com/pmihaylov/spliit.git
git fetch upstream
git merge upstream/main  # Get latest Spliit features

# 3. Create navidocs-specific branch
git checkout -b feature/navidocs-yacht-integration

# 4. Customize for boat workflows
- Modify user roles (owner, captain, crew vs. Spliit's split member)
- Enhance expense model with boat_id, maintenance_log_id, etc.
- Add IF.TTT compliance layer (signatures, hashes, citations)
- Implement reimbursement approval workflow
- Add multi-currency with exchange rate tracking
- Integrate receipt OCR (use our Google Vision pipeline)
- Add export to Excel/CSV for accountants
- Implement access controls (captain sees own expenses, owner sees all)

3.2 Integration with NaviDocs Backend

Architecture Diagram:

┌─────────────────────────────────────────────────────────────┐
│                  NaviDocs Frontend (Vue 3)                   │
│                                                              │
│  Expenses Page: /app/boat/{boatId}/accounting               │
│  - Receipt upload modal                                     │
│  - Expense list (with search, filters, sort)                │
│  - Reimbursement requests (captain view)                    │
│  - Approval flow (owner view)                               │
│  - Export to Excel                                          │
└──────────────────┬──────────────────────────────────────────┘
                   │ HTTP/REST
┌──────────────────▼──────────────────────────────────────────┐
│             NaviDocs API Layer (Express.js)                  │
│                                                              │
│  Routes:                                                     │
│  POST   /api/expenses               Create expense (OCR)    │
│  GET    /api/expenses?filter=...    List expenses          │
│  GET    /api/expenses/{id}          Get expense detail      │
│  PUT    /api/expenses/{id}          Update expense          │
│  POST   /api/expenses/{id}/approve  Owner approve           │
│  POST   /api/expenses/{id}/reject   Owner reject            │
│  POST   /api/receipts/upload        Upload receipt image    │
│  POST   /api/receipts/{id}/ocr      Trigger OCR processing │
│  GET    /api/reimbursements         List requests           │
│  POST   /api/reimbursements         Create request          │
│  POST   /api/reimbursements/{id}/approve  Owner approve    │
│  GET    /api/expenses/export/excel  Export for accountant  │
│                                                              │
│  Middleware:                                                 │
│  - Authentication (JWT)                                     │
│  - Authorization (boat access control)                      │
│  - IF.TTT signing (Ed25519)                                 │
│  - Audit logging                                            │
└──────────────────┬──────────────────────────────────────────┘
                   │ ORM/Query
┌──────────────────▼──────────────────────────────────────────┐
│            Service Layer (Business Logic)                    │
│                                                              │
│  ExpenseService:                                             │
│  - createExpense(data) → IF.TTT signed record              │
│  - approveExpense(expenseId, userId)                       │
│  - getExpensesByBoat(boatId, filters)                      │
│  - calculateReimbursement(expenseIds)                       │
│  - exportToExcel(boatId, dateRange)                        │
│                                                              │
│  OCRService:                                                 │
│  - processReceipt(imagePath) → structured data             │
│  - categorizeExpense(data)                                  │
│  - extractWithGoogle(imagePath) | Tesseract(imagePath)    │
│                                                              │
│  ReimbursementService:                                       │
│  - createRequest(captainId, expenseIds)                    │
│  - approveRequest(ownerId, requestId)                      │
│  - calculateOwedAmount(captainId, boatId)                  │
│                                                              │
│  IF.TTTService:                                              │
│  - signExpense(expenseData, signingKey) → Ed25519 sig     │
│  - hashExpense(expenseData) → SHA-256                      │
│  - generateCitationId(boatId, expenseId)                   │
└──────────────────┬──────────────────────────────────────────┘
                   │ Database
┌──────────────────▼──────────────────────────────────────────┐
│              SQLite3 Database (on Disk)                      │
│                                                              │
│  Tables:                                                     │
│  - expenses                                                  │
│  - reimbursement_requests                                    │
│  - expense_categories                                        │
│  - exchange_rates                                            │
│  - receipt_images (metadata + hash)                         │
│  - if_signatures (IF.TTT compliance log)                    │
│  - audit_logs (all changes to expenses)                     │
└──────────────────────────────────────────────────────────────┘

External Services:
┌─────────────────────────────────────────────────────────────┐
│  Google Cloud Vision API (OCR)                               │
│  - Document text detection                                  │
│  - Structured data extraction                               │
│  - Handwriting recognition (for handwritten receipts)      │
│                                                              │
│  Background Job Queue (BullMQ + Redis)                      │
│  - Process OCR async (don't block user)                     │
│  - Generate exports (Excel files)                           │
│  - Send reimbursement notifications                         │
│                                                              │
│  WhatsApp Business API (to S2-H08)                          │
│  - Send approval requests to owner                          │
│  - Send reimbursement notifications to captain             │
│  - Inbound: "@NaviDocs log expense €45 fuel"              │
└─────────────────────────────────────────────────────────────┘

3.3 API Endpoint Specifications

POST /api/receipts/upload
  Description: Upload receipt image and trigger OCR
  Auth: Required (JWT)
  Body:
    image: File (JPEG/PNG, max 5MB)
    boat_id: string (UUID)
    captured_date: string (ISO 8601, optional)
    description: string (optional)
  Response:
    receipt_id: string
    upload_status: "uploaded"
    ocr_job_id: string  # For async polling
    ocr_status: "queued"
    ocr_estimated_time_seconds: 5
  Status: 201 Created

Webhook: /api/receipts/{receipt_id}/ocr-complete
  When: OCR processing finishes
  Payload:
    receipt_id: string
    extracted_data:
      vendor: string
      amount: number
      currency: string
      date: string (ISO 8601)
      vat: number
      confidence: object (per field)
    ocr_status: "success" | "partial" | "failed"

---

POST /api/expenses
  Description: Create expense from OCR data or manual entry
  Auth: Required (JWT)
  Body:
    boat_id: string (UUID)
    amount: number
    currency: string (EUR|USD|GBP)
    vendor: string
    date: string (ISO 8601)
    category_id: string (UUID)
    description: string (optional)
    receipt_id: string (optional)
    vat_amount: number (optional)
    linked_maintenance_id: string (optional)
    linked_inventory_id: string (optional)
  Response:
    id: string (UUID)
    status: "pending" | "pending_captain_approval" | "recorded"
    created_by_role: string (owner|captain|crew)
    citation_id: string (if://receipt/navidocs/boat-{boatId}/expense-{expenseId})
    ed25519_signature: string  # IF.TTT signing
    content_hash: string (SHA-256)
  Status: 201 Created

---

GET /api/expenses
  Description: List expenses for a boat with filtering
  Auth: Required (JWT)
  Query Params:
    boat_id: string (required)
    category: string (optional, UUID)
    date_from: string (ISO 8601, optional)
    date_to: string (ISO 8601, optional)
    status: string (pending|approved|rejected|paid, optional)
    created_by: string (owner|captain|crew, optional)
    payment_method: string (cash|card|bank_transfer, optional)
    min_amount: number (optional)
    max_amount: number (optional)
    sort: string (date_desc|amount_desc|vendor_asc, default=date_desc)
    page: number (default=1)
    limit: number (default=25, max=100)
  Response:
    expenses: array
      - id, vendor, amount, currency, date, category, status, created_by_role
    total_count: number
    page: number
    total_pages: number
    filters_applied: object
  Status: 200 OK

---

POST /api/expenses/{expense_id}/approve
  Description: Owner approves captain reimbursement
  Auth: Required (JWT)
  Body:
    approved_by_user_id: string (owner's user ID)
    notes: string (optional)
  Response:
    id: string
    status: "approved"
    approved_at: string (ISO 8601 timestamp)
    approved_by_user_id: string
  Status: 200 OK

---

POST /api/reimbursements
  Description: Captain submits reimbursement request
  Auth: Required (JWT)
  Body:
    boat_id: string (UUID)
    captain_user_id: string (UUID)
    expense_ids: array (of expense UUIDs)
    notes: string (optional)
  Response:
    id: string (UUID)
    status: "submitted"
    total_requested: number
    total_requested_currency: string
    submitted_at: string (ISO 8601)
  Status: 201 Created

---

POST /api/reimbursements/{request_id}/approve
  Description: Owner approves reimbursement request
  Auth: Required (JWT)
  Body:
    owner_user_id: string
    approved_amount: number (optional, for partial approval)
    payment_method: string (bank_transfer|cash|card)
    payment_date: string (ISO 8601)
    notes: string (optional)
  Response:
    id: string
    status: "approved"
    total_approved: number
    approved_at: string
    payment_method: string
  Status: 200 OK

---

GET /api/expenses/export/excel
  Description: Export expenses to Excel for accountant
  Auth: Required (JWT)
  Query Params:
    boat_id: string (required)
    date_from: string (ISO 8601)
    date_to: string (ISO 8601)
    include_rejected: boolean (default=false)
  Response:
    File: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
    Filename: navidocs-expenses-{boatId}-{dateFrom}-{dateTo}.xlsx
    Contents:
      - Expense sheet (all expenses with all fields)
      - Reimbursements sheet (captain reimbursement summary)
      - Summary sheet (totals by category, currency, status)
      - VAT report (VAT totals by jurisdiction)
      - IF.TTT audit (citation IDs, signatures, hashes for compliance)
  Status: 200 OK

---

POST /api/categories/suggest
  Description: ML-powered category suggestion based on vendor/amount
  Auth: Required (JWT)
  Body:
    vendor: string
    amount: number
    description: string (optional)
    boat_id: string (optional)
  Response:
    suggestions: array
      - category_id: string
      - category_name: string
      - confidence: number (0.0-1.0)
      - reason: string (vendor_history|pattern_match|ml_model)
  Status: 200 OK

4. Multi-User Expense Tracking Workflow

4.1 Owner Expense Recording

Owner logs in → Navigates to Accounting/Expenses

Option A: Manual Entry
├─ Clicks "Add Expense"
├─ Fills form:
│  ├─ Vendor: "Marina Porto"
│  ├─ Amount: €245.50
│  ├─ Date: 2025-11-13
│  ├─ Category: Marina Fees
│  ├─ Payment Method: Card
│  └─ Notes: "November mooring"
├─ Clicks "Save"
└─ Expense created, status: "recorded"

Option B: Photo Upload (OCR)
├─ Clicks "Upload Receipt"
├─ Takes photo or selects from gallery
├─ Waits for OCR processing (2-5 seconds)
├─ Reviews extracted data
│  ├─ Vendor: "Marina Porto" ✓
│  ├─ Amount: €245.50 (confidence: 92%) ✓
│  ├─ Date: 2025-11-13 ✓
│  ├─ Category: Marina Fees (suggested) ✓
│  └─ VAT: €37.50 ✓
├─ Can edit any field before saving
├─ Clicks "Save"
└─ Expense created with IF.TTT signature

4.2 Captain Expense Reimbursement Flow

Captain purchases provisions for €52.30 with personal cash

Step 1: Captain logs expense
├─ Opens mobile app (any time, any place)
├─ Clicks "Add Expense"
├─ Takes photo of receipt
├─ System extracts: Vendor: "Supermarket", Amount: €52.30, Date: today
├─ Selects category: "Provisions"
├─ Notes: "Weekly supplies"
└─ Clicks "Submit for Approval"
   └─ Expense status: "pending" (awaits owner approval)
   └─ Notification sent to owner: "Captain requested reimbursement: €52.30"

Step 2: Expense aggregation (optional)
├─ Captain can submit multiple expenses together
├─ Clicks "Create Reimbursement Request"
├─ Selects expenses: [52.30 + 35.00 + 18.50] = €105.80
├─ Adds note: "Personal cash advances from Oct 20-Nov 13"
├─ Clicks "Submit Request"
└─ Reimbursement request created
   └─ Status: "submitted"
   └─ Notification: Owner sees request in dashboard

Step 3: Owner reviews & approves
├─ Owner logs into app
├─ Sees reimbursement request notification
├─ Clicks to expand request details:
│  ├─ Captain: "Marco (Captain)"
│  ├─ Total: €105.80
│  ├─ Expenses:
│  │  ├─ Supermarket (Provisions): €52.30 ← owner can expand receipt image
│  │  ├─ Bakery (Provisions): €35.00 ← approve/reject individual items
│  │  └─ Marine Supply (Supplies): €18.50
│  └─ Notes: "Personal cash advances from Oct 20-Nov 13"
├─ Owner has options:
│  ├─ [Approve All] → All expenses marked "approved"
│  ├─ [Approve Partial] → Select which to approve
│  ├─ [Reject] → All returned to captain (stays pending)
│  ├─ [Message Captain] → Ask for clarification (via WhatsApp)
│  └─ [Reject Item] → Specific items rejected, others approved
├─ Owner clicks "Approve All"
└─ Expenses status updated to "approved"
   └─ Notification: Captain sees "Your reimbursement of €105.80 approved!"

Step 4: Payment processing
├─ Owner marks reimbursement as "paid"
├─ Owner selects payment method:
│  ├─ Bank transfer (account details pre-filled from captain profile)
│  ├─ Cash (mark as paid after handing over cash)
│  ├─ Card (transfer via app if available)
│  └─ Other
├─ Enters payment date & reference (bank transaction ID)
├─ Clicks "Mark Paid"
└─ Reimbursement status: "paid"
   └─ Expenses status: "paid"
   └─ Notification: Captain receives "Reimbursement sent: €105.80 on 2025-11-14"

Step 5: Audit trail
├─ Expense has complete IF.TTT audit trail:
│  ├─ who: Captain (Ed25519 signature)
│  ├─ when: 2025-10-20 10:15 AM
│  ├─ what: Original receipt photo + extracted data
│  ├─ owner_approval: Owner name, timestamp
│  ├─ payment: Bank transfer ref 12345678
│  └─ citation_id: if://receipt/navidocs/boat-123/expense-4567
└─ Accountant can verify chain of custody

4.3 Crew Expense Flow

Similar to captain, but:

  • Captain must pre-approve before owner sees it
  • Workflow: Crew logs → Captain approves → Owner approves → Marked paid
  • Prevents owner seeing every crew purchase individually
Crew buys ice for €8.50 with boat cash

Step 1: Crew logs expense
├─ App logs: "Crew purchased ice for €8.50"
├─ Takes receipt photo (if available)
└─ Status: "pending_captain_approval"
   └─ Captain notification: "Crew expense pending: €8.50"

Step 2: Captain reviews (filters out small/pre-approved items)
├─ Captain sees list of crew expenses pending approval
├─ Captain can:
│  ├─ [Approve] → Status becomes "pending" (awaits owner approval)
│  ├─ [Reject] → Status "rejected" (expense removed from boat cash log)
│  ├─ [Auto-Approve] → Set rules:
│  │  ├─ Items under €20: auto-approve
│  │  ├─ Specific categories: auto-approve (ice, provisions)
│  └─ [View] → See expense details + receipt image
└─ Captain approves (or auto-approved by rule)
   └─ Status: "pending" (awaits owner approval)
   └─ Owner sees in daily summary: "5 crew expenses, €47.30 total"

Step 3: Owner bulk-approves crew expenses
├─ Owner opens dashboard
├─ Sees: "5 pending crew expenses (€47.30)"
├─ Owner options:
│  ├─ [Approve All] → All marked "approved"
│  ├─ [View Details] → Expand to see each item
│  └─ [Reject Items] → Sends back to captain with questions
├─ Owner clicks "Approve All"
└─ All crew expenses: "approved"
   └─ Captain notified: "5 crew expenses approved (€47.30)"
   └─ Owner knows they owe captain €47.30 in boat cash reimbursement

Alternative: Boat Cash Account
├─ If boat maintains cash drawer (e.g., €500)
├─ Transactions: [+€500 starting balance] → [-€8.50 ice] → [-€23.00 fuel] → etc.
├─ Owner periodically reimburses boat cash drawer
├─ Captain tracks: "Boat cash remaining: €250.30"
└─ Reconciliation: Physical count vs. app records

5. Reimbursement Workflow (Detailed)

5.1 States & Transitions

Captain Expense States:
┌─────────────┐
│   pending   │  Captain just logged expense (personal cash)
└──────┬──────┘
       │ (Owner approves)
       ▼
┌─────────────┐
│  approved   │  Owner approved reimbursement amount
└──────┬──────┘
       │ (Owner pays captain)
       ▼
┌─────────────┐
│    paid     │  Captain received payment
└─────────────┘

Alternative: Partial Approval
┌─────────────┐
│   pending   │
└──────┬──────┘
       │ (Owner partially approves)
       ▼
┌─────────────┐
│   partial   │  Some items approved, others rejected/questioned
└──────┬──────┘
       │ (Captain modifies; Owner reviews again)
       ▼
┌─────────────┐
│  approved   │  All items resolved
└──────┬──────┘
       │ (Owner pays)
       ▼
┌─────────────┐
│    paid     │
└─────────────┘

Rejection Path:
┌─────────────┐
│   pending   │
└──────┬──────┘
       │ (Owner rejects)
       ▼
┌─────────────┐
│  rejected   │  Owner didn't approve (personal expense, disallowed, etc.)
└─────────────┘
       (Captain can re-submit with justification or drop claim)

5.2 Notification Triggers

Event Notify Channel Message
Captain submits expense Owner WhatsApp + App "Marco requested €52.30 reimbursement for provisions"
Owner approves expense Captain WhatsApp + App "Your €52.30 expense approved! Payment will be sent by 2025-11-15"
Owner rejects expense Captain WhatsApp + App "Your €52.30 provisions expense was rejected. See note: Please check receipt - amount doesn't match vendor markup"
Owner marks paid Captain WhatsApp + App "Reimbursement of €245.80 sent via bank transfer"
Bulk crew expenses approved Captain WhatsApp + App "5 crew expenses approved (€47.30)"
Reimbursement request pending Owner Email + App "3-day reminder: Marco's reimbursement request (€105.80) pending your approval"

6. WhatsApp AI Integration (S2-H08 Dependency)

6.1 Expense Logging via WhatsApp

Integration Point: S2-H08 (WhatsApp Group Integration) handles chat webhook; S2-H06 (Accounting) handles expense processing

Example: Captain in WhatsApp group chat

Captain: "@NaviDocs log expense €45 diesel"
│
▼ (IF.bus to S2-H08)

S2-H08 WhatsApp Agent:
├─ Receives message via WhatsApp Business API webhook
├─ Parses: amount=€45, category=fuel, description="diesel"
├─ Checks if creator has authorization (captain role for this boat)
├─ Calls: POST /api/expenses with parsed data
│
▼ (IF.bus to S2-H06)

S2-H06 Accounting Service:
├─ Creates expense record:
│  ├─ amount: 45.00
│  ├─ currency: EUR
│  ├─ category: Fuel
│  ├─ description: (from chat context)
│  ├─ created_by: captain
│  └─ status: pending_captain_approval (if crew) or pending (if captain)
├─ Generates IF.TTT signature + citation
└─ Returns expense_id

S2-H08 WhatsApp Agent:
├─ Posts confirmation: "@Marco ✓ Logged: €45 diesel on 2025-11-13"
├─ Optional: Adds quick buttons:
│  ├─ [View Receipt] → Link to expense detail
│  ├─ [Edit] → Links to app to upload receipt photo
│  └─ [More] → Shows recent 5 expenses
└─ Stores in IF.TTT audit log: message hash + citation

Command Syntax:

@NaviDocs log expense [amount] [currency] [category] [description]
@NaviDocs log [amount] [currency] [description]  # auto-category

Examples:
@NaviDocs log expense €45 fuel             → €45.00, Fuel, auto-date
@NaviDocs log €35 EUR provisions           → €35.00, Provisions
@NaviDocs log $80 marina fees Antibes      → $80 USD, Marina, Antibes
@NaviDocs log 45 GBP diesel fuel Kingston  → £45.00, Fuel, Kingston

Advanced:
@NaviDocs log expense €150 maintenance Hull repair invoice #12345
→ Links to maintenance log if ID provided
@NaviDocs reimbursement request [list of recent expense IDs]
→ Creates reimbursement request from last N expenses

7. Export Functionality for Accountants

7.1 Excel Export Structure

File: navidocs-expenses-[BoatID]-[2025-01-01]-[2025-12-31].xlsx

Sheet 1: Expenses
├─ Row 1: Header row
│  ├─ Expense ID | Vendor | Category | Amount | Currency | Date | VAT | Net Amount
│  ├─ Payment Method | Created By | Status | Receipt Image Link | Notes
│  ├─ Maintenance Log Link | Inventory Link | Citation ID
├─ Rows 2+: Data rows (one per expense)
└─ Footer: Totals row (sum by column)

Sheet 2: Reimbursements
├─ Reimbursement ID | Captain | Total Requested | Total Approved | Total Paid
├─ Currency | Request Date | Status | Approved Date | Paid Date | Payment Method
└─ Notes

Sheet 3: Summary Statistics
├─ Date Range: 2025-01-01 to 2025-12-31
├─ Total Expenses: €XXX
├─ Breakdown by Category:
│  ├─ Fuel: €X (5% of total)
│  ├─ Marina: €Y (20% of total)
│  ├─ Maintenance: €Z (40% of total)
│  └─ ...
├─ Breakdown by Payment Method:
│  ├─ Cash: €A
│  ├─ Card: €B
│  └─ Bank Transfer: €C
├─ Breakdown by Created By:
│  ├─ Owner: €D
│  ├─ Captain: €E
│  └─ Crew: €F
├─ Currency Conversion Summary:
│  ├─ EUR: €XXX
│  ├─ USD: €YYY (converted)
│  └─ GBP: €ZZZ (converted)
├─ Reimbursements Total:
│  ├─ Requested: €RRR
│  ├─ Approved: €AAA
│  └─ Paid: €PPP

Sheet 4: VAT Report
├─ Date | Category | Vendor | Gross | VAT % | VAT Amount | Net
├─ ...
├─ Total VAT Collected by Category:
│  ├─ Fuel: €X VAT (from €Y gross)
│  ├─ Maintenance: €A VAT (from €B gross)
│  └─ ...
└─ Total VAT: €VVV (eligible for deduction if business boat)

Sheet 5: IF.TTT Audit Trail
├─ Expense ID | Citation ID | Ed25519 Signature | SHA-256 Hash
├─ Creator | Creation Date | Approver | Approval Date
├─ Payment Date | Payment Reference
├─ Receipt Image Hash | Receipt Image URL
└─ [Full audit for compliance verification]

Sheet 6: Data Dictionary
├─ Column explanations
├─ Category list (full hierarchy)
├─ Payment method definitions
└─ Status definitions

7.2 Export API

// Generate Excel export
GET /api/expenses/export/excel?boat_id=X&from=2025-01-01&to=2025-12-31&format=detailed

Response Headers:
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename=navidocs-expenses-[boatId]-2025.xlsx

Response: Binary XLSX file (generated via ExcelJS or similar library)

// Also support CSV export for quick import to accounting software
GET /api/expenses/export/csv?boat_id=X&sheet=expenses&from=2025-01-01&to=2025-12-31

Response Headers:
Content-Type: text/csv; charset=utf-8
Content-Disposition: attachment; filename=navidocs-expenses-[boatId]-2025.csv

Response: CSV data (directly importable to QuickBooks, Xero, FreshBooks, etc.)

8. IF.TTT Compliance Architecture

8.1 Signature & Hash Implementation

// if-compliance.service.js

const crypto = require('crypto');
const ed25519 = require('libsodium').crypto_sign_detached;

class IFTTTService {

  /**
   * Generate SHA-256 hash of expense data (for tamper detection)
   */
  async hashExpense(expenseData) {
    const canonical = {
      boat_id: expenseData.boat_id,
      amount: expenseData.amount,
      currency: expenseData.currency,
      vendor: expenseData.vendor,
      date: expenseData.date,
      category_id: expenseData.category_id,
      vat_amount: expenseData.vat_amount,
      payment_method: expenseData.payment_method,
      description: expenseData.description,
      created_by_user_id: expenseData.created_by_user_id,
      created_by_role: expenseData.created_by_role,
      receipt_image_hash: expenseData.receipt_image_hash
    };

    const jsonString = JSON.stringify(canonical, Object.keys(canonical).sort());
    const hash = crypto.createHash('sha256').update(jsonString).digest('hex');

    return hash;
  }

  /**
   * Generate Ed25519 signature of expense (proof of creator identity)
   */
  async signExpense(expenseData, creatorPrivateKey) {
    const contentHash = await this.hashExpense(expenseData);

    // Ed25519 sign the content hash
    const signature = ed25519(Buffer.from(contentHash), Buffer.from(creatorPrivateKey));

    return signature.toString('hex');
  }

  /**
   * Verify Ed25519 signature (ensure creator identity)
   */
  async verifySignature(contentHash, signature, creatorPublicKey) {
    try {
      const verified = sodium.crypto_sign_open(
        Buffer.from(signature + contentHash, 'hex'),
        Buffer.from(creatorPublicKey, 'hex')
      );
      return true;
    } catch (e) {
      return false;
    }
  }

  /**
   * Generate citation ID (permanent reference to this expense)
   */
  generateCitationId(boatId, expenseId) {
    // Format: if://receipt/navidocs/boat-{boatId}/expense-{expenseId}
    return `if://receipt/navidocs/boat-${boatId}/expense-${expenseId}`;
  }

  /**
   * Generate receipt image hash (for image tamper detection)
   */
  async hashReceiptImage(imagePath) {
    const fs = require('fs').promises;
    const fileBuffer = await fs.readFile(imagePath);
    const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
    return hash;
  }

  /**
   * Complete IF.TTT compliance record
   */
  async createIFTTTRecord(expenseData, creatorPrivateKey, receiptImagePath) {
    const contentHash = await this.hashExpense(expenseData);
    const receiptImageHash = receiptImagePath
      ? await this.hashReceiptImage(receiptImagePath)
      : null;
    const signature = await this.signExpense(expenseData, creatorPrivateKey);
    const citationId = this.generateCitationId(expenseData.boat_id, expenseData.id);

    return {
      content_hash: contentHash,
      ed25519_signature: signature,
      citation_id: citationId,
      receipt_image_hash: receiptImageHash,
      creator_public_key: derivePublicKey(creatorPrivateKey),  // Stored for verification
      signed_at: new Date().toISOString()
    };
  }

  /**
   * Audit verification: prove expense hasn't been tampered with
   */
  async verifyExpenseIntegrity(expense) {
    // 1. Recalculate content hash
    const currentHash = await this.hashExpense(expense);
    if (currentHash !== expense.content_hash) {
      return {
        valid: false,
        reason: "Content hash mismatch - expense data has been modified"
      };
    }

    // 2. Verify Ed25519 signature
    const signatureValid = await this.verifySignature(
      currentHash,
      expense.ed25519_signature,
      expense.creator_public_key
    );
    if (!signatureValid) {
      return {
        valid: false,
        reason: "Ed25519 signature invalid - creator identity cannot be verified"
      };
    }

    // 3. Verify receipt image hash (if image exists)
    if (expense.receipt_image_url && expense.receipt_image_hash) {
      const currentImageHash = await this.hashReceiptImage(expense.receipt_image_url);
      if (currentImageHash !== expense.receipt_image_hash) {
        return {
          valid: false,
          reason: "Receipt image hash mismatch - image has been modified"
        };
      }
    }

    return {
      valid: true,
      verified_at: new Date().toISOString(),
      verifications: [
        "Content hash verified (no tampering)",
        "Ed25519 signature verified (creator identity confirmed)",
        "Receipt image hash verified (no image tampering)"
      ]
    };
  }
}

module.exports = new IFTTTService();

8.2 IF.TTT Compliance Checklist

IF.TTT Compliance for Accounting Module:

Receipt Image Handling:
✅ SHA-256 hash of receipt image file
✅ Hash stored in database (immutable)
✅ Original image stored in S3 with CRC32 check
✅ Any image modification detected via hash mismatch

Expense Creator Identity:
✅ Ed25519 signature of expense data
✅ Signature generated with creator's private key
✅ Public key stored (derived from private key)
✅ Any future creator verification possible

Audit Trail:
✅ Citation ID: if://receipt/navidocs/boat-{boatId}/expense-{expenseId}
✅ Permanent reference (never changes)
✅ Includes: who, what, when, where
✅ Linkable: boat UUID, expense UUID
✅ Resolvable: GET /api/citations/if://receipt/navidocs/...

Complete Immutable Record:
✅ Original receipt image + hash
✅ Extracted OCR data (vendor, amount, date, VAT)
✅ Creator identity (captain, crew, owner)
✅ Creation timestamp
✅ All approvals + timestamps + approver identity
✅ Payment tracking (method, date, reference)
✅ Any modifications logged separately (audit_log field)

Data Integrity Verification:
✅ Verify content hash (no data tampering)
✅ Verify Ed25519 signature (creator identity)
✅ Verify receipt image hash (no image tampering)
✅ All three checks must pass for "verified" status

Multi-User Workflow Traceability:
✅ Captain creates: Ed25519 sig + timestamp
✅ Owner approves: separate approval record + sig + timestamp
✅ Payment marked: payment timestamp + method + reference
✅ Full chain of custody maintained

Accountant Audit Requirements:
✅ Expense can be traced back to receipt image
✅ Receipt image authenticity verified (hash)
✅ Creator identity verified (Ed25519 signature)
✅ Approval chain visible (timestamps, names)
✅ Payment status documented
✅ VAT/tax calculations auditable
✅ Multi-currency conversions with exchange rates

Regulatory Compliance:
✅ EU Tax Authority (CJEU) - receipt authenticity: SHA-256 hash
✅ GDPR - access logs show who viewed expense
✅ GDPR - right to deletion: anonymize but keep hash + signatures
✅ Audit trails: 7-year retention minimum
✅ Document preservation: receipt images stored indefinitely

Cryptographic Standards:
✅ SHA-256 (NIST standard, FIPS 180-4)
✅ Ed25519 (quantum-resistant signature scheme, RFC 8032)
✅ All cryptographic operations via libsodium (battle-tested, auditedlib)

Export & Compliance:
✅ Excel export includes IF.TTT audit sheet
✅ Citation IDs in export (accountant can verify)
✅ Signature hashes in export (for verification)
✅ CSV export suitable for QuickBooks import
✅ Signatures + hashes preserved in all exports

9. Database Integration Points

9.1 Foreign Key Relationships

-- Link expenses to other modules

-- 1. Maintenance Log Integration
--    "This fuel expense is related to engine maintenance"
ALTER TABLE expenses
ADD COLUMN maintenance_log_id VARCHAR(36),
ADD FOREIGN KEY (maintenance_log_id) REFERENCES maintenance_log(id);

-- Query: Find all expenses related to specific maintenance
SELECT e.* FROM expenses e
WHERE e.maintenance_log_id = '...'
AND e.boat_id = '...';

-- 2. Inventory Integration
--    "This €500 expense is for the new autopilot (inventory item)"
ALTER TABLE expenses
ADD COLUMN inventory_item_id VARCHAR(36),
ADD FOREIGN KEY (inventory_item_id) REFERENCES boat_inventory(id);

-- Query: What was total expense for this item?
SELECT SUM(e.amount) as total_cost, e.currency
FROM expenses e
WHERE e.inventory_item_id = '...'
AND e.boat_id = '...';

-- 3. Calendar Integration
--    "This expense is for the scheduled hull repaint work"
ALTER TABLE expenses
ADD COLUMN calendar_event_id VARCHAR(36),
ADD FOREIGN KEY (calendar_event_id) REFERENCES calendar_events(id);

-- Query: Budget vs actual for planned work
SELECT
  ce.title,
  ce.budget_amount,
  SUM(e.amount) as actual_amount,
  (ce.budget_amount - SUM(e.amount)) as variance
FROM calendar_events ce
LEFT JOIN expenses e ON e.calendar_event_id = ce.id
WHERE ce.event_type = 'work_planned'
AND ce.boat_id = '...'
GROUP BY ce.id;

-- 4. Vendor (Contact) Integration
ALTER TABLE expenses
ADD COLUMN vendor_id VARCHAR(36),
ADD FOREIGN KEY (vendor_id) REFERENCES boat_contacts(id);

-- Query: Show most frequently-used vendors
SELECT c.name, COUNT(*) as transaction_count, SUM(e.amount) as total_spent
FROM expenses e
JOIN boat_contacts c ON e.vendor_id = c.id
WHERE e.boat_id = '...'
GROUP BY c.id
ORDER BY total_spent DESC;

-- 5. Crew/Captain Tracking
ALTER TABLE expenses
ADD COLUMN created_by_user_id VARCHAR(36),
ADD FOREIGN KEY (created_by_user_id) REFERENCES users(id);

-- Query: How much did captain spend this month?
SELECT SUM(e.amount) as monthly_spend
FROM expenses e
JOIN users u ON e.created_by_user_id = u.id
WHERE e.boat_id = '...'
AND YEAR(e.purchase_date) = 2025
AND MONTH(e.purchase_date) = 11
AND u.role = 'captain';

10. Integration with S2-H08 (WhatsApp) & S2-H03 (Maintenance)

10.1 IF.bus Communication Protocol

{
  "performative": "request",
  "sender": "if://agent/session-2/haiku-06",
  "receiver": ["if://agent/session-2/haiku-08", "if://agent/session-2/haiku-03"],
  "conversation_id": "if://conversation/navidocs-accounting-2025-11-13",
  "content": {
    "integration": "Expense tracking needs WhatsApp AI commands and maintenance log linking",
    "api_requirements": {
      "whatsapp_integration": {
        "command": "POST /api/expenses from WhatsApp @NaviDocs command",
        "format": "@NaviDocs log expense €45 fuel",
        "handler": "S2-H08 WhatsApp agent → S2-H06 expense endpoint",
        "webhook": "POST /api/expenses with: {amount, currency, category, description, created_by_whatsapp_user_id}"
      },
      "maintenance_link": {
        "command": "POST /api/expenses with optional maintenance_log_id",
        "format": "expense can reference maintenance_log record",
        "handler": "S2-H06 creates expense linked to maintenance",
        "query": "GET /api/expenses?maintenance_log_id=X shows all expenses for service"
      }
    },
    "schema": {
      "POST /api/expenses": {
        "amount": "number",
        "currency": "enum(EUR|USD|GBP)",
        "category": "string or category_id",
        "description": "string",
        "linked_maintenance_id": "uuid (optional)",
        "receipt_image_url": "string (optional)",
        "created_by_whatsapp_user_id": "string (optional, if from WhatsApp)"
      }
    },
    "examples": [
      {
        "scenario": "Captain logs fuel expense via WhatsApp",
        "whatsapp_message": "@NaviDocs log expense €45 diesel",
        "s2h08_action": "Parse message → call POST /api/expenses",
        "s2h06_action": "Create expense record, confirm in WhatsApp"
      },
      {
        "scenario": "Owner wants to link fuel expense to engine maintenance",
        "api_call": "PUT /api/expenses/exp-123 with {maintenance_log_id: 'maint-456'}",
        "s2h03_query": "GET /api/expenses?maintenance_log_id=maint-456 (from maintenance module)"
      }
    ],
    "dependencies": [
      "S2-H08: WhatsApp API webhook → expense creation",
      "S2-H03: Maintenance log provides maintenance_log_id for linking",
      "S2-H06: Expense service accepts both direct API calls and WhatsApp-triggered calls"
    ]
  },
  "citation_ids": ["if://citation/navidocs-accounting-design"],
  "timestamp": "2025-11-13T10:00:00Z",
  "sequence_num": 1
}

10.2 S2-H03 (Maintenance) Integration

Maintenance log provides:

  • maintenance_log_id: UUID to link expenses
  • service_type: "engine", "electronics", "hull", etc. → helps categorize expenses
  • date: Service date → can auto-fill expense date
  • cost_estimate: Maintenance module tracks estimated vs. actual

Expense module queries maintenance:

// When captain logs expense for engine service
POST /api/expenses {
  amount: 450.00,
  currency: "EUR",
  category: "Maintenance",
  description: "Engine oil change",
  maintenance_log_id: "maint-789"  // Links back to maintenance record
}

// Maintenance module can then query:
GET /api/expenses?maintenance_log_id=maint-789
// Response: All expenses associated with that maintenance work
// Allows: Track estimate vs actual spending

// Dashboard shows:
// "Engine Service (Nov 13)"
// └─ Estimated Cost: €400
// └─ Actual Expenses: €450 (oil change) + €85 (labor) = €535
// └─ Variance: +€135 (33% over estimate)

Implementation Roadmap

Phase 1: Core Expense Tracking (Week 1-2)

  • Database schema: expenses, categories, reimbursement_requests
  • API endpoints: POST /expenses, GET /expenses, GET /reimbursements
  • Authorization: boat access control, role-based approval
  • Manual expense entry UI (Vue form)
  • Export to CSV (basic)

Phase 2: Receipt OCR (Week 2-3)

  • Receipt upload endpoint: POST /api/receipts/upload
  • Google Vision API integration
  • Tesseract fallback
  • Extraction: vendor, amount, date, VAT, category
  • Review & approval UI

Phase 3: Reimbursement Workflow (Week 3-4)

  • Reimbursement request creation & approval
  • Captain → Owner approval flow
  • Payment tracking (method, date, reference)
  • Notifications (WhatsApp integration with S2-H08)

Phase 4: IF.TTT Compliance (Week 4)

  • SHA-256 hashing (content + receipt images)
  • Ed25519 signatures (creator identity)
  • Citation ID generation
  • Audit trail storage
  • Compliance verification API

Phase 5: Advanced Features (Week 5+)

  • Multi-currency support with exchange rate tracking
  • Category hierarchy & suggestions
  • Vendor database & recurring expense templates
  • Integration with maintenance log (S2-H03)
  • Advanced search & reporting
  • Excel export with summary sheets
  • WhatsApp bot commands (@NaviDocs log expense)

Configuration & Environment

# .env variables for accounting module

# Google Cloud Vision API
GOOGLE_VISION_PROJECT_ID=navidocs-prod
GOOGLE_VISION_KEY_FILE=/etc/secrets/google-vision-key.json
GOOGLE_VISION_API_KEY=AIza...

# OCR Processing
TESSERACT_PATH=/usr/bin/tesseract
OCR_TIMEOUT_SECONDS=30
OCR_WEBHOOK_URL=https://api.navidocs.io/api/receipts/ocr-callback

# Expense Module
ACCOUNTING_MODULE_ENABLED=true
MULTI_CURRENCY_SUPPORT=true
PRIMARY_CURRENCY_EUR=true
SUPPORTED_CURRENCIES=EUR,USD,GBP

# Reimbursement
REIMBURSEMENT_APPROVAL_REQUIRED=true
CREW_APPROVAL_REQUIRED=true
CAPTAIN_APPROVAL_FOR_CREW=true

# IF.TTT Compliance
IFTTT_SIGNING_ENABLED=true
ED25519_PRIVATE_KEY_PATH=/etc/secrets/accounting-ed25519-key.pem
CITATION_ID_BASE=if://receipt/navidocs

# Export
EXCEL_EXPORT_ENABLED=true
CSV_EXPORT_ENABLED=true
EXPORT_INCLUDE_SIGNATURES=true
EXPORT_INCLUDE_AUDIT_TRAIL=true

# WhatsApp Integration (S2-H08)
WHATSAPP_API_WEBHOOK_URL=https://api.navidocs.io/api/expenses/whatsapp-webhook
WHATSAPP_EXPENSE_LOGGING_ENABLED=true

Deliverable Summary

S2-H06 has completed:

  1. Multi-user expense tracking design

    • Owner, Captain, Crew roles with separate workflows
    • Expense categories (hierarchical, boat-specific)
    • Database schema with reimbursement tracking
  2. Receipt/Invoice OCR workflow

    • Photo upload (camera + gallery)
    • Google Vision + Tesseract processing
    • Auto-extraction (vendor, amount, date, VAT, category)
    • Review & edit UI before saving
    • IF.TTT compliance (SHA-256 + signature)
  3. Accounting module architecture

    • Recommendation: Spliit fork (MIT license, receipt-focused)
    • Integration with NaviDocs backend (Express.js + SQLite)
    • Multi-currency support (€, $, £)
  4. Reimbursement workflow

    • Captain submit → OCR extract → Owner approve → Mark paid
    • Crew pre-approval by captain (optional bulk approval)
    • Partial approval & rejection handling
    • Payment tracking & notification triggers
  5. WhatsApp AI integration (S2-H08 dependency)

    • "@NaviDocs log expense €45 fuel" → Auto-creates expense
    • IF.bus communication protocol defined
    • Webhook integration with WhatsApp Business API
  6. Export functionality

    • Excel export (detailed, summary, VAT, audit sheets)
    • CSV export for accountant software (QuickBooks, Xero)
    • IF.TTT audit sheet included
  7. IF.TTT compliance checklist

    • SHA-256 hashing (receipt image + expense data)
    • Ed25519 signatures (creator identity)
    • Citation ID format (if://receipt/navidocs/boat-{id}/expense-{id})
    • Audit trail storage (immutable)
    • Integrity verification API

Status: COMPLETE

File: /home/user/navidocs/intelligence/session-2/accounting-integration-spec.md

Integration Dependencies:

  • S2-H08 (WhatsApp integration) - for @NaviDocs command execution
  • S2-H03 (Maintenance log) - for expense linking to maintenance records

Next Steps (for S2-H10 synthesis):

  • Integrate with calendar system (budget vs actual for planned work)
  • Integrate with VAT/tax compliance (Agent 3A)
  • Ensure expense notifications flow to WhatsApp group (S2-H08)
  • Design database indices for performance (accounting queries on 10K+ expenses)