Audience: finance managers, accountants, support engineers Difficulty: intermediate
What this covers
The accounting behaviour Solabooks enforces: when a transaction hits the ledger, what posting and reversal mean, how period locks and lock dates work, the VAT period lifecycle, inventory costing assumptions, AR/AP behaviour, the default accounts the system requires, and the common mistakes the system prevents.
This page is policy-level — for the literal permission to perform an action see permission-matrix.
Draft vs posted
Every transactional document — invoice, bill, credit note, debit note, refund, expense, payment, journal entry, sales receipt — has a status field. The system distinguishes draft from posted.
- Draft. The document exists. It has lines, taxes, totals. It is visible in lists. It does not affect the general ledger. Drafts can be edited freely (subject to permission) and deleted without leaving a trail. Drafts do not consume a final number from the numbering sequence — they hold a placeholder.
- Posted. The document is finalised. A journal entry is created in the ledger. The document number is locked. Most fields become read-only. Editing a posted document requires either an unpost (where permission and feature gate allow) or a reversal followed by a new document.
Permissions split along this line: *.edit_draft covers draft edits;
*.post, *.unpost, *.void, *.reverse cover lifecycle changes.
Policies are in app/Policies/{Invoice,Bill,JournalEntry,Payment,...}Policy.php.
Reversal
Reversal is the safe alternative to deleting a posted document. The system writes a contra journal entry dated the reversal date and links it to the original. Both rows remain in the audit trail.
- Allowed for: posted invoices, bills, credit notes, debit notes, expenses, payments, journals, fixed-asset acquisitions.
- Not allowed for: documents in a locked period (must reopen first), VAT returns that have been settled, banking reconciliations that are audit-final.
- Permission:
<area>.reverse(e.g.journals.reverse,sales.payments.reverse,purchases.bills.unpostfor AP).
Lock date
The lock date is the latest date below which no new posting can occur. It is set per organization in Solabooks > Settings > Periods.
- Posting a document with a date on or before the lock date is rejected with the error "Period locked".
- Owners and managers can update the lock date forward without limit.
- Owners (and the
periods.unlock/periods.reopenpermissions) can move the lock date backwards, but doing so is audited and surfaces in Admin > Activity Logs.
The lock date is independent of the fiscal-year close — it is a day-level guardrail, where fiscal close is a structured month/quarter operation.
Period close
Periods are typically monthly, generated by periods.generate. Each
period has a status: open, locked, closed.
locked— no posts; reversals allowed if the post was inside the period.closed— the period is finalised. Reversals requireperiods.reopen.- Closing the final period of a fiscal year creates the year-end roll (retained earnings movement) automatically.
Required permissions:
| Action | Permission |
|---|---|
| View periods | periods.view |
| Lock | periods.lock |
| Unlock | periods.unlock |
| Close | periods.close |
| Reopen | periods.reopen |
| Generate | periods.generate |
All gated on period_locking_enabled.
Fiscal year
Configured at first run via the Setup Wizard (Settings > Setup). Every transaction must fall within an active fiscal year. Posting outside any fiscal year is rejected with "Fiscal year not configured".
Year-end close locks all periods in the year and creates a closing journal that zeroes out income and expense accounts to retained earnings.
VAT period
Independent of the accounting period. Configured per organization
(monthly, quarterly, or annual) when vat_enabled is true.
Lifecycle of a VAT return:
- Draft.
vat_returns.create. The return aggregates posted transactions whose tax dates fall in the VAT period. - Finalized.
vat_returns.finalize. The return is locked but no accounting entries are written yet. - Reporting posted.
vat_returns.post_reporting. The system writes the journal entry that moves output VAT and input VAT to a clearing account. - Settled.
vat_returns.settle. Records the bank payment to the tax authority and closes the return. Once settled the return is immutable. - Carry forward.
vat_returns.carry_forward. If the return is in credit (input > output), the credit can be carried to the next period.
Reverse-charge handling is conditional on reverse_charge_enabled and
adds a self-charged-VAT line on relevant purchases.
Inventory costing
Inventory items default to average cost (weighted moving average). The cost recalculates on every receipt. Sales pull the current average.
- Negative inventory is allowed by default — sales can reduce on-hand below zero. The COGS posting waits for the next receipt to adjust.
- Receiving a purchase order moves goods from "on order" to "on hand" and writes the COGS-relevant cost.
- Landed costs (
tracker.landed_costs) add freight and duty to item cost retroactively when applied to a posted bill.
Reorder-point alerts (inventory.reorder_point) raise an internal
notification when on-hand falls below the configured threshold; they do
not block sales.
AR / sales behaviour
- Quotes (
tracker.quotes_estimates). No accounting impact. Can be sent to a customer, accepted on the public quote page (/q/{token}), and converted to an invoice. Conversion requiresquotes.convert. - Sales orders (
tracker.sales_orders+sales.use_sales_orders). No accounting impact. Optional approval step (sales_orders.approve). Convert to an invoice viasales_orders.convert. - Invoices. Posting writes:
Dr Accounts Receivable / Cr Revenue / Cr Output VAT. AR sub-ledger is updated. - Customer payments. Allocated to one or more invoices. Posting
writes
Dr Bank / Cr Accounts Receivable. Partial allocations are supported — unallocated balance sits on the customer. - Credit notes. Reduce a posted invoice or stand alone as a refund.
Application (
sales.credit_notes.apply) is a separate step from posting. - Sales receipts. Cash sale — posts
Dr Bank / Cr Revenuein one step, no AR. - Refund receipts. Reverse a posted sales receipt or credit note.
AP / purchases behaviour
- Purchase orders (
enable_purchase_orders). No accounting impact. Optional approval (enable_po_approvals), receiving (enable_po_receiving), and 3-way match (enable_3_way_match). - Bills. Posting writes:
Dr Expense or Inventory / Dr Input VAT / Cr Accounts Payable. - Bill payments. Allocated to one or more bills. Posting writes
Dr Accounts Payable / Cr Bank. - Expenses (direct pay). One-step bill + payment.
Dr Expense / Cr Bank. No AP balance. - Debit notes. Mirror credit notes on the AP side — reduce a posted bill or stand alone.
- Supplier refunds. Cash returned by a supplier.
Journal entries
Manual journals require balanced debits and credits. Drafts can be out-of-balance and saved. Posting validates the balance before writing.
journals.delete_hard is a soft permission — even with the right, the
ledger row is preserved if the entry has been referenced by another
document.
Required default accounts
The following accounts must exist (created by the Setup Wizard or first posting). Missing-account errors block posting with "Required account missing".
| Default account | Used by |
|---|---|
| Accounts Receivable | invoice posting, customer payments |
| Accounts Payable | bill posting, bill payments |
| Sales Revenue | invoice line default |
| Cost of Goods Sold | invoice line for inventory items |
| Inventory | bill receipt for inventory items |
| Output VAT | tax-charged invoice posting |
| Input VAT | tax-paid bill posting |
| Bank (default) | sales receipts, expenses, payments |
| Retained Earnings | year-end close |
| Discount Given / Received | invoice/bill discount lines |
| Foreign Exchange Gain/Loss | multi-currency adjustments |
| Rounding Account | line-level rounding |
Multi-currency
When multi_currency_enabled is on:
- Each transaction has a document currency and an exchange rate at the document date.
- Posting writes both the document amount and the base-currency equivalent.
- AR/AP balances are revalued via base-currency adjustments
(
tracker.base_currency_adjustments+fx_adjustments.*) — typically at period end. - Realised FX gain/loss is recognised on payment when the rate has moved.
Common mistakes the system prevents
- Posting into a closed or locked period — blocked at the service layer before the journal is written.
- Editing a posted document directly — blocked by the policy, must unpost or reverse.
- Deleting a posted invoice/bill that has a payment — blocked; void or reverse the payment first.
- Posting an invoice for a customer in a different organization — blocked by the org-scoping middleware.
- Posting a journal entry that is out of balance — blocked at submit.
- Settling a VAT return that hasn't been finalized — blocked by the state machine.
- Reconciling a bank statement against transactions in a closed period —
blocked when
bank_reconciliation_enabledis on.