Reconciliation Logic - Technical Documentation
For system architecture and process flow overview, see Reconciliation System.Overview
The reconciliation process handles linking GL entries together to mark them as reconciled. It must handle several complex scenarios including entries from closed accounting periods, entries already part of IN_PROGRESS reconciliation groups, and auto-booking for small differences.Core Principles
1. Immutability of Closed Periods
- GL entries from closed accounting periods MUST NEVER be modified
- Their reconciliation group IDs remain unchanged
- They can participate in reconciliation but are read-only
2. Reuse IN_PROGRESS Groups
- If entries already belong to an IN_PROGRESS reconciliation group, complete that group
- Don’t create duplicate groups for the same set of entries
- This happens when closing periods creates pending groups
3. Protect Already-Exported Data
reconciled_at(DateTime): ALWAYS update to NOW() when completing (reflects current completion time)reconciled_on(String, Belegfeld 1): NEVER change if already set (DATEV export integrity)reference_document_*fields: NEVER change if already set (DATEV export integrity)- Once exported, Belegfeld 1 and reference document data are immutable
4. Smart Group Strategy
- 1 existing IN_PROGRESS group → Complete it
- Multiple IN_PROGRESS groups → Fail reconciliation (inconsistent state, requires investigation)
- No existing groups → Create new group (standard reconciliation)
Process Flow
STEP 1: Analyze Entries
- Check BOTH credit and debit sides (don’t rely on entry.isCredit)
- Track which entries have which groups
- Log warnings for closed period entries
STEP 2: Determine Strategy
STEP 3: Create or Update Group
STEP 4: Update Reference Document
STEP 5: Update GL Entries
- Never modify entries from closed periods
- When completing existing group, check if entry already has that group
- Only update entries that need updating
- Log all decisions for debugging
STEP 6: Create Activity Logs
Create activity logs ONLY for entries that were actually updated (exclude closed period entries).STEP 7: Connect Documents & Update Statuses
Link documents together and update their statuses based on reconciliation state.Example Scenarios
Scenario 1: Reconciling with Closed Period Entry
Setup:- Invoice (ID: inv-1) from closed period 2024-Q1
credit_reconciliation_group_id: “group-abc” (IN_PROGRESS)accounting_period.is_closed: true
- Payment (ID: pay-1) from open period 2024-Q2
credit_reconciliation_group_id: nullaccounting_period.is_closed: false
- Detect existing IN_PROGRESS group: “group-abc”
- Strategy: Complete existing group “group-abc”
- Update group: “group-abc” → status=COMPLETED
- Update GL entries:
- Invoice: SKIP (closed period)
- Payment: SET
credit_reconciliation_group_id= “group-abc”
- Invoice: unchanged (immutable)
- Payment: linked to “group-abc”
- Group “group-abc”: COMPLETED
Scenario 2: Fresh Reconciliation (No Existing Groups)
Setup:- Entry A: No reconciliation group
- Entry B: No reconciliation group
- No existing IN_PROGRESS groups found
- Strategy: Create new group
- Create group: “group-new” with status=COMPLETED
- Update both entries to link to “group-new”
- Both entries linked to new group “group-new”
Scenario 3: Multiple IN_PROGRESS Groups (Error Case)
Setup:- Entry A:
credit_reconciliation_group_id: “group-1” (IN_PROGRESS,reconciled_on: “GID-001”) - Entry B:
credit_reconciliation_group_id: “group-2” (IN_PROGRESS,reconciled_on: “GID-002”)
- Multiple IN_PROGRESS groups detected: [“group-1”, “group-2”]
- Strategy: Fail reconciliation (inconsistent state)
- Return error: “MULTIPLE_IN_PROGRESS_GROUPS”
- User must investigate and fix the data inconsistency
- Reconciliation fails with error
- Both groups remain unchanged
- Both entries remain unchanged
- Requires manual investigation to determine which group is correct
Logging & Debugging
The process logs at each decision point:- Closed period detection
- Existing group analysis
- Strategy selection
- Entry update results (updated vs skipped)
- Reasons for skipping entries
Variables & State
| Variable | Type | Purpose |
|---|---|---|
finalReconciliationId | string | The reconciliation group ID to use |
shouldCreateNewGroup | boolean | Whether to CREATE vs UPDATE group |
isCompletingExistingGroup | boolean | Whether we’re completing an existing IN_PROGRESS group |
existingGroupIds | Set<string> | All IN_PROGRESS group IDs found |
entriesWithExistingGroups | Map | Tracking which entries have which groups |
entriesFromClosedPeriods | Array | Entries that cannot be modified |
updatedEntryIds | Array | Entries that were actually updated |
skippedEntryIds | Array | Entries that were skipped (with reasons) |
Common Issues & Solutions
Issue: Entry from closed period gets modified
Cause: Not checkingaccounting_period.is_closed before update
Solution: Skip update if glEntry.accounting_period?.is_closed === true
Issue: Duplicate groups created for same entries
Cause: Not detecting existing IN_PROGRESS groups Solution: Check BOTH credit and debit sides for all entriesIssue: Entries already in group get “updated” unnecessarily
Cause: Not checking if entry already has the target group Solution: When completing existing group, skip if entry.group_id === finalReconciliationIdIssue: Belegfeld 1 (reconciled_on) gets changed after DATEV export
Cause: Always setting reference document fields when completing groups Solution: Check ifreconciled_on or reference_document_number already exist; skip update if set
Note: reconciled_at (DateTime) should ALWAYS be updated to NOW() when completing a group - it reflects the current completion time. Only reconciled_on (String/Belegfeld 1) and reference document fields must be preserved.
Testing Checklist
- Reconcile two fresh entries → creates new group
- Reconcile with closed period entry → closed entry unchanged
- Reconcile entries already in IN_PROGRESS group → completes that group
- Reconcile entries in different IN_PROGRESS groups → creates new group
- Reconcile with auto-booking → auto-booking entry handled correctly
- Check activity logs created only for updated entries
- Verify closed period entries never modified
- Verify no duplicate updates when completing existing group
- Verify reconciled_at timestamp ALWAYS updated to NOW() when completing
- Verify reconciled_on (Belegfeld 1) preserved when already set
- Verify reference_document_ fields preserved when already set*
- Reconcile again with already-exported group → Belegfeld 1 unchanged, reconciled_at updated