import { METERS, meterDef } from "~/lib/meters";
import type { MeterId } from "~/lib/meters";
import { MeteringError } from "./record";
import type { BillingSettings } from "./quota";
/** Numbers from plan config and the price table, inlined into SQL; never user input. */
function sqlNumber(value: number): string {
if (!Number.isFinite(value)) throw new MeteringError("Non-finite billing constant");
return String(value);
}
const MICRO_PER_CENT = 1e6;
/** Overage microcents of every other priced counter meter this period, holds included. */
function otherCountersSql(settings: BillingSettings): string {
const cases = METERS.filter((meter) => meter.kind !== "gauge" && meter.microCentsPerUnit > 0)
.map((meter) => {
const included = sqlNumber(settings.plan.included[meter.id] ?? 0);
const price = sqlNumber(meter.microCentsPerUnit);
return `WHEN '${meter.id}' THEN MAX(0, p.quantity - p.credit_covered + p.reserved - ${included}) * ${price}`;
})
.join(" ");
return `COALESCE((SELECT SUM(CASE p.meter ${cases} ELSE 0 END)
FROM usage_periods p WHERE p.account_id = ?2 AND p.period = ?4 AND p.meter != ?3), 0)`;
}
/** Overage microcents of the held meter once this hold (?5) lands, less the prepaid credit still available. */
function heldMeterSql(settings: BillingSettings, meter: MeterId, headroom: string): string {
const included = sqlNumber(settings.plan.included[meter] ?? 0);
const price = sqlNumber(meterDef(meter)?.microCentsPerUnit ?? 0);
return `COALESCE((SELECT MAX(0, p.quantity - p.credit_covered + p.reserved + ?5 - (${headroom}) - ${included}) * ${price}
FROM usage_periods p WHERE p.account_id = ?2 AND p.period = ?4 AND p.meter = ?3), 0)`;
}
function gaugeSpendSql(settings: BillingSettings): string {
const cases = METERS.filter((meter) => meter.kind === "gauge" && meter.microCentsPerUnit > 0)
.map((meter) => {
const included = sqlNumber(settings.plan.included[meter.id] ?? 0);
return `WHEN '${meter.id}' THEN MAX(0, g.peak - ${included}) * ${sqlNumber(meter.microCentsPerUnit)}`;
})