/* ════════════════════════════════════════════════════════ NASAMA PROPERTIES — ADDITIONAL PRINT DOCUMENTS Cash Flow · Changes in Equity · Notes to Financial Statements Purpose-built A4 print layout — never shown on screen. ════════════════════════════════════════════════════════ */ /* ════════════════════════════════════════════════════════ CASH FLOW PRINT DOCUMENT ════════════════════════════════════════════════════════ */ function CFPrintDoc({ accounts, filteredTxns, openingLedger, toDateLedger, dateFilter, settings }) { const company = settings?.company || "Nasama Properties"; const currency = settings?.currency || "AED"; const trn = settings?.trn; const periodLine = dateFilter.from && dateFilter.to ? `For the period ${fmtDate(dateFilter.from)} to ${fmtDate(dateFilter.to)}` : dateFilter.to ? `As of ${fmtDate(dateFilter.to)}` : "All Periods"; // Account classifications const cashAccounts = (accounts || []).filter(isCashAccount); const fixedAssetAccts = (accounts || []).filter(isFixedAssetAccount); const equityAccts = (accounts || []).filter(a => a.type === "Equity"); const cashIds = new Set(cashAccounts.map(a => a.id)); const fixedAssetIds = new Set(fixedAssetAccts.map(a => a.id)); const equityIds = new Set(equityAccts.map(a => a.id)); // Opening & closing cash const openingCash = cashAccounts.reduce((s, a) => s + accountBalance(a, openingLedger || {}), 0); const closingCash = cashAccounts.reduce((s, a) => s + accountBalance(a, toDateLedger || {}), 0); // Classify cash flows let operating = 0, investing = 0, financing = 0; const opLines = [], invLines = [], finLines = []; (filteredTxns || []).forEach(txn => { const r = classifyTxnCash(txn, cashIds, fixedAssetIds, equityIds); if (!r) return; operating += r.operating; investing += r.investing; financing += r.financing; const memo = txn.memo || (txn.lines && txn.lines[0] && txn.lines[0].memo) || ""; if (r.operating !== 0) opLines.push({ ref: txn.ref, memo, amount: r.operating, date: txn.date }); if (r.investing !== 0) invLines.push({ ref: txn.ref, memo, amount: r.investing, date: txn.date }); if (r.financing !== 0) finLines.push({ ref: txn.ref, memo, amount: r.financing, date: txn.date }); }); const netMovement = operating + investing + financing; // Shared col group for activity tables function ActivityCols() { return ( ); } function ActivityTableHead() { return ( {["Date", "Ref", "Description", "Amount"].map((h, i) => ( {h} ))} ); } function ActivitySection({ label, lines, subtotal }) { const amtColor = subtotal >= 0 ? PD.green : PD.red; return (
{lines.length === 0 ? (
No {label.toLowerCase()} in this period.
) : ( {lines.map((ln, i) => ( ))}
{ln.date ? fmtDate(ln.date) : "—"} {ln.ref ? (ln.ref.length > 12 ? ln.ref.slice(0, 10) + "…" : ln.ref) : "—"} {ln.memo || "Transaction"} = 0 ? PD.green : PD.red, whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums" }}> {ln.amount >= 0 ? "+" : ""}{pFmt(ln.amount)}
)} {/* Subtotal row */}
Net Cash from {label} {pFmt(subtotal)}
); } return (
{/* Cash accounts summary */} {cashAccounts.length > 0 && (
Cash & Bank Accounts: {cashAccounts.map((a, i) => ( {a.code} {a.name}{i < cashAccounts.length - 1 ? " · " : ""} ))}
)} {/* Reconciliation block */}
{[ { label: "Opening Cash & Bank Balance", value: openingCash, color: PD.inkMd }, { label: "Net Cash from Operating Activities", value: operating, color: operating >= 0 ? PD.green : PD.red }, { label: "Net Cash from Investing Activities", value: investing, color: investing >= 0 ? PD.green : PD.red }, { label: "Net Cash from Financing Activities", value: financing, color: financing >= 0 ? PD.green : PD.red }, { label: "Net Movement in Cash", value: netMovement, color: netMovement >= 0 ? PD.green : PD.red, bold: true }, ].map((row, i) => ( ))}
{row.label} {pFmt(row.value)}
{/* Closing balance — grand total */}
Closing Cash & Bank Balance = 0 ? PD.green : PD.red, whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums" }}> {pFmt(closingCash)}
); } /* ════════════════════════════════════════════════════════ STATEMENT OF CHANGES IN EQUITY — PRINT DOCUMENT ════════════════════════════════════════════════════════ */ function EquityPrintDoc({ accounts, filteredLedger, openingLedger, toDateLedger, totalRev, totalExp, dateFilter, settings }) { const company = settings?.company || "Nasama Properties"; const currency = settings?.currency || "AED"; const trn = settings?.trn; const netIncome = (totalRev || 0) - (totalExp || 0); const periodLine = dateFilter.from && dateFilter.to ? `For the period ${fmtDate(dateFilter.from)} to ${fmtDate(dateFilter.to)}` : dateFilter.to ? `As of ${fmtDate(dateFilter.to)}` : "All Periods"; const equityAccounts = (accounts || []).filter(a => a.type === "Equity"); function isCapital(a) { return /capital/i.test(a.name || ""); } function isDrawing(a) { return /drawing|draw|withdrawal/i.test(a.name || ""); } function isRetained(a) { return /retained|reserve|profit/i.test(a.name || ""); } const capitalAccts = equityAccounts.filter(isCapital); const drawingAccts = equityAccounts.filter(a => isDrawing(a) && !isCapital(a)); const retainedAccts = equityAccounts.filter(a => isRetained(a) && !isCapital(a) && !isDrawing(a)); const otherEquity = equityAccounts.filter(a => !isCapital(a) && !isDrawing(a) && !isRetained(a)); function sumBal(accts, ledger) { return accts.reduce((s, a) => s + accountBalance(a, ledger || {}), 0); } const openCapital = sumBal(capitalAccts, openingLedger); const openDrawings = sumBal(drawingAccts, openingLedger); const openRetained = sumBal(retainedAccts, openingLedger); const openOther = sumBal(otherEquity, openingLedger); const openTotal = sumBal(equityAccounts, openingLedger); const perCapital = sumBal(capitalAccts, filteredLedger); const perDrawings = sumBal(drawingAccts, filteredLedger); const perOther = sumBal(otherEquity, filteredLedger); const closeCapital = sumBal(capitalAccts, toDateLedger); const closeDrawings = sumBal(drawingAccts, toDateLedger); const closeRetained = sumBal(retainedAccts, toDateLedger); const closeOther = sumBal(otherEquity, toDateLedger); const closeTotal = sumBal(equityAccounts, toDateLedger) + netIncome; // 5-column table: Label | Opening | Movement | Net Income | Closing const colW = [null, 120, 120, 120, 120]; const hdr = ["Component", "Opening Balance", "Movement", "Net Income", "Closing Balance"]; function AmtTd({ val, bold, nc }) { const c = nc ? (val >= 0 ? PD.green : PD.red) : PD.inkDk; return ( {pFmt(val)} ); } function DataRow({ label, opening, movement, ni, closing, bold }) { return ( {label} ); } return (
{/* Table header */} {colW.slice(1).map((w, i) => )} {hdr.map((h, i) => ( ))} {capitalAccts.length > 0 && ( )} {drawingAccts.length > 0 && ( )} {retainedAccts.length > 0 && ( )} {otherEquity.length > 0 && ( )}
{h}
{/* Note */}
Note: Net income for the period is sourced from the Profit & Loss statement and is reflected as an addition to retained earnings before any distribution. Drawings represent direct withdrawals by the owner and reduce total equity.
); } /* ════════════════════════════════════════════════════════ NOTES TO FINANCIAL STATEMENTS — PRINT DOCUMENT ════════════════════════════════════════════════════════ */ function NotesPrintDoc({ accounts, filteredLedger, toDateLedger, filteredTxns, totalRev, totalExp, totalAssets, totalLiabilities, totalEquity, dateFilter, settings }) { const company = settings?.company || "Nasama Properties"; const currency = settings?.currency || "AED"; const trn = settings?.trn; const netIncome = (totalRev || 0) - (totalExp || 0); const periodLine = dateFilter.from && dateFilter.to ? `For the period ${fmtDate(dateFilter.from)} to ${fmtDate(dateFilter.to)}` : dateFilter.to ? `As of ${fmtDate(dateFilter.to)}` : "All Periods"; const periodLabel = dateFilter.from && dateFilter.to ? `${fmtDate(dateFilter.from)} to ${fmtDate(dateFilter.to)}` : periodLine; const generated = new Date().toLocaleString("en-GB", { day: "2-digit", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit", }); const revenueAccts = (accounts || []).filter(a => a.type === "Revenue" && accountBalance(a, filteredLedger) !== 0) .sort((a, b) => accountBalance(b, filteredLedger) - accountBalance(a, filteredLedger)); const expenseAccts = (accounts || []).filter(a => a.type === "Expense" && accountBalance(a, filteredLedger) !== 0) .sort((a, b) => accountBalance(b, filteredLedger) - accountBalance(a, filteredLedger)); const cashAccts = (accounts || []).filter(isCashAccount); const liabilityAccts = (accounts || []).filter(a => a.type === "Liability" && accountBalance(a, toDateLedger) !== 0); const equityAccts = (accounts || []).filter(a => a.type === "Equity" && accountBalance(a, toDateLedger) !== 0); const totalCash = cashAccts.reduce((s, a) => s + accountBalance(a, toDateLedger || {}), 0); // ── Shared note heading function NoteHead({ number, title }) { return (
{number}. {title}
); } // ── Info key-value grid function InfoTable({ rows }) { return ( {rows.map(([k, v], i) => v != null && ( ))}
{k} {v}
); } // ── Account breakdown table function AcctTable({ rows, ledger }) { if (!rows || rows.length === 0) { return
No activity in this period.
; } return ( {rows.map(a => { const bal = accountBalance(a, ledger || {}); return ( ); })}
{a.code} {a.name} {pFmt(bal)}
); } // ── Policy bullet function Bullet({ text }) { return (
{text}
); } return (
{/* Note 1 — General Information */} {/* Note 2 — Basis of Preparation */}
These financial statements have been prepared under generally accepted accounting principles (GAAP) using the double-entry bookkeeping method. All amounts are stated in {currency} unless otherwise indicated.
{/* Note 3 — Cash & Bank */} {cashAccts.length === 0 ? (
No cash or bank accounts configured.
) : (
= 0 ? PD.green : PD.red} />
)} {/* Note 4 — Revenue */} {totalRev > 0 && } {/* Note 5 — Expenses */} {totalExp > 0 && } {/* Note 6 — Equity */}
Equity represents the residual interest in assets after deducting liabilities. The entity is structured as a single-owner enterprise.
{/* Note 7 — Liabilities (if any) */} {liabilityAccts.length > 0 && (
)} {/* Note 8 — Other Disclosures */} 0 ? "8" : "7"} title="Other Disclosures" />
Prepared by: {company} · Accounting System v2 · {generated}
); }