ProfitAndLossReport
Printable Statement of Profit or Loss: revenue, cost of sales, gross profit, grouped operating expenses, operating profit, other income and finance costs, profit before tax, tax and profit for the period, with comparative period, variance and variance %.
import { ProfitAndLossReport } from "@cynco-labs/ui"; Example
import { useState } from "react";
import { DocumentBrandProvider, GroupedTabButton, ProfitAndLossReport } 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: "month", end: "2026-10-31" },
basis: "accrual",
currency: "MYR",
columns: [{ label: "Oct 2026" }, { label: "Oct 2025" }],
variance: true,
filters: [
{ label: "Branch", value: "All branches" },
{ label: "Department", value: "All" },
],
preparedBy: "Aisyah Rahman, Finance Manager",
preparedOn: "2026-11-01",
notes:
"Management accounts — unaudited. Income tax is provided at the expected effective rate of 22% for YA 2026. Note references are to the notes in the year-end financial statements.",
revenue: [
{
code: "4000-001",
label: "Sales — furniture (local)",
values: ["368450.00", "321780.00"],
variance: "46670.00",
variancePct: "14.5",
},
{
code: "4000-002",
label: "Sales — export",
values: ["52300.00", "61200.00"],
variance: "-8900.00",
variancePct: "-14.5",
},
{
code: "4100-001",
label: "Installation and delivery income",
values: ["14860.00", "11240.00"],
variance: "3620.00",
variancePct: "32.2",
},
{
code: "4900-001",
label: "Sales returns and discounts",
values: ["-6215.00", "-4980.00"],
variance: "-1235.00",
variancePct: "-24.8",
},
],
totalRevenue: { values: ["429395.00", "389240.00"], variance: "40155.00", variancePct: "10.3" },
costOfSales: [
{
code: "5000-001",
label: "Cost of goods sold",
values: ["-232640.00", "-205310.00"],
variance: "-27330.00",
variancePct: "-13.3",
},
{
code: "5100-001",
label: "Direct labour",
values: ["-28400.00", "-26150.00"],
variance: "-2250.00",
variancePct: "-8.6",
},
{
code: "5200-001",
label: "Freight inwards and import duties",
values: ["-9870.00", "-8640.00"],
variance: "-1230.00",
variancePct: "-14.2",
},
],
totalCostOfSales: {
values: ["-270910.00", "-240100.00"],
variance: "-30810.00",
variancePct: "-12.8",
},
grossProfit: { values: ["158485.00", "149140.00"], variance: "9345.00", variancePct: "6.3" },
operatingExpenses: [
{
label: "Staff costs",
lines: [
{
code: "6100-001",
label: "Salaries and wages",
values: ["-48600.00", "-44200.00"],
variance: "-4400.00",
variancePct: "-10.0",
},
{
code: "6100-002",
label: "EPF, SOCSO and EIS",
values: ["-6912.40", "-6284.60"],
variance: "-627.80",
variancePct: "-10.0",
},
{
code: "6100-003",
label: "Staff welfare and HRDF levy",
values: ["-1850.00", "-2120.00"],
variance: "270.00",
variancePct: "12.7",
},
],
total: { values: ["-57362.40", "-52604.60"], variance: "-4757.80", variancePct: "-9.0" },
},
{
label: "Premises",
lines: [
{
code: "6200-001",
label: "Rental — factory and showroom",
values: ["-18500.00", "-18500.00"],
variance: "0.00",
variancePct: "0.0",
},
{
code: "6200-002",
label: "Utilities (TNB, Air Selangor)",
values: ["-4386.55", "-3912.30"],
variance: "-474.25",
variancePct: "-12.1",
},
{
code: "6200-003",
label: "Repairs and maintenance",
values: ["-1240.00", "-2860.00"],
variance: "1620.00",
variancePct: "56.6",
},
],
total: { values: ["-24126.55", "-25272.30"], variance: "1145.75", variancePct: "4.5" },
},
{
label: "Selling and distribution",
lines: [
{
code: "6300-001",
label: "Advertising and promotion",
values: ["-7450.00", "-5200.00"],
variance: "-2250.00",
variancePct: "-43.3",
},
{
code: "6300-002",
label: "Sales commission",
values: ["-8420.00", "-7310.00"],
variance: "-1110.00",
variancePct: "-15.2",
},
{
code: "6300-003",
label: "Delivery and transport",
values: ["-5680.20", "-4950.80"],
variance: "-729.40",
variancePct: "-14.7",
},
],
total: { values: ["-21550.20", "-17460.80"], variance: "-4089.40", variancePct: "-23.4" },
},
{
label: "Administrative",
lines: [
{
code: "6400-001",
label: "Audit and professional fees",
values: ["-2500.00", "-2500.00"],
variance: "0.00",
variancePct: "0.0",
},
{
code: "6400-002",
label: "Software subscriptions",
values: ["-1180.00", "-890.00"],
variance: "-290.00",
variancePct: "-32.6",
},
{
code: "6400-003",
label: "Insurance",
values: ["-1325.00", "-1250.00"],
variance: "-75.00",
variancePct: "-6.0",
},
{
code: "6400-004",
label: "Bank charges",
values: ["-412.35", "-386.10"],
variance: "-26.25",
variancePct: "-6.8",
},
{
code: "6500-001",
label: "Depreciation and amortisation",
values: ["-9930.00", "-9210.00"],
variance: "-720.00",
variancePct: "-7.8",
note: "5",
},
],
total: { values: ["-15347.35", "-14236.10"], variance: "-1111.25", variancePct: "-7.8" },
},
],
totalOperatingExpenses: {
values: ["-118386.50", "-109573.80"],
variance: "-8812.70",
variancePct: "-8.0",
},
operatingProfit: { values: ["40098.50", "39566.20"], variance: "532.30", variancePct: "1.3" },
otherIncome: [
{
code: "7100-001",
label: "Interest income — fixed deposit",
values: ["820.00", "640.00"],
variance: "180.00",
variancePct: "28.1",
},
{
code: "7100-002",
label: "Realised foreign exchange gain/(loss)",
values: ["-1135.40", "2280.00"],
variance: "-3415.40",
variancePct: "-149.8",
},
],
otherExpenses: [
{
code: "8100-001",
label: "Hire purchase interest",
values: ["-1264.00", "-1410.00"],
variance: "146.00",
variancePct: "10.4",
},
{
code: "8100-002",
label: "Term loan interest",
values: ["-2980.00", "-3240.00"],
variance: "260.00",
variancePct: "8.0",
},
{
code: "8100-003",
label: "Lease interest",
values: ["-760.00", "-880.00"],
variance: "120.00",
variancePct: "13.6",
},
],
profitBeforeTax: { values: ["34779.10", "36956.20"], variance: "-2177.10", variancePct: "-5.9" },
tax: [
{
code: "8800-001",
label: "Income tax expense (estimated)",
values: ["-7651.00", "-8130.00"],
variance: "479.00",
variancePct: "5.9",
note: "6",
},
],
netProfit: { values: ["27128.10", "28826.20"], variance: "-1698.10", variancePct: "-5.9" },
};
export default function ProfitAndLossReportDemo() {
return <Shell render={() => <ProfitAndLossReport report={DATA} />} />;
} Use
Monthly management accounts, year-to-date and annual P&L packs, board papers — anywhere the P&L leaves Cynco as paper or PDF.
Props
report: ProfitAndLossData — period {kind, end, start?, months?}, basis?, currency?, filters?, title?, columns [{label, sublabel?}], variance?, codes?, revenue[], totalRevenue, costOfSales[], totalCostOfSales, grossProfit, operatingExpenses [{label, lines[], total, totalLabel?}], totalOperatingExpenses, operatingProfit, otherIncome?, otherExpenses?, profitBeforeTax, tax[], netProfit, preparedBy?, preparedOn?, notes? (lines are {label, code?, note?, values[], variance?, variancePct?}); plus brand?, size?, stamp?, fit?, isolate?, className.
Notes
Built from ReportHeader + StatementTable + ReportFooter on the kit's DocumentPage. Minimal / classic / modern in black ink only, any font, EN / BM / EN+BM (row and column labels stacked bilingually). Costs are passed as negative figures and print in brackets; every total and variance is printed as given, never recomputed. Gross/operating/pre-tax/net labels switch to 'loss' when the given figure is negative. A4 by default; the column header repeats on each printed page, totals avoid starting a page.