Sample output from the Customer Profitability Review prompt in the Sonar AI Prompt Library, run against a NetSuite test account. Every name and number here is test data. Back to the post · The library
Customer Profitability ReviewNetSuite · OneWorld · USD

Customer Profitability
Review FY2025 – FY2026

Gross-margin contribution by customer across 24 posted periods (Oct 2024 – Sep 2026), reconciled to the general ledger, with concentration, trend, product-mix, collections and data-integrity analysis.

Prepared13 September 2026
Prepared forTim Dietrich · Administrator
SourceGeneral ledger (transactionaccountingline), SuiteQL
BasisPosting transactions · Subsidiaries 1–3 · accrual

01Executive summary

The business earned $1.72M of gross profit on $2.89M of customer revenue over the review window. The headline 59.5% margin is not the operating reality: one quarter of revenue is a zero-cost service item on thirty one-off invoices, almost all of it still unpaid. Product margin is 44.9%, stable, and concentrated in a small B2B core.

Revenue · 24 months
$2.89M
114 customers with activity · 1,834 sales documents
Gross profit
$1.72M
59.5% blended · 44.9% on product
Service item, zero COGS
$764K
26.4% of revenue · 30 invoices · still open
Top 20% of customers
87%
of gross profit (23 accounts) · top 10 = 56%
Overdue receivables
$795K
of $928K open · $473K past 90 days
Product revenue, L12 vs prior 12
vs

Six findings that matter

  1. Reported margin is inflated by a mis-classified service item.SVC_Delivery Service (item 284) generated $763,724 across 30 invoices — one per customer, no cost of sales, posted to Sales : Revenue – Products with no class. It accounts for every 100%-margin customer in the book. Excluding it, gross margin is 44.9% on $2.13M.
  2. Those same invoices are the receivables problem. of the $928K open A/R sits on service invoices; is overdue, up to 498 days. Product customers, by contrast, carry $138K open with a single overdue account (Blockster Inc., $53K, 72 days).
  3. Profit is a 23-account business.The top 20% of customers deliver 87% of gross profit and 88% of revenue. Ten accounts deliver 56% of profit. The remaining 91 customers — 66 of them B2C individuals — contribute $261K of revenue at a healthy 49% margin but only $129K of profit combined.
  4. The largest customer is shrinking.Jones Manufacturing (rank 1, $317K revenue, $133K GP) is down 28% in the last twelve months versus the prior twelve, with $3.2K in August and nothing posted in September. Marshall Industries (+340% YoY, $102K L12) is the fastest-growing product account.
  5. Product margin is remarkably uniform — which limits pricing leverage.Across the 80 product customers, margin runs 38.4% – 65.2% with a median of 49.3% and an interquartile range of just 6 points. Apparel is the structurally weakest category (41.6%) and Realpoint inc. — the most Apparel-heavy large account — has the lowest large-account margin at 40.0%.
  6. Four small integrity defects sit in the ledger.Three item fulfillments posted $784 of COGS with no matching invoice (customers 1035, 1107, 1129); one cash refund (Jane Jackson, −$658) reversed revenue without reversing cost. Immaterial in dollars, material as a control signal.
Scope in one line. Revenue and cost of sales attributed to a customer via the transaction's entity, drawn from posting GL lines on invoices, cash sales, item fulfillments, credit memos and cash refunds in Subsidiaries 1–3. The synthetic monthly "Beg Balance" journals ($19.8M income / $12.8M COGS) carry no customer and are excluded. Gross margin only — no operating-expense allocation. See §12.

02Portfolio economics

Revenue decomposes into three streams with very different cost structures. Product sales carry cost of goods at roughly 55% of price; freight recovery is a small pass-through; the service item carries no cost at all.

Revenue streamRevenueShareCost of salesGross profitMargin
Product sales (net of returns)$2,104,85072.8%$1,171,583$933,26744.3%
Freight revenue$23,1180.8%—$23,118n/a
SVC_Delivery Service review$763,72426.4%$0$763,724100.0%
Total$2,891,692100%$1,171,583$1,720,10959.5%

Product sales figure includes −$911 of credit memos and refunds. "Product + freight" margin = 44.9% ($956K on $2.13M) and is the operating margin used elsewhere in this report. Freight cost is expensed, not in COGS, and is not customer-attributable (see A6).

Margin distribution — product customers

Eighty customers bought product with positive revenue. Their gross margins cluster tightly around 49%; the distribution has almost no left tail, which is consistent with list-price selling and standard-cost inventory rather than negotiated discounting.

Distribution of gross margin, 80 product customers (service-only customers excluded). Red band = customers below 42% — the bottom of the interquartile range. Median 49.3% · P25 45.9% · P75 51.8% · min 38.4% (Blockster Inc.) · max 65.2% (Cherry Hines, $2.4K).

03Concentration & Pareto

Gross profit is highly concentrated. The curve below orders customers by contribution and plots cumulative share. The knee sits at roughly 25 customers; beyond it, each additional customer adds well under half a percent.

Cumulative gross-profit share by customer rank (114 customers). Red marker = top 20% boundary (23 customers, 87.1%). Dashed line = top 10 (56.4%).

Top 10 by gross profit

#CustomerGPMargin

Concentration metrics

Largest customer share of revenue11.0%
Top 5 share of gross profit
Top 10 share of gross profit56.4%
Top 20% (23) share of gross profit87.1%
Customers needed for 50% of GP
Customers needed for 90% of GP
Median customer revenue$4,423
Customers with negative GP4 · −$1,442

Concentration is structural, not incidental: the eight Subsidiary 2 accounts and roughly fifteen Subsidiary 1 B2B accounts are the profit engine. B2C is a long, thin, low-risk tail.

04Segment profitability

Each cut below is exhaustive — the rows sum to the $2.89M / $1.72M totals. Margins shown are blended (including the service item where present); the Product margin column strips it out.

By subsidiary

SubsidiaryCustomersRevenueGross profitMarginOpen A/ROverdue

Subsidiary 2 is eight accounts, all managed by Matt Fisher, producing 29% of revenue. Its four product accounts (Panaderia, Pineapple Republic, Realpoint, Recreational Outfitters) run at 44.8% product margin — in line with Subsidiary 1. One unattributed customer (#1129) carries $19 of COGS.

By sales representative

Sales repCustomersRevenueGross profitMarginProduct marginOverdue A/R

Joel Williams' 80.8% margin is an artefact: six of his nine customers are service-only. His product book (Marshall Industries, Meetz Industries) runs at 42.5%. Unassigned = 66 B2C individuals plus two walk-in/unknown records.

By customer category

CategoryCustomersRevenueGross profitMarginAvg revenue / customer

Information Technology, Consulting, Others, Construction and Services categories are 100% margin because they bought only the service item. Manufacturing (10 accounts, $1.18M) and Retail (8 accounts, $694K) are the product business.

By product category (class) — product revenue only

ClassCustomers buyingRevenueGross profitMargin

Home & Decor (mattresses, headboards, box springs) is the volume and profit leader. Apparel is 4 points weaker than the other two main categories and is the primary driver of the below-median margins at Realpoint, Design Excellence and Emily Horner. "Unclassified" is the −$556 of return lines with no class.

B2B vs B2C

Customer typeCustomersRevenueGPMarginAvg revenueAvg documentsGP per document

GP per document is the report's cost-to-serve proxy: every sales document (invoice or cash sale) implies order entry, picking, shipping and settlement effort. A B2B document yields roughly 40× the gross profit of a B2C one. B2C is entirely cash-sale — zero receivables, zero collection effort — which offsets part of that gap.

05Customer positioning map

Each customer is placed by revenue (log scale) against gross margin. The horizontal line is the product-business margin of 44.9%; the vertical line is the median customer revenue of $4,423. Service-only customers cluster at 100% and are shown in gray because their margin is not a pricing outcome.

Revenue vs gross margin, 110 customers with positive revenue. Red = product customers with margin below 44.9% and revenue above median ("Volume" — large but thin); navy = product customers above both thresholds ("Strategic"); gray = service-only. Hover a point for the name.

QuadrantDefinitionCustomersRevenueGross profitMargin

Thresholds: revenue ≥ $4,423 (median) and product margin ≥ 44.9%. Service-only customers are reported separately because they have no cost base.

06Trend — 24 months

Monthly revenue is shown as stacked bars — product and freight in navy, the service item in red — with cost of sales as a line. The service item arrives in irregular, large lumps; the product base is steadier and has stepped up through 2026.

Monthly posted revenue by posting period, Oct 2024 – Sep 2026. Sep 2026 is a partial month (transactions dated up to 22 Sep are posted). Line = cost of sales. Data labels in $K.

Twelve-month comparison

MeasureOct 24 – Sep 25Oct 25 – Sep 26Change

Movers — product customers, L12 vs prior 12

CustomerPrior 12Last 12Δ

Largest absolute changes among customers with product revenue in either window.

07Top 25 customer profiles

Ranked by gross profit. The sparkline is monthly revenue for the last twelve periods (final bar, Sep 2026, is a partial month). "Days to pay" is the median days from invoice to the linked customer payment; "—" means no invoice has been paid (cash-sale customers have no receivable to pay).

#CustomerSub · RepCategoryRevenueCOGSGPMarginYoYDocsSKUsLead classOpen A/RDays to payLast 12 months

YoY = product+service revenue, last twelve months vs prior twelve; "new" = no revenue in the prior window. Open A/R includes tax and therefore can exceed recognised revenue.

08Collections & working capital

Receivables are the single largest value-at-risk item in this review. The aging is not a portfolio problem; it is the tail of the service invoices.

Open invoice balances by days past due at 13 Sep 2026. $928,247 across 39 invoices; 25 invoices are overdue.

Payment behaviour — paid invoices

Invoices paid (linked to a customer payment)736
Median days invoice → payment0
P90 days invoice → payment3
Maximum days273
Paid after due date44 (6.0%)
Terms in useNet 30 (46 B2B) · none (68 B2C)

The product B2B accounts are exemplary payers: every one of the top-13 product customers has a median of 0 days and zero late payments. The near-zero figures reflect the demo-data generation pattern (payment recorded on invoice date) and should not be read as real DSO.

Largest overdue balances

CustomerOverdueDaysType

09Risk register

Ordered by financial exposure. Severity reflects the combination of dollars and the confidence that action is required.

10Recommended actions

  1. Reclassify and collect the service invoices.Move SVC_Delivery Service (item 284) to a service-revenue account and assign a class; then run a targeted collection programme on the overdue balance, oldest first (Kasson Ltd 498 days, Mercury Co. 451, Schmidt & Sons 404). Until this is done, present margin to management on a product-only basis (44.9%).
  2. Account-plan the top 23.Formalise quarterly reviews for the accounts that produce 87% of profit. Priority: Jones Manufacturing (−28% YoY, zero September) for retention; Marshall Industries (+340%) for capacity and credit-limit review; Blockster Inc. for credit hold until the $53K 72-day balance clears.
  3. Address the Apparel margin gap.Apparel runs 41.6% against 45.5% for Home & Decor. A 3-point improvement on $694K of Apparel revenue is ~$21K of annualised gross profit. Start with Realpoint inc. ($143K Apparel at 39.8%) and Design Excellence ($234K Apparel at 44.3%).
  4. Close the unbilled-shipment loop.Three item fulfillments (customers 1035, 1107, 1129) posted COGS with no invoice. Add a weekly exception check — fulfilled sales orders with no OrdBill link — to the month-end close.
  5. Treat B2C as a channel, not as customers.66 individuals, $261K revenue, 49% margin, ~45 cash sales each, zero receivables. Manage on channel economics (basket size, category mix, store) rather than account plans; consider a loyalty mechanism to lift the $85 average basket.
  6. Build toward contribution margin.This review stops at gross margin. Freight expense ($23K recovered — cost unknown), sales commission and warehouse cost are not customer-attributable in the current chart of accounts. Tagging shipping expense lines with the customer, or adopting a revenue-based allocation, would make the next iteration a true cost-to-serve analysis.

11Full customer ledger

All 114 customers with gross-profit activity. Click any column header to sort; type to filter. Amounts in USD, rounded to the dollar.

GP #CustomerSubCategoryRep RevenueServiceCOGSGPMarginCum GP Last 12Prior 12Active mo.DocsSKUsLead class Open A/ROverdueMax daysFirst saleLast saleTrend

Service = revenue from item 284 (included in Revenue). Cum GP = cumulative share of total gross profit at this rank. Active months = posting periods with non-zero revenue (of 24). Docs = invoices + cash sales. SKUs = distinct non-freight items purchased.

12Methodology, assumptions & queries

Data lineage

Every figure derives from posting general-ledger lines (transactionaccountingline) joined to their transaction header, transaction line and account, aggregated in SuiteQL and reduced in a sandboxed JavaScript worker. No saved searches, reports or spreadsheets were used as intermediates. Four queries feed the analysis:

#QueryRowsPurpose
Q1Customer P&L cube4,618Revenue and COGS by customer × posting month × transaction type × account type × class × subsidiary × stream (product / freight / service)
Q2Customer master274Name, individual flag, subsidiary, category, sales rep, terms
Q3Sales documents with settlement1,834Every posted invoice and cash sale with total, unpaid balance, due date, and the date of the linked customer payment
Q4SKU breadth110Distinct non-freight items per customer
Q0Reconciliation probe25Income / COGS by transaction type, entity presence and journal source — used to define scope and tie out

Reconciliation to the general ledger

ComponentIncomeCOGSTreatment
Invoices, cash sales, credit memos, cash refunds, item fulfillments — with entity$2,891,691.74$1,171,583.02In scope — 100% attributed
Income on item receipts ($149.95) and other income on fulfillments without entity (−$223.00)−$73.05—Excluded — not customer sales
Inventory adjustments, revaluations, worksheets, vendor credits—$9,047.73Excluded — no customer
"Beg Balance Entries" journals JE102–JE149 (synthetic demo load)$19,827,291.79$12,839,559.94Excluded — no customer, not transactional
Other journals—$100.00Excluded
GL Income / COGS, Subsidiaries 1–3, all posting$22,718,910.48$14,020,290.69

Attributed revenue equals the sum of the in-scope transaction types in Q0 to the cent ($2,730,599.60 + $162,003.31 − $253.18 − $657.99 = $2,891,691.74). Attributed COGS equals ItemShip $1,087,837.78 + CashSale $83,745.24 = $1,171,583.02 exactly.

Assumptions and limitations

  1. Attribution key. A GL line belongs to the customer named in transaction.entity of its own document. Item fulfillments carry the sales-order customer, so COGS lands on the same customer as the invoice it relates to.
  2. Gross margin, not contribution margin. Only Income, Other Income and COGS account types are included. Selling, warehouse, freight-out and administrative expenses are not allocated. The 44.9% product margin is therefore an upper bound on customer contribution.
  3. Period basis. Figures are by posting period (accounting month), not transaction date. Sep 2026 is in progress; documents dated after the report date (up to 22 Sep 2026) are already posted and are included.
  4. Elimination subsidiary excluded. Lines on Subsidiary 4 (xElim) are excluded; it has no P&L activity in this window. Subsidiary is taken from the transaction line because the header field is not exposed to SuiteQL.
  5. Revenue streams. "Service" = item 284 (SVC_Delivery Service); "Freight" = lines whose item type is ShipItem; everything else is "Product". Product class comes from the line-level class.
  6. Freight cost. Freight revenue ($23,118) is included in revenue with no offsetting cost because shipping expense is not posted against customers in this ledger.
  7. Payment timing. Days-to-pay uses the latest customer payment linked via nexttransactionlinelink (linktype Payment). Invoices settled by deposit application (1 case) or unlinked are excluded from the timing statistics but not from balances.
  8. Aging. Days past due = 13 Sep 2026 minus duedate; balances use foreignamountunpaid. Account is single-currency USD, so foreign and base amounts are identical.
  9. Demo-data artefacts. Customer datecreated values (e.g. 1 Oct 2026) post-date their first sale and are not used. Payments recorded on invoice date compress days-to-pay toward zero. Customer 3901 (privacy canary, inactive) has no activity.
  10. Rounding. Query results are rounded to cents; report tables to whole dollars; percentages to one decimal. Column totals may differ from displayed components by ±$1.

Q0 — Reconciliation probe

SELECT t.type, a.accttype,
       CASE WHEN t.entity IS NULL THEN 'no-entity' ELSE 'has-entity' END AS ent,
       CASE WHEN t.memo LIKE 'Beg Balance%' THEN 'begbal' ELSE 'real' END AS src,
       COUNT(DISTINCT t.id)        AS txns,
       ROUND(SUM(-tal.amount), 2)  AS net_credit
FROM transactionaccountingline tal
JOIN transaction t      ON tal.transaction = t.id
JOIN transactionline tl ON tl.transaction = t.id AND tl.id = tal.transactionline
JOIN account a          ON tal.account = a.id
WHERE t.posting = 'T' AND tal.posting = 'T'
  AND tl.subsidiary IN (1, 2, 3)
  AND a.accttype IN ('Income', 'OthIncome', 'COGS', 'Expense', 'OthExpense')
GROUP BY t.type, a.accttype,
         CASE WHEN t.entity IS NULL THEN 'no-entity' ELSE 'has-entity' END,
         CASE WHEN t.memo LIKE 'Beg Balance%' THEN 'begbal' ELSE 'real' END
ORDER BY t.type, a.accttype

Q1 — Customer P&L cube

SELECT t.entity                          AS cust,
       TO_CHAR(ap.startdate, 'YYYY-MM')  AS ym,
       t.type                            AS ttype,
       a.accttype,
       tl.class                          AS cls,
       tl.subsidiary                     AS sub,
       CASE WHEN tl.item = 284            THEN 'SVC'
            WHEN tl.itemtype = 'ShipItem' THEN 'FRT'
            ELSE 'PRD' END               AS kind,
       ROUND(SUM(-tal.amount), 2)        AS amt,
       COUNT(DISTINCT t.id)              AS txns
FROM transactionaccountingline tal
JOIN transaction t        ON tal.transaction = t.id
JOIN transactionline tl   ON tl.transaction = t.id AND tl.id = tal.transactionline
JOIN account a            ON tal.account = a.id
JOIN accountingperiod ap  ON ap.id = t.postingperiod
WHERE t.posting = 'T' AND tal.posting = 'T'
  AND tl.subsidiary IN (1, 2, 3)
  AND t.entity IS NOT NULL
  AND t.type IN ('CustInvc', 'CashSale', 'ItemShip', 'CustCred', 'CashRfnd')
  AND a.accttype IN ('Income', 'OthIncome', 'COGS')
GROUP BY t.entity, TO_CHAR(ap.startdate, 'YYYY-MM'), t.type, a.accttype,
         tl.class, tl.subsidiary,
         CASE WHEN tl.item = 284 THEN 'SVC' WHEN tl.itemtype = 'ShipItem' THEN 'FRT' ELSE 'PRD' END

Q2 — Customer master

SELECT c.id, c.entityid, c.companyname, c.altname, c.isperson, c.isinactive,
       c.creditlimit, c.subsidiary AS sub,
       cc.name     AS category,
       e.entityid  AS salesrep,
       tm.name     AS terms,
       TO_CHAR(c.datecreated, 'YYYY-MM-DD') AS created
FROM customer c
LEFT JOIN customercategory cc ON cc.id = c.category
LEFT JOIN employee e          ON e.id  = c.salesrep
LEFT JOIN term tm             ON tm.id = c.terms

Q3 — Sales documents with settlement date

SELECT t.id, t.entity AS cust, t.type AS ttype, t.status,
       TO_CHAR(t.trandate, 'YYYY-MM-DD') AS trandate,
       TO_CHAR(t.duedate,  'YYYY-MM-DD') AS duedate,
       t.foreigntotal        AS total,
       t.foreignamountunpaid AS unpaid,
       ( SELECT MAX(TO_CHAR(p.trandate, 'YYYY-MM-DD'))
         FROM nexttransactionlinelink l
         JOIN transaction p ON p.id = l.nextdoc
         WHERE l.previousdoc = t.id AND l.linktype = 'Payment' AND p.type = 'CustPymt' ) AS paid_on
FROM transaction t
WHERE t.type IN ('CustInvc', 'CashSale')
  AND t.posting = 'T'
  AND t.entity IS NOT NULL

Q4 — SKU breadth per customer

SELECT t.entity AS cust, COUNT(DISTINCT tl.item) AS skus
FROM transactionaccountingline tal
JOIN transaction t      ON tal.transaction = t.id
JOIN transactionline tl ON tl.transaction = t.id AND tl.id = tal.transactionline
JOIN account a          ON tal.account = a.id
WHERE t.posting = 'T' AND tal.posting = 'T'
  AND tl.subsidiary IN (1, 2, 3)
  AND t.entity IS NOT NULL
  AND t.type IN ('CustInvc', 'CashSale')
  AND a.accttype IN ('Income', 'OthIncome')
  AND tl.itemtype <> 'ShipItem'
GROUP BY t.entity

Reducer logic (summary)

Q1 rows are folded per customer: Income/OthIncome lines add to revenue (split by stream and class); COGS lines add to cost. Gross profit = revenue − COGS; margin = GP ÷ revenue where revenue > 0. Customers are ranked by GP and the cumulative share computed. "Last 12" = posting months 2025-10 … 2026-09; "Prior 12" = 2024-10 … 2025-09. Q3 supplies document counts, open and overdue balances (due date < 13 Sep 2026), maximum days past due, and days-to-pay statistics from paid_on − trandate. Q4 supplies SKU counts. Segment tables are simple group-bys over the customer set. The customer table embedded in this document is the reducer's output; every chart and derived figure on this page is computed from it in the browser, so the document is internally consistent by construction.