| 1 | /** |
| 2 | * Moving workspaces off Stripe's test mode before the live key: what a |
| 3 | * detach's result says, as plain lines. No Workers imports, so it is tested. |
| 4 | */ |
| 5 | import type { WorkspaceDetach } from "@g1t/contracts"; |
| 6 | |
| 7 | import { usd } from "./money.ts"; |
| 8 | |
| 9 | /** The slugs left alone by "Detach every workspace" unless staff change them. */ |
| 10 | export const DEFAULT_EXCLUDED = ["syntaqx", "flagon-io"]; |
| 11 | |
| 12 | /** What staff type to detach every workspace (billing checks it too). */ |
| 13 | export const CONFIRM_ALL = "detach"; |
| 14 | |
| 15 | /** "syntaqx, flagon-io" or "syntaqx flagon-io" as slugs; blank is none. */ |
| 16 | export function parseExclude(raw: string): string[] { |
| 17 | return [...new Set(raw.split(/[\s,]+/).map((slug) => slug.trim().toLowerCase()).filter(Boolean))]; |
| 18 | } |
| 19 | |
| 20 | function count(n: number, one: string, many: string): string { |
| 21 | return `${n} ${n === 1 ? one : many}`; |
| 22 | } |
| 23 | |
| 24 | /** Each change a detach makes to a workspace, in the order it makes them. */ |
| 25 | export function detachChanges(row: WorkspaceDetach): string[] { |
| 26 | const out: string[] = []; |
| 27 | if (row.customer || row.card_cached) out.push(`Customer${row.card ? ` and card ${row.card}` : ""} forgotten`); |
| 28 | if (row.subscriptions > 0) out.push(`${count(row.subscriptions, "plan", "plans")} canceled`); |
| 29 | if (row.card_checks > 0) out.push(`${count(row.card_checks, "card check", "card checks")} removed (trial kept)`); |
| 30 | if (row.checkouts > 0) out.push(`${count(row.checkouts, "open checkout", "open checkouts")} removed`); |
| 31 | if (row.ai_reload) out.push("AI auto-reload off"); |
| 32 | if (row.autopay_cleared) out.push("Declined autopay cleared"); |
| 33 | if (row.invoices_voided > 0) out.push(`${count(row.invoices_voided, "invoice", "invoices")} void`); |
| 34 | if (row.months_skipped.length > 0) out.push(`${row.months_skipped.join(", ")} closed as skipped`); |
| 35 | if (row.credit_grants_closed > 0) { |
| 36 | out.push(`${count(row.credit_grants_closed, "AI credit grant", "AI credit grants")} closed (${usd(row.credit_closed_micros, { cents: true })} unused)`); |
| 37 | } |
| 38 | if (row.payments_reversed_micros > 0) out.push(`${usd(row.payments_reversed_micros, { cents: true })} of test payments reversed`); |
| 39 | if (row.plan_payments_reversed_micros > 0) out.push(`${usd(row.plan_payments_reversed_micros, { cents: true })} of test plan payments reversed`); |
| 40 | if (row.written_off_micros > 0) out.push(`${usd(row.written_off_micros, { cents: true })} owed written off as goodwill`); |
| 41 | if (row.pending_months_marked > 0) { |
| 42 | out.push(`${count(row.pending_months_marked, "metered figure", "metered figures")} of past months never charged (${usd(row.pending_marked_micros, { cents: true })})`); |
| 43 | } |
| 44 | if (row.pending_baselined > 0) { |
| 45 | out.push(`This month's ${count(row.pending_baselined, "metered figure", "metered figures")} so far never charged (${usd(row.pending_baselined_micros, { cents: true })})`); |
| 46 | } |
| 47 | if (row.plan_credit_cleared_micros > 0) out.push(`${usd(row.plan_credit_cleared_micros, { cents: true })} of this month's included usage restored`); |
| 48 | if (row.dispute_owed_cleared_micros > 0) out.push(`${usd(row.dispute_owed_cleared_micros, { cents: true })} owed for lost test disputes cleared`); |
| 49 | if (row.disputes_closed > 0) out.push(`${count(row.disputes_closed, "open test dispute", "open test disputes")} closed`); |
| 50 | if (row.stripe_canceled > 0) out.push(`${count(row.stripe_canceled, "subscription", "subscriptions")} canceled at Stripe`); |
| 51 | for (const error of row.stripe_errors) out.push(`Stripe refused: ${error}`); |
| 52 | return out; |
| 53 | } |