@ambushsoftworks/nestjs-payments-graphql 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -0
- package/LICENSE +21 -0
- package/README.md +612 -0
- package/dist/gateways/payment-gateway.interface.d.ts +7 -1
- package/dist/gateways/stripe/stripe-webhook.controller.js +4 -0
- package/dist/gateways/stripe/stripe-webhook.controller.js.map +1 -1
- package/dist/gateways/stripe/stripe.gateway.d.ts +2 -2
- package/dist/gateways/stripe/stripe.gateway.js +6 -2
- package/dist/gateways/stripe/stripe.gateway.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/payment.service.js +6 -0
- package/dist/services/payment.service.js.map +1 -1
- package/package.json +41 -2
- package/prisma/payment-models.prisma +504 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// @ambushsoftworks/nestjs-payments-graphql — Reference Prisma schema
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// This file is a DOCUMENTATION FRAGMENT. It is not loaded by Prisma at runtime
|
|
6
|
+
// and it is not imported automatically anywhere. Copy the models and enums
|
|
7
|
+
// below into your own `schema.prisma` and wire up the consumer-owned relations
|
|
8
|
+
// (marked with `// CONSUMER:`) to the models in your application.
|
|
9
|
+
//
|
|
10
|
+
// Field shapes are fixed by the package's repository interfaces
|
|
11
|
+
// (IInvoiceRepository, IPaymentRepository, etc.). You may:
|
|
12
|
+
// - Rename models freely (your repo adapter is the mapping layer).
|
|
13
|
+
// - Add columns (e.g. a soft-delete flag, additional audit fields).
|
|
14
|
+
// - Swap column types for compatible ones (e.g. `@db.VarChar(255)` on strings).
|
|
15
|
+
//
|
|
16
|
+
// You may NOT:
|
|
17
|
+
// - Change the cardinality of a field (e.g. make a required field optional
|
|
18
|
+
// without reflecting that in your repository adapter).
|
|
19
|
+
// - Drop tracked enum values (listed below).
|
|
20
|
+
//
|
|
21
|
+
// The package is database-agnostic — PostgreSQL examples are shown (e.g.
|
|
22
|
+
// `@db.Decimal(5, 4)`); swap for your provider's equivalents as needed.
|
|
23
|
+
//
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
// Model checklist
|
|
26
|
+
// -----------------------------------------------------------------------------
|
|
27
|
+
//
|
|
28
|
+
// Always required:
|
|
29
|
+
// - Invoice
|
|
30
|
+
// - InvoiceLineItem
|
|
31
|
+
// - Payment
|
|
32
|
+
// - Refund
|
|
33
|
+
// - PaymentConfig
|
|
34
|
+
// - PaymentProvider
|
|
35
|
+
// - ProcessedWebhookEvent
|
|
36
|
+
//
|
|
37
|
+
// Required when `features.paymentPlans = true`:
|
|
38
|
+
// - PaymentPlan
|
|
39
|
+
// - PaymentPlanInstallment
|
|
40
|
+
//
|
|
41
|
+
// Required when `features.recurringInvoices = true`:
|
|
42
|
+
// - RecurringInvoice
|
|
43
|
+
// - RecurringInvoiceLineItem
|
|
44
|
+
// - PaymentCustomer
|
|
45
|
+
//
|
|
46
|
+
// All enums are required if you use the corresponding model.
|
|
47
|
+
// =============================================================================
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// -----------------------------------------------------------------------------
|
|
51
|
+
// Enums
|
|
52
|
+
// -----------------------------------------------------------------------------
|
|
53
|
+
// These enum value sets must match exactly — core services compare against
|
|
54
|
+
// these strings. Add new values at your own risk; the package will not
|
|
55
|
+
// recognize them.
|
|
56
|
+
|
|
57
|
+
enum InvoiceStatus {
|
|
58
|
+
DRAFT
|
|
59
|
+
SENT
|
|
60
|
+
PARTIALLY_PAID
|
|
61
|
+
PAID
|
|
62
|
+
OVERDUE
|
|
63
|
+
VOID
|
|
64
|
+
REFUNDED
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
enum PaymentMethod {
|
|
68
|
+
CREDIT_CARD
|
|
69
|
+
DEBIT
|
|
70
|
+
E_TRANSFER
|
|
71
|
+
BANK_TRANSFER
|
|
72
|
+
CHEQUE
|
|
73
|
+
CASH
|
|
74
|
+
OTHER
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
enum PaymentStatus {
|
|
78
|
+
PENDING
|
|
79
|
+
PENDING_CONFIRMATION
|
|
80
|
+
PROCESSING
|
|
81
|
+
SUCCEEDED
|
|
82
|
+
FAILED
|
|
83
|
+
CANCELLED
|
|
84
|
+
REJECTED
|
|
85
|
+
REFUNDED
|
|
86
|
+
PARTIALLY_REFUNDED
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
enum RefundStatus {
|
|
90
|
+
PENDING
|
|
91
|
+
PROCESSING
|
|
92
|
+
SUCCEEDED
|
|
93
|
+
FAILED
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
enum PaymentPlanStatus {
|
|
97
|
+
ACTIVE
|
|
98
|
+
COMPLETED
|
|
99
|
+
CANCELLED
|
|
100
|
+
DEFAULTED
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
enum InstallmentStatus {
|
|
104
|
+
PENDING
|
|
105
|
+
INVOICED
|
|
106
|
+
PAID
|
|
107
|
+
OVERDUE
|
|
108
|
+
WAIVED
|
|
109
|
+
CANCELLED
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
enum RecurringIntervalUnit {
|
|
113
|
+
DAYS
|
|
114
|
+
MONTHS
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
enum RecurringInvoiceStatus {
|
|
118
|
+
ACTIVE
|
|
119
|
+
PAUSED
|
|
120
|
+
CANCELLED
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
// -----------------------------------------------------------------------------
|
|
125
|
+
// Core models (always required)
|
|
126
|
+
// -----------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
model PaymentConfig {
|
|
129
|
+
id String @id @default(uuid())
|
|
130
|
+
divisionId String @unique // CONSUMER: tenant/org/division scope — unique per tenant
|
|
131
|
+
|
|
132
|
+
// E-transfer settings
|
|
133
|
+
eTransferEnabled Boolean @default(false)
|
|
134
|
+
eTransferEmail String?
|
|
135
|
+
eTransferRecipient String? // Display name for e-transfer recipient
|
|
136
|
+
eTransferAutoDeposit Boolean @default(true)
|
|
137
|
+
|
|
138
|
+
// Defaults applied when an invoice omits currency or tax rate
|
|
139
|
+
defaultCurrency String @default("USD")
|
|
140
|
+
defaultTaxRate Decimal? @db.Decimal(5, 4) // e.g. 0.1300 for 13%
|
|
141
|
+
|
|
142
|
+
createdAt DateTime @default(now())
|
|
143
|
+
updatedAt DateTime @updatedAt
|
|
144
|
+
|
|
145
|
+
providers PaymentProvider[]
|
|
146
|
+
|
|
147
|
+
// CONSUMER: add @relation to your Tenant/Division/Organization model
|
|
148
|
+
// division Division @relation(fields: [divisionId], references: [id], onDelete: Cascade)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
model PaymentProvider {
|
|
152
|
+
id String @id @default(uuid())
|
|
153
|
+
paymentConfigId String
|
|
154
|
+
providerType String // e.g. "stripe"
|
|
155
|
+
isActive Boolean @default(true)
|
|
156
|
+
config Json? // Non-secret config (account IDs, webhook endpoint IDs)
|
|
157
|
+
|
|
158
|
+
createdAt DateTime @default(now())
|
|
159
|
+
updatedAt DateTime @updatedAt
|
|
160
|
+
|
|
161
|
+
paymentConfig PaymentConfig @relation(fields: [paymentConfigId], references: [id], onDelete: Cascade)
|
|
162
|
+
|
|
163
|
+
@@unique([paymentConfigId, providerType])
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
model Invoice {
|
|
167
|
+
id String @id @default(uuid())
|
|
168
|
+
divisionId String // CONSUMER: tenant/org scope
|
|
169
|
+
invoiceNumber String @unique
|
|
170
|
+
status InvoiceStatus @default(DRAFT)
|
|
171
|
+
|
|
172
|
+
// CONSUMER: opaque foreign key to your client/customer/contact record.
|
|
173
|
+
// The package treats this as an opaque string.
|
|
174
|
+
clientDetailsId String
|
|
175
|
+
|
|
176
|
+
// Polymorphic entity reference — what this invoice is FOR (booking, order,
|
|
177
|
+
// enrollment, etc.). Both fields nullable so invoices may stand alone.
|
|
178
|
+
referenceType String?
|
|
179
|
+
referenceId String?
|
|
180
|
+
|
|
181
|
+
// Payment plan link (populated when an invoice is generated from an installment)
|
|
182
|
+
paymentPlanId String?
|
|
183
|
+
installmentNumber Int?
|
|
184
|
+
|
|
185
|
+
// Recurring invoice link (populated when an invoice is generated from a recurring config)
|
|
186
|
+
recurringInvoiceId String?
|
|
187
|
+
|
|
188
|
+
// Financial amounts — ALL IN CENTS (integer minor units).
|
|
189
|
+
// taxRate is a decimal fraction: 0.1300 = 13%.
|
|
190
|
+
subtotal Int @default(0)
|
|
191
|
+
taxRate Decimal @default(0) @db.Decimal(5, 4)
|
|
192
|
+
taxAmount Int @default(0)
|
|
193
|
+
discount Int @default(0)
|
|
194
|
+
total Int @default(0)
|
|
195
|
+
amountPaid Int @default(0)
|
|
196
|
+
amountDue Int @default(0)
|
|
197
|
+
|
|
198
|
+
currency String @default("USD")
|
|
199
|
+
|
|
200
|
+
// E-transfer — code is the client-visible confirmation string, answer is
|
|
201
|
+
// the security answer the client must provide to the bank.
|
|
202
|
+
eTransferCode String? @unique
|
|
203
|
+
eTransferAnswer String?
|
|
204
|
+
|
|
205
|
+
issuedAt DateTime?
|
|
206
|
+
dueAt DateTime?
|
|
207
|
+
|
|
208
|
+
notes String? // Internal staff notes (never exposed to clients)
|
|
209
|
+
clientNotes String? // Visible on invoice to client
|
|
210
|
+
|
|
211
|
+
// Consumer-defined key-value pairs (opaque to the package)
|
|
212
|
+
metadata Json?
|
|
213
|
+
|
|
214
|
+
// Audit fields
|
|
215
|
+
createdBy String // CONSUMER: accountId/userId who created this invoice
|
|
216
|
+
voidedBy String?
|
|
217
|
+
voidedAt DateTime?
|
|
218
|
+
voidReason String?
|
|
219
|
+
|
|
220
|
+
createdAt DateTime @default(now())
|
|
221
|
+
updatedAt DateTime @updatedAt
|
|
222
|
+
|
|
223
|
+
// Relations
|
|
224
|
+
lineItems InvoiceLineItem[]
|
|
225
|
+
payments Payment[]
|
|
226
|
+
// Reverse relation for PaymentPlanInstallment.invoiceId one-to-one
|
|
227
|
+
installment PaymentPlanInstallment?
|
|
228
|
+
paymentPlan PaymentPlan? @relation("InvoicePlan", fields: [paymentPlanId], references: [id], onDelete: SetNull)
|
|
229
|
+
recurringInvoice RecurringInvoice? @relation(fields: [recurringInvoiceId], references: [id])
|
|
230
|
+
|
|
231
|
+
// CONSUMER: add relations to your tenant and client models.
|
|
232
|
+
// Use `onDelete: Restrict` on financial relations — data-retention rules
|
|
233
|
+
// typically forbid cascade-deleting invoices when a client is removed.
|
|
234
|
+
// division Division @relation(fields: [divisionId], references: [id], onDelete: Restrict)
|
|
235
|
+
// clientDetails Details @relation(fields: [clientDetailsId], references: [id], onDelete: Restrict)
|
|
236
|
+
|
|
237
|
+
@@index([divisionId, status])
|
|
238
|
+
@@index([clientDetailsId])
|
|
239
|
+
@@index([referenceType, referenceId])
|
|
240
|
+
@@index([paymentPlanId])
|
|
241
|
+
@@index([recurringInvoiceId])
|
|
242
|
+
@@index([status, dueAt]) // For overdue scheduler
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
model InvoiceLineItem {
|
|
246
|
+
id String @id @default(uuid())
|
|
247
|
+
invoiceId String
|
|
248
|
+
|
|
249
|
+
description String
|
|
250
|
+
quantity Int @default(1)
|
|
251
|
+
unitPrice Int // cents
|
|
252
|
+
sortOrder Int @default(0)
|
|
253
|
+
isTaxable Boolean @default(true)
|
|
254
|
+
|
|
255
|
+
// Optional polymorphic reference — what this line item represents
|
|
256
|
+
referenceType String?
|
|
257
|
+
referenceId String?
|
|
258
|
+
|
|
259
|
+
// Consumer-defined key-value pairs (opaque to the package)
|
|
260
|
+
metadata Json?
|
|
261
|
+
|
|
262
|
+
createdAt DateTime @default(now())
|
|
263
|
+
updatedAt DateTime @updatedAt
|
|
264
|
+
|
|
265
|
+
invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
|
|
266
|
+
|
|
267
|
+
@@index([invoiceId, sortOrder])
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
model Payment {
|
|
271
|
+
id String @id @default(uuid())
|
|
272
|
+
invoiceId String
|
|
273
|
+
amount Int // cents
|
|
274
|
+
currency String @default("USD")
|
|
275
|
+
method PaymentMethod
|
|
276
|
+
status PaymentStatus @default(PENDING)
|
|
277
|
+
|
|
278
|
+
// Provider fields (both null for manual payments, both set for online)
|
|
279
|
+
provider String? // e.g. "stripe"
|
|
280
|
+
providerPaymentId String?
|
|
281
|
+
providerData Json? // Provider-specific metadata — NEVER log raw card data
|
|
282
|
+
receiptUrl String?
|
|
283
|
+
|
|
284
|
+
// Manual payment fields
|
|
285
|
+
referenceNumber String? // cheque number, e-transfer confirmation, etc.
|
|
286
|
+
notes String?
|
|
287
|
+
processedAt DateTime? // When payment was actually processed (may differ from createdAt)
|
|
288
|
+
receivedBy String? // CONSUMER: accountId who recorded the manual payment
|
|
289
|
+
|
|
290
|
+
// Reconciliation — set true when gateway cancellation fails during voidInvoice
|
|
291
|
+
needsReconciliation Boolean @default(false)
|
|
292
|
+
|
|
293
|
+
// Audit
|
|
294
|
+
createdBy String // CONSUMER: accountId/userId who created the payment
|
|
295
|
+
|
|
296
|
+
createdAt DateTime @default(now())
|
|
297
|
+
updatedAt DateTime @updatedAt
|
|
298
|
+
|
|
299
|
+
invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Restrict)
|
|
300
|
+
refunds Refund[]
|
|
301
|
+
|
|
302
|
+
// Note: null-null is valid (manual payments) — PostgreSQL unique allows multiple nulls.
|
|
303
|
+
@@unique([provider, providerPaymentId])
|
|
304
|
+
@@index([invoiceId, status])
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
model Refund {
|
|
308
|
+
id String @id @default(uuid())
|
|
309
|
+
paymentId String
|
|
310
|
+
amount Int // cents
|
|
311
|
+
currency String @default("USD")
|
|
312
|
+
status RefundStatus @default(PENDING)
|
|
313
|
+
reason String?
|
|
314
|
+
|
|
315
|
+
// Provider fields (null for manual refunds)
|
|
316
|
+
providerRefundId String?
|
|
317
|
+
providerData Json?
|
|
318
|
+
|
|
319
|
+
// Audit
|
|
320
|
+
createdBy String // CONSUMER: accountId/userId who created the refund
|
|
321
|
+
processedBy String? // CONSUMER: accountId who processed a manual refund
|
|
322
|
+
|
|
323
|
+
createdAt DateTime @default(now())
|
|
324
|
+
updatedAt DateTime @updatedAt
|
|
325
|
+
|
|
326
|
+
payment Payment @relation(fields: [paymentId], references: [id], onDelete: Restrict)
|
|
327
|
+
|
|
328
|
+
@@index([paymentId])
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
model ProcessedWebhookEvent {
|
|
332
|
+
// Stripe event IDs are their own primary key.
|
|
333
|
+
// The package's IWebhookIdempotencyRepository uses this to no-op on replays.
|
|
334
|
+
eventId String @id
|
|
335
|
+
processedAt DateTime @default(now())
|
|
336
|
+
|
|
337
|
+
@@index([processedAt]) // For TTL cleanup via cleanupBefore()
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
// -----------------------------------------------------------------------------
|
|
342
|
+
// Payment plan models (required when features.paymentPlans = true)
|
|
343
|
+
// -----------------------------------------------------------------------------
|
|
344
|
+
|
|
345
|
+
model PaymentPlan {
|
|
346
|
+
id String @id @default(uuid())
|
|
347
|
+
divisionId String // CONSUMER: tenant/org scope
|
|
348
|
+
status PaymentPlanStatus @default(ACTIVE)
|
|
349
|
+
|
|
350
|
+
clientDetailsId String // CONSUMER: opaque client ID
|
|
351
|
+
|
|
352
|
+
// Polymorphic entity reference
|
|
353
|
+
referenceType String?
|
|
354
|
+
referenceId String?
|
|
355
|
+
|
|
356
|
+
totalAmount Int // cents
|
|
357
|
+
currency String @default("USD")
|
|
358
|
+
|
|
359
|
+
name String?
|
|
360
|
+
description String?
|
|
361
|
+
|
|
362
|
+
createdBy String // CONSUMER: accountId
|
|
363
|
+
cancelledBy String?
|
|
364
|
+
cancelledAt DateTime?
|
|
365
|
+
cancelReason String?
|
|
366
|
+
|
|
367
|
+
createdAt DateTime @default(now())
|
|
368
|
+
updatedAt DateTime @updatedAt
|
|
369
|
+
|
|
370
|
+
installments PaymentPlanInstallment[]
|
|
371
|
+
invoices Invoice[] @relation("InvoicePlan")
|
|
372
|
+
|
|
373
|
+
// CONSUMER: add relations to your tenant and client models.
|
|
374
|
+
// division Division @relation(fields: [divisionId], references: [id], onDelete: Restrict)
|
|
375
|
+
// clientDetails Details @relation(fields: [clientDetailsId], references: [id], onDelete: Restrict)
|
|
376
|
+
|
|
377
|
+
@@index([divisionId, status])
|
|
378
|
+
@@index([clientDetailsId])
|
|
379
|
+
@@index([referenceType, referenceId])
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
model PaymentPlanInstallment {
|
|
383
|
+
id String @id @default(uuid())
|
|
384
|
+
paymentPlanId String
|
|
385
|
+
sequenceOrder Int
|
|
386
|
+
amount Int // cents
|
|
387
|
+
status InstallmentStatus @default(PENDING)
|
|
388
|
+
dueAt DateTime?
|
|
389
|
+
|
|
390
|
+
// One-to-one link to the generated invoice (null until invoiced).
|
|
391
|
+
// INVARIANT: when set, Invoice.paymentPlanId and Invoice.installmentNumber
|
|
392
|
+
// must also be set atomically by the consumer repository adapter.
|
|
393
|
+
invoiceId String? @unique
|
|
394
|
+
|
|
395
|
+
createdAt DateTime @default(now())
|
|
396
|
+
updatedAt DateTime @updatedAt
|
|
397
|
+
|
|
398
|
+
paymentPlan PaymentPlan @relation(fields: [paymentPlanId], references: [id], onDelete: Cascade)
|
|
399
|
+
invoice Invoice? @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
|
|
400
|
+
|
|
401
|
+
@@index([paymentPlanId, sequenceOrder])
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
// -----------------------------------------------------------------------------
|
|
406
|
+
// Recurring invoice models (required when features.recurringInvoices = true)
|
|
407
|
+
// -----------------------------------------------------------------------------
|
|
408
|
+
|
|
409
|
+
model RecurringInvoice {
|
|
410
|
+
id String @id @default(uuid())
|
|
411
|
+
divisionId String // CONSUMER: tenant/org scope
|
|
412
|
+
|
|
413
|
+
// Schedule configuration
|
|
414
|
+
intervalUnit RecurringIntervalUnit
|
|
415
|
+
intervalValue Int
|
|
416
|
+
anchorDate DateTime // Immutable after creation — all next dates are computed from this
|
|
417
|
+
nextInvoiceAt DateTime
|
|
418
|
+
lastGeneratedAt DateTime? // Null = never generated; set after each successful generation
|
|
419
|
+
|
|
420
|
+
status RecurringInvoiceStatus @default(ACTIVE)
|
|
421
|
+
autoSend Boolean @default(true)
|
|
422
|
+
|
|
423
|
+
// Invoice template fields
|
|
424
|
+
clientDetailsId String // CONSUMER: opaque client ID
|
|
425
|
+
currency String @default("USD")
|
|
426
|
+
taxRate Decimal @default(0) @db.Decimal(5, 4)
|
|
427
|
+
notes String?
|
|
428
|
+
clientNotes String?
|
|
429
|
+
|
|
430
|
+
// Polymorphic entity reference
|
|
431
|
+
referenceType String?
|
|
432
|
+
referenceId String?
|
|
433
|
+
|
|
434
|
+
// Auto-charge (Phase 2) — uses the stored PaymentCustomer for the client
|
|
435
|
+
autoCharge Boolean @default(false)
|
|
436
|
+
metadata Json?
|
|
437
|
+
consecutiveFailures Int @default(0)
|
|
438
|
+
lastFailureReason String?
|
|
439
|
+
|
|
440
|
+
// Lifecycle tracking
|
|
441
|
+
pausedAt DateTime?
|
|
442
|
+
pausedBy String?
|
|
443
|
+
cancelledAt DateTime?
|
|
444
|
+
cancelledBy String?
|
|
445
|
+
cancelReason String?
|
|
446
|
+
|
|
447
|
+
createdBy String
|
|
448
|
+
createdAt DateTime @default(now())
|
|
449
|
+
updatedAt DateTime @updatedAt
|
|
450
|
+
|
|
451
|
+
templateLineItems RecurringInvoiceLineItem[]
|
|
452
|
+
generatedInvoices Invoice[]
|
|
453
|
+
|
|
454
|
+
// CONSUMER: add relations to your tenant and client models.
|
|
455
|
+
// division Division @relation(fields: [divisionId], references: [id], onDelete: Restrict)
|
|
456
|
+
// clientDetails Details @relation(fields: [clientDetailsId], references: [id], onDelete: Restrict)
|
|
457
|
+
|
|
458
|
+
@@index([status, nextInvoiceAt]) // Used by the generator claim query
|
|
459
|
+
@@index([divisionId, status])
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
model RecurringInvoiceLineItem {
|
|
463
|
+
id String @id @default(uuid())
|
|
464
|
+
recurringInvoiceId String
|
|
465
|
+
|
|
466
|
+
description String
|
|
467
|
+
quantity Int @default(1)
|
|
468
|
+
unitPrice Int // cents
|
|
469
|
+
sortOrder Int @default(0)
|
|
470
|
+
isTaxable Boolean @default(true)
|
|
471
|
+
|
|
472
|
+
referenceType String?
|
|
473
|
+
referenceId String?
|
|
474
|
+
|
|
475
|
+
metadata Json?
|
|
476
|
+
|
|
477
|
+
createdAt DateTime @default(now())
|
|
478
|
+
updatedAt DateTime @updatedAt
|
|
479
|
+
|
|
480
|
+
recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
|
|
481
|
+
|
|
482
|
+
@@index([recurringInvoiceId, sortOrder])
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
model PaymentCustomer {
|
|
486
|
+
// Maps a (division, client, provider) triple to a provider-side customer ID
|
|
487
|
+
// (e.g. Stripe Customer). Powers auto-charge on recurring invoices and
|
|
488
|
+
// future saved-payment-method flows.
|
|
489
|
+
id String @id @default(uuid())
|
|
490
|
+
divisionId String
|
|
491
|
+
clientDetailsId String
|
|
492
|
+
provider String // "stripe"
|
|
493
|
+
providerCustomerId String
|
|
494
|
+
|
|
495
|
+
createdAt DateTime @default(now())
|
|
496
|
+
updatedAt DateTime @updatedAt
|
|
497
|
+
|
|
498
|
+
// CONSUMER: add relations to your tenant and client models.
|
|
499
|
+
// division Division @relation(fields: [divisionId], references: [id], onDelete: Restrict)
|
|
500
|
+
// clientDetails Details @relation(fields: [clientDetailsId], references: [id], onDelete: Restrict)
|
|
501
|
+
|
|
502
|
+
@@unique([divisionId, clientDetailsId, provider])
|
|
503
|
+
@@index([providerCustomerId])
|
|
504
|
+
}
|