core PK: id 15 required 3 unique

Description

Records each batch transfer of approved expense and reimbursement data to an external accounting system (Xledger, Microsoft Dynamics, or generic). Captures the full lifecycle of an export attempt — from initiation through field mapping, submission, and outcome — providing an immutable audit trail and enabling retry, rollback, and idempotency guarantees.

24
Attributes
6
Indexes
7
Validation Rules
15
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Globally unique identifier for the export record.
PKrequiredunique
organization_id uuid Foreign key to the organization that initiated this export. Enforces tenant isolation — all queries must be scoped by this column.
required
created_by_user_id uuid Foreign key to the user (Coordinator or Org Admin) who initiated the export. NULL for scheduler-triggered automated exports.
-
system_type enum The target accounting system for this export batch.
required
status enum Lifecycle status of the export attempt.
required
batch_reference string Internal batch identifier generated at export initiation. Used for idempotency checks and grouping related export records.
requiredunique
external_reference_id string The journal entry, voucher, or batch ID assigned by the target accounting system on successful submission. NULL until the system confirms receipt.
-
period_start datetime Start of the reporting or export period covered by this batch (e.g., first day of the month for scheduled exports).
-
period_end datetime End of the reporting or export period covered by this batch. Must be after period_start when both are set.
-
expense_ids json Array of expense UUIDs included in this export batch. Stored as JSON array for auditability without requiring a separate join table.
required
reimbursement_ids json Array of reimbursement UUIDs included in this batch. May be empty if export covers only expense metadata.
required
expense_count integer Denormalized count of expenses in this batch. Derived from expense_ids.length at write time for fast aggregation queries.
required
reimbursement_count integer Denormalized count of reimbursements in this batch.
required
total_amount decimal Sum of all reimbursement amounts in this export batch in NOK, with two decimal places. Used for reconciliation with the accounting system.
required
payload_hash string SHA-256 hash of the sorted expense_ids + reimbursement_ids array. Used for idempotency: prevents duplicate submissions of the identical batch.
requiredunique
field_mapping_snapshot json Snapshot of the organization's expense-type-to-account-code mapping at export time. Preserved for audit purposes even if the live mapping changes later.
-
dry_run boolean True if this export was a validation-only dry run that did not commit records to the accounting system.
required
retry_count integer Number of times this export has been retried after an initial failure. Caps at 3 before the export is marked permanently failed.
required
error_details json Structured error payload from the accounting system adapter on failure. Includes error code, message, and offending field path. NULL on success.
-
exported_at datetime Timestamp when the accounting system confirmed receipt (status changed to completed). NULL while pending or processing.
-
rolled_back_at datetime Timestamp when the export was rolled back. NULL unless status is rolled_back.
-
rolled_back_by_user_id uuid User who triggered the rollback. NULL unless status is rolled_back.
-
created_at datetime Record creation timestamp, set at insert time and never modified.
required
updated_at datetime Last modification timestamp. Updated on every status transition.
required

Database Indexes

idx_accounting_export_org_created
btree

Columns: organization_id, created_at

idx_accounting_export_org_status
btree

Columns: organization_id, status

idx_accounting_export_payload_hash
btree unique

Columns: payload_hash

idx_accounting_export_batch_reference
btree unique

Columns: batch_reference

idx_accounting_export_org_period
btree

Columns: organization_id, period_start, period_end

idx_accounting_export_status_created
btree

Columns: status, created_at

Validation Rules

batch_must_contain_items error

Validation failed

period_range_valid error

Validation failed

system_type_matches_org_config error

Validation failed

total_amount_matches_sum error

Validation failed

expense_count_matches_array_length error

Validation failed

external_reference_required_on_completion error

Validation failed

error_details_required_on_failure warning

Validation failed

Business Rules

immutable_on_completion
on_update

Once status transitions to 'completed', the record is immutable. No fields except audit metadata may be changed. Rollback is modeled as a new record, not a deletion.

approved_expenses_only
on_create

Only expenses and reimbursements with status 'approved' may be included in an export batch. The service layer validates all IDs against approval status before creating the record.

idempotency_via_payload_hash
on_create

Before creating a new export record, the service computes a SHA-256 hash of the sorted expense_ids + reimbursement_ids. If a record with the same payload_hash already exists and is not in 'failed' or 'rolled_back' status, the request is rejected as a duplicate.

tenant_isolation
always

All read and write operations must include an organization_id WHERE clause. Cross-organization access is forbidden even for Global Admins querying operational data.

retry_cap
on_update

An export may be retried at most 3 times (retry_count max: 3). After reaching the cap, the status is permanently set to 'failed' and manual intervention is required.

Enforced by: Accounting API Service
dry_run_excluded_from_reimbursement_update
on_create

Exports where dry_run = true must not update the export_status on reimbursement records. Dry runs are validation-only and produce no committed state.

no_delete
on_delete

Accounting export records are never hard-deleted. They form part of the financial audit trail. Rollback sets status to 'rolled_back' and records the rollback actor and timestamp.

field_mapping_snapshot_required_for_completed
on_update

When an export transitions to 'completed', a field_mapping_snapshot must be present. This preserves the mapping configuration that was active at export time for future audit reconciliation.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
by_date
Retention
archive_after_1year