Initial import of zappier-edge from zapier monorepo

This commit is contained in:
George Lambert 2026-09-11 12:58:25 -04:00
commit b8d239e759
120 changed files with 19867 additions and 0 deletions

119
src/db/invoice-repo.ts Normal file
View file

@ -0,0 +1,119 @@
import Database from 'better-sqlite3';
import { Invoice, InvoiceLine, InvoiceRepo } from '../invoicing';
export class SqliteInvoiceRepo implements InvoiceRepo {
constructor(private db: Database.Database) {
this.db.exec(`
CREATE TABLE IF NOT EXISTS invoices (
id TEXT PRIMARY KEY,
customer_id TEXT NOT NULL,
period TEXT NOT NULL,
status TEXT NOT NULL,
total_cents INTEGER NOT NULL,
credit_cents INTEGER NOT NULL,
billable_cents INTEGER NOT NULL,
billing_type TEXT NOT NULL DEFAULT 'stripe',
po_number TEXT,
issued_at_ms INTEGER,
due_at_ms INTEGER,
paid_at_ms INTEGER
);
CREATE TABLE IF NOT EXISTS invoice_lines (
invoice_id TEXT NOT NULL,
endpoint_id TEXT NOT NULL,
calls INTEGER NOT NULL,
cents INTEGER NOT NULL,
PRIMARY KEY (invoice_id, endpoint_id)
);
`);
}
save(invoice: Invoice): void {
this.db
.prepare(
`INSERT INTO invoices
(id, customer_id, period, status, total_cents, credit_cents, billable_cents,
billing_type, po_number, issued_at_ms, due_at_ms, paid_at_ms)
VALUES (@id, @customerId, @period, @status, @totalCents, @creditCents, @billableCents,
@billingType, @poNumber, @issuedAtMs, @dueAtMs, @paidAtMs)
ON CONFLICT(id) DO UPDATE SET
customer_id = @customerId, period = @period, status = @status,
total_cents = @totalCents, credit_cents = @creditCents,
billable_cents = @billableCents, billing_type = @billingType,
po_number = @poNumber, issued_at_ms = @issuedAtMs,
due_at_ms = @dueAtMs, paid_at_ms = @paidAtMs`,
)
.run({
...invoice,
poNumber: invoice.poNumber ?? null,
issuedAtMs: invoice.issuedAtMs ?? null,
dueAtMs: invoice.dueAtMs ?? null,
paidAtMs: invoice.paidAtMs ?? null,
});
// Replace the line set atomically with the invoice row.
this.db.prepare('DELETE FROM invoice_lines WHERE invoice_id = ?').run(invoice.id);
const insert = this.db.prepare(
'INSERT INTO invoice_lines (invoice_id, endpoint_id, calls, cents) VALUES (?, ?, ?, ?)',
);
for (const line of invoice.lines) {
insert.run(invoice.id, line.endpointId, line.calls, line.cents);
}
}
get(id: string): Invoice | undefined {
const row = this.db.prepare('SELECT * FROM invoices WHERE id = ?').get(id) as
| Record<string, unknown>
| undefined;
return row ? this.toInvoice(row) : undefined;
}
list(filter: { customerId?: string; period?: string; status?: Invoice['status'] }): Invoice[] {
const where: string[] = [];
const params: Record<string, string> = {};
if (filter.customerId) {
where.push('customer_id = @customerId');
params.customerId = filter.customerId;
}
if (filter.period) {
where.push('period = @period');
params.period = filter.period;
}
if (filter.status) {
where.push('status = @status');
params.status = filter.status;
}
const sql = `SELECT * FROM invoices ${where.length ? `WHERE ${where.join(' AND ')}` : ''} ORDER BY id`;
const rows = this.db.prepare(sql).all(params) as Record<string, unknown>[];
return rows.map((r) => this.toInvoice(r));
}
nextSequence(period: string): number {
const { n } = this.db
.prepare('SELECT COUNT(*) AS n FROM invoices WHERE period = ?')
.get(period) as { n: number };
return n + 1;
}
private toInvoice(row: Record<string, unknown>): Invoice {
const lines = this.db
.prepare('SELECT endpoint_id, calls, cents FROM invoice_lines WHERE invoice_id = ? ORDER BY endpoint_id')
.all(row.id as string) as { endpoint_id: string; calls: number; cents: number }[];
return {
id: row.id as string,
customerId: row.customer_id as string,
period: row.period as string,
status: row.status as Invoice['status'],
lines: lines.map(
(l): InvoiceLine => ({ endpointId: l.endpoint_id, calls: l.calls, cents: l.cents }),
),
totalCents: row.total_cents as number,
creditCents: row.credit_cents as number,
billableCents: row.billable_cents as number,
billingType: row.billing_type as Invoice['billingType'],
poNumber: (row.po_number as string | null) ?? undefined,
issuedAtMs: (row.issued_at_ms as number | null) ?? undefined,
dueAtMs: (row.due_at_ms as number | null) ?? undefined,
paidAtMs: (row.paid_at_ms as number | null) ?? undefined,
};
}
}