sudo's Overview adds up this month the way the Costs page does, so the two can't disagree. Before, the Overview kept its own count from the ledger. That count left usage paid with credit nowhere: neither revenue nor given. It also estimated what gifts cost as price less the margin. So a month of comped use read as a red margin on nothing sold. Now billing's Overview carries month_margin, the Costs model's figures for the month through its last rolled-up day (margin_through), and the Costs page and the Overview add up the same days with one function, overall_of. In the model, credits spent count as given (promotional, goodwill) or as sold (paid, refund), and so do the free tier, discounts and testing resets. Given is what it actually cost, and cost includes Cloudflare's bill. The Overview's revenue, margin on what was sold, Given by g1t, cost line (Cloudflare and models apart, with a link to Costs & margin) and the Given this month table (cost by why) read from it. The header says through which day. Before a day of the month is rolled up, it falls back to the ledger as before. The six-month chart and This month by kind still read the ledger. Checked: cargo test -p g1t-billing (292; new: the Overview and the Costs page add up the same days), apps/sudo tsc -b and npm test (159; new: marginApart, dayShort).
- G1t-Session:
- ses_01m5075p3weck8yhgj6kdx5nqb
9 files+226−490/9 viewed
| 1 | 1 | import assert from "node:assert/strict"; | |
| 2 | 2 | import { test } from "node:test"; | |
| 3 | 3 | ||
| 4 | − | import { axisDollars, barPath, change, marginOf, monthBars, monthLong, monthShort, niceCeiling, shares, ticks } from "./chart.ts"; | |
| 4 | + | import { axisDollars, barPath, change, dayShort, marginOf, monthBars, monthLong, monthShort, niceCeiling, shares, ticks } from "./chart.ts"; | |
| 5 | 5 | ||
| 6 | 6 | const M = 1_000_000; | |
| 7 | 7 | const month = (m: string, charged: number, cost: number) => ({ month: m, chargedMicros: charged, costMicros: cost, paidMicros: 0 }); | |
| ⋯ | |||
| 91 | 91 | assert.deepEqual(shares([50, 100, 0, -5]), [0.5, 1, 0, 0]); | |
| 92 | 92 | assert.deepEqual(shares([0, 0]), [0, 0]); | |
| 93 | 93 | }); | |
| 94 | + | ||
| 95 | + | test("a day, briefly", () => { | |
| 96 | + | assert.equal(dayShort("2026-10-14"), "Oct 14"); | |
| 97 | + | assert.equal(dayShort("2026-10-01"), "Oct 1"); | |
| 98 | + | assert.equal(dayShort("soon"), "soon"); | |
| 99 | + | }); | |
| 21 | 21 | return match && MONTHS_LONG[index] ? `${MONTHS_LONG[index]} ${match[1]}` : month; | |
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | + | /** `2026-10-14` → `Oct 14`; anything else as it came. */ | |
| 25 | + | export function dayShort(day: string): string { | |
| 26 | + | const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(day); | |
| 27 | + | const name = match ? MONTHS[Number(match[2]) - 1] : undefined; | |
| 28 | + | return match && name ? `${name} ${Number(match[3])}` : day; | |
| 29 | + | } | |
| 30 | + | ||
| 24 | 31 | /** | |
| 25 | 32 | * The top of an axis for values up to `max`: the next 1, 2, 2.5 or 5 | |
| 26 | 33 | * times a power of ten, so its ticks are round numbers. A dollar when |
| 6 | 6 | age, | |
| 7 | 7 | answerDueAt, | |
| 8 | 8 | givenLabel, | |
| 9 | + | marginApart, | |
| 9 | 10 | givenParts, | |
| 10 | 11 | givenTotal, | |
| 11 | 12 | goodwillLine, | |
| ⋯ | |||
| 149 | 150 | netMicros: -5_000_000, | |
| 150 | 151 | }); | |
| 151 | 152 | }); | |
| 153 | + | ||
| 154 | + | test("the month by the Costs model: given is cost already, and the margin is on what was sold", () => { | |
| 155 | + | const parts = marginApart({ usageMicros: 720_000, plansMicros: 0, costMicros: 1_300_000, givenMicros: 700_000, soldMarginMicros: 120_000, soldMarginPercent: 16.67, marginMicros: -580_000 }); | |
| 156 | + | assert.deepEqual(parts, { revenueMicros: 720_000, soldCostMicros: 600_000, marginMicros: 120_000, marginPercent: 17, netMicros: -580_000 }); | |
| 157 | + | // Nothing sold: no percent. | |
| 158 | + | assert.equal(marginApart({ usageMicros: 0, plansMicros: 0, costMicros: 30_080_000, givenMicros: 30_080_000, soldMarginMicros: 0, soldMarginPercent: null, marginMicros: -30_080_000 }).marginPercent, null); | |
| 159 | + | }); | |
| 5 | 5 | * and margin kept apart from what g1t gives. Money is in micros. No | |
| 6 | 6 | * Workers or React imports, so it can be tested under Node. | |
| 7 | 7 | */ | |
| 8 | − | import type { GivenFigures, MonthFigures } from "@g1t/contracts"; | |
| 8 | + | import type { GivenFigures, MonthFigures, OverallMargin } from "@g1t/contracts"; | |
| 9 | 9 | ||
| 10 | 10 | // --- Requests ------------------------------------------------------------------- | |
| 11 | 11 | ||
| ⋯ | |||
| 231 | 231 | netMicros: revenueMicros - month.costMicros, | |
| 232 | 232 | }; | |
| 233 | 233 | } | |
| 234 | + | ||
| 235 | + | /** | |
| 236 | + | * The same parts as `moneyApart`, from the Costs page's model (billing's | |
| 237 | + | * `OverallMargin`), where what was given is already its cost and credits | |
| 238 | + | * spent are among it. | |
| 239 | + | */ | |
| 240 | + | export function marginApart( | |
| 241 | + | o: Pick<OverallMargin, "usageMicros" | "plansMicros" | "costMicros" | "givenMicros" | "soldMarginMicros" | "soldMarginPercent" | "marginMicros">, | |
| 242 | + | ): { revenueMicros: number; soldCostMicros: number; marginMicros: number; marginPercent: number | null; netMicros: number } { | |
| 243 | + | const revenueMicros = o.usageMicros + o.plansMicros; | |
| 244 | + | return { | |
| 245 | + | revenueMicros, | |
| 246 | + | soldCostMicros: Math.max(0, o.costMicros - (o.givenMicros ?? 0)), | |
| 247 | + | marginMicros: o.soldMarginMicros ?? revenueMicros - Math.max(0, o.costMicros - (o.givenMicros ?? 0)), | |
| 248 | + | marginPercent: o.soldMarginPercent == null ? null : Math.round(o.soldMarginPercent), | |
| 249 | + | netMicros: o.marginMicros, | |
| 250 | + | }; | |
| 251 | + | } | |
| 7 | 7 | import { KindBreakdown, MonthsChart } from "~/components/charts"; | |
| 8 | 8 | import { SignalBadge, StaffName, StageBadge } from "~/components/sales"; | |
| 9 | 9 | import { Avatar, Badge, Notice, PageHeader, Section, Stat } from "~/components/ui"; | |
| 10 | − | import { change, monthLong } from "~/lib/chart"; | |
| 11 | − | import { givenLabel, givenTotal, moneyApart } from "~/lib/pricing"; | |
| 10 | + | import { change, dayShort, monthLong } from "~/lib/chart"; | |
| 11 | + | import { givenLabel, givenTotal, marginApart, moneyApart } from "~/lib/pricing"; | |
| 12 | + | import { givenParts } from "~/lib/costs"; | |
| 12 | 13 | import { usd } from "~/lib/money"; | |
| 13 | 14 | import { admin, identity } from "~/lib/services.server"; | |
| 14 | 15 | import { settle } from "~/lib/settle"; | |
| ⋯ | |||
| 48 | 49 | const plans = current?.plansMicros ?? 0; | |
| 49 | 50 | const given = overview?.given ?? []; | |
| 50 | 51 | const givenSum = givenTotal(given); | |
| 51 | − | const money = moneyApart({ chargedMicros: charged, plansMicros: plans, costMicros: cost }, givenSum.costMicros); | |
| 52 | + | // The Costs page's model when billing has rolled up a day of this month: | |
| 53 | + | // the same figures, so the two pages never disagree. Before that, the | |
| 54 | + | // ledger's month so far. | |
| 55 | + | const mm = overview?.monthMargin ?? null; | |
| 56 | + | const money = mm ? marginApart(mm) : moneyApart({ chargedMicros: charged, plansMicros: plans, costMicros: cost }, givenSum.costMicros); | |
| 57 | + | const usage = mm ? mm.usageMicros : charged; | |
| 58 | + | const plansIn = mm ? mm.plansMicros : plans; | |
| 59 | + | const allCost = mm ? mm.costMicros : cost; | |
| 60 | + | const givenRows = mm ? givenParts(mm) : []; | |
| 52 | 61 | const growth = previous ? change(previous.chargedMicros + (previous.plansMicros ?? 0), money.revenueMicros) : null; | |
| 53 | 62 | ||
| 54 | 63 | return ( | |
| 55 | 64 | <main className="mx-auto max-w-6xl px-4 py-8 sm:py-10"> | |
| 56 | 65 | <PageHeader | |
| 57 | 66 | title="Overview" | |
| 58 | − | description={overview ? <>The business in {monthLong(overview.month)}, so far, and who needs a word.</> : "The business at a glance."} | |
| 67 | + | description={ | |
| 68 | + | overview ? ( | |
| 69 | + | <> | |
| 70 | + | The business in {monthLong(overview.month)} | |
| 71 | + | {overview.marginThrough ? <>, through {dayShort(overview.marginThrough)}</> : ", so far"}, and who needs a word. | |
| 72 | + | </> | |
| 73 | + | ) : ( | |
| 74 | + | "The business at a glance." | |
| 75 | + | ) | |
| 76 | + | } | |
| 59 | 77 | /> | |
| 60 | 78 | ||
| 61 | 79 | {growthError ? ( | |
| ⋯ | |||
| 99 | 117 | value={usd(money.revenueMicros)} | |
| 100 | 118 | hint={ | |
| 101 | 119 | <> | |
| 102 | − | {usd(charged)} usage, {usd(plans)} plans | |
| 120 | + | {usd(usage)} usage, {usd(plansIn)} plans | |
| 103 | 121 | {growth == null ? "" : ` · ${growth >= 0 ? "+" : "−"}${Math.abs(growth)}% on last month`} | |
| 104 | 122 | </> | |
| 105 | 123 | } | |
| ⋯ | |||
| 115 | 133 | } | |
| 116 | 134 | tone={money.revenueMicros > 0 && money.marginMicros < 0 ? "danger" : undefined} | |
| 117 | 135 | /> | |
| 118 | − | <Stat | |
| 119 | − | label="Given by g1t" | |
| 120 | − | value={usd(givenSum.micros)} | |
| 121 | − | hint={`At price; it cost g1t ${usd(givenSum.costMicros)}. Not a loss on sales.`} | |
| 122 | − | /> | |
| 136 | + | {mm ? ( | |
| 137 | + | <Stat | |
| 138 | + | label="Given by g1t" | |
| 139 | + | value={usd(mm.givenMicros)} | |
| 140 | + | hint={`What it cost g1t, ${givenRows.length === 0 ? "nothing yet" : "by why below"}. Not a loss on sales.`} | |
| 141 | + | /> | |
| 142 | + | ) : ( | |
| 143 | + | <Stat | |
| 144 | + | label="Given by g1t" | |
| 145 | + | value={usd(givenSum.micros)} | |
| 146 | + | hint={`At price; it cost g1t ${usd(givenSum.costMicros)}. Not a loss on sales.`} | |
| 147 | + | /> | |
| 148 | + | )} | |
| 123 | 149 | <Stat | |
| 124 | 150 | label="Plans" | |
| 125 | 151 | value={String(overview.activePlans ?? 0)} | |
| ⋯ | |||
| 127 | 153 | /> | |
| 128 | 154 | </div> | |
| 129 | 155 | <p className="mt-2 text-xs text-faint"> | |
| 130 | − | Cost to g1t in all {usd(cost)}; revenue less all of it, given included: {usd(money.netMicros)}. | |
| 156 | + | Cost to g1t in all {usd(allCost)} | |
| 157 | + | {mm ? ` (Cloudflare ${usd(mm.cloudflareCostMicros ?? 0)}, models ${usd(mm.modelsCostMicros ?? 0)})` : ""}; revenue less all of it, given included:{" "} | |
| 158 | + | {usd(money.netMicros)}.{" "} | |
| 159 | + | <Link to="/costs" className="text-accent hover:underline"> | |
| 160 | + | Costs & margin | |
| 161 | + | </Link> | |
| 131 | 162 | </p> | |
| 132 | 163 | ||
| 133 | 164 | {overview.pools && ( | |
| ⋯ | |||
| 158 | 189 | ||
| 159 | 190 | <Section | |
| 160 | 191 | title="Given this month, apart from margin" | |
| 161 | − | description="Internal use, trials, the open-source pool, goodwill and what g1t covered: at price, and what it cost." | |
| 192 | + | description={ | |
| 193 | + | mm | |
| 194 | + | ? "What g1t's gifts cost it, by why: comped workspaces, free use, trials, the open-source pool, discounts, and credits from g1t as they are spent." | |
| 195 | + | : "Internal use, trials, the open-source pool, goodwill and what g1t covered: at price, and what it cost." | |
| 196 | + | } | |
| 162 | 197 | className="mt-6" | |
| 163 | 198 | > | |
| 164 | − | {given.length === 0 ? ( | |
| 199 | + | {mm ? ( | |
| 200 | + | givenRows.length === 0 ? ( | |
| 201 | + | <p className="text-sm text-muted">Nothing given yet this month.</p> | |
| 202 | + | ) : ( | |
| 203 | + | <div className="-mx-4 -my-4 overflow-x-auto sm:-mx-5 sm:-my-5"> | |
| 204 | + | <table className="w-full min-w-80 text-sm"> | |
| 205 | + | <thead> | |
| 206 | + | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 207 | + | <th className="px-4 py-2 font-medium sm:pl-5">Why</th> | |
| 208 | + | <th className="px-4 py-2 text-right font-medium sm:pr-5">Cost to g1t</th> | |
| 209 | + | </tr> | |
| 210 | + | </thead> | |
| 211 | + | <tbody> | |
| 212 | + | {givenRows.map(([why, micros]) => ( | |
| 213 | + | <tr key={why} className="border-b border-line last:border-0"> | |
| 214 | + | <td className="px-4 py-2 first-letter:uppercase sm:pl-5">{why}</td> | |
| 215 | + | <td className="tabular px-4 py-2 text-right sm:pr-5">{usd(micros)}</td> | |
| 216 | + | </tr> | |
| 217 | + | ))} | |
| 218 | + | </tbody> | |
| 219 | + | </table> | |
| 220 | + | </div> | |
| 221 | + | ) | |
| 222 | + | ) : given.length === 0 ? ( | |
| 165 | 223 | <p className="text-sm text-muted">Nothing given yet this month.</p> | |
| 166 | 224 | ) : ( | |
| 167 | 225 | <div className="-mx-4 -my-4 overflow-x-auto sm:-mx-5 sm:-my-5"> | |
| 2219 | 2219 | /// What g1t gave this month, by source, apart from its margin. | |
| 2220 | 2220 | #[serde(default)] | |
| 2221 | 2221 | pub given: Vec<GivenFigures>, | |
| 2222 | + | /// This month so far by the Costs & margin model (`margin_days`), the | |
| 2223 | + | /// same figures the Costs page adds up: money in, every cost | |
| 2224 | + | /// (Cloudflare's bill included), and what was given by why, credits | |
| 2225 | + | /// spent among it. Through `margin_through`, the last day rolled up. | |
| 2226 | + | /// None before the first day of the month is rolled up. | |
| 2227 | + | #[serde(default)] | |
| 2228 | + | pub month_margin: Option<OverallMargin>, | |
| 2229 | + | /// The last day `month_margin` holds. YYYY-MM-DD. | |
| 2230 | + | #[serde(default)] | |
| 2231 | + | pub margin_through: Option<String>, | |
| 2222 | 2232 | /// g1t's own and Flagon's workspaces this month: what their use cost, | |
| 2223 | 2233 | /// and why they are not charged. | |
| 2224 | 2234 | #[serde(default)] |
| 656 | 656 | planMrrMicros?: number; | |
| 657 | 657 | /** What g1t gave this month, by source, apart from its margin. */ | |
| 658 | 658 | given?: GivenFigures[]; | |
| 659 | + | /** | |
| 660 | + | * This month so far by the Costs & margin model, the same figures the | |
| 661 | + | * Costs page adds up (Cloudflare's bill included, credits spent among | |
| 662 | + | * what was given), through `marginThrough`. Null before the month's | |
| 663 | + | * first day is rolled up. | |
| 664 | + | */ | |
| 665 | + | monthMargin?: OverallMargin | null; | |
| 666 | + | /** The last day `monthMargin` holds. YYYY-MM-DD. */ | |
| 667 | + | marginThrough?: string | null; | |
| 659 | 668 | /** g1t's own and Flagon's workspaces: what their use cost, and why they are not charged. */ | |
| 660 | 669 | internal?: InternalUse[]; | |
| 661 | 670 | openRequests?: number; |
| 488 | 488 | u | |
| 489 | 489 | } | |
| 490 | 490 | ||
| 491 | + | /** | |
| 492 | + | * All of g1t over some rolled-up days: money in, every cost, what was | |
| 493 | + | * given by why, and the margins on everything and on what was sold. The | |
| 494 | + | * Costs page and the Overview both add up the days with this, so they | |
| 495 | + | * cannot disagree. | |
| 496 | + | */ | |
| 497 | + | pub(crate) fn overall_of(days: &[ProductDay]) -> OverallMargin { | |
| 498 | + | let mut overall = OverallMargin::default(); | |
| 499 | + | for d in days { | |
| 500 | + | overall.cost_micros += d.cost(); | |
| 501 | + | overall.given_micros += d.given.total(); | |
| 502 | + | if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { | |
| 503 | + | overall.models_cost_micros += d.cost(); | |
| 504 | + | } else { | |
| 505 | + | overall.cloudflare_cost_micros += d.cost(); | |
| 506 | + | } | |
| 507 | + | overall.given_comped_micros += d.given.comped; | |
| 508 | + | overall.given_free_micros += d.given.free; | |
| 509 | + | overall.given_trial_micros += d.given.trial; | |
| 510 | + | overall.given_pool_micros += d.given.pool; | |
| 511 | + | overall.given_discount_micros += d.given.discount; | |
| 512 | + | overall.given_credit_promotional_micros += d.given.credit_promotional; | |
| 513 | + | overall.given_credit_goodwill_micros += d.given.credit_goodwill; | |
| 514 | + | overall.given_reset_micros += d.given.reset; | |
| 515 | + | overall.given_unpaid_micros += d.given.unpaid; | |
| 516 | + | let sold = (d.cost() - d.given.total()).max(0); | |
| 517 | + | if OVERHEAD.contains(&d.bucket.as_str()) { | |
| 518 | + | overall.plans_micros += d.cash_micros; | |
| 519 | + | overall.running_cost_micros += sold; | |
| 520 | + | } else if d.bucket == UNMAPPED { | |
| 521 | + | overall.usage_micros += d.cash_micros; | |
| 522 | + | overall.unmapped_cost_micros += sold; | |
| 523 | + | } else { | |
| 524 | + | overall.usage_micros += d.cash_micros; | |
| 525 | + | overall.usage_cost_micros += sold; | |
| 526 | + | } | |
| 527 | + | } | |
| 528 | + | let revenue = overall.usage_micros + overall.plans_micros; | |
| 529 | + | overall.margin_micros = revenue - overall.cost_micros; | |
| 530 | + | overall.margin_percent = margin_percent(revenue, overall.cost_micros); | |
| 531 | + | let sold = (overall.cost_micros - overall.given_micros).max(0); | |
| 532 | + | overall.sold_margin_micros = revenue - sold; | |
| 533 | + | overall.sold_margin_percent = margin_percent(revenue, sold); | |
| 534 | + | overall | |
| 535 | + | } | |
| 536 | + | ||
| 491 | 537 | /// Margin as a share of what was charged, in percent; None when nothing was. | |
| 492 | 538 | pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> { | |
| 493 | 539 | (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64) | |
| ⋯ | |||
| 1814 | 1860 | })) | |
| 1815 | 1861 | } | |
| 1816 | 1862 | ||
| 1863 | + | /// The month starting `month_start` (YYYY-MM-01) so far, as rolled up: | |
| 1864 | + | /// its figures and the last day they hold; None before its first day | |
| 1865 | + | /// is rolled up. | |
| 1866 | + | pub(crate) async fn month_margin(&self, month_start: &str) -> Result<Option<(OverallMargin, String)>> { | |
| 1867 | + | let until = rfc3339(now_ms())[..10].to_owned(); | |
| 1868 | + | let days = self.margin_days(month_start, &until).await?; | |
| 1869 | + | let Some(last) = days.iter().map(|d| d.day.clone()).max() else { | |
| 1870 | + | return Ok(None); | |
| 1871 | + | }; | |
| 1872 | + | Ok(Some((overall_of(&days), last))) | |
| 1873 | + | } | |
| 1874 | + | ||
| 1817 | 1875 | /// `admin_costs`: the Costs & margin page. | |
| 1818 | 1876 | pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> { | |
| 1819 | 1877 | let until = rfc3339(now_ms())[..10].to_owned(); | |
| ⋯ | |||
| 1823 | 1881 | let rules = self.rules().await?; | |
| 1824 | 1882 | ||
| 1825 | 1883 | let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new(); | |
| 1826 | − | let mut overall = OverallMargin::default(); | |
| 1827 | 1884 | for d in &days { | |
| 1828 | 1885 | let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin { | |
| 1829 | 1886 | bucket: d.bucket.clone(), | |
| ⋯ | |||
| 1836 | 1893 | p.own_cost_micros += d.own_cost_micros; | |
| 1837 | 1894 | p.value_micros += d.value_micros; | |
| 1838 | 1895 | p.cost_micros += d.cost(); | |
| 1839 | − | overall.cost_micros += d.cost(); | |
| 1840 | − | overall.given_micros += d.given.total(); | |
| 1841 | − | if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { | |
| 1842 | − | overall.models_cost_micros += d.cost(); | |
| 1843 | − | } else { | |
| 1844 | − | overall.cloudflare_cost_micros += d.cost(); | |
| 1845 | − | } | |
| 1846 | − | overall.given_comped_micros += d.given.comped; | |
| 1847 | − | overall.given_free_micros += d.given.free; | |
| 1848 | − | overall.given_trial_micros += d.given.trial; | |
| 1849 | − | overall.given_pool_micros += d.given.pool; | |
| 1850 | − | overall.given_discount_micros += d.given.discount; | |
| 1851 | − | overall.given_credit_promotional_micros += d.given.credit_promotional; | |
| 1852 | − | overall.given_credit_goodwill_micros += d.given.credit_goodwill; | |
| 1853 | − | overall.given_reset_micros += d.given.reset; | |
| 1854 | − | overall.given_unpaid_micros += d.given.unpaid; | |
| 1855 | − | let sold = (d.cost() - d.given.total()).max(0); | |
| 1856 | − | if OVERHEAD.contains(&d.bucket.as_str()) { | |
| 1857 | − | overall.plans_micros += d.cash_micros; | |
| 1858 | − | overall.running_cost_micros += sold; | |
| 1859 | − | } else if d.bucket == UNMAPPED { | |
| 1860 | − | overall.usage_micros += d.cash_micros; | |
| 1861 | − | overall.unmapped_cost_micros += sold; | |
| 1862 | − | } else { | |
| 1863 | − | overall.usage_micros += d.cash_micros; | |
| 1864 | − | overall.usage_cost_micros += sold; | |
| 1865 | − | } | |
| 1866 | 1896 | } | |
| 1867 | 1897 | for p in products.values_mut() { | |
| 1868 | 1898 | p.margin_micros = p.value_micros - p.cost_micros; | |
| 1869 | 1899 | p.margin_percent = margin_percent(p.value_micros, p.cost_micros); | |
| 1870 | 1900 | } | |
| 1871 | − | let revenue = overall.usage_micros + overall.plans_micros; | |
| 1872 | − | overall.margin_micros = revenue - overall.cost_micros; | |
| 1873 | − | overall.margin_percent = margin_percent(revenue, overall.cost_micros); | |
| 1874 | − | let sold = (overall.cost_micros - overall.given_micros).max(0); | |
| 1875 | − | overall.sold_margin_micros = revenue - sold; | |
| 1876 | − | overall.sold_margin_percent = margin_percent(revenue, sold); | |
| 1901 | + | let mut overall = overall_of(&days); | |
| 1877 | 1902 | // The plan's included usage was paid for by the plan's price: it is | |
| 1878 | 1903 | // money in for the usage it covered, and out of what the plans | |
| 1879 | 1904 | // leave for running g1t. | |
| ⋯ | |||
| 2575 | 2600 | } | |
| 2576 | 2601 | ||
| 2577 | 2602 | #[test] | |
| 2603 | + | fn the_overview_and_the_costs_page_add_up_the_same_days() { | |
| 2604 | + | // Models: $1 of cost, $0.40 of it on promotional credit (given), $0.60 sold for $0.72. | |
| 2605 | + | let models = ProductDay { | |
| 2606 | + | day: "2026-10-14".into(), | |
| 2607 | + | bucket: "models".into(), | |
| 2608 | + | own_cost_micros: 1_000_000, | |
| 2609 | + | value_micros: 1_200_000, | |
| 2610 | + | cash_micros: 720_000, | |
| 2611 | + | given: Given { credit_promotional: 400_000, ..Given::default() }, | |
| 2612 | + | ..ProductDay::default() | |
| 2613 | + | }; | |
| 2614 | + | // A comped workspace's Cloudflare share: all of it given. | |
| 2615 | + | let comped = ProductDay { | |
| 2616 | + | day: "2026-10-15".into(), | |
| 2617 | + | bucket: "workers".into(), | |
| 2618 | + | cf_cost_micros: 300_000, | |
| 2619 | + | given: Given { comped: 300_000, ..Given::default() }, | |
| 2620 | + | ..ProductDay::default() | |
| 2621 | + | }; | |
| 2622 | + | let overall = overall_of(&[models, comped]); | |
| 2623 | + | assert_eq!((overall.cost_micros, overall.given_micros), (1_300_000, 700_000)); | |
| 2624 | + | assert_eq!((overall.given_credit_promotional_micros, overall.given_comped_micros), (400_000, 300_000)); | |
| 2625 | + | assert_eq!(overall.usage_micros, 720_000); | |
| 2626 | + | // Margin on what was sold: $0.72 in against the $0.60 not given. | |
| 2627 | + | assert_eq!(overall.sold_margin_micros, 120_000); | |
| 2628 | + | assert_eq!(overall.margin_micros, 720_000 - 1_300_000); | |
| 2629 | + | assert_eq!(overall_of(&[]).sold_margin_percent, None); | |
| 2630 | + | } | |
| 2631 | + | ||
| 2632 | + | #[test] | |
| 2578 | 2633 | fn usage_paid_for_with_credit_is_given_not_money_in() { | |
| 2579 | 2634 | // $1.20 of usage on $1 of cost, all of it paid with promotional credit. | |
| 2580 | 2635 | let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)]; | |
| 600 | 600 | let open_requests = count("SELECT COUNT(*) AS n FROM limit_requests WHERE status = 'open'", vec![]).await?; | |
| 601 | 601 | let open_spikes = count("SELECT COUNT(*) AS n FROM spikes WHERE status = 'open'", vec![]).await?; | |
| 602 | 602 | let overages = self.admin_overages(AdminOveragesArgs {}).await?.len() as u32; | |
| 603 | + | let (month_margin, margin_through) = match self.month_margin(&format!("{month}-01")).await? { | |
| 604 | + | Some((figures, through)) => (Some(figures), Some(through)), | |
| 605 | + | None => (None, None), | |
| 606 | + | }; | |
| 603 | 607 | Ok(Overview { | |
| 608 | + | month_margin, | |
| 609 | + | margin_through, | |
| 604 | 610 | revenue_micros: this.charged_micros + this.plans_micros, | |
| 605 | 611 | active_plans: active_plans as u32, | |
| 606 | 612 | plan_mrr_micros: active_plans * i64::from(self.plans.plan_monthly_cents) * 10_000, |
Comments
Sign in to comment.