core PK: id 8 required 1 unique

Description

An approval decision record for a reimbursement claim, capturing whether the decision was automatic (threshold-based) or manual (coordinator attestation), the approver identity, decision timestamp, and optional decision notes. Supports full audit trail of the claims approval lifecycle.

12
Attributes
6
Indexes
9
Validation Rules
9
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Immutable surrogate primary key for the approval record. Generated server-side (UUIDv4) at creation time.
PKrequiredunique
reimbursement_id uuid Foreign key referencing the parent reimbursement claim. Multiple approval records can exist for the same reimbursement (e.g., initial auto-approval followed by a coordinator override).
required
approver_id uuid Foreign key referencing the users table for the coordinator or administrator who made a manual decision. NULL when decision_type is 'automatic', populated for all manual decisions.
-
decision enum The outcome of the approval evaluation. 'approved' marks the claim as cleared for payment forwarding. 'rejected' terminates the claim. 'clarification_requested' places the claim back with the submitter pending additional information.
required
decision_type enum Distinguishes the mechanism by which this approval decision was made. 'automatic' means the Approval Threshold Service evaluated the claim against configured km/amount thresholds and approved without human intervention. 'manual' means a coordinator or admin explicitly acted via the Approval Action Widget.
required
notes text Optional free-text notes entered by the coordinator explaining the decision rationale, particularly for rejections or clarification requests. Displayed in the Approval Claim Detail Screen and surfaced in audit exports.
-
decided_at datetime UTC timestamp of when this approval decision was recorded. For automatic decisions this is the moment the threshold evaluation completed. For manual decisions this is when the coordinator submitted the action.
required
rule_context json Snapshot of the threshold configuration values that were active at the time of an automatic decision. Includes km_threshold, amount_threshold, and organization_id to make automatic decisions reproducible and auditable even after threshold configuration changes. NULL for manual decisions.
-
organization_id uuid Foreign key referencing the organization whose threshold configuration and coordinator scope apply to this approval. Enables multi-tenant scoped audit queries without joining through the reimbursement hierarchy.
required
is_override boolean True when this approval record supersedes a previous decision on the same reimbursement claim (e.g., a coordinator manually overriding an earlier auto-approval or a previous manual decision). Used by the UI to visually distinguish override entries in the approval history.
required
previous_approval_id uuid Self-referential foreign key pointing to the approval record being overridden. NULL for initial decisions. Populated when is_override is true to create a linked override chain.
-
created_at datetime UTC timestamp of row insertion. Distinct from decided_at to support scenarios where a decision is recorded asynchronously (e.g., batch processing). For synchronous decisions both fields share the same value.
required

Database Indexes

idx_reimbursement_approval_reimbursement_id
btree

Columns: reimbursement_id

idx_reimbursement_approval_approver_id
btree

Columns: approver_id

idx_reimbursement_approval_decided_at
btree

Columns: decided_at

idx_reimbursement_approval_organization_decision
btree

Columns: organization_id, decision

idx_reimbursement_approval_reimbursement_decided_at
btree

Columns: reimbursement_id, decided_at

idx_reimbursement_approval_decision_type
btree

Columns: decision_type

Validation Rules

reimbursement_id_must_exist error

Validation failed

decided_at_not_in_future error

Validation failed

decision_enum_is_valid error

Validation failed

decision_type_enum_is_valid error

Validation failed

notes_length_within_limit error

Validation failed

approver_role_authorization error

Validation failed

approver_organization_scope_match error

Validation failed

previous_approval_id_must_exist_when_override error

Validation failed

rule_context_valid_json_for_automatic error

Validation failed

Business Rules

approval_records_are_immutable
on_update

Once an approval record is created it must never be updated or deleted. Corrections are handled by creating a new approval record with is_override=true and previous_approval_id referencing the record being superseded. This preserves a complete, tamper-evident audit trail across the lifecycle of each claim.

automatic_decision_requires_null_approver
on_create

When decision_type is 'automatic', approver_id MUST be NULL. Automatic approvals are system-generated by the Approval Threshold Service and have no human actor. Storing a user ID in this scenario would misrepresent the approval audit trail.

manual_decision_requires_approver
on_create

When decision_type is 'manual', approver_id MUST reference a valid user who holds Coordinator, Organization Administrator, or Global Administrator role in the same organization as the reimbursement claim. Peer Mentors cannot approve claims.

automatic_decision_requires_rule_context_snapshot
on_create

All automatic approvals must store a rule_context JSON snapshot capturing the threshold configuration values (km_threshold, amount_threshold, organization_id) active at evaluation time. This enables reproducible audits even after threshold configuration is later changed by an administrator.

reimbursement_status_sync_on_approval
on_create

Creating an approval record with decision='approved' or 'rejected' must atomically update the parent reimbursement's status field within the same database transaction. Approved claims with decision='approved' and decision_type='automatic' set status to 'auto_approved'; manual 'approved' sets 'manually_approved'; 'rejected' sets 'rejected'. The reimbursement and approval insert must succeed or fail together.

approved_claim_triggers_accounting_forwarding
on_create

Any approval record with decision='approved' (regardless of automatic or manual type) must trigger downstream forwarding of the parent claim to the external accounting system (Xledger for Blindeforbundet, Dynamics portal for HLF) via the Accounting Integration Adapter. This event is dispatched only after the approval transaction commits.

override_chain_limited_to_active_claims
on_create

A new approval record with is_override=true may only be created for reimbursements that are in 'pending', 'auto_approved', or 'clarification_requested' status. Claims in terminal states ('manually_approved', 'rejected') cannot be overridden without an explicit administrative unlock action.

audit_log_entry_required_for_every_approval
on_create

Every approval record insertion must be accompanied by a corresponding entry in the audit_logs table written by the Approval Audit Logger. The audit entry captures: reimbursement_id, approver_id or 'system', decision, decision_type, organization_id, decided_at, and a serialized snapshot of rule_context for automatic decisions. Audit logging failures must roll back the entire transaction.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Entity Relationships

reimbursement
incoming one_to_many

A reimbursement claim can have multiple approval records (e.g., initial auto-approval followed by manual override)

optional
user
incoming one_to_many

A coordinator (user) makes many manual approval decisions on reimbursement claims

optional