@saasicat/spec 0.5.0 → 0.7.0
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/admin-api.openapi.yaml +242 -160
- package/package.json +1 -1
- package/prisma-fragments/01-subscription.prisma +5 -18
- package/prisma-fragments/03-plan-versions.prisma +8 -1
- package/prisma-fragments/05-bundle.prisma +103 -0
- package/prisma-fragments/06-catalog-entries.prisma +9 -4
- package/prisma-fragments/11-subscription-bundle.prisma +49 -0
- package/prisma-fragments/README.md +21 -18
- package/schemas/admin-manifest.schema.json +0 -1
- package/sql/constraints.postgres.sql +0 -9
- package/sql/reference-schema.postgres.sql +43 -93
- package/prisma-fragments/05-bundle-business-type.prisma +0 -203
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
// =============================================================================
|
|
2
|
-
// SaaS platform Prisma fragment: Bundle + BusinessType (plan catalog extension)
|
|
3
|
-
// =============================================================================
|
|
4
|
-
//
|
|
5
|
-
// REFERENCE SNIPPET — see 03-plan-versions.prisma for versioning conventions.
|
|
6
|
-
//
|
|
7
|
-
// Global catalog tables (no tenantId, no RLS). Write access only via the
|
|
8
|
-
// SUPER_ADMIN guard in the AdminController of the `@saasicat/nest`
|
|
9
|
-
// package. Entries are assigned to the respective consumer app via
|
|
10
|
-
// `projectKey`.
|
|
11
|
-
//
|
|
12
|
-
// Versioning pattern identical to PlanVersion:
|
|
13
|
-
// - at most 1 draft (publishedAt IS NULL) per identity — constraint via a
|
|
14
|
-
// partial unique index in the migration.
|
|
15
|
-
// - multiple published versions, monotonically incremental `version`.
|
|
16
|
-
// - `supersededAt` marks "no longer actively marketed"; existing bookings
|
|
17
|
-
// stay on the committed version (contractual plan guarantee P1).
|
|
18
|
-
//
|
|
19
|
-
// Naming note: GESCHAEFTSTYP_SPEC §3.1 partly speaks of `BundleDefinition`
|
|
20
|
-
// (with suffix) and `BusinessType` (without) — that is asymmetric. SPEC_V2
|
|
21
|
-
// unified this to a consistent `Bundle` / `BusinessType` (each without the
|
|
22
|
-
// `Definition` suffix). This file follows the V2 convention.
|
|
23
|
-
|
|
24
|
-
// -----------------------------------------------------------------------------
|
|
25
|
-
// Bundle — reusable component of features + quotas + pricing.
|
|
26
|
-
//
|
|
27
|
-
// Tenants do **not** book bundles directly; they are a SuperAdmin maintenance
|
|
28
|
-
// shell that is referenced in BusinessType versions (m:n via
|
|
29
|
-
// BusinessTypeBundle). Root entity without content — the purchasable fields
|
|
30
|
-
// (features, quotas, pricing) live on BundleVersion.
|
|
31
|
-
// -----------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
model Bundle {
|
|
34
|
-
id String @id @default(uuid())
|
|
35
|
-
projectKey String // e.g. 'clubapp' | 'demoapp' | …
|
|
36
|
-
bundleKey String // SCREAMING_SNAKE_CASE, e.g. 'SPORT', 'ACCOUNTING', 'EVERSEND_PEPPOL'
|
|
37
|
-
label String
|
|
38
|
-
description String?
|
|
39
|
-
icon String?
|
|
40
|
-
sortOrder Int @default(0)
|
|
41
|
-
|
|
42
|
-
// Locale translations { "en": { label, description }, … }. SPEC_V2 §6.4.
|
|
43
|
-
i18n Json @default("{}")
|
|
44
|
-
|
|
45
|
-
createdAt DateTime @default(now())
|
|
46
|
-
updatedAt DateTime @updatedAt
|
|
47
|
-
deletedAt DateTime?
|
|
48
|
-
|
|
49
|
-
versions BundleVersion[]
|
|
50
|
-
|
|
51
|
-
@@unique([projectKey, bundleKey])
|
|
52
|
-
@@index([projectKey, deletedAt])
|
|
53
|
-
@@map("bundles")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// -----------------------------------------------------------------------------
|
|
57
|
-
// BundleVersion — versioned composition (features, quotas, pricing).
|
|
58
|
-
// `features` is FeatureKey[] (references Discovery / FeatureCatalogEntry).
|
|
59
|
-
// `quotas` is Record<QuotaKey, number>; -1 = unlimited; missing key = 0.
|
|
60
|
-
// `compatibility` is BundleCompatibility (`businessTypeKeys?` /
|
|
61
|
-
// `planIds?` whitelists). Both empty = usable everywhere.
|
|
62
|
-
// `pricingOverrides` is Array<BundlePricingOverride> for context-dependent
|
|
63
|
-
// prices (see GESCHAEFTSTYP_SPEC §6.1, "most-specific wins").
|
|
64
|
-
// -----------------------------------------------------------------------------
|
|
65
|
-
|
|
66
|
-
model BundleVersion {
|
|
67
|
-
id String @id @default(uuid())
|
|
68
|
-
bundleId String
|
|
69
|
-
version Int
|
|
70
|
-
baseVersionId String? // predecessor; null for v1
|
|
71
|
-
|
|
72
|
-
features Json // FeatureKey[]
|
|
73
|
-
quotas Json @default("{}") // Record<QuotaKey, number>
|
|
74
|
-
compatibility Json @default("{}") // BundleCompatibility
|
|
75
|
-
pricingOverrides Json @default("[]") // BundlePricingOverride[]
|
|
76
|
-
|
|
77
|
-
monthlyNet Decimal? @db.Decimal(10, 2) // default price; null = only via override
|
|
78
|
-
yearlyNet Decimal? @db.Decimal(10, 2)
|
|
79
|
-
marketed Boolean @default(true)
|
|
80
|
-
|
|
81
|
-
publishedAt DateTime?
|
|
82
|
-
supersededAt DateTime?
|
|
83
|
-
publishedChanges Json? // VersionChange[] — diff to predecessor version
|
|
84
|
-
changeNote String @default("")
|
|
85
|
-
nonRegressive Boolean @default(true)
|
|
86
|
-
|
|
87
|
-
createdByUserId String?
|
|
88
|
-
publishedByUserId String?
|
|
89
|
-
|
|
90
|
-
createdAt DateTime @default(now())
|
|
91
|
-
updatedAt DateTime @updatedAt
|
|
92
|
-
|
|
93
|
-
bundle Bundle @relation(fields: [bundleId], references: [id], onDelete: Cascade)
|
|
94
|
-
baseVersion BundleVersion? @relation("BundleVersionLineage", fields: [baseVersionId], references: [id])
|
|
95
|
-
derivedVersions BundleVersion[] @relation("BundleVersionLineage")
|
|
96
|
-
businessTypeBundles BusinessTypeBundle[]
|
|
97
|
-
|
|
98
|
-
@@unique([bundleId, version])
|
|
99
|
-
@@index([bundleId, supersededAt])
|
|
100
|
-
@@index([bundleId, publishedAt])
|
|
101
|
-
@@map("bundle_versions")
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// -----------------------------------------------------------------------------
|
|
105
|
-
// BusinessType — business vertical (club type, industry variant).
|
|
106
|
-
//
|
|
107
|
-
// Examples for a club app: SPORT_VEREIN, MOSCHEE_GEMEINDE,
|
|
108
|
-
// KIRCHEN_GEMEINDE, SOZIAL_TRAEGER. Example for a commerce app: AUTO_HANDEL.
|
|
109
|
-
// At **most one** BusinessType is referenced per subscription (via the
|
|
110
|
-
// associated BusinessTypeVersion). Multiple bundles at once = a dedicated
|
|
111
|
-
// BusinessType that composes the bundles internally.
|
|
112
|
-
// -----------------------------------------------------------------------------
|
|
113
|
-
|
|
114
|
-
model BusinessType {
|
|
115
|
-
id String @id @default(uuid())
|
|
116
|
-
projectKey String
|
|
117
|
-
businessTypeKey String // SCREAMING_SNAKE_CASE
|
|
118
|
-
label String
|
|
119
|
-
description String?
|
|
120
|
-
icon String?
|
|
121
|
-
sortOrder Int @default(0)
|
|
122
|
-
|
|
123
|
-
createdAt DateTime @default(now())
|
|
124
|
-
updatedAt DateTime @updatedAt
|
|
125
|
-
deletedAt DateTime?
|
|
126
|
-
|
|
127
|
-
versions BusinessTypeVersion[]
|
|
128
|
-
|
|
129
|
-
@@unique([projectKey, businessTypeKey])
|
|
130
|
-
@@index([projectKey, deletedAt])
|
|
131
|
-
@@map("business_types")
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// -----------------------------------------------------------------------------
|
|
135
|
-
// BusinessTypeVersion — versioned composition of referenced bundles.
|
|
136
|
-
// `quotaOverrides` is Partial<Record<QuotaKey, number>>; missing key →
|
|
137
|
-
// Σ(bundle quotas), set key → replaces the sum (-1 = unlimited).
|
|
138
|
-
// `monthlyNet` null → effective price = Σ(bundle prices). Set → override.
|
|
139
|
-
// -----------------------------------------------------------------------------
|
|
140
|
-
|
|
141
|
-
model BusinessTypeVersion {
|
|
142
|
-
id String @id @default(uuid())
|
|
143
|
-
businessTypeId String
|
|
144
|
-
version Int
|
|
145
|
-
baseVersionId String?
|
|
146
|
-
|
|
147
|
-
quotaOverrides Json @default("{}") // Partial<Record<QuotaKey, number>>
|
|
148
|
-
|
|
149
|
-
monthlyNet Decimal? @db.Decimal(10, 2) // null = Σ(bundle prices)
|
|
150
|
-
yearlyNet Decimal? @db.Decimal(10, 2)
|
|
151
|
-
marketed Boolean @default(true)
|
|
152
|
-
|
|
153
|
-
publishedAt DateTime?
|
|
154
|
-
supersededAt DateTime?
|
|
155
|
-
publishedChanges Json?
|
|
156
|
-
changeNote String @default("")
|
|
157
|
-
nonRegressive Boolean @default(true)
|
|
158
|
-
|
|
159
|
-
createdByUserId String?
|
|
160
|
-
publishedByUserId String?
|
|
161
|
-
|
|
162
|
-
createdAt DateTime @default(now())
|
|
163
|
-
updatedAt DateTime @updatedAt
|
|
164
|
-
|
|
165
|
-
businessType BusinessType @relation(fields: [businessTypeId], references: [id], onDelete: Cascade)
|
|
166
|
-
baseVersion BusinessTypeVersion? @relation("BusinessTypeVersionLineage", fields: [baseVersionId], references: [id])
|
|
167
|
-
derivedVersions BusinessTypeVersion[] @relation("BusinessTypeVersionLineage")
|
|
168
|
-
bundles BusinessTypeBundle[]
|
|
169
|
-
|
|
170
|
-
// Opposite side of Subscription.businessTypeVersion (01-subscription.prisma,
|
|
171
|
-
// M5). Subscription carries `businessTypeVersionId String?` plus the CHECK
|
|
172
|
-
// constraint "at least one of planVersionId / businessTypeVersionId set"
|
|
173
|
-
// (sql/constraints.postgres.sql).
|
|
174
|
-
subscriptionsCurrent Subscription[] @relation("SubscriptionBusinessTypeVersion")
|
|
175
|
-
|
|
176
|
-
@@unique([businessTypeId, version])
|
|
177
|
-
@@index([businessTypeId, supersededAt])
|
|
178
|
-
@@index([businessTypeId, publishedAt])
|
|
179
|
-
@@map("business_type_versions")
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// -----------------------------------------------------------------------------
|
|
183
|
-
// BusinessTypeBundle — m:n junction between BusinessTypeVersion and
|
|
184
|
-
// BundleVersion. Stores the *concrete* BundleVersion (not just the root), so
|
|
185
|
-
// that a published BusinessType stays deterministic — even when the bundle
|
|
186
|
-
// later gets a newer version.
|
|
187
|
-
// onDelete: Restrict for bundleVersion prevents deleting a BundleVersion that
|
|
188
|
-
// is still referenced in an active BusinessTypeVersion (additionally a hard
|
|
189
|
-
// block in the publish strict check).
|
|
190
|
-
// -----------------------------------------------------------------------------
|
|
191
|
-
|
|
192
|
-
model BusinessTypeBundle {
|
|
193
|
-
businessTypeVersionId String
|
|
194
|
-
bundleVersionId String
|
|
195
|
-
sortOrder Int @default(0)
|
|
196
|
-
|
|
197
|
-
businessTypeVersion BusinessTypeVersion @relation(fields: [businessTypeVersionId], references: [id], onDelete: Cascade)
|
|
198
|
-
bundleVersion BundleVersion @relation(fields: [bundleVersionId], references: [id], onDelete: Restrict)
|
|
199
|
-
|
|
200
|
-
@@id([businessTypeVersionId, bundleVersionId])
|
|
201
|
-
@@index([bundleVersionId])
|
|
202
|
-
@@map("business_type_bundles")
|
|
203
|
-
}
|