Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

Commit

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
syntaqxcommitted Parent66d1e9bBrowse files
9 files+226−490/9 viewed
+7−1
11 import assert from "node:assert/strict";
22 import { test } from "node:test";
33
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";
55
66 const M = 1_000_000;
77 const month = (m: string, charged: number, cost: number) => ({ month: m, chargedMicros: charged, costMicros: cost, paidMicros: 0 });
9191 assert.deepEqual(shares([50, 100, 0, -5]), [0.5, 1, 0, 0]);
9292 assert.deepEqual(shares([0, 0]), [0, 0]);
9393 });
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+});
+7−0
2121 return match && MONTHS_LONG[index] ? `${MONTHS_LONG[index]} ${match[1]}` : month;
2222 }
2323
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+
2431 /**
2532 * The top of an axis for values up to `max`: the next 1, 2, 2.5 or 5
2633 * times a power of ten, so its ticks are round numbers. A dollar when
+8−0
66 age,
77 answerDueAt,
88 givenLabel,
9+ marginApart,
910 givenParts,
1011 givenTotal,
1112 goodwillLine,
149150 netMicros: -5_000_000,
150151 });
151152 });
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+});
+19−1
55 * and margin kept apart from what g1t gives. Money is in micros. No
66 * Workers or React imports, so it can be tested under Node.
77 */
8−import type { GivenFigures, MonthFigures } from "@g1t/contracts";
8+import type { GivenFigures, MonthFigures, OverallMargin } from "@g1t/contracts";
99
1010 // --- Requests -------------------------------------------------------------------
1111
231231 netMicros: revenueMicros - month.costMicros,
232232 };
233233 }
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+}
+71−13
77 import { KindBreakdown, MonthsChart } from "~/components/charts";
88 import { SignalBadge, StaffName, StageBadge } from "~/components/sales";
99 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";
1213 import { usd } from "~/lib/money";
1314 import { admin, identity } from "~/lib/services.server";
1415 import { settle } from "~/lib/settle";
4849 const plans = current?.plansMicros ?? 0;
4950 const given = overview?.given ?? [];
5051 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) : [];
5261 const growth = previous ? change(previous.chargedMicros + (previous.plansMicros ?? 0), money.revenueMicros) : null;
5362
5463 return (
5564 <main className="mx-auto max-w-6xl px-4 py-8 sm:py-10">
5665 <PageHeader
5766 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+ }
5977 />
6078
6179 {growthError ? (
99117 value={usd(money.revenueMicros)}
100118 hint={
101119 <>
102− {usd(charged)} usage, {usd(plans)} plans
120+ {usd(usage)} usage, {usd(plansIn)} plans
103121 {growth == null ? "" : ` · ${growth >= 0 ? "+" : "−"}${Math.abs(growth)}% on last month`}
104122 </>
105123 }
115133 }
116134 tone={money.revenueMicros > 0 && money.marginMicros < 0 ? "danger" : undefined}
117135 />
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+ )}
123149 <Stat
124150 label="Plans"
125151 value={String(overview.activePlans ?? 0)}
127153 />
128154 </div>
129155 <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 &amp; margin
161+ </Link>
131162 </p>
132163
133164 {overview.pools && (
158189
159190 <Section
160191 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+ }
162197 className="mt-6"
163198 >
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 ? (
165223 <p className="text-sm text-muted">Nothing given yet this month.</p>
166224 ) : (
167225 <div className="-mx-4 -my-4 overflow-x-auto sm:-mx-5 sm:-my-5">
+10−0
22192219 /// What g1t gave this month, by source, apart from its margin.
22202220 #[serde(default)]
22212221 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>,
22222232 /// g1t's own and Flagon's workspaces this month: what their use cost,
22232233 /// and why they are not charged.
22242234 #[serde(default)]
+9−0
656656 planMrrMicros?: number;
657657 /** What g1t gave this month, by source, apart from its margin. */
658658 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;
659668 /** g1t's own and Flagon's workspaces: what their use cost, and why they are not charged. */
660669 internal?: InternalUse[];
661670 openRequests?: number;
+89−34
488488 u
489489 }
490490
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+
491537 /// Margin as a share of what was charged, in percent; None when nothing was.
492538 pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
493539 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
18141860 }))
18151861 }
18161862
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+
18171875 /// `admin_costs`: the Costs & margin page.
18181876 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
18191877 let until = rfc3339(now_ms())[..10].to_owned();
18231881 let rules = self.rules().await?;
18241882
18251883 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
1826− let mut overall = OverallMargin::default();
18271884 for d in &days {
18281885 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
18291886 bucket: d.bucket.clone(),
18361893 p.own_cost_micros += d.own_cost_micros;
18371894 p.value_micros += d.value_micros;
18381895 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− }
18661896 }
18671897 for p in products.values_mut() {
18681898 p.margin_micros = p.value_micros - p.cost_micros;
18691899 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
18701900 }
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);
18771902 // The plan's included usage was paid for by the plan's price: it is
18781903 // money in for the usage it covered, and out of what the plans
18791904 // leave for running g1t.
25752600 }
25762601
25772602 #[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]
25782633 fn usage_paid_for_with_credit_is_given_not_money_in() {
25792634 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
25802635 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
+6−0
600600 let open_requests = count("SELECT COUNT(*) AS n FROM limit_requests WHERE status = 'open'", vec![]).await?;
601601 let open_spikes = count("SELECT COUNT(*) AS n FROM spikes WHERE status = 'open'", vec![]).await?;
602602 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+ };
603607 Ok(Overview {
608+ month_margin,
609+ margin_through,
604610 revenue_micros: this.charged_micros + this.plans_micros,
605611 active_plans: active_plans as u32,
606612 plan_mrr_micros: active_plans * i64::from(self.plans.plan_monthly_cents) * 10_000,

Comments

Sign in to comment.