EPIC-015: Processing Account Integration¶
Status: โ Complete (TDD Aligned) Vision Anchor:
decision-5-processing-accountPhase: 3 Duration: 2 weeks Dependencies: EPIC-002, EPIC-004
๐ฏ Objective¶
Implement Processing virtual account for tracking in-transit transfers between accounts, achieving zero-balance pairing validation and automatic transfer detection, maintaining accounting equation integrity at all times.
Core Rules:
sum(all accounts) + Processing = constant
Balance = 0 โ Auto-Accept Transfer Pair โ
Balance โ 0 โ Pending Review โ ๏ธ
๐ฅ Multi-Role Review¶
| Role | Focus | Review Opinion |
|---|---|---|
| ๐ Reconciler | Transfer detection | Keyword-based pattern matching with confidence scoring (โฅ85 auto-accept), handles unpaired transfers |
| ๐๏ธ Architect | System design | Processing account as system account (is_system=true), 3-phase reconciliation flow integration |
| ๐ Accountant | Business logic | Double-entry compliance (DEBIT Processing on OUT, CREDIT Processing on IN), equation holds at all times |
| ๐ป Developer | Performance requirements | Single-query transfer pairing with confidence scoring, O(nยฒ) pairing acceptable for MVP |
| ๐งช Tester | Coverage verification | 97% line coverage (processing_account.py), 40 tests (33 unit + 7 integration), all pass |
| ๐ PM | User experience | Unpaired transfers visible via Processing balance โ 0, supports manual confirmation |
โ Task Checklist¶
Task 1.1: Documentation¶
- [x] Create
docs/ssot/processing_account.md(485 lines) - [x] ยง1: Concept & Purpose
- [x] ยง2: Accounting Rules
- [x] ยง3: Data Model Design
- [x] ยง4: Transfer Detection SOP-001
- [x] ยง5: Service API
- [x] ยง6: Test Requirements
- [x] ยง7: Integration Points (3-phase flow spec)
Task 1.2: Data Model Design¶
- [x] Add
is_systemcolumn toaccountstable - [x] Alembic migration
c955c65dcc1f_add_is_system_to_accounts.py - [x] Update
Accountmodel and schema - [x] Update
account_service.pyto handle system accounts
Task 1.3: Backend Logic¶
- [x]
services/processing_account.py(487 lines) - Core service - [x]
get_or_create_processing_account()- Idempotent account creation - [x]
detect_transfer_pattern()- Keyword matching (SOP-001) - [x]
create_transfer_out_entry()- DEBIT Processing, CREDIT source - [x]
create_transfer_in_entry()- DEBIT destination, CREDIT Processing - [x]
find_transfer_pairs()- Confidence scoring (amount 40%, description 30%, date 20%) - [x]
get_processing_balance()- Current balance query - [x]
get_unpaired_transfers()- List unmatched transfers - [x] Scoring functions implementation
- [x]
_score_amount_match()- Amount similarity (exact โค0.01, close โค0.10, moderate โค1.00) - [x]
_score_description_match()- SequenceMatcher (60%) + token overlap (40%) - [x]
_score_date_proximity()- Same day=100%, 7-day window - [x]
_calculate_pair_confidence()- Weighted formula application - [x] Test coverage: 33 tests, 97% line coverage
Task 1.4: Reconciliation Integration¶
- [x] Modified
services/reconciliation.py(128 lines added) - [x] Phase 1: Transfer Detection (lines 766-866) - BEFORE normal matching
- [x] Phase 2: Normal Matching (lines 867-989) - Existing logic preserved
- [x] Phase 3: Auto-Pairing (lines 990-1020) - AFTER all matching
- [x] Integration test coverage: 7 tests in
test_transfer_integration.py - [x] Bug fixes:
- [x] Lazy loading issue (added
selectinload(JournalEntry.lines)) - [x] Status filter expansion (
POSTEDโ[POSTED, RECONCILED]in 2 queries) - [x] Duplicate join clause removal
- [x] All 171 tests passing (33 processing + 138 reconciliation)
๐งช Test Cases¶
Test Organization: Tests organized by feature blocks using ACx.y.z numbering. Coverage: See
apps/backend/tests/accounting/test_processing_account.pyandapps/backend/tests/reconciliation/test_transfer_integration.py
AC15.1: Processing Account Creation¶
| ID | Test Case | Test Function | File | Priority |
|---|---|---|---|---|
| AC15.1.1 | Processing Account Created | test_create_processing_account_once |
accounting/test_processing_account.py |
P0 |
| AC15.1.2 | Idempotent Creation | test_processing_account_idempotent |
accounting/test_processing_account.py |
P0 |
| AC15.1.3 | Hidden from User Accounts | test_processing_account_hidden_from_list |
accounting/test_processing_account.py |
P0 |
| AC15.1.4 | Per-User Isolation | test_processing_account_per_user |
accounting/test_processing_account.py |
P0 |
AC15.2: Transfer Entry Creation¶
| ID | Test Case | Test Function | File | Priority |
|---|---|---|---|---|
| AC15.2.1 | Transfer OUT Entry | test_transfer_out_debits_processing |
accounting/test_processing_account.py |
P0 |
| AC15.2.2 | Transfer IN Entry | test_transfer_in_credits_processing |
accounting/test_processing_account.py |
P0 |
| AC15.2.3 | Paired Transfers Zero Balance | test_paired_transfers_zero_processing_balance |
accounting/test_processing_account.py |
P0 |
AC15.3: Accounting Integrity¶
| ID | Test Case | Test Function | File | Priority |
|---|---|---|---|---|
| AC15.3.1 | Unpaired Transfer Visible | test_unpaired_transfer_shows_in_balance |
accounting/test_processing_account.py |
P0 |
| AC15.3.2 | Accounting Equation Holds | test_processing_account_maintains_equation |
accounting/test_processing_account.py |
P0 |
AC15.4: Transfer Detection¶
| ID | Test Case | Test Function | File | Priority |
|---|---|---|---|---|
| AC15.4.1 | Keyword Detection | test_detect_transfer_keywords |
accounting/test_processing_account.py |
P0 |
| AC15.4.2 | Non-Transfer Detection | test_detect_no_description |
accounting/test_processing_account.py |
P0 |
| AC15.4.3 | Auto-Pairing Above Threshold | test_find_transfer_pairs_above_threshold |
accounting/test_processing_account.py |
P0 |
AC15.5: Scoring Functions¶
| ID | Test Case | Test Function | File | Priority |
|---|---|---|---|---|
| AC15.5.1 | Amount Scoring (Exact) | test_score_amount_exact_match |
accounting/test_processing_account.py |
P0 |
| AC15.5.2 | Amount Scoring (Tiers) | test_score_amount_* (9 tests) |
accounting/test_processing_account.py |
P0 |
| AC15.5.3 | Description Scoring | test_score_description_* (4 tests) |
accounting/test_processing_account.py |
P1 |
| AC15.5.4 | Date Scoring | test_score_date_* (5 tests) |
accounting/test_processing_account.py |
P1 |
AC15.6: Reconciliation Integration¶
| ID | Test Case | Test Function | File | Priority |
|---|---|---|---|---|
| AC15.6.1 | Transfer Detection During Reconciliation | test_transfer_out_detected_creates_processing_entry |
reconciliation/test_transfer_integration.py |
P0 |
| AC15.6.2 | Transfer Detection Skips (No Account) | test_transfer_detection_skips_when_no_account_linked |
reconciliation/test_transfer_integration.py |
P0 |
| AC15.6.3 | Transfer IN Detection | test_transfer_in_detected_creates_processing_entry |
reconciliation/test_transfer_integration.py |
P0 |
| AC15.6.4 | Auto-Pairing Phase | test_auto_pair_matching_transfer_same_amount |
reconciliation/test_transfer_integration.py |
P0 |
| AC15.6.5 | Unpaired Transfer Balance | test_unpaired_transfer_leaves_nonzero_balance |
reconciliation/test_transfer_integration.py |
P0 |
| AC15.6.6 | Normal Matching Preserved | test_non_transfer_proceeds_to_normal_matching |
reconciliation/test_transfer_integration.py |
P0 |
Traceability Result: - Total AC IDs: 24 - Requirements converted to AC IDs: 100% (EPIC-015 checklist + must-have standards) - Requirements with test references: 100% - Test files: 2
๐ Acceptance Criteria¶
๐ข Must Have¶
| Standard | Verification | Weight |
|---|---|---|
| Double-entry compliance | All transfers create balanced entries (DEBIT = CREDIT) | ๐ด Critical |
| Accounting equation holds | test_processing_account_maintains_equation |
๐ด Critical |
| Transfer detection accuracy | Keyword matching per SOP-001 | Required |
| Auto-pair threshold โฅ85 | test_find_transfer_pairs_above_threshold |
Required |
| Zero-balance validation | Processing balance = 0 for matched pairs | ๐ด Critical |
| Line coverage โฅ97% | processing_account.py coverage report |
Required |
๐ Nice to Have¶
| Standard | Verification | Status |
|---|---|---|
| ML-based pattern learning | History score (currently 0.0) | โณ Future |
| Multi-currency transfer support | After EPIC-005 (Multi-Currency) | โณ Future |
| Real-time transfer monitoring | WebSocket alerts for unpaired transfers | โณ Future |
๐ซ Not Acceptable Signals¶
- Float usage for monetary amounts (MUST use Decimal)
- Unbalanced entries (debit โ credit)
- Accounting equation violations (sum โ constant)
- Processing balance โ 0 for confirmed pairs
- Coverage < 95%
๐ SSOT References¶
- processing_account.md - Full specification (485 lines)
- schema.md - Account model with is_system flag
- accounting.md - Double-entry rules
- reconciliation.md - Matching thresholds
๐ Deliverables¶
- [x]
docs/ssot/processing_account.md(485 lines) - SSOT specification - [x]
apps/backend/migrations/versions/c955c65dcc1f_add_is_system_to_accounts.py- Migration - [x]
apps/backend/src/models/account.py- is_system flag added - [x]
apps/backend/src/schemas/account.py- is_system schema field - [x]
apps/backend/src/services/account_service.py- System account handling (31 lines) - [x]
apps/backend/src/services/processing_account.py(487 lines) - Core service - [x]
apps/backend/src/services/reconciliation.py- 3-phase flow integration (128 lines added) - [x]
apps/backend/src/services/accounting.py- Import/export updates (9 lines) - [x]
apps/backend/tests/accounting/test_processing_account.py(811 lines) - Unit tests (33 tests) - [x]
apps/backend/tests/reconciliation/test_transfer_integration.py(569 lines) - Integration tests (7 tests)
Git Commit: SHA 16ee6e9 - "feat: integrate Processing account with reconciliation engine (Task 1.4)"
๐ Technical Debt¶
| Item | Priority | Planned Resolution |
|---|---|---|
| Over-matching risk | P1 | Track matched IN entries to prevent multiple OUT entries pairing with same IN |
| Input validation | P2 | Add positive amount, valid UUID checks to public functions |
| Zero-amount transfers | P2 | Add test coverage for zero-amount edge case |
| Concurrent operations | P2 | Add transaction isolation tests (DB integrity risk) |
| History score placeholder | P3 | ML-based pattern learning (v2.0) |
Issues & Gaps¶
Code Review Findings (from background agents)¶
Architecture โ : - 3-phase flow correctly follows SSOT ยง7 - Phase 1 runs BEFORE Phase 2, Phase 3 runs AFTER - Existing reconciliation logic unchanged (backward compatible) - Error handling prevents reconciliation breakdown
Code Quality โ ๏ธ:
1. Over-matching risk: Multiple OUT entries could pair with same IN entry (low probability)
- Impact: Medium (rare edge case)
- Fix: Track matched IN entries in find_transfer_pairs()
2. Missing input validation: No checks for positive amounts, valid UUIDs
- Impact: Low (caught by DB constraints)
- Fix: Add validation to public functions
3. History score: Hardcoded to 0.0 (acceptable, noted in comments)
Test Coverage โ ๏ธ:
1. Zero-amount transfers: Not tested (source handles it, but no test)
- Impact: Medium (edge case)
- Fix: Add test in test_processing_account.py
2. Concurrent operations: Not tested
- Impact: High (DB integrity risk)
- Fix: Add transaction isolation tests
3. Empty string descriptions: Partially tested
- Impact: Low (code handles it)
- Fix: Add explicit test
Conclusion: Code is production-ready with 97% coverage, 40 passing tests. Issues are non-blocking for MVP (all tests pass, accounting equation holds). Address technical debt before scaling to production volume.
โ Q&A (Clarification Required)¶
Q1: What happens to unpaired transfers?¶
Decision: Visible via
get_unpaired_transfers()and Processing balance โ 0. User confirms manually, creating regular journal entry to close Processing account.
Q2: Auto-pairing threshold rationale?¶
Decision: Use 85 (same as reconciliation auto-accept) to maintain consistent confidence bar across system.
Q3: Multi-currency transfer support?¶
Decision: Out of scope for EPIC-015. Process transfers in base currency only. Multi-currency handled in EPIC-005 (Reporting).
Q4: History score implementation?¶
Decision: Hardcoded to 0.0 in MVP. ML-based pattern learning deferred to v2.0 (requires transaction history dataset).
Q5: Processing account visibility?¶
Decision: Hidden from
list_accounts()(is_system=true), but balance and transfers visible via dedicated endpoints (get_processing_balance(),get_unpaired_transfers()).
๐ Timeline¶
| Phase | Content | Status |
|---|---|---|
| Week 1 | Task 1.1-1.2 (Documentation + Data Model) | โ Done |
| Week 1-2 | Task 1.3 (Backend Logic + Tests) | โ Done |
| Week 2 | Task 1.4 (Reconciliation Integration + Bug Fixes) | โ Done |
Total Duration: 2 weeks (Jan 2026 - Feb 2026)
๐ Related EPICs¶
- EPIC-002 (Double-Entry Core) - Foundation for Processing account entries
- EPIC-004 (Reconciliation Engine) - Extended with 3-phase transfer detection
- EPIC-005 (Reporting) - Multi-currency support for future transfer handling
๐ Test Evidence¶
Coverage Report¶
processing_account.py: 97% coverage (472/487 statements)
reconciliation.py: 90% coverage (925/1028 statements)
Total: 171 tests passing (33 + 138)
Integration Test Results¶
test_transfer_integration.py::TestTransferDetectionDuringReconciliation PASSED (3 tests)
test_transfer_integration.py::TestTransferAutoPairingPhase PASSED (2 tests)
test_transfer_integration.py::TestNormalMatchingPhaseIntegration PASSED (2 tests)
Accounting Equation Validation¶
# From test_processing_account_maintains_equation
assets = db_assets + processing_balance
liabilities_equity = db_liabilities + db_equity
assert abs(assets - liabilities_equity) < Decimal("0.01") # โ
PASSED
Completed: February 2026 Agent: Sisyphus (primary development agent) Review: Code review completed by 3 background explore agents
๐ UI Gap Audit (April 2026) โ Processing Account Visibility¶
Origin: UI gap audit against vision.md (in-flight transfers must be visible). Backend processing-account flow is complete but the dashboard does not show pending in-transit balance, so users cannot see "money on the road".
Acceptance Criteria¶
- [x] AC15.7.1 API endpoint
GET /api/accounts/processing/summaryreturns{pending_count, pending_total, currency, oldest_pending_date} - [x] AC15.7.2 Dashboard "Processing / In-Transit" card renders the four fields with currency code
- [x] AC15.7.3 Card click-through navigates to
/processinglisting pending transfers (existing or new page) with line items{from_account, to_account, amount, initiated_date, days_outstanding} - [x] AC15.7.4 Pending entries older than 7 days render a warning badge on the listing row
- [x] AC15.7.5 Frontend test mounts ProcessingSummaryCard and asserts
pending_count+pending_totallabels render
Priority: P0-quick-win โ small UI surface; backend already exposes processing-account state. Estimated effort: 1-2 days backend (summary endpoint) + 2-3 days frontend (card + listing).