# Rule: GL periods, year-end closing, opening balances, ledgers

How UNO structures the general ledger across fiscal years, and what every balance / P&L figure must do about it. Verified against `cJournal.vb` and three client databases (2026-10-31).

## The date
- The accounting date is **`trp_valuedate`** (UNO filters every GL report on it). It carries a time of day → an inclusive end date is `< DATEADD(day, 1, to)`, never `<= to`.
- Invoice vouchers carry `trp_valuedate = inp_edate` exactly (verified 8,050/8,050). `inp_vdate` (invoice value date) is NOT the GL date.

## Voucher types that are NOT activity (jvtype master, verified on 3 clients 2026-11-05)
| code | jvname | role |
|---|---|---|
| `CL` | Closing Voucher (system) | year-end closing of income/expenses into class 1 |
| `CX` | Closing Local | the same closing in the local-currency (LBP) book — Golden Pharm has both every year |
| `OB` | Opening Balance (system) | Jan-1 restatement of balance-sheet accounts |
| `OX` | Opening Local | the same restatement in the local book (Golden Pharm every year, LPHARM 2022) |
| `FI` | First Inventory | initial stock opening |
Every "…Local" type (`JX` journal, `PX` payment, `RX` receipt, `SL` sales, `RS` sales return, `PL` purchase, `RP` purchase return, `DL` difference of exchange) is the LBP-book twin of the system type and counts as normal activity.

## Year-end closing vouchers (`CL`, `CX`)
- Dated Dec-31. **Debit every income account and credit every expense account to zero**, and book the result into retained earnings (class 1).
- **Any P&L-type PERIOD sum must exclude `CL`/`CX`** (and the opening types `OB`/`OX`/`FI`): sales, expenses, P&L, trends, period compare, branch/cost-centre P&L, salesman, VAT period activity. UNO's own "…WithoutClosing" reports do this. Without it a window containing Dec-31 shows sales ≈ 0 or hugely negative (Golden Pharm 2024-12-31: −307B LBP).
- **As-of BALANCE SHEETS keep `CL`/`CX`**: they are what moves the year's result into retained earnings.
- **P&L classification = `AccountTypes.SystemAccType`** ('Income' credit−debit, 'Expenses' debit−credit), exactly UNO's `ProfitAndLoss`. Never classify by the sign of a group's net (an "Un Classified" P&L group with a credit balance was being counted as income) and never by `AccType` alone (clients add types such as "Other Expenses"). Non-balance-sheet accounts typed "Un Classified" are reported separately, outside net profit.
- **Default P&L period = the fiscal year** (Jan-1 of the `to` year): with closings excluded, "all dates" would add every year since inception.
- Tools: helper `uno_pl_excl()`.

## Opening-balance vouchers (`OB` and `OX`, dated Jan-1)
- UNO carries balance-sheet balances into the new fiscal year with an OB (and, in the LBP book, OX) voucher per account **while keeping all prior-year detail in the same tables**. Summing all history therefore counts the carried balance TWICE (Golden Pharm bank: all-history 327M vs UNO 47.7M on 2024-12-31). Missing `OX` alone left Golden Pharm's balance sheet 25% out of balance.
- Not every client restates: Ghazi (IPER CERAMICA) has one initial OB (2015) and carries balances through history; its later `OB`-typed vouchers are mid-year adjustments.
- **Rule (per account):** balance as of D = Σ lines from that account's **latest Jan-1 OB/OX voucher ≤ D**; if the account has no Jan-1 opening, all history ≤ D. Mid-year OB-typed entries are ordinary history. This reproduces UNO's `GetAccountBalanceFromYearBegining` where openings exist and stays correct where they don't; on Golden Pharm it equals the simpler "company-wide start at the last Jan-1 opening" method to the pound for every account (verified 2026-11-05).
- Statement of account: opening = that as-of balance on the day before `from`; a Jan-1 OB line inside the range that merely restates an existing balance is not shown as a movement.
- Tools: helpers `uno_ob_join()` / `uno_ob_where()` — used by account_balance, trial_balance, balance_sheet, cash_position, cash_flow, cash_coverage, emergency_reserve, cash_forecast, financial_ratios, vat_summary, third_party_balance, aged_receivables/payables, supplier_spend (DPO), statement_of_account.

## Protected vouchers, the GL view, and lines with no account
- `trp_protected = 1` = locked against editing (UNO "Protected Transactions"; a view filter in the journal screen, not a visibility rule in cJournal's report SQL). The tools INCLUDE protected vouchers, as UNO's trial balance does. LPHARM has 21.
- `vwJV` = `trans_child` ⋈ `trans_parent` ⋈ `account` …: no duplication from images/cheques on any client (row counts equal), but it DROPS journal lines whose account number is blank/unknown (LPHARM: 18 lines, almost all 0.00 on SA vouchers). `book_health` reports them (`lines_with_no_account`).

## Two ledgers in one table (`tType`)
- `trans_parent.tType` / `inv_parent.tType` is a **ledger selector**: `'P'` and `'O'` ("official"). UNO can save a voucher into both at once (`tType='A'` → a P copy and an O copy linked by `P_inp_id`), and its reports read ONE ledger at a time (hard-coded `tType='P'`).
- Fleet status 2026-10-31: all three client companies have ONLY `P` rows. The bakery test DB has 1,371 `O` vouchers that are NOT twins of P rows (disjoint books).
- Tools currently sum both. `book_health` and `get_sync_status` report the ledgers present and warn when more than one exists. **Default ledger is a business decision for Mazen** (see `docs/DECISIONS_NEEDED.md` D9).

## Cost centres
- `vwCostCenterAnalysis.Value` = the ALLOCATED share of a GL line (amount × `Percentage`/100), in the line currency (× `rate` for USD). A line split across centres appears once per centre with its share. Sum `Value`, never `amountl` (summing the full line overstated split centres up to 19×).

## Aging and due dates
- UNO ages by **due date** = invoice date + `customer.PayTermsDays` / `supplier.PayTermsDays` (via `account.AccountID`), compares dates day-only, and records actual payment allocation in `InvoicePayments` (invoice ↔ receipt voucher, `Paid`, `totalhtpaid`, `vatpaid`, tender split). **Where that table is populated (LPHARM 184k rows, Ghazi 20k) the invoice-level open list is `open_invoices`** — remaining = `inp_gtotal − Σ Paid − Σ returns referencing the invoice` (UNO GetOutstandingInvoice / LoadDueInvoicesByClient). `aged_receivables`/`aged_payables` remain the GL view (oldest-first allocation of unlinked receipts); where allocations are sparse (Golden Pharm: 17 rows) `open_invoices` says so and the GL aging is the better figure.
- DSO = AR ÷ CREDIT sales (sales-invoice debits to Client accounts), not total sales incl. POS cash.

## Balanced-voucher rule (UNO `CheckJVIfBalanced`, cJournal.vb :8727)
- A voucher is UNBALANCED only when **|ΣD−ΣC| in USD > 1 (rounded to cents) OR in LBP > 100 (rounded to units)** — both legs tested. Smaller residuals are invoice VAT/FX rounding. `book_health.unbalanced_voucher_count` uses this rule (LPHARM: 11 vouchers; the old |ΔLBP|>1 rule flagged 449); `rounding_residual_voucher_count` keeps the strict count.

## More UNO GL conventions (from the VB classes, 2026-11-09)
- **Closing universe = account number first digit 6/7** (`GetExpensesAndIncomeJV`), while the P&L uses `SystemAccType`. An account typed Income/Expenses but numbered outside 6/7 is in the P&L yet never closed — `fiscal_year_status` lists such mismatches and both results.
- **Depreciation vouchers = type `DP`**; asset accounts carry `depreciation` (rate %), `depreciation_acnbr` (accumulated-depreciation account) and `expenses_acnbr` — `fixed_assets` builds the register from them.
- **Cash-flow classification** exists in UNO (`vwCashFlow_AllTypes.IsCash`): the non-cash lines of a voucher that touches cash/bank, grouped by account type, are the sources (credits) and uses (debits) of cash — `cash_flow.by_counter_account_type`.
- **Cheques** live in `vwCheckManagement`; `ChkAmount` is in the cheque's own currency (never add LBP and USD cheques); UNO's "due" rule = `IsAutomate = 1 AND ChkValueDate <= today`.
- **Receipts by collector**: an RV voucher's customer credit line pairs with the debit (cash/bank/van) line of the same voucher — `receipts_list`.
- **Credit notes** = voucher type `CN`, debit notes `DB` (local twin `XD`).
- **Voucher-level view** `vwJVMain` (one row per voucher, debit totals, author, created date) backs `journal_vouchers`; `voucher_lines` shows the full double entry.
- Fiscal years: `CompanyYear` + `AccountingPeriods` (`vwAccountingPeriod`); all three clients use calendar years (verified 2026-11-09), so the fiscal-year default (Jan-1) stands. `fiscal_periods` exposes them.

## Customers, suppliers, cash tills
- A customer is an account with `thirdparty_type = 'Client'`. A POS/walk-in sale has `POS_ID` set and its third party is the till's **cash account** (name like "Cash On Hand - POS-LBP") — never treat it as a named customer; never use name heuristics (`LIKE '%Cash%'`).
- "Invoiced to customer" = debits on sales vouchers (SA/SL/DN/CS) only; "purchased from supplier" = credits on purchase vouchers (PU/PL/GR) only. Payment/JV/opening entries on those accounts are not sales or purchases.
- Consolidate a party by NAME (+ type) across currency legs; a LIKE match that spans several different parties must ask (`needs_clarification`).
