Appearance
Audit Trail & Controls
PinkApple maintains a complete, immutable audit trail of every financial action in the system. This page explains how the system tracks changes, prevents unauthorised modifications, and supports external audit requirements.
Activity Log
Every action that creates, modifies, or deletes financial data is recorded in the activity_log table. This log is:
- Append-only — Records cannot be edited or deleted by any user
- Automatic — Logged by stored procedures, not application code (cannot be bypassed)
- Timestamped — Server-side
created_atusing database clock (not client time)
What Is Logged
| Field | Content |
|---|---|
| Action | Operation type (CREATE, UPDATE, DELETE, APPROVE, REJECT, POST, VOID, REVERSE, FX_REVALUATION, IC_ELIMINATION, etc.) |
| Table Name | The database table affected |
| Record ID | Primary key of the affected record |
| Old Value | JSON snapshot of the record before the change (for updates) |
| New Value | JSON snapshot of the record after the change |
| Changed By | User ID of the person who performed the action |
| Business Unit | Which BU the action was performed in |
| IP Address | Client IP address at the time of action |
| User Agent | Browser/device identifier |
| Batch Number | Groups related actions together (e.g., all lines in a journal entry share a batch) |
| Status | FINAL, PENDING, REVERSED, etc. |
Querying the Audit Trail
Authorised users can view the activity log filtered by:
- Date range
- User
- Action type
- Entity (GL_JOURNAL_ENTRY, FISCAL_PERIOD, BUDGET, etc.)
- Business unit
Immutability Guarantees
Posted Journal Entries Cannot Be Deleted
Once a journal entry has posted_flag = 1:
- It cannot be physically deleted from the database
- It cannot have its amounts or accounts changed
- The only way to undo it is via a reversal journal — a new entry with equal and opposite amounts that references the original
Reversal Mechanism
When an entry needs to be corrected:
- A new journal entry is created with inverted debits/credits
- The original entry gets
reversed_flag = 1andreversed_entry_idpointing to the correction - Both entries remain visible in the ledger permanently
- The activity log records who reversed it and why
Void Flag
Entries that were created in error (before posting) can be voided:
voided_flag = 1marks the entry as void- Voided entries remain in the database but are excluded from all balance calculations
- The void action is logged in the activity log
Source Type Classification
Every journal entry carries a source_type that identifies how it was created:
| Source Type | Origin |
|---|---|
BACKOFFICE | Manual journal entry by an accountant |
LOAN | Loan disbursement, repayment, or fee |
DEPOSIT | Deposit account transaction |
DRIVING | Driving school payment |
SHARES | Share capital transaction |
FX_REVALUATION | Automated FX revaluation at period end |
IC_ELIMINATION | Intercompany elimination entry |
RECURRING_TEMPLATE | Scheduled recurring journal |
PLATFORM_CASH_COMMISSION | Platform commission posting |
REVERSAL | Reversal of a prior entry |
This allows auditors to immediately identify system-generated vs. manual entries.
Period Controls
Period State Machine
Fiscal periods follow a strict lifecycle that prevents back-posting:
NOT_OPENED → OPEN → SOFT_CLOSED → HARD_CLOSED → LOCKED| State | Who Can Post | Auditor Implication |
|---|---|---|
| NOT_OPENED | Nobody | Future period, no transactions possible |
| OPEN | Authorised users | Normal operations |
| SOFT_CLOSED | Controllers only (designated users) | Month-end adjustments window |
| HARD_CLOSED | Nobody | Period finalised, no changes allowed |
| LOCKED | Nobody, ever | Regulatory retention, completely immutable |
Backdating Controls
The system enforces:
- Maximum posting lag — Configurable number of days a transaction can be backdated
- Future posting restriction — Can be disabled entirely via
GL_ALLOW_FUTURE_POSTING - Adjustment periods — Period 13/14/15/16 exist specifically for post-close audit adjustments, accessible only with elevated permissions
Period Close Audit Trail
Every period state change (open, close, reopen) is recorded in:
bu_period_audit— Who changed the state, when, from what state to what state, with notesactivity_log— Standard audit entry
Data Retention
Balance Integrity
- Daily balances are captured at 10 levels (COA, header, major, foundation, sub-ledger × daily/monthly)
- Period-end snapshots are captured in
fiscal_period_balance_snapshotat the time of close - These snapshots are immutable and serve as the point-in-time reference for financial statements
No Physical Deletion
The system does not physically delete financial records. All "deletions" in the GL domain are soft:
- Journal entries: voided, never deleted
- Sub-ledgers: deactivated (
is_active = 0), never dropped - COA accounts: deactivated, never removed from the hierarchy
- Budget records: versioned, never overwritten
Segregation of Duties
The permission system enforces separation between:
| Action | Required Permission | Cannot Overlap With |
|---|---|---|
| Create journal entry | CREATE_GL_JOURNAL_ENTRY | Approve journal entry |
| Approve journal entry | APPROVE_GL_JOURNAL_ENTRY | Create journal entry |
| Post journal entry | POST_GL_JOURNAL_ENTRY | — |
| Close fiscal period | CLOSE_FISCAL_PERIOD | Reopen fiscal period |
| Run FX revaluation | RUN_FX_REVALUATION | — |
| Modify budgets | CREATE_BUDGET / UPDATE_BUDGET | Approve budgets |
Authority Limits
GL posting approval policies can enforce monetary thresholds:
- Entries below a threshold: auto-approved
- Entries above a threshold: routed through multi-step approval chain
- Each step in the chain requires a different user (same user cannot approve their own entry)
System Integrity Checks
Sub-Ledger ↔ Control Account Reconciliation
At every period close, the system automatically verifies:
SUM(all sub-ledger balances for a COA) = COA control account balanceIf this check fails, the period cannot be closed until the discrepancy is resolved.
Balanced Entry Enforcement
The GL posting engine validates at the point of posting:
- Total debits must equal total credits (to the cent)
- Unbalanced entries are rejected with an error — they never reach the ledger
- This is enforced in the database layer (stored procedure), not the application layer
Cash Balance Enforcement
Accounts tagged as CASH_FAMILY have additional controls:
- The system checks the current balance before posting a credit
- If the credit would make the balance negative, the posting is blocked
- This prevents overdrafts on cash accounts
Next Steps
- Journal Entry Controls — Detailed control matrix for journal entries
- GL Posting Approval — Approval chain configuration
- Month-End Close — Period close procedures and readiness checks
