tenseleyflow/shithub / 371e7e3

Browse files

billing/queries: UpsertInvoiceForSubject for polymorphic invoice upserts

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
371e7e3edaac7541ff397d162b5b6d79fa5cc06f
Parents
d2130f2
Tree
8b403be

3 changed files

StatusFile+-
M internal/billing/queries/billing.sql 70 0
M internal/billing/sqlc/billing.sql.go 142 0
M internal/billing/sqlc/querier.go 7 0
internal/billing/queries/billing.sqlmodified
@@ -333,6 +333,76 @@ WHERE subject_kind = sqlc.arg(subject_kind)::billing_subject_kind
333333
 ORDER BY created_at DESC, id DESC
334334
 LIMIT sqlc.arg(lim)::integer;
335335
 
336
+-- name: UpsertInvoiceForSubject :one
337
+-- PRO04 polymorphic invoice upsert. Writes (subject_kind,
338
+-- subject_id) directly; org_id stays NULL for user-kind rows (per
339
+-- the 0074 migration's nullable change). The existing
340
+-- UpsertInvoice query stays as the org-kind path during the
341
+-- transitional deploy — both can coexist because the UNIQUE
342
+-- (provider, stripe_invoice_id) prevents duplicate rows.
343
+INSERT INTO billing_invoices (
344
+    subject_kind,
345
+    subject_id,
346
+    provider,
347
+    stripe_invoice_id,
348
+    stripe_customer_id,
349
+    stripe_subscription_id,
350
+    status,
351
+    number,
352
+    currency,
353
+    amount_due_cents,
354
+    amount_paid_cents,
355
+    amount_remaining_cents,
356
+    hosted_invoice_url,
357
+    invoice_pdf_url,
358
+    period_start,
359
+    period_end,
360
+    due_at,
361
+    paid_at,
362
+    voided_at
363
+)
364
+VALUES (
365
+    sqlc.arg(subject_kind)::billing_subject_kind,
366
+    sqlc.arg(subject_id)::bigint,
367
+    'stripe',
368
+    sqlc.arg(stripe_invoice_id)::text,
369
+    sqlc.arg(stripe_customer_id)::text,
370
+    sqlc.narg(stripe_subscription_id)::text,
371
+    sqlc.arg(status)::billing_invoice_status,
372
+    sqlc.arg(number)::text,
373
+    sqlc.arg(currency)::text,
374
+    sqlc.arg(amount_due_cents)::bigint,
375
+    sqlc.arg(amount_paid_cents)::bigint,
376
+    sqlc.arg(amount_remaining_cents)::bigint,
377
+    sqlc.arg(hosted_invoice_url)::text,
378
+    sqlc.arg(invoice_pdf_url)::text,
379
+    sqlc.narg(period_start)::timestamptz,
380
+    sqlc.narg(period_end)::timestamptz,
381
+    sqlc.narg(due_at)::timestamptz,
382
+    sqlc.narg(paid_at)::timestamptz,
383
+    sqlc.narg(voided_at)::timestamptz
384
+)
385
+ON CONFLICT (provider, stripe_invoice_id) DO UPDATE
386
+   SET subject_kind = EXCLUDED.subject_kind,
387
+       subject_id = EXCLUDED.subject_id,
388
+       stripe_customer_id = EXCLUDED.stripe_customer_id,
389
+       stripe_subscription_id = EXCLUDED.stripe_subscription_id,
390
+       status = EXCLUDED.status,
391
+       number = EXCLUDED.number,
392
+       currency = EXCLUDED.currency,
393
+       amount_due_cents = EXCLUDED.amount_due_cents,
394
+       amount_paid_cents = EXCLUDED.amount_paid_cents,
395
+       amount_remaining_cents = EXCLUDED.amount_remaining_cents,
396
+       hosted_invoice_url = EXCLUDED.hosted_invoice_url,
397
+       invoice_pdf_url = EXCLUDED.invoice_pdf_url,
398
+       period_start = EXCLUDED.period_start,
399
+       period_end = EXCLUDED.period_end,
400
+       due_at = EXCLUDED.due_at,
401
+       paid_at = EXCLUDED.paid_at,
402
+       voided_at = EXCLUDED.voided_at,
403
+       updated_at = now()
404
+RETURNING *;
405
+
336406
 -- ─── billing_webhook_events ────────────────────────────────────────
337407
 
338408
 -- name: CreateWebhookEventReceipt :one
internal/billing/sqlc/billing.sql.gomodified
@@ -1743,3 +1743,145 @@ func (q *Queries) UpsertInvoice(ctx context.Context, db DBTX, arg UpsertInvoiceP
17431743
 	)
17441744
 	return i, err
17451745
 }
1746
+
1747
+const upsertInvoiceForSubject = `-- name: UpsertInvoiceForSubject :one
1748
+INSERT INTO billing_invoices (
1749
+    subject_kind,
1750
+    subject_id,
1751
+    provider,
1752
+    stripe_invoice_id,
1753
+    stripe_customer_id,
1754
+    stripe_subscription_id,
1755
+    status,
1756
+    number,
1757
+    currency,
1758
+    amount_due_cents,
1759
+    amount_paid_cents,
1760
+    amount_remaining_cents,
1761
+    hosted_invoice_url,
1762
+    invoice_pdf_url,
1763
+    period_start,
1764
+    period_end,
1765
+    due_at,
1766
+    paid_at,
1767
+    voided_at
1768
+)
1769
+VALUES (
1770
+    $1::billing_subject_kind,
1771
+    $2::bigint,
1772
+    'stripe',
1773
+    $3::text,
1774
+    $4::text,
1775
+    $5::text,
1776
+    $6::billing_invoice_status,
1777
+    $7::text,
1778
+    $8::text,
1779
+    $9::bigint,
1780
+    $10::bigint,
1781
+    $11::bigint,
1782
+    $12::text,
1783
+    $13::text,
1784
+    $14::timestamptz,
1785
+    $15::timestamptz,
1786
+    $16::timestamptz,
1787
+    $17::timestamptz,
1788
+    $18::timestamptz
1789
+)
1790
+ON CONFLICT (provider, stripe_invoice_id) DO UPDATE
1791
+   SET subject_kind = EXCLUDED.subject_kind,
1792
+       subject_id = EXCLUDED.subject_id,
1793
+       stripe_customer_id = EXCLUDED.stripe_customer_id,
1794
+       stripe_subscription_id = EXCLUDED.stripe_subscription_id,
1795
+       status = EXCLUDED.status,
1796
+       number = EXCLUDED.number,
1797
+       currency = EXCLUDED.currency,
1798
+       amount_due_cents = EXCLUDED.amount_due_cents,
1799
+       amount_paid_cents = EXCLUDED.amount_paid_cents,
1800
+       amount_remaining_cents = EXCLUDED.amount_remaining_cents,
1801
+       hosted_invoice_url = EXCLUDED.hosted_invoice_url,
1802
+       invoice_pdf_url = EXCLUDED.invoice_pdf_url,
1803
+       period_start = EXCLUDED.period_start,
1804
+       period_end = EXCLUDED.period_end,
1805
+       due_at = EXCLUDED.due_at,
1806
+       paid_at = EXCLUDED.paid_at,
1807
+       voided_at = EXCLUDED.voided_at,
1808
+       updated_at = now()
1809
+RETURNING id, org_id, provider, stripe_invoice_id, stripe_customer_id, stripe_subscription_id, status, number, currency, amount_due_cents, amount_paid_cents, amount_remaining_cents, hosted_invoice_url, invoice_pdf_url, period_start, period_end, due_at, paid_at, voided_at, created_at, updated_at, subject_kind, subject_id
1810
+`
1811
+
1812
+type UpsertInvoiceForSubjectParams struct {
1813
+	SubjectKind          BillingSubjectKind
1814
+	SubjectID            int64
1815
+	StripeInvoiceID      string
1816
+	StripeCustomerID     string
1817
+	StripeSubscriptionID pgtype.Text
1818
+	Status               BillingInvoiceStatus
1819
+	Number               string
1820
+	Currency             string
1821
+	AmountDueCents       int64
1822
+	AmountPaidCents      int64
1823
+	AmountRemainingCents int64
1824
+	HostedInvoiceUrl     string
1825
+	InvoicePdfUrl        string
1826
+	PeriodStart          pgtype.Timestamptz
1827
+	PeriodEnd            pgtype.Timestamptz
1828
+	DueAt                pgtype.Timestamptz
1829
+	PaidAt               pgtype.Timestamptz
1830
+	VoidedAt             pgtype.Timestamptz
1831
+}
1832
+
1833
+// PRO04 polymorphic invoice upsert. Writes (subject_kind,
1834
+// subject_id) directly; org_id stays NULL for user-kind rows (per
1835
+// the 0074 migration's nullable change). The existing
1836
+// UpsertInvoice query stays as the org-kind path during the
1837
+// transitional deploy — both can coexist because the UNIQUE
1838
+// (provider, stripe_invoice_id) prevents duplicate rows.
1839
+func (q *Queries) UpsertInvoiceForSubject(ctx context.Context, db DBTX, arg UpsertInvoiceForSubjectParams) (BillingInvoice, error) {
1840
+	row := db.QueryRow(ctx, upsertInvoiceForSubject,
1841
+		arg.SubjectKind,
1842
+		arg.SubjectID,
1843
+		arg.StripeInvoiceID,
1844
+		arg.StripeCustomerID,
1845
+		arg.StripeSubscriptionID,
1846
+		arg.Status,
1847
+		arg.Number,
1848
+		arg.Currency,
1849
+		arg.AmountDueCents,
1850
+		arg.AmountPaidCents,
1851
+		arg.AmountRemainingCents,
1852
+		arg.HostedInvoiceUrl,
1853
+		arg.InvoicePdfUrl,
1854
+		arg.PeriodStart,
1855
+		arg.PeriodEnd,
1856
+		arg.DueAt,
1857
+		arg.PaidAt,
1858
+		arg.VoidedAt,
1859
+	)
1860
+	var i BillingInvoice
1861
+	err := row.Scan(
1862
+		&i.ID,
1863
+		&i.OrgID,
1864
+		&i.Provider,
1865
+		&i.StripeInvoiceID,
1866
+		&i.StripeCustomerID,
1867
+		&i.StripeSubscriptionID,
1868
+		&i.Status,
1869
+		&i.Number,
1870
+		&i.Currency,
1871
+		&i.AmountDueCents,
1872
+		&i.AmountPaidCents,
1873
+		&i.AmountRemainingCents,
1874
+		&i.HostedInvoiceUrl,
1875
+		&i.InvoicePdfUrl,
1876
+		&i.PeriodStart,
1877
+		&i.PeriodEnd,
1878
+		&i.DueAt,
1879
+		&i.PaidAt,
1880
+		&i.VoidedAt,
1881
+		&i.CreatedAt,
1882
+		&i.UpdatedAt,
1883
+		&i.SubjectKind,
1884
+		&i.SubjectID,
1885
+	)
1886
+	return i, err
1887
+}
internal/billing/sqlc/querier.gomodified
@@ -62,6 +62,13 @@ type Querier interface {
6262
 	// to the polymorphic shape, a follow-up migration drops `org_id` and
6363
 	// this query loses the legacy column from its INSERT list.
6464
 	UpsertInvoice(ctx context.Context, db DBTX, arg UpsertInvoiceParams) (BillingInvoice, error)
65
+	// PRO04 polymorphic invoice upsert. Writes (subject_kind,
66
+	// subject_id) directly; org_id stays NULL for user-kind rows (per
67
+	// the 0074 migration's nullable change). The existing
68
+	// UpsertInvoice query stays as the org-kind path during the
69
+	// transitional deploy — both can coexist because the UNIQUE
70
+	// (provider, stripe_invoice_id) prevents duplicate rows.
71
+	UpsertInvoiceForSubject(ctx context.Context, db DBTX, arg UpsertInvoiceForSubjectParams) (BillingInvoice, error)
6572
 }
6673
 
6774
 var _ Querier = (*Queries)(nil)