Initial import of zappier-edge from zapier monorepo
This commit is contained in:
commit
b8d239e759
120 changed files with 19867 additions and 0 deletions
119
src/db/invoice-repo.ts
Normal file
119
src/db/invoice-repo.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue