@saasicat/spec 1.0.0-rc.2 → 1.0.0-rc.20
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/README.md +30 -0
- package/acceptance/README.md +3 -3
- package/acceptance/mfa/totp-verify-good-and-bad-code.yaml +6 -6
- package/admin-api.openapi.yaml +328 -77
- package/cli-conventions.md +19 -19
- package/index.cjs +3 -0
- package/index.d.cts +3 -1
- package/index.d.ts +2 -0
- package/index.js +9 -1
- package/package.json +4 -2
- package/prisma-fragments/01-subscription.prisma +34 -47
- package/prisma-fragments/03-plan-versions.prisma +3 -4
- package/prisma-fragments/05-bundle.prisma +4 -5
- package/prisma-fragments/06-catalog-entries.prisma +24 -24
- package/prisma-fragments/07-promotion.prisma +2 -3
- package/prisma-fragments/08-subscription-contract.prisma +42 -5
- package/prisma-fragments/09-pending-registration.prisma +32 -27
- package/prisma-fragments/11-subscription-bundle.prisma +17 -0
- package/prisma-fragments/12-applied-settings.prisma +55 -0
- package/prisma-fragments/13-subscriber.prisma +94 -0
- package/prisma-fragments/14-payments.prisma +100 -0
- package/prisma-fragments/README.md +40 -22
- package/schemas/admin-manifest.schema.json +9 -2
- package/schemas/plan-catalog.schema.json +239 -18
- package/schemas/tenant-ledger.schema.json +236 -0
- package/sql/1.0-a-contract-names-its-subscriber.postgres.sql +383 -0
- package/sql/1.0-a-payment-method-is-a-gateway-reference.postgres.sql +177 -0
- package/sql/1.0-a-settings-change-carries-its-order.postgres.sql +73 -0
- package/sql/1.0-line-items-record-their-money.postgres.sql +134 -0
- package/sql/1.0-remove-project-key.postgres.sql +203 -0
- package/sql/1.0-the-applied-settings-are-recorded.postgres.sql +67 -0
- package/sql/constraints.postgres.sql +76 -0
- package/sql/reference-schema.postgres.sql +299 -77
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
-- prisma-fragments/09-pending-registration.prisma
|
|
15
15
|
-- prisma-fragments/10-super-admin.prisma
|
|
16
16
|
-- prisma-fragments/11-subscription-bundle.prisma
|
|
17
|
+
-- prisma-fragments/12-applied-settings.prisma
|
|
18
|
+
-- prisma-fragments/13-subscriber.prisma
|
|
19
|
+
-- prisma-fragments/14-payments.prisma
|
|
17
20
|
-- plus the normative constraints from sql/constraints.postgres.sql.
|
|
18
21
|
-- Do not edit by hand — change the fragments/constraints and regenerate.
|
|
19
22
|
|
|
@@ -26,9 +29,6 @@ CREATE TYPE "BillingCycle" AS ENUM ('MONTHLY', 'YEARLY');
|
|
|
26
29
|
-- CreateEnum
|
|
27
30
|
CREATE TYPE "SubscriptionStatus" AS ENUM ('TRIAL', 'ACTIVE', 'PAST_DUE', 'CANCELED', 'PENDING_SALES');
|
|
28
31
|
|
|
29
|
-
-- CreateEnum
|
|
30
|
-
CREATE TYPE "SubscriptionPaymentType" AS ENUM ('CARD', 'SEPA', 'PAYPAL', 'KLARNA', 'INVOICE');
|
|
31
|
-
|
|
32
32
|
-- CreateEnum
|
|
33
33
|
CREATE TYPE "PromoCodeValueType" AS ENUM ('PERCENT', 'ABSOLUTE');
|
|
34
34
|
|
|
@@ -60,8 +60,11 @@ CREATE TABLE "subscriptions" (
|
|
|
60
60
|
"trialEndsAt" TIMESTAMP(3),
|
|
61
61
|
"startedAt" TIMESTAMP(3),
|
|
62
62
|
"canceledAt" TIMESTAMP(3),
|
|
63
|
+
"canceledEffectiveAt" TIMESTAMP(3),
|
|
63
64
|
"currentPeriodStart" TIMESTAMP(3),
|
|
64
65
|
"currentPeriodEnd" TIMESTAMP(3),
|
|
66
|
+
"minimumTermUntil" TIMESTAMP(3),
|
|
67
|
+
"billingAnchorDay" INTEGER,
|
|
65
68
|
"planVersionId" TEXT NOT NULL,
|
|
66
69
|
"pendingPlanVersionId" TEXT,
|
|
67
70
|
"pendingPlanVersionEffectiveAt" TIMESTAMP(3),
|
|
@@ -89,29 +92,9 @@ CREATE TABLE "subscriptions" (
|
|
|
89
92
|
CONSTRAINT "subscriptions_pkey" PRIMARY KEY ("id")
|
|
90
93
|
);
|
|
91
94
|
|
|
92
|
-
-- CreateTable
|
|
93
|
-
CREATE TABLE "subscription_payment_methods" (
|
|
94
|
-
"id" TEXT NOT NULL,
|
|
95
|
-
"subscriptionId" TEXT NOT NULL,
|
|
96
|
-
"type" "SubscriptionPaymentType" NOT NULL,
|
|
97
|
-
"cardName" TEXT,
|
|
98
|
-
"cardBrand" TEXT,
|
|
99
|
-
"cardLast4" TEXT,
|
|
100
|
-
"cardExp" TEXT,
|
|
101
|
-
"ibanLast4" TEXT,
|
|
102
|
-
"ibanName" TEXT,
|
|
103
|
-
"paypalEmail" TEXT,
|
|
104
|
-
"klarnaPlan" TEXT,
|
|
105
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
106
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
107
|
-
|
|
108
|
-
CONSTRAINT "subscription_payment_methods_pkey" PRIMARY KEY ("id")
|
|
109
|
-
);
|
|
110
|
-
|
|
111
95
|
-- CreateTable
|
|
112
96
|
CREATE TABLE "checkout_offers" (
|
|
113
97
|
"id" TEXT NOT NULL,
|
|
114
|
-
"projectKey" TEXT NOT NULL,
|
|
115
98
|
"planKey" TEXT NOT NULL,
|
|
116
99
|
"planVersionId" TEXT,
|
|
117
100
|
"billingCycle" TEXT NOT NULL,
|
|
@@ -197,7 +180,6 @@ CREATE TABLE "promo_code_validation_logs" (
|
|
|
197
180
|
-- CreateTable
|
|
198
181
|
CREATE TABLE "plans" (
|
|
199
182
|
"id" TEXT NOT NULL,
|
|
200
|
-
"projectKey" TEXT NOT NULL,
|
|
201
183
|
"planKey" TEXT NOT NULL,
|
|
202
184
|
"label" TEXT NOT NULL,
|
|
203
185
|
"description" TEXT,
|
|
@@ -257,7 +239,6 @@ CREATE TABLE "audit_logs" (
|
|
|
257
239
|
-- CreateTable
|
|
258
240
|
CREATE TABLE "bundles" (
|
|
259
241
|
"id" TEXT NOT NULL,
|
|
260
|
-
"projectKey" TEXT NOT NULL,
|
|
261
242
|
"bundleKey" TEXT NOT NULL,
|
|
262
243
|
"label" TEXT NOT NULL,
|
|
263
244
|
"description" TEXT,
|
|
@@ -302,7 +283,6 @@ CREATE TABLE "bundle_versions" (
|
|
|
302
283
|
-- CreateTable
|
|
303
284
|
CREATE TABLE "capability_catalog_entries" (
|
|
304
285
|
"id" TEXT NOT NULL,
|
|
305
|
-
"projectKey" TEXT NOT NULL,
|
|
306
286
|
"capabilityKey" TEXT NOT NULL,
|
|
307
287
|
"label" TEXT NOT NULL,
|
|
308
288
|
"description" TEXT,
|
|
@@ -327,7 +307,6 @@ CREATE TABLE "capability_catalog_entries" (
|
|
|
327
307
|
-- CreateTable
|
|
328
308
|
CREATE TABLE "feature_catalog_entries" (
|
|
329
309
|
"id" TEXT NOT NULL,
|
|
330
|
-
"projectKey" TEXT NOT NULL,
|
|
331
310
|
"featureKey" TEXT NOT NULL,
|
|
332
311
|
"label" TEXT NOT NULL,
|
|
333
312
|
"description" TEXT,
|
|
@@ -356,7 +335,6 @@ CREATE TABLE "feature_catalog_entries" (
|
|
|
356
335
|
-- CreateTable
|
|
357
336
|
CREATE TABLE "quota_catalog_entries" (
|
|
358
337
|
"id" TEXT NOT NULL,
|
|
359
|
-
"projectKey" TEXT NOT NULL,
|
|
360
338
|
"quotaKey" TEXT NOT NULL,
|
|
361
339
|
"label" TEXT NOT NULL,
|
|
362
340
|
"description" TEXT,
|
|
@@ -382,7 +360,6 @@ CREATE TABLE "quota_catalog_entries" (
|
|
|
382
360
|
-- CreateTable
|
|
383
361
|
CREATE TABLE "marketing_projections" (
|
|
384
362
|
"id" TEXT NOT NULL,
|
|
385
|
-
"projectKey" TEXT NOT NULL,
|
|
386
363
|
"targetType" TEXT NOT NULL,
|
|
387
364
|
"targetVersionId" TEXT NOT NULL,
|
|
388
365
|
"locale" TEXT NOT NULL DEFAULT 'de',
|
|
@@ -405,8 +382,7 @@ CREATE TABLE "marketing_projections" (
|
|
|
405
382
|
|
|
406
383
|
-- CreateTable
|
|
407
384
|
CREATE TABLE "marketing_settings" (
|
|
408
|
-
"id" TEXT NOT NULL,
|
|
409
|
-
"projectKey" TEXT NOT NULL,
|
|
385
|
+
"id" TEXT NOT NULL DEFAULT 'marketing-settings',
|
|
410
386
|
"activeLocales" JSONB NOT NULL DEFAULT '[]',
|
|
411
387
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
412
388
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
@@ -417,7 +393,6 @@ CREATE TABLE "marketing_settings" (
|
|
|
417
393
|
-- CreateTable
|
|
418
394
|
CREATE TABLE "promotions" (
|
|
419
395
|
"id" TEXT NOT NULL,
|
|
420
|
-
"projectKey" TEXT NOT NULL,
|
|
421
396
|
"internalLabel" TEXT NOT NULL,
|
|
422
397
|
"type" TEXT NOT NULL,
|
|
423
398
|
"value" JSONB NOT NULL,
|
|
@@ -441,8 +416,11 @@ CREATE TABLE "promotions" (
|
|
|
441
416
|
-- CreateTable
|
|
442
417
|
CREATE TABLE "subscription_contracts" (
|
|
443
418
|
"id" TEXT NOT NULL,
|
|
444
|
-
"projectKey" TEXT NOT NULL,
|
|
445
419
|
"tenantId" TEXT NOT NULL,
|
|
420
|
+
"subscriberId" TEXT NOT NULL,
|
|
421
|
+
"subscriberSnapshot" JSONB NOT NULL,
|
|
422
|
+
"issuerSnapshot" JSONB,
|
|
423
|
+
"partiesMigrated" BOOLEAN NOT NULL DEFAULT false,
|
|
446
424
|
"status" "SubscriptionContractStatus" NOT NULL DEFAULT 'active',
|
|
447
425
|
"effectiveFrom" TIMESTAMP(3) NOT NULL,
|
|
448
426
|
"effectiveUntil" TIMESTAMP(3),
|
|
@@ -474,6 +452,9 @@ CREATE TABLE "contract_line_items" (
|
|
|
474
452
|
"priceNet" DECIMAL(10,2) NOT NULL,
|
|
475
453
|
"priceGross" DECIMAL(10,2) NOT NULL,
|
|
476
454
|
"billingCycle" TEXT NOT NULL,
|
|
455
|
+
"currency" TEXT NOT NULL,
|
|
456
|
+
"taxRate" DECIMAL(5,2) NOT NULL,
|
|
457
|
+
"taxAmount" DECIMAL(10,2) NOT NULL,
|
|
477
458
|
"minimumTermUntil" TIMESTAMP(3),
|
|
478
459
|
"featuresSnapshot" JSONB NOT NULL DEFAULT '[]',
|
|
479
460
|
"quotaEffectsSnapshot" JSONB NOT NULL DEFAULT '{}',
|
|
@@ -506,7 +487,16 @@ CREATE TABLE "PendingRegistration" (
|
|
|
506
487
|
"configJson" JSONB,
|
|
507
488
|
"billingCycle" TEXT,
|
|
508
489
|
"appliedPromoCode" TEXT,
|
|
490
|
+
"addressLine1" TEXT,
|
|
491
|
+
"addressLine2" TEXT,
|
|
492
|
+
"postalCode" TEXT,
|
|
493
|
+
"city" TEXT,
|
|
494
|
+
"country" TEXT,
|
|
495
|
+
"vatId" TEXT,
|
|
496
|
+
"taxNumber" TEXT,
|
|
509
497
|
"checkoutSessionId" TEXT,
|
|
498
|
+
"checkoutGatewayAccount" TEXT,
|
|
499
|
+
"gatewayCustomerRef" TEXT,
|
|
510
500
|
"checkoutStartedAt" TIMESTAMP(3),
|
|
511
501
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
512
502
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
@@ -515,19 +505,6 @@ CREATE TABLE "PendingRegistration" (
|
|
|
515
505
|
CONSTRAINT "PendingRegistration_pkey" PRIMARY KEY ("id")
|
|
516
506
|
);
|
|
517
507
|
|
|
518
|
-
-- CreateTable
|
|
519
|
-
CREATE TABLE "PaymentEventLog" (
|
|
520
|
-
"id" TEXT NOT NULL,
|
|
521
|
-
"eventId" TEXT NOT NULL,
|
|
522
|
-
"provider" TEXT NOT NULL,
|
|
523
|
-
"sessionId" TEXT,
|
|
524
|
-
"status" TEXT NOT NULL,
|
|
525
|
-
"payload" JSONB,
|
|
526
|
-
"processedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
527
|
-
|
|
528
|
-
CONSTRAINT "PaymentEventLog_pkey" PRIMARY KEY ("id")
|
|
529
|
-
);
|
|
530
|
-
|
|
531
508
|
-- CreateTable
|
|
532
509
|
CREATE TABLE "super_admin_users" (
|
|
533
510
|
"id" TEXT NOT NULL,
|
|
@@ -562,6 +539,9 @@ CREATE TABLE "subscription_bundles" (
|
|
|
562
539
|
"bundleVersionId" TEXT NOT NULL,
|
|
563
540
|
"startedAt" TIMESTAMP(3) NOT NULL,
|
|
564
541
|
"minimumTermEndsAt" TIMESTAMP(3),
|
|
542
|
+
"billingCycle" TEXT,
|
|
543
|
+
"currentPeriodStart" TIMESTAMP(3),
|
|
544
|
+
"currentPeriodEnd" TIMESTAMP(3),
|
|
565
545
|
"canceledAt" TIMESTAMP(3),
|
|
566
546
|
"canceledEffectiveAt" TIMESTAMP(3),
|
|
567
547
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
@@ -570,6 +550,127 @@ CREATE TABLE "subscription_bundles" (
|
|
|
570
550
|
CONSTRAINT "subscription_bundles_pkey" PRIMARY KEY ("id")
|
|
571
551
|
);
|
|
572
552
|
|
|
553
|
+
-- CreateTable
|
|
554
|
+
CREATE TABLE "applied_settings" (
|
|
555
|
+
"id" TEXT NOT NULL DEFAULT 'installation',
|
|
556
|
+
"fingerprint" TEXT NOT NULL,
|
|
557
|
+
"settings" JSONB NOT NULL,
|
|
558
|
+
"source" TEXT NOT NULL,
|
|
559
|
+
"appliedAt" TIMESTAMP(3) NOT NULL,
|
|
560
|
+
|
|
561
|
+
CONSTRAINT "applied_settings_pkey" PRIMARY KEY ("id")
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
-- CreateTable
|
|
565
|
+
CREATE TABLE "settings_changes" (
|
|
566
|
+
"id" TEXT NOT NULL,
|
|
567
|
+
"seq" SERIAL NOT NULL,
|
|
568
|
+
"noticedAt" TIMESTAMP(3) NOT NULL,
|
|
569
|
+
"source" TEXT NOT NULL,
|
|
570
|
+
"previous" JSONB NOT NULL,
|
|
571
|
+
"current" JSONB NOT NULL,
|
|
572
|
+
"acknowledgedAt" TIMESTAMP(3),
|
|
573
|
+
"acknowledgedBy" TEXT,
|
|
574
|
+
|
|
575
|
+
CONSTRAINT "settings_changes_pkey" PRIMARY KEY ("id")
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
-- CreateTable
|
|
579
|
+
CREATE TABLE "subscribers" (
|
|
580
|
+
"id" TEXT NOT NULL,
|
|
581
|
+
"customerSequence" SERIAL NOT NULL,
|
|
582
|
+
"customerNumberPrefix" TEXT NOT NULL DEFAULT '',
|
|
583
|
+
"legalName" TEXT NOT NULL,
|
|
584
|
+
"vatId" TEXT,
|
|
585
|
+
"taxNumber" TEXT,
|
|
586
|
+
"addressLine1" TEXT,
|
|
587
|
+
"addressLine2" TEXT,
|
|
588
|
+
"postalCode" TEXT,
|
|
589
|
+
"city" TEXT,
|
|
590
|
+
"country" TEXT,
|
|
591
|
+
"invoiceEmail" TEXT,
|
|
592
|
+
"migrated" BOOLEAN NOT NULL DEFAULT false,
|
|
593
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
594
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
595
|
+
|
|
596
|
+
CONSTRAINT "subscribers_pkey" PRIMARY KEY ("id")
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
-- CreateTable
|
|
600
|
+
CREATE TABLE "subscriber_tenants" (
|
|
601
|
+
"id" TEXT NOT NULL,
|
|
602
|
+
"subscriberId" TEXT NOT NULL,
|
|
603
|
+
"tenantId" TEXT NOT NULL,
|
|
604
|
+
"linkedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
605
|
+
"unlinkedAt" TIMESTAMP(3),
|
|
606
|
+
|
|
607
|
+
CONSTRAINT "subscriber_tenants_pkey" PRIMARY KEY ("id")
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
-- CreateTable
|
|
611
|
+
CREATE TABLE "subscriber_corrections" (
|
|
612
|
+
"id" TEXT NOT NULL,
|
|
613
|
+
"subscriberId" TEXT NOT NULL,
|
|
614
|
+
"previous" JSONB NOT NULL,
|
|
615
|
+
"corrected" JSONB NOT NULL,
|
|
616
|
+
"reason" TEXT NOT NULL,
|
|
617
|
+
"correctedBy" TEXT NOT NULL,
|
|
618
|
+
"correctedAt" TIMESTAMP(3) NOT NULL,
|
|
619
|
+
|
|
620
|
+
CONSTRAINT "subscriber_corrections_pkey" PRIMARY KEY ("id")
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
-- CreateTable
|
|
624
|
+
CREATE TABLE "subscriber_payment_methods" (
|
|
625
|
+
"id" TEXT NOT NULL,
|
|
626
|
+
"subscriberId" TEXT NOT NULL,
|
|
627
|
+
"gatewayAccount" TEXT NOT NULL,
|
|
628
|
+
"provider" TEXT NOT NULL,
|
|
629
|
+
"customerRef" TEXT NOT NULL,
|
|
630
|
+
"paymentMethodRef" TEXT NOT NULL,
|
|
631
|
+
"type" TEXT NOT NULL,
|
|
632
|
+
"brand" TEXT,
|
|
633
|
+
"last4" TEXT NOT NULL,
|
|
634
|
+
"expiryMonth" INTEGER,
|
|
635
|
+
"expiryYear" INTEGER,
|
|
636
|
+
"country" TEXT,
|
|
637
|
+
"bankCode" TEXT,
|
|
638
|
+
"mandateReference" TEXT,
|
|
639
|
+
"status" TEXT NOT NULL DEFAULT 'ACTIVE',
|
|
640
|
+
"confirmedAt" TIMESTAMP(3) NOT NULL,
|
|
641
|
+
"replacedAt" TIMESTAMP(3),
|
|
642
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
643
|
+
|
|
644
|
+
CONSTRAINT "subscriber_payment_methods_pkey" PRIMARY KEY ("id")
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
-- CreateTable
|
|
648
|
+
CREATE TABLE "subscriber_payment_method_setups" (
|
|
649
|
+
"id" TEXT NOT NULL,
|
|
650
|
+
"subscriberId" TEXT NOT NULL,
|
|
651
|
+
"gatewayAccount" TEXT NOT NULL,
|
|
652
|
+
"sessionRef" TEXT NOT NULL,
|
|
653
|
+
"customerRef" TEXT NOT NULL,
|
|
654
|
+
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
655
|
+
"completedAt" TIMESTAMP(3),
|
|
656
|
+
|
|
657
|
+
CONSTRAINT "subscriber_payment_method_setups_pkey" PRIMARY KEY ("id")
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
-- CreateTable
|
|
661
|
+
CREATE TABLE "PaymentEventLog" (
|
|
662
|
+
"id" TEXT NOT NULL,
|
|
663
|
+
"gatewayAccount" TEXT NOT NULL,
|
|
664
|
+
"eventId" TEXT NOT NULL,
|
|
665
|
+
"provider" TEXT NOT NULL,
|
|
666
|
+
"sessionId" TEXT,
|
|
667
|
+
"status" TEXT NOT NULL,
|
|
668
|
+
"payload" JSONB,
|
|
669
|
+
"processedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
670
|
+
|
|
671
|
+
CONSTRAINT "PaymentEventLog_pkey" PRIMARY KEY ("id")
|
|
672
|
+
);
|
|
673
|
+
|
|
573
674
|
-- CreateIndex
|
|
574
675
|
CREATE UNIQUE INDEX "subscriptions_tenantId_key" ON "subscriptions"("tenantId");
|
|
575
676
|
|
|
@@ -586,10 +687,7 @@ CREATE INDEX "subscriptions_pendingPlanVersionId_idx" ON "subscriptions"("pendin
|
|
|
586
687
|
CREATE INDEX "subscriptions_currentPeriodEnd_idx" ON "subscriptions"("currentPeriodEnd");
|
|
587
688
|
|
|
588
689
|
-- CreateIndex
|
|
589
|
-
CREATE
|
|
590
|
-
|
|
591
|
-
-- CreateIndex
|
|
592
|
-
CREATE INDEX "checkout_offers_projectKey_status_idx" ON "checkout_offers"("projectKey", "status");
|
|
690
|
+
CREATE INDEX "checkout_offers_status_idx" ON "checkout_offers"("status");
|
|
593
691
|
|
|
594
692
|
-- CreateIndex
|
|
595
693
|
CREATE UNIQUE INDEX "promo_codes_code_key" ON "promo_codes"("code");
|
|
@@ -622,10 +720,10 @@ CREATE INDEX "promo_code_validation_logs_ipHash_createdAt_idx" ON "promo_code_va
|
|
|
622
720
|
CREATE INDEX "promo_code_validation_logs_sessionId_createdAt_idx" ON "promo_code_validation_logs"("sessionId", "createdAt");
|
|
623
721
|
|
|
624
722
|
-- CreateIndex
|
|
625
|
-
CREATE INDEX "
|
|
723
|
+
CREATE INDEX "plans_deletedAt_idx" ON "plans"("deletedAt");
|
|
626
724
|
|
|
627
725
|
-- CreateIndex
|
|
628
|
-
CREATE UNIQUE INDEX "
|
|
726
|
+
CREATE UNIQUE INDEX "plans_planKey_key" ON "plans"("planKey");
|
|
629
727
|
|
|
630
728
|
-- CreateIndex
|
|
631
729
|
CREATE INDEX "plan_versions_planId_supersededAt_idx" ON "plan_versions"("planId", "supersededAt");
|
|
@@ -652,10 +750,10 @@ CREATE INDEX "audit_logs_entity_action_createdAt_idx" ON "audit_logs"("entity",
|
|
|
652
750
|
CREATE INDEX "audit_logs_actorTag_createdAt_idx" ON "audit_logs"("actorTag", "createdAt");
|
|
653
751
|
|
|
654
752
|
-- CreateIndex
|
|
655
|
-
CREATE INDEX "
|
|
753
|
+
CREATE INDEX "bundles_deletedAt_idx" ON "bundles"("deletedAt");
|
|
656
754
|
|
|
657
755
|
-- CreateIndex
|
|
658
|
-
CREATE UNIQUE INDEX "
|
|
756
|
+
CREATE UNIQUE INDEX "bundles_bundleKey_key" ON "bundles"("bundleKey");
|
|
659
757
|
|
|
660
758
|
-- CreateIndex
|
|
661
759
|
CREATE INDEX "bundle_versions_bundleId_supersededAt_idx" ON "bundle_versions"("bundleId", "supersededAt");
|
|
@@ -670,46 +768,46 @@ CREATE INDEX "bundle_versions_bundleId_validFrom_idx" ON "bundle_versions"("bund
|
|
|
670
768
|
CREATE UNIQUE INDEX "bundle_versions_bundleId_version_key" ON "bundle_versions"("bundleId", "version");
|
|
671
769
|
|
|
672
770
|
-- CreateIndex
|
|
673
|
-
CREATE INDEX "
|
|
771
|
+
CREATE INDEX "capability_catalog_entries_codeStatus_idx" ON "capability_catalog_entries"("codeStatus");
|
|
674
772
|
|
|
675
773
|
-- CreateIndex
|
|
676
|
-
CREATE INDEX "
|
|
774
|
+
CREATE INDEX "capability_catalog_entries_featureKey_idx" ON "capability_catalog_entries"("featureKey");
|
|
677
775
|
|
|
678
776
|
-- CreateIndex
|
|
679
|
-
CREATE UNIQUE INDEX "
|
|
777
|
+
CREATE UNIQUE INDEX "capability_catalog_entries_capabilityKey_key" ON "capability_catalog_entries"("capabilityKey");
|
|
680
778
|
|
|
681
779
|
-- CreateIndex
|
|
682
|
-
CREATE INDEX "
|
|
780
|
+
CREATE INDEX "feature_catalog_entries_discoveryStatus_idx" ON "feature_catalog_entries"("discoveryStatus");
|
|
683
781
|
|
|
684
782
|
-- CreateIndex
|
|
685
|
-
CREATE INDEX "
|
|
783
|
+
CREATE INDEX "feature_catalog_entries_plannedOnly_idx" ON "feature_catalog_entries"("plannedOnly");
|
|
686
784
|
|
|
687
785
|
-- CreateIndex
|
|
688
|
-
CREATE UNIQUE INDEX "
|
|
786
|
+
CREATE UNIQUE INDEX "feature_catalog_entries_featureKey_key" ON "feature_catalog_entries"("featureKey");
|
|
689
787
|
|
|
690
788
|
-- CreateIndex
|
|
691
|
-
CREATE INDEX "
|
|
789
|
+
CREATE INDEX "quota_catalog_entries_discoveryStatus_idx" ON "quota_catalog_entries"("discoveryStatus");
|
|
692
790
|
|
|
693
791
|
-- CreateIndex
|
|
694
|
-
CREATE UNIQUE INDEX "
|
|
792
|
+
CREATE UNIQUE INDEX "quota_catalog_entries_quotaKey_key" ON "quota_catalog_entries"("quotaKey");
|
|
695
793
|
|
|
696
794
|
-- CreateIndex
|
|
697
|
-
CREATE INDEX "
|
|
795
|
+
CREATE INDEX "marketing_projections_targetType_locale_priority_idx" ON "marketing_projections"("targetType", "locale", "priority");
|
|
698
796
|
|
|
699
797
|
-- CreateIndex
|
|
700
798
|
CREATE UNIQUE INDEX "marketing_projections_targetType_targetVersionId_locale_key" ON "marketing_projections"("targetType", "targetVersionId", "locale");
|
|
701
799
|
|
|
702
800
|
-- CreateIndex
|
|
703
|
-
CREATE
|
|
801
|
+
CREATE INDEX "promotions_targetType_validFrom_validTo_idx" ON "promotions"("targetType", "validFrom", "validTo");
|
|
704
802
|
|
|
705
803
|
-- CreateIndex
|
|
706
|
-
CREATE INDEX "
|
|
804
|
+
CREATE INDEX "subscription_contracts_tenantId_status_effectiveFrom_idx" ON "subscription_contracts"("tenantId", "status", "effectiveFrom");
|
|
707
805
|
|
|
708
806
|
-- CreateIndex
|
|
709
|
-
CREATE INDEX "
|
|
807
|
+
CREATE INDEX "subscription_contracts_subscriberId_idx" ON "subscription_contracts"("subscriberId");
|
|
710
808
|
|
|
711
809
|
-- CreateIndex
|
|
712
|
-
CREATE INDEX "
|
|
810
|
+
CREATE INDEX "subscription_contracts_status_idx" ON "subscription_contracts"("status");
|
|
713
811
|
|
|
714
812
|
-- CreateIndex
|
|
715
813
|
CREATE INDEX "subscription_contracts_originalOfferId_idx" ON "subscription_contracts"("originalOfferId");
|
|
@@ -730,13 +828,7 @@ CREATE INDEX "PendingRegistration_status_expiresAt_idx" ON "PendingRegistration"
|
|
|
730
828
|
CREATE INDEX "PendingRegistration_tenantSlug_idx" ON "PendingRegistration"("tenantSlug");
|
|
731
829
|
|
|
732
830
|
-- CreateIndex
|
|
733
|
-
CREATE
|
|
734
|
-
|
|
735
|
-
-- CreateIndex
|
|
736
|
-
CREATE INDEX "PaymentEventLog_sessionId_idx" ON "PaymentEventLog"("sessionId");
|
|
737
|
-
|
|
738
|
-
-- CreateIndex
|
|
739
|
-
CREATE INDEX "PaymentEventLog_status_processedAt_idx" ON "PaymentEventLog"("status", "processedAt");
|
|
831
|
+
CREATE INDEX "PendingRegistration_checkoutGatewayAccount_checkoutSessionI_idx" ON "PendingRegistration"("checkoutGatewayAccount", "checkoutSessionId");
|
|
740
832
|
|
|
741
833
|
-- CreateIndex
|
|
742
834
|
CREATE UNIQUE INDEX "super_admin_users_email_key" ON "super_admin_users"("email");
|
|
@@ -753,15 +845,54 @@ CREATE INDEX "subscription_bundles_bundleVersionId_idx" ON "subscription_bundles
|
|
|
753
845
|
-- CreateIndex
|
|
754
846
|
CREATE INDEX "subscription_bundles_canceledEffectiveAt_idx" ON "subscription_bundles"("canceledEffectiveAt");
|
|
755
847
|
|
|
848
|
+
-- CreateIndex
|
|
849
|
+
CREATE UNIQUE INDEX "settings_changes_seq_key" ON "settings_changes"("seq");
|
|
850
|
+
|
|
851
|
+
-- CreateIndex
|
|
852
|
+
CREATE INDEX "settings_changes_acknowledgedAt_noticedAt_idx" ON "settings_changes"("acknowledgedAt", "noticedAt");
|
|
853
|
+
|
|
854
|
+
-- CreateIndex
|
|
855
|
+
CREATE UNIQUE INDEX "subscribers_customerSequence_key" ON "subscribers"("customerSequence");
|
|
856
|
+
|
|
857
|
+
-- CreateIndex
|
|
858
|
+
CREATE INDEX "subscriber_tenants_tenantId_idx" ON "subscriber_tenants"("tenantId");
|
|
859
|
+
|
|
860
|
+
-- CreateIndex
|
|
861
|
+
CREATE INDEX "subscriber_tenants_subscriberId_idx" ON "subscriber_tenants"("subscriberId");
|
|
862
|
+
|
|
863
|
+
-- CreateIndex
|
|
864
|
+
CREATE INDEX "subscriber_corrections_subscriberId_correctedAt_idx" ON "subscriber_corrections"("subscriberId", "correctedAt");
|
|
865
|
+
|
|
866
|
+
-- CreateIndex
|
|
867
|
+
CREATE INDEX "subscriber_payment_methods_subscriberId_status_idx" ON "subscriber_payment_methods"("subscriberId", "status");
|
|
868
|
+
|
|
869
|
+
-- CreateIndex
|
|
870
|
+
CREATE INDEX "subscriber_payment_methods_gatewayAccount_status_idx" ON "subscriber_payment_methods"("gatewayAccount", "status");
|
|
871
|
+
|
|
872
|
+
-- CreateIndex
|
|
873
|
+
CREATE UNIQUE INDEX "subscriber_payment_methods_gatewayAccount_paymentMethodRef_key" ON "subscriber_payment_methods"("gatewayAccount", "paymentMethodRef");
|
|
874
|
+
|
|
875
|
+
-- CreateIndex
|
|
876
|
+
CREATE INDEX "subscriber_payment_method_setups_subscriberId_idx" ON "subscriber_payment_method_setups"("subscriberId");
|
|
877
|
+
|
|
878
|
+
-- CreateIndex
|
|
879
|
+
CREATE UNIQUE INDEX "subscriber_payment_method_setups_gatewayAccount_sessionRef_key" ON "subscriber_payment_method_setups"("gatewayAccount", "sessionRef");
|
|
880
|
+
|
|
881
|
+
-- CreateIndex
|
|
882
|
+
CREATE INDEX "PaymentEventLog_sessionId_idx" ON "PaymentEventLog"("sessionId");
|
|
883
|
+
|
|
884
|
+
-- CreateIndex
|
|
885
|
+
CREATE INDEX "PaymentEventLog_status_processedAt_idx" ON "PaymentEventLog"("status", "processedAt");
|
|
886
|
+
|
|
887
|
+
-- CreateIndex
|
|
888
|
+
CREATE UNIQUE INDEX "PaymentEventLog_gatewayAccount_eventId_key" ON "PaymentEventLog"("gatewayAccount", "eventId");
|
|
889
|
+
|
|
756
890
|
-- AddForeignKey
|
|
757
891
|
ALTER TABLE "subscriptions" ADD CONSTRAINT "subscriptions_planVersionId_fkey" FOREIGN KEY ("planVersionId") REFERENCES "plan_versions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
758
892
|
|
|
759
893
|
-- AddForeignKey
|
|
760
894
|
ALTER TABLE "subscriptions" ADD CONSTRAINT "subscriptions_pendingPlanVersionId_fkey" FOREIGN KEY ("pendingPlanVersionId") REFERENCES "plan_versions"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
761
895
|
|
|
762
|
-
-- AddForeignKey
|
|
763
|
-
ALTER TABLE "subscription_payment_methods" ADD CONSTRAINT "subscription_payment_methods_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "subscriptions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
764
|
-
|
|
765
896
|
-- AddForeignKey
|
|
766
897
|
ALTER TABLE "promo_code_redemptions" ADD CONSTRAINT "promo_code_redemptions_promoCodeId_fkey" FOREIGN KEY ("promoCodeId") REFERENCES "promo_codes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
767
898
|
|
|
@@ -780,6 +911,9 @@ ALTER TABLE "bundle_versions" ADD CONSTRAINT "bundle_versions_bundleId_fkey" FOR
|
|
|
780
911
|
-- AddForeignKey
|
|
781
912
|
ALTER TABLE "bundle_versions" ADD CONSTRAINT "bundle_versions_baseVersionId_fkey" FOREIGN KEY ("baseVersionId") REFERENCES "bundle_versions"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
782
913
|
|
|
914
|
+
-- AddForeignKey
|
|
915
|
+
ALTER TABLE "subscription_contracts" ADD CONSTRAINT "subscription_contracts_subscriberId_fkey" FOREIGN KEY ("subscriberId") REFERENCES "subscribers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
916
|
+
|
|
783
917
|
-- AddForeignKey
|
|
784
918
|
ALTER TABLE "contract_line_items" ADD CONSTRAINT "contract_line_items_contractId_fkey" FOREIGN KEY ("contractId") REFERENCES "subscription_contracts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
785
919
|
|
|
@@ -789,6 +923,18 @@ ALTER TABLE "subscription_bundles" ADD CONSTRAINT "subscription_bundles_subscrip
|
|
|
789
923
|
-- AddForeignKey
|
|
790
924
|
ALTER TABLE "subscription_bundles" ADD CONSTRAINT "subscription_bundles_bundleVersionId_fkey" FOREIGN KEY ("bundleVersionId") REFERENCES "bundle_versions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
791
925
|
|
|
926
|
+
-- AddForeignKey
|
|
927
|
+
ALTER TABLE "subscriber_tenants" ADD CONSTRAINT "subscriber_tenants_subscriberId_fkey" FOREIGN KEY ("subscriberId") REFERENCES "subscribers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
928
|
+
|
|
929
|
+
-- AddForeignKey
|
|
930
|
+
ALTER TABLE "subscriber_corrections" ADD CONSTRAINT "subscriber_corrections_subscriberId_fkey" FOREIGN KEY ("subscriberId") REFERENCES "subscribers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
931
|
+
|
|
932
|
+
-- AddForeignKey
|
|
933
|
+
ALTER TABLE "subscriber_payment_methods" ADD CONSTRAINT "subscriber_payment_methods_subscriberId_fkey" FOREIGN KEY ("subscriberId") REFERENCES "subscribers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
934
|
+
|
|
935
|
+
-- AddForeignKey
|
|
936
|
+
ALTER TABLE "subscriber_payment_method_setups" ADD CONSTRAINT "subscriber_payment_method_setups_subscriberId_fkey" FOREIGN KEY ("subscriberId") REFERENCES "subscribers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
937
|
+
|
|
792
938
|
-- =============================================================================
|
|
793
939
|
-- SaaSiCat — normative PostgreSQL constraints the Prisma DSL cannot express.
|
|
794
940
|
-- =============================================================================
|
|
@@ -808,3 +954,79 @@ CREATE UNIQUE INDEX IF NOT EXISTS plan_versions_draft_per_plan
|
|
|
808
954
|
|
|
809
955
|
CREATE UNIQUE INDEX IF NOT EXISTS bundle_versions_draft_per_bundle
|
|
810
956
|
ON bundle_versions ("bundleId") WHERE "publishedAt" IS NULL;
|
|
957
|
+
|
|
958
|
+
-- `marketing_settings` holds at most ONE row.
|
|
959
|
+
--
|
|
960
|
+
-- The Prisma DSL cannot say this, and the constant default on `id` does not: a
|
|
961
|
+
-- default applies only where the caller omits the value, and a primary key
|
|
962
|
+
-- accepts every distinct one. A consumer writing the row directly could make as
|
|
963
|
+
-- many as it liked, and the repository would then read one and ignore the rest.
|
|
964
|
+
--
|
|
965
|
+
-- `ADD CONSTRAINT` has no `IF NOT EXISTS`, and this file is applied again on
|
|
966
|
+
-- every deployment — so it is dropped first. Two plain statements rather than a
|
|
967
|
+
-- dollar-quoted `DO` block on purpose: every consumer of this file splits it at
|
|
968
|
+
-- the statement separator, and a dollar-quoted body would need each of those
|
|
969
|
+
-- splitters to become a SQL lexer. For the same reason no comment in this file
|
|
970
|
+
-- may contain that separator — one here did, and three splitters cut the
|
|
971
|
+
-- sentence in half.
|
|
972
|
+
ALTER TABLE marketing_settings
|
|
973
|
+
DROP CONSTRAINT IF EXISTS marketing_settings_is_a_singleton;
|
|
974
|
+
ALTER TABLE marketing_settings
|
|
975
|
+
ADD CONSTRAINT marketing_settings_is_a_singleton CHECK ("id" = 'marketing-settings');
|
|
976
|
+
|
|
977
|
+
-- `applied_settings` holds at most ONE row: the installation's.
|
|
978
|
+
--
|
|
979
|
+
-- Same reasoning as above, and the same two plain statements. The row mirrors
|
|
980
|
+
-- the settings the installation applied at its last start, and an installation
|
|
981
|
+
-- serves one application, so there is exactly one configuration to record.
|
|
982
|
+
ALTER TABLE applied_settings
|
|
983
|
+
DROP CONSTRAINT IF EXISTS applied_settings_is_a_singleton;
|
|
984
|
+
ALTER TABLE applied_settings
|
|
985
|
+
ADD CONSTRAINT applied_settings_is_a_singleton CHECK ("id" = 'installation');
|
|
986
|
+
|
|
987
|
+
-- A subscriber is live for at most ONE tenant, and a tenant has at most ONE
|
|
988
|
+
-- live subscriber. A link that ended keeps its row with `unlinkedAt` set, so the
|
|
989
|
+
-- tenants a subscriber had before stay in its history. Two partial unique
|
|
990
|
+
-- indexes, because a link that is over must not count against the next one.
|
|
991
|
+
CREATE UNIQUE INDEX IF NOT EXISTS subscriber_tenants_live_per_tenant
|
|
992
|
+
ON subscriber_tenants ("tenantId") WHERE "unlinkedAt" IS NULL;
|
|
993
|
+
|
|
994
|
+
CREATE UNIQUE INDEX IF NOT EXISTS subscriber_tenants_live_per_subscriber
|
|
995
|
+
ON subscriber_tenants ("subscriberId") WHERE "unlinkedAt" IS NULL;
|
|
996
|
+
|
|
997
|
+
-- A subscriber has at most ONE payment method in use. The one a newer payment
|
|
998
|
+
-- method replaced keeps its row as `REPLACED`, so it does not count here.
|
|
999
|
+
CREATE UNIQUE INDEX IF NOT EXISTS subscriber_payment_methods_active_per_subscriber
|
|
1000
|
+
ON subscriber_payment_methods ("subscriberId") WHERE "status" = 'ACTIVE';
|
|
1001
|
+
|
|
1002
|
+
-- A gateway session is confirmed ONCE.
|
|
1003
|
+
--
|
|
1004
|
+
-- A gateway delivers every event at least once, and it may report one session
|
|
1005
|
+
-- through more than one event — a completed form and the payment method behind
|
|
1006
|
+
-- it can arrive as two. `eventId` tells those apart, so both would be claimed,
|
|
1007
|
+
-- and both would record the session's payment method: for a sign-up that means
|
|
1008
|
+
-- a second tenant for one registration.
|
|
1009
|
+
--
|
|
1010
|
+
-- Both adapters claim with `ON CONFLICT DO NOTHING` and no conflict target, so
|
|
1011
|
+
-- this index turns the second confirmation into no row rather than an error,
|
|
1012
|
+
-- and the caller reads it as the duplicate it is. Partial on the kind, because
|
|
1013
|
+
-- a failed setup and an event SaaSiCat does not act on say nothing about the
|
|
1014
|
+
-- session being confirmed. A claim carrying no session does not collide:
|
|
1015
|
+
-- PostgreSQL counts nulls as distinct in a unique index.
|
|
1016
|
+
CREATE UNIQUE INDEX IF NOT EXISTS payment_event_log_confirmation_per_session
|
|
1017
|
+
ON "PaymentEventLog" ("gatewayAccount", "sessionId")
|
|
1018
|
+
WHERE "status" = 'payment-method-confirmed';
|
|
1019
|
+
|
|
1020
|
+
-- Customer numbers count from 10001, so a number has five digits up to 99999
|
|
1021
|
+
-- and none reads as a count of subscribers. Two statements: the first makes
|
|
1022
|
+
-- 10001 where the sequence starts over, which a restart of its identity reads,
|
|
1023
|
+
-- and the second moves a sequence that has never handed out a number there. A
|
|
1024
|
+
-- second run finds the start already set and the sequence used, and an
|
|
1025
|
+
-- installation without the subscriber tables has no such sequence.
|
|
1026
|
+
ALTER SEQUENCE IF EXISTS "subscribers_customerSequence_seq" START WITH 10001;
|
|
1027
|
+
|
|
1028
|
+
SELECT setval(format('%I.%I', schemaname, sequencename)::regclass, 10001, false)
|
|
1029
|
+
FROM pg_sequences
|
|
1030
|
+
WHERE schemaname = current_schema()
|
|
1031
|
+
AND sequencename = 'subscribers_customerSequence_seq'
|
|
1032
|
+
AND last_value IS NULL;
|