Solavel Solavel Docs

Accounting Rules

docs/reference/accounting-rules.md

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.unpost for 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.reopen permissions) 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 require periods.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:

  1. Draft. vat_returns.create. The return aggregates posted transactions whose tax dates fall in the VAT period.
  2. Finalized. vat_returns.finalize. The return is locked but no accounting entries are written yet.
  3. Reporting posted. vat_returns.post_reporting. The system writes the journal entry that moves output VAT and input VAT to a clearing account.
  4. Settled. vat_returns.settle. Records the bank payment to the tax authority and closes the return. Once settled the return is immutable.
  5. 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 requires quotes.convert.
  • Sales orders (tracker.sales_orders + sales.use_sales_orders). No accounting impact. Optional approval step (sales_orders.approve). Convert to an invoice via sales_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 Revenue in 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_enabled is on.

Related

Source: docs/reference/accounting-rules.md ← All documentation