Skip to content

Rules Engine

The RulesEngine is a pure business logic component (IDesign Engine layer) that evaluates company policies deterministically. It never makes I/O calls -- it receives data and returns pass/fail decisions with reasons.

Design Principles

  1. Deterministic -- same input always produces the same result, no LLM involved
  2. Configurable -- rules are YAML, not code; customers can customize via dashboard
  3. Auditable -- every evaluation is logged with rule name, input, and result
  4. Composable -- rules can reference other rules and combine with AND/OR logic

Rule Evaluation Flow

Input Data ──→ RulesEngine.evaluate(rule_type, data)
                    │
                    ├── Load rules for company + rule_type
                    ├── Evaluate each rule condition
                    ├── Return: PASS | WARN | FAIL + reasons[]
                    └── Log evaluation to audit trail

Rule Types

1. Expense Policy (expense_policy)

Controls what expenses employees can submit and what requires approval.

rule_type: expense_policy
config:
  travel:
    max_daily_rate: 800        # NOK per diem
    receipt_required_above: 500 # NOK
    approval_required_above: 5000
    allowed_categories:
      - taxi
      - flight
      - train
      - hotel
      - meals
      - parking
    forbidden_categories:
      - alcohol
      - entertainment_personal
    max_single_expense: 20000
    max_monthly_per_employee: 50000

  office:
    receipt_required_above: 200
    approval_required_above: 2000
    max_single_expense: 10000

  representation:
    max_per_person: 550        # Tax-deductible limit (2026)
    max_attendees: 20
    requires_attendee_list: true
    requires_business_purpose: true

Evaluation example:

Input:  {category: "taxi", amount: 1200, has_receipt: false, employee_id: 12}
Rule:   receipt_required_above: 500
Result: FAIL — "Kvittering påkrevd for beløp over 500 kr (beløp: 1 200 kr)"

2. Approval Thresholds (approval_threshold)

Defines who can approve what, based on amount and category.

rule_type: approval_threshold
config:
  levels:
    - max_amount: 5000
      approver: auto          # No human approval needed
    - max_amount: 25000
      approver: department_manager
    - max_amount: 100000
      approver: cfo
    - max_amount: null         # Unlimited
      approver: ceo

  overrides:
    - category: representation
      always_requires: department_manager
    - category: it_equipment
      above: 10000
      requires: it_manager

3. Account Mapping (account_mapping)

Rules for automatically selecting the correct ledger account based on expense category, supplier, or description.

rule_type: account_mapping
config:
  by_category:
    taxi: 7140           # Reise og diett
    flight: 7140
    hotel: 7140
    meals_business: 7350 # Representasjon
    meals_travel: 7140
    office_supplies: 6540
    software: 6440       # Programvare
    phone: 6900
    internet: 6900
    postage: 6800
    cleaning: 6360

  by_supplier_org_number:
    "923609016": 6900    # Telenor → Telefon
    "985615616": 6440    # Microsoft → Programvare
    "979191138": 6900    # Telia → Telefon

  by_description_pattern:
    - pattern: "(?i)fly|flight|sas|norwegian|wideroe"
      account: 7140
    - pattern: "(?i)hotell|hotel|scandic|thon|radisson"
      account: 7140
    - pattern: "(?i)parkering|parking|apcoa|europark"
      account: 7160
    - pattern: "(?i)drivstoff|bensin|diesel|circle.k|esso|shell"
      account: 7020

  default_account: 7700  # Annen kostnad

4. Payment Deadline (payment_deadline)

Controls when to alert about upcoming and overdue payments.

rule_type: payment_deadline
config:
  alert_days_before_due: 3
  overdue_alert_intervals: [1, 3, 7, 14, 30]  # days after due date
  escalation:
    - days_overdue: 7
      notify: accountant
    - days_overdue: 14
      notify: [accountant, cfo]
    - days_overdue: 30
      notify: [accountant, cfo, ceo]
      action: suggest_reminder

  customer_invoices:
    reminder_after_days: 14
    second_reminder_after_days: 28
    collection_after_days: 42
    reminder_fee: 70           # NOK (inkassovarsel)
    interest_rate: 0.0833      # Forsinkelsesrente per month

5. Payroll Validation (payroll_validation)

Flags anomalies in payroll before processing.

rule_type: payroll_validation
config:
  deviation_threshold_percent: 20  # Flag if salary deviates >20% from 6-month average
  max_overtime_hours: 40           # Per month
  min_gross_salary: 0              # Allow zero (unpaid leave)
  max_gross_salary: 200000         # NOK per month
  require_timesheet: true          # Must have timesheet entries for hourly employees

  statutory:
    employer_tax_rate: 0.141       # Arbeidsgiveravgift zone 1
    holiday_pay_rate: 0.12         # Feriepenger (12% standard, 14.3% for 60+)
    holiday_pay_rate_senior: 0.143
    senior_age: 60

6. VAT Validation (vat_validation)

Ensures correct VAT treatment on all transactions.

rule_type: vat_validation
config:
  rates:
    standard: 0.25
    food: 0.15
    transport: 0.12
    exempt: 0.00
    outside_scope: null

  rules:
    - description: "Domestic goods and services default to 25%"
      condition: "country == 'NO' && !is_exempt"
      expected_rate: 0.25

    - description: "Food and beverages at 15%"
      condition: "account >= 4000 && account <= 4099"
      expected_rate: 0.15

    - description: "Passenger transport at 12%"
      condition: "category == 'transport' && is_passenger"
      expected_rate: 0.12

    - description: "Reverse charge on foreign services"
      condition: "country != 'NO' && is_service"
      expected_rate: 0.25
      note: "Both ingoing and outgoing VAT must be recorded"

  flag_missing_vat: true
  flag_zero_vat_domestic: true

7. Month-End Checklist (month_end_checklist)

Defines what must be completed before a month can be closed.

rule_type: month_end_checklist
config:
  items:
    - id: depreciation
      description: "Avskrivninger bokført"
      check: "voucher exists with description matching 'avskriv' for period"
      required: true

    - id: salary_reconciliation
      description: "Lønn avstemt mot bank"
      check: "salary total matches bank outflow"
      required: true

    - id: supplier_accruals
      description: "Leverandørfakturaer periodisert"
      check: "no supplier invoices dated next month with voucher in current month"
      required: true

    - id: prepaid_expenses
      description: "Forutbetalte kostnader fordelt"
      check: "account 1700-1799 reviewed"
      required: false
      reminder: true

    - id: trial_balance
      description: "Saldobalanse generert og gjennomgått"
      check: "trial balance generated"
      required: true

    - id: vat_return
      description: "MVA-oppgave klargjort"
      check: "VAT postings balanced"
      required: true
      frequency: bimonthly  # Only in odd-numbered months

8. Compliance Rules (compliance)

Norwegian accounting law (bokforingsloven) requirements.

rule_type: compliance
config:
  bokforingsloven:
    - rule: "Every transaction must have a voucher"
      check: "no postings without voucher_id"
      severity: critical

    - rule: "Vouchers must have date, description, and amount"
      check: "all vouchers have non-null date, description, postings"
      severity: critical

    - rule: "No backdating beyond 30 days"
      check: "voucher_date >= today - 30"
      severity: warning

    - rule: "Attachments required for external vouchers"
      check: "supplier_invoices have attachment_count > 0"
      severity: warning

    - rule: "Sequential voucher numbering"
      check: "no gaps in voucher number sequence per year"
      severity: warning

  retention:
    primary_documentation: 3650  # 10 years in days
    secondary_documentation: 1825  # 5 years in days
    audit_trail: 3650

Combining Rules

Rules can be combined using logical operators:

combined_rule:
  all_of:  # AND
    - rule: expense_policy.travel
    - rule: approval_threshold
    - rule: vat_validation

  any_of:  # OR (pass if any passes)
    - rule: account_mapping.by_supplier_org_number
    - rule: account_mapping.by_description_pattern
    - rule: account_mapping.by_category

Custom Rules

Companies can add custom rules via the dashboard or API:

rule_type: custom
config:
  name: "Friday lunch limit"
  description: "Lunch expenses on Fridays capped at 200 kr per person"
  condition: "category == 'meals' && day_of_week == 5"
  max_amount_per_person: 200
  action: warn