# StatementOfAccountDocument

A customer or vendor statement: statement date, period, account no. and terms; the party with an account summary (opening, invoices & charges, payments & credits, closing balance); every transaction with date, type, number, description, debit, credit and running balance; an aging strip ending in the total due; payment details and a tear-off remittance slip.

```tsx
import { StatementOfAccountDocument } from "@cynco-labs/ui";
```

## Usage

```tsx
import { useState } from "react";
import {
	DocumentBrandProvider,
	GroupedTabButton,
	StatementOfAccountDocument,
} from "@cynco-labs/ui";

const LAYOUTS = [
	{ value: "minimal", label: "Minimal" },
	{ value: "classic", label: "Classic" },
	{ value: "modern", label: "Modern" },
];

const LOCALES = [
	{ value: "en", label: "EN" },
	{ value: "ms", label: "BM" },
	{ value: "en-ms", label: "EN + BM" },
];

function Tabs(p) {
	return (
		<div style={{ width: p.w }}>
			<GroupedTabButton value={p.value} onValueChange={p.onChange} options={p.options} />
		</div>
	);
}

function Shell(p) {
	const [l, setL] = useState("minimal"),
		[g, setG] = useState("en"),
		[z, setZ] = useState(p.sizes ? p.sizes[0].value : "a4");
	return (
		<div className="flex flex-col gap-3">
			<div className="flex flex-wrap items-center gap-2">
				<Tabs w="270px" value={l} onChange={setL} options={LAYOUTS} />
				<Tabs w="210px" value={g} onChange={setG} options={LOCALES} />
				{p.sizes ? <Tabs w="130px" value={z} onChange={setZ} options={p.sizes} /> : null}
			</div>
			<DocumentBrandProvider brand={Object.assign({}, p.brand, { layout: l, locale: g })}>
				{p.render(z)}
			</DocumentBrandProvider>
		</div>
	);
}

const BRAND = {
	company: {
		name: "Pengedar Minuman Borneo Sdn Bhd",
		registrationNo: "201201031275 (1015489-V)",
		sstNo: "S10-1810-32000985",
		tin: "C24019873640",
		address: ["Lot 5, Lorong Kolombong Jaya 2, Inanam", "88450 Kota Kinabalu, Sabah"],
		phone: "+60 88-431 220",
		email: "ar@pmborneo.com.my",
	},
	footerNote: "Thank you for your continued support.",
};

const SOA = {
	number: "SOA-2026-10-0147",
	date: "2026-10-31",
	periodFrom: "2026-10-01",
	periodTo: "2026-10-31",
	accountNo: "C-0147",
	terms: "Net 14",
	party: {
		name: "Kafe Pelangi Enterprise",
		registrationNo: "202003154872 (KT0458721-X)",
		tin: "IG24758193020",
		address: ["G-12, Imago Shopping Mall, Jalan Coastal", "88000 Kota Kinabalu, Sabah"],
		attention: "Ms. Joanne Chong",
	},
	openingBalance: "6480.50",
	totalDebit: "14278.05",
	totalCredit: "8185.30",
	closingBalance: "12573.25",
	transactions: [
		{
			date: "2026-10-02",
			type: "payment",
			number: "OR-2026-0611",
			description: "Payment received — DuitNow",
			credit: "3200.00",
			balance: "3280.50",
		},
		{
			date: "2026-10-03",
			type: "invoice",
			number: "INV-2026-1402",
			description: "Soft drinks & mineral water, 48 ctn",
			debit: "1862.40",
			balance: "5142.90",
		},
		{
			date: "2026-10-07",
			type: "invoice",
			number: "INV-2026-1419",
			description: "Arabica beans 1 kg × 20, syrups",
			debit: "2315.00",
			balance: "7457.90",
		},
		{
			date: "2026-10-09",
			type: "creditNote",
			number: "CN-2026-0088",
			description: "Damaged cartons returned (INV-2026-1402)",
			credit: "124.80",
			balance: "7333.10",
		},
		{
			date: "2026-10-10",
			type: "invoice",
			number: "INV-2026-1433",
			description: "Juice concentrate, 12 btl",
			debit: "986.50",
			balance: "8319.60",
		},
		{
			date: "2026-10-14",
			type: "invoice",
			number: "INV-2026-1450",
			description: "Mineral water 1.5 L, 60 ctn",
			debit: "1540.00",
			balance: "9859.60",
		},
		{
			date: "2026-10-15",
			type: "payment",
			number: "OR-2026-0648",
			description: "Payment received — IBG",
			credit: "2860.50",
			balance: "6999.10",
		},
		{
			date: "2026-10-17",
			type: "invoice",
			number: "INV-2026-1468",
			description: "Arabica beans, fresh milk, syrups",
			debit: "2104.20",
			balance: "9103.30",
		},
		{
			date: "2026-10-21",
			type: "invoice",
			number: "INV-2026-1481",
			description: "Paper cups 12 oz, 20 ctn",
			debit: "760.00",
			balance: "9863.30",
		},
		{
			date: "2026-10-22",
			type: "debitNote",
			number: "DN-2026-0019",
			description: "Re-delivery charge, 20 Oct",
			debit: "45.00",
			balance: "9908.30",
		},
		{
			date: "2026-10-24",
			type: "invoice",
			number: "INV-2026-1497",
			description: "Soft drinks, 32 ctn",
			debit: "1318.75",
			balance: "11227.05",
		},
		{
			date: "2026-10-28",
			type: "payment",
			number: "OR-2026-0702",
			description: "Part payment — cheque 118842",
			credit: "2000.00",
			balance: "9227.05",
		},
		{
			date: "2026-10-29",
			type: "invoice",
			number: "INV-2026-1510",
			description: "Arabica beans, oat milk, cups",
			debit: "2452.80",
			balance: "11679.85",
		},
		{
			date: "2026-10-31",
			type: "invoice",
			number: "INV-2026-1524",
			description: "Mineral water 500 ml, 40 ctn",
			debit: "893.40",
			balance: "12573.25",
		},
	],
	aging: {
		current: "7574.15",
		days1to30: "4579.10",
		days31to60: "0.00",
		days61to90: "420.00",
		days90plus: "0.00",
	},
	totalDue: "12573.25",
	payment: {
		bankName: "Public Bank",
		accountName: "Pengedar Minuman Borneo Sdn Bhd",
		accountNumber: "3197 5520 14",
		reference: "C-0147",
	},
	notes:
		"RM 420.00 from INV-2026-1188 (August) is now over 60 days. Please settle to avoid a hold on deliveries.",
};

export default function StatementOfAccountDocumentDemo() {
	return <Shell brand={BRAND} render={() => <StatementOfAccountDocument statement={SOA} />} />;
}
```

## Use

Month-end customer statements and dunning, or vendor statements for reconciliation (role: 'vendor' drops the remittance slip).

## Props

statement: StatementOfAccountData { number?, date, periodFrom, periodTo, accountNo?, terms?, currency?, role? (customer | vendor), party: DocParty, openingBalance, totalDebit?, totalCredit?, closingBalance, transactions: {date, type, number?, description?, debit?, credit?, balance}[], aging: {current, days1to30, days31to60, days61to90, days90plus}, totalDue, payment? {bankName?, accountName?, accountNumber?, reference?, qr?}, notes?, remittance? }; plus brand?, size?, stamp?, fit?, isolate?, className.

## Notes

Running balances, aging buckets and totals are printed as given (negative balances in brackets, zero buckets as an em dash). Transaction type accepts a label key (invoice, payment, creditNote, debitNote, refund, journal), {en, ms} or text, so types translate. A 'Balance brought forward' row opens the table and a closing-balance row ends it. Multi-page: the table header repeats on every printed page, rows never split; aging, payment details, footer and the remittance slip follow the last row. Remittance slip: dashed cut line with scissors, pay-to, customer / account / statement date / reference, amount due, and a blank for the amount enclosed.

Category: Documents. Source: `packages/ui/src/documents/operations.tsx`.
