Reimbursement Approval
Data Entity
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.
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
Columns: reimbursement_id
idx_reimbursement_approval_approver_id
Columns: approver_id
idx_reimbursement_approval_decided_at
Columns: decided_at
idx_reimbursement_approval_organization_decision
Columns: organization_id, decision
idx_reimbursement_approval_reimbursement_decided_at
Columns: reimbursement_id, decided_at
idx_reimbursement_approval_decision_type
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
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
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
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
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
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
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
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
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.
CRUD Operations
Storage Configuration
Entity Relationships
A reimbursement claim can have multiple approval records (e.g., initial auto-approval followed by manual override)
A coordinator (user) makes many manual approval decisions on reimbursement claims