TrialBalanceDocument
Printable trial balance: account code, account name, debit and credit grouped by account type, matching totals with a double rule, and a quiet 'Balanced' or red 'Out of balance by RM x' line.
import { TrialBalanceDocument } from "@cynco-labs/ui"; Example
import { useState } from "react";
import {
DocumentBrandProvider,
GroupedTabButton,
TrialBalanceDocument,
sumAmounts,
} from "@cynco-labs/ui";
const BRAND = {
company: {
name: "Harmoni Perabot Sdn Bhd",
registrationNo: "201801034567 (1290456-T)",
sstNo: "P11-1809-32001234",
tin: "C23456789010",
address: ["Lot 15, Jalan Perindustrian Bukit Minyak 7", "14100 Simpang Ampat, Pulau Pinang"],
phone: "+60 4-508 2210",
email: "finance@harmoniperabot.my",
},
layout: "minimal",
locale: "en",
footerNote: "Thank you for your business.",
};
function Shell(p) {
const [l, setL] = useState("minimal"),
[g, setG] = useState("en"),
[x, setX] = useState(p.extra ? p.extra.options[0].value : "");
return (
<div className="flex flex-col gap-3">
<div className="flex flex-wrap gap-2">
<GroupedTabButton
className="min-w-[260px] flex-1"
value={l}
onValueChange={setL}
options={[
{ value: "minimal", label: "Minimal" },
{ value: "classic", label: "Classic" },
{ value: "modern", label: "Modern" },
]}
/>
<GroupedTabButton
className="w-[190px]"
value={g}
onValueChange={setG}
options={[
{ value: "en", label: "EN" },
{ value: "ms", label: "BM" },
{ value: "en-ms", label: "EN+BM" },
]}
/>
{p.extra ? (
<GroupedTabButton
className="w-[230px]"
value={x}
onValueChange={setX}
options={p.extra.options}
/>
) : null}
</div>
<DocumentBrandProvider brand={Object.assign({}, BRAND, { layout: l, locale: g })}>
{p.render(x)}
</DocumentBrandProvider>
</div>
);
}
const DATA = {
period: { kind: "asAt", end: "2026-10-31" },
currency: "MYR",
preparedBy: "Aisyah Rahman",
preparedOn: "2026-11-01",
accounts: [
{ code: "1000-001", name: "Maybank current account", group: "assets", debit: "127091.65" },
{ code: "1000-002", name: "Public Bank fixed deposit", group: "assets", debit: "150000.00" },
{ code: "1000-003", name: "Petty cash", group: "assets", debit: "2500.00" },
{ code: "1100-001", name: "Trade receivables", group: "assets", debit: "524180.25" },
{
code: "1150-001",
name: "Other receivables, deposits and prepayments",
group: "assets",
debit: "46718.35",
},
{ code: "1300-001", name: "Inventories — finished goods", group: "assets", debit: "862340.50" },
{
code: "1500-001",
name: "Property, plant and equipment — cost",
group: "assets",
debit: "2148600.00",
},
{
code: "1500-901",
name: "Accumulated depreciation — PPE",
group: "assets",
credit: "864000.00",
},
{ code: "1600-001", name: "Right-of-use assets", group: "assets", debit: "186000.00" },
{ code: "1700-001", name: "Intangible assets — software", group: "assets", debit: "24500.00" },
{ code: "2000-001", name: "Trade payables", group: "liabilities", credit: "386240.60" },
{
code: "2100-001",
name: "Other payables and accruals",
group: "liabilities",
credit: "92415.00",
},
{ code: "2200-001", name: "SST payable", group: "liabilities", credit: "18642.35" },
{ code: "2300-001", name: "Hire purchase payables", group: "liabilities", credit: "62000.00" },
{
code: "2400-001",
name: "Term loan — Maybank Islamic",
group: "liabilities",
credit: "480000.00",
},
{ code: "2500-001", name: "Lease liabilities", group: "liabilities", credit: "156000.00" },
{ code: "2600-001", name: "Current tax liabilities", group: "liabilities", credit: "32180.00" },
{
code: "2700-001",
name: "Deferred tax liabilities",
group: "liabilities",
credit: "46800.00",
},
{ code: "3000-001", name: "Share capital", group: "equity", credit: "500000.00" },
{
code: "3100-001",
name: "Retained earnings — opening",
group: "equity",
credit: "1295395.75",
},
{ code: "3200-001", name: "Dividends paid", group: "equity", debit: "120000.00" },
{ code: "4000-001", name: "Sales — furniture (local)", group: "revenue", credit: "3482615.00" },
{ code: "4000-002", name: "Sales — export", group: "revenue", credit: "541280.00" },
{
code: "4100-001",
name: "Installation and delivery income",
group: "revenue",
credit: "131450.00",
},
{ code: "4900-001", name: "Sales returns and discounts", group: "revenue", debit: "58940.00" },
{ code: "5000-001", name: "Cost of goods sold", group: "costOfSales", debit: "2198760.40" },
{ code: "5100-001", name: "Direct labour", group: "costOfSales", debit: "276150.00" },
{
code: "5200-001",
name: "Freight inwards and import duties",
group: "costOfSales",
debit: "94215.60",
},
{ code: "6100-001", name: "Salaries and wages", group: "expenses", debit: "471300.00" },
{ code: "6100-002", name: "EPF, SOCSO and EIS", group: "expenses", debit: "67024.80" },
{ code: "6100-003", name: "Staff welfare and HRDF levy", group: "expenses", debit: "19640.00" },
{
code: "6200-001",
name: "Rental — factory and showroom",
group: "expenses",
debit: "185000.00",
},
{
code: "6200-002",
name: "Utilities (TNB, Air Selangor)",
group: "expenses",
debit: "41870.25",
},
{ code: "6200-003", name: "Repairs and maintenance", group: "expenses", debit: "18420.00" },
{ code: "6300-001", name: "Advertising and promotion", group: "expenses", debit: "62300.00" },
{ code: "6300-002", name: "Sales commission", group: "expenses", debit: "79860.00" },
{ code: "6300-003", name: "Delivery and transport", group: "expenses", debit: "53115.40" },
{ code: "6400-001", name: "Audit and professional fees", group: "expenses", debit: "25000.00" },
{ code: "6400-002", name: "Software subscriptions", group: "expenses", debit: "11440.00" },
{ code: "6400-003", name: "Insurance", group: "expenses", debit: "13250.00" },
{ code: "6400-004", name: "Bank charges", group: "expenses", debit: "4108.90" },
{
code: "6500-001",
name: "Depreciation and amortisation",
group: "expenses",
debit: "96300.00",
},
{
code: "7100-001",
name: "Interest income — fixed deposit",
group: "otherIncomeAndExpenses",
credit: "7860.00",
},
{
code: "7100-002",
name: "Realised foreign exchange gain/(loss)",
group: "otherIncomeAndExpenses",
debit: "3420.60",
},
{
code: "8100-001",
name: "Hire purchase interest",
group: "otherIncomeAndExpenses",
debit: "13140.00",
},
{
code: "8100-002",
name: "Term loan interest",
group: "otherIncomeAndExpenses",
debit: "30750.00",
},
{ code: "8100-003", name: "Lease interest", group: "otherIncomeAndExpenses", debit: "8100.00" },
{
code: "8800-001",
name: "Income tax expense (estimated)",
group: "taxation",
debit: "72842.00",
},
],
totalDebit: "8096878.70",
totalCredit: "8096878.70",
balanced: true,
};
const OUT = Object.assign({}, DATA, {
balanced: false,
difference: "120.00",
totalCredit: sumAmounts([DATA.totalCredit, "-120.00"]),
accounts: DATA.accounts.map((a) =>
a.code === "2100-001" ? Object.assign({}, a, { credit: sumAmounts([a.credit, "-120.00"]) }) : a,
),
});
export default function TrialBalanceDocumentDemo() {
return (
<Shell
extra={{
options: [
{ value: "ok", label: "Balanced" },
{ value: "out", label: "Out of balance" },
],
}}
render={(x) => <TrialBalanceDocument report={x === "out" ? OUT : DATA} />}
/>
);
} Use
Month-end close packs, auditor requests, the TB attached to journals. (Named …Document because TrialBalanceReport is the on-screen report.)
Props
report: TrialBalanceData — period, accounts [{code, name, group?, debit?, credit?}], totalDebit, totalCredit, balanced, difference?, basis?, currency?, filters?, preparedBy?, preparedOn?, notes?; plus brand?, size?, stamp?, fit?, isolate?, className.
Notes
Consecutive accounts with the same group print under one heading (group can be a label key like 'assets', 'costOfSales', 'expenses'). Empty Dr/Cr sides stay blank, rows are hairline-ruled for long lists. Balanced state is a data flag, never derived. All layouts and locales; A4, header repeats across pages.