Bookkeeping App Development
Build custom app solutions with Scrums.com's expert development team. With an NPS (Net Promoter Score) of 82, Scrums.com crafts cost-effective, custom applications that drive results.
Accounting software is one of the most constraint-heavy categories in FinTech: every number must balance, every transaction must be traceable to a source document, and the data model must satisfy the requirements of multiple tax jurisdictions, audit frameworks, and accounting standards simultaneously. Scrums.com builds dedicated engineering teams for companies developing bookkeeping and accounting platforms: SMB accounting SaaS, accounting modules within broader ERP products, tax compliance platforms, and financial management tools embedded in banking or FinTech products.
Double-Entry Ledger Architecture
The double-entry ledger is the non-negotiable core of any accounting system. Every financial event creates at least two journal entries: a debit to one account and a credit to another, with the sum of all debits always equalling the sum of all credits. Violating this invariant corrupts the books.
The journal_entries table is append-only. A journal entry record carries: the entry ID, a human-readable reference, the transaction date (not the system timestamp: accounting uses the economic event date), a created_at system timestamp for audit purposes, and two or more journal_lines rows (debit or credit, account ID, amount as NUMERIC(19,4), currency code ISO 4217). A database CHECK constraint validates that the sum of debits equals the sum of credits before any entry is committed. Entries are never updated or deleted; corrections are made by posting a reversing entry followed by the correct entry, so that the ledger history is always complete and auditable.
The chart of accounts defines the account hierarchy: assets, liabilities, equity, income, and expenses at the top level, with configurable sub-accounts. Account codes follow a numbering convention (1xxx assets, 2xxx liabilities, 3xxx equity, 4xxx income, 5xxx expenses) that maps to standard accounting frameworks (GAAP, IFRS). The chart of accounts is version-controlled: adding or renaming an account creates a new version, and historical reports always render against the chart of accounts version that was active during the reporting period.
Financial statements (trial balance, profit and loss, balance sheet, cash flow statement) are computed as projections over the journal entry ledger; they are read-model queries, not stored aggregates. Corrections that post reversing entries are immediately reflected in all reports without needing to recompute stored balances.
Bank Reconciliation and Transaction Ingestion
Bank reconciliation (matching bank statement transactions against ledger entries) is the most time-consuming manual accounting task and the one most amenable to automation.
Bank transaction ingestion follows a three-tier architecture: open banking APIs (Plaid, TrueLayer, direct bank APIs) for institutions that support it; screen scraping via a secure credential vault (AES-256 with envelope encryption, AWS KMS or HashiCorp Vault) for those that do not; manual CSV/OFX import as a fallback. All three paths normalise to a canonical bank_transactions schema: date, amount (NUMERIC(19,4) with currency), description, reference, bank-assigned transaction ID as the deduplication key.
The reconciliation engine matches bank transactions to unreconciled journal lines using a multi-pass algorithm. The first pass attempts exact matches on amount and date. The second pass attempts amount-only matches within a plus or minus 3 day date tolerance (handles cut-off timing differences). The third pass applies rule-based fuzzy matching: a bank transaction from a payroll processor name is matched to a journal line coded to the salary expense account. Unmatched transactions are surfaced in the reconciliation queue for manual review. The match decision (automatic or manual) is recorded in a reconciliation_matches table with a confidence score and match method.
Suggested journal entries are generated automatically for ingested bank transactions that do not yet have a matching journal entry. The suggestion engine uses a merchant-to-account mapping table and an ML classifier trained on historical transaction-to-account coding decisions. Suggested entries are presented for approval (never automatically posted) so that the accountant retains authority over the books.
Bookkeeping platforms like these are built and delivered by dedicated engineering teams through our mobile app development service.
Multi-Currency Accounting and Tax Infrastructure
Multi-currency accounting is one of the most complex correctness challenges in accounting software. Getting it wrong produces financial statements that do not balance and tax returns that are wrong.
Every journal line carries an explicit ISO 4217 currency code. Transactions in foreign currencies are recorded at the exchange rate on the transaction date (the spot rate). For balance sheet accounts denominated in foreign currencies, a period-end revaluation journal is posted to reflect the change in exchange rate between the transaction date and the reporting date; this revaluation entry credits or debits an unrealised_fx_gain_loss account and is reversed at the start of the next period. Realised FX gain/loss is posted at the point of settlement (when a foreign currency invoice is paid, the difference between the invoice rate and the payment rate creates a realised_fx_gain_loss entry).
Exchange rates are stored in an fx_rates table with source (ECB, open exchange rates, bank rate), rate date, and bid/ask spread. Rates are never overwritten; the table is append-only. Period-end revaluations read the closing rate for the period from the fx_rates table and post the batch of revaluation journal entries as a single transaction.
Tax calculation integration handles VAT/GST at the transaction level: the tax code is an attribute on the journal line (standard rate, reduced rate, exempt, zero-rated) and the VAT return is a projection query over lines coded to taxable accounts within the return period. For jurisdictions with complex multi-rate or place-of-supply rules (EU VAT, US sales tax), the platform delegates to a tax compliance service (Avalara, Vertex, TaxJar) and stores the returned rate and jurisdiction on the transaction line as an immutable snapshot.
Financial Reporting and Audit Trail
The quality of financial reporting is the measure by which accountants and auditors judge accounting software.
Standard report generation (trial balance, profit and loss statement, balance sheet, cash flow statement) operates as parameterised queries against the journal ledger, filtered by entity, date range, and chart of accounts version. Reports are computed on demand so that they always reflect the current ledger state including any corrections. For large ledgers, query performance is maintained by materialising monthly trial balance snapshots as a background job; the report query reads from the snapshot for all periods except the current partial period, which is queried live.
Comparative period reporting (this year vs last year, this quarter vs last quarter) requires that both periods use the same chart of accounts structure. The reporting engine resolves account mapping across versions using the account version history, so that comparatives are meaningful rather than showing structural changes as variance.
Audit trail completeness is a regulatory requirement. Every change to accounting data is tracked: journal entry creation (who, when, source document reference), manual reconciliation match (who approved, what was matched), chart of accounts modification (who changed what, what was the previous state). The audit log is write-only from the application layer; modifications require out-of-band database access, which itself generates a log entry.
GDPR and financial data retention requirements often conflict: GDPR mandates erasure of personal data on request, while tax law mandates financial record retention for 7 years (UK HMRC) or longer. The resolution is pseudonymisation: a GDPR erasure request anonymises personal identifiers (name, email, address) on the contact record while leaving financial transactions intact. The journal entries reference the anonymised contact by a persistent but non-identifying UUID.
Explore Scrums.com's FinTech engineering team for accounting platform projects, or start a conversation today.
Frequently Asked Questions
How do you enforce the double-entry invariant across distributed services where journal lines might be written by different microservices?
All journal lines belonging to the same entry are written in a single database transaction with a CHECK constraint that validates debit sum equals credit sum before commit. If any line fails validation or the service crashes mid-write, the transaction rolls back completely; no partial entries ever reach the ledger. For cross-service journal entries (where different services contribute lines to the same economic event), a saga pattern with a central journal coordinator service collects all lines and posts them atomically. Services signal readiness but never write directly to the journal.
What is the data model for handling multiple entities (companies) within a single accounting platform instance?
Each entity owns a separate chart of accounts, journal ledger, and bank accounts. Entity-level isolation is enforced by an entity_id foreign key on every ledger table with row-level security ensuring that a user with access to one entity cannot query another entity's data. Inter-company transactions (a parent company lending to a subsidiary) create matching journal entries in both entities' ledgers with a cross-reference field linking the two entries, enabling inter-company elimination at group consolidation time.
How do you handle the tension between GDPR erasure requests and financial record retention obligations?
Financial records carry two categories of data: the financial fact (amount, account, date) which must be retained for regulatory compliance periods (7 years under UK HMRC, 10 years under some EU jurisdictions), and the personal identifiers attached to counterparties which are subject to GDPR erasure. The resolution is pseudonymisation: an erasure request anonymises personal identifiers on the contact record and replaces the display name on journal entries with a non-identifying UUID. The financial transaction is preserved intact for audit and tax compliance. The pseudonymisation event is logged so that regulators can verify compliance with both regimes.
Don't Just Take Our Word for It
Hear from some of our amazing customers who are building with Scrums.com Teams.
Find Related App Types
Credit Management App
Internet Banking System App
Energy App
Food Inventory App
P2P Lending App
Grocery Delivery App
Good Reads From Our Blog
Stay up-to-date with the latest trends, best practices, and insightful discussions in the world of mobile app development. Explore our blog for articles on everything from platform updates to development strategies.
Essential Guides
Gain a deeper understanding of crucial topics in mobile app development, including platform strategies, user experience best practices, and effective development workflows with expertly crafted guides.













.png)
