@saasicat/spec 1.0.0-rc.7 → 1.0.0-rc.9
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
CHANGED
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "PlanCatalog",
|
|
5
5
|
"description": "Language-neutral definition of a plan catalog + app identity for a SaaS app. Maintained by the consumer as a YAML/JSON file (`config/saas.yaml`) and mirrored into platform tables at backend boot.",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"required": ["schemaVersion", "app", "currency", "vatRate"],
|
|
7
|
+
"required": ["schemaVersion", "app", "currency", "vatRate", "tenantBilling"],
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"properties": {
|
|
10
10
|
"schemaVersion": {
|
|
@@ -54,6 +54,54 @@
|
|
|
54
54
|
"maximum": 100,
|
|
55
55
|
"description": "VAT rate in percent. Required (even when 0)."
|
|
56
56
|
},
|
|
57
|
+
"tenantBilling": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"description": "Commercial settings for the tenant-facing self-service routes. Required, and required member by member: every one of these has a money or a legal consequence, and a value left out is still a decision — just an invisible one. The file is read at boot, so a change lands on the next restart.",
|
|
61
|
+
"required": ["cancellationNoticeDays", "selfServiceBlockedPlans"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"cancellationNoticeDays": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"description": "Days of notice before a term ends, one number per rhythm. A cancellation declared after the window has closed takes effect at the first period end that actually serves the notice. Two numbers rather than one because a monthly and a yearly contract cannot share a notice period: a fortnight is unusual on a year, and three months is void against a consumer on a month. No ceiling is enforced — §309 Nr. 9 BGB caps it at one month in German consumer contracts, and an installation serving businesses is not bound by that.",
|
|
67
|
+
"required": ["monthly", "yearly"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"monthly": {
|
|
70
|
+
"type": "integer",
|
|
71
|
+
"minimum": 0,
|
|
72
|
+
"description": "Notice days on a monthly rhythm. 0 means there is no door to be shut out of: a cancellation on the last day still lands at the term end."
|
|
73
|
+
},
|
|
74
|
+
"yearly": {
|
|
75
|
+
"type": "integer",
|
|
76
|
+
"minimum": 0,
|
|
77
|
+
"description": "Notice days on a yearly rhythm. Raise it and the cut is hard — a declaration made after the window lands one full year later."
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"selfServiceBlockedPlans": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"description": "Plans a tenant may not move to or away from without talking to sales. Both lists are required and may be empty; an empty list is the explicit statement that self-service reaches every plan.",
|
|
85
|
+
"required": ["asTarget", "asSource"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"asTarget": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": {
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"description": "Plan IDs that may not be selected via self-service — typically ENTERPRISE, which only a special contract activates."
|
|
93
|
+
},
|
|
94
|
+
"asSource": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": {
|
|
97
|
+
"type": "string"
|
|
98
|
+
},
|
|
99
|
+
"description": "Plan IDs that may not be left via self-service — typically an active special contract, whose change goes through sales."
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
57
105
|
"marketing": {
|
|
58
106
|
"type": "object",
|
|
59
107
|
"additionalProperties": false,
|
|
@@ -62,7 +110,10 @@
|
|
|
62
110
|
"properties": {
|
|
63
111
|
"availableLocales": {
|
|
64
112
|
"type": "array",
|
|
65
|
-
"items": {
|
|
113
|
+
"items": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"pattern": "^[a-z]{2}(-[A-Z]{2})?$"
|
|
116
|
+
},
|
|
66
117
|
"minItems": 1,
|
|
67
118
|
"uniqueItems": true,
|
|
68
119
|
"description": "Allowed language pool the app may market. First = default locale. From it the SuperAdmin activates a subset in the marketing catalog (LocaleManager)."
|
|
@@ -71,16 +122,19 @@
|
|
|
71
122
|
},
|
|
72
123
|
"features": {
|
|
73
124
|
"type": "array",
|
|
74
|
-
"items": {
|
|
125
|
+
"items": {
|
|
126
|
+
"$ref": "#/$defs/FeatureDef"
|
|
127
|
+
},
|
|
75
128
|
"description": "Master list of all feature flags of the project. Plans may only reference keys declared here."
|
|
76
129
|
},
|
|
77
130
|
"plans": {
|
|
78
131
|
"type": "array",
|
|
79
|
-
"items": {
|
|
132
|
+
"items": {
|
|
133
|
+
"$ref": "#/$defs/PlanDef"
|
|
134
|
+
},
|
|
80
135
|
"description": "Optional. When omitted, plans come exclusively from the AdminUI / DB."
|
|
81
136
|
}
|
|
82
137
|
},
|
|
83
|
-
|
|
84
138
|
"$defs": {
|
|
85
139
|
"FeatureDef": {
|
|
86
140
|
"type": "object",
|
|
@@ -92,8 +146,13 @@
|
|
|
92
146
|
"pattern": "^[A-Z][A-Z0-9_]+$",
|
|
93
147
|
"description": "SCREAMING_SNAKE_CASE; unique per catalog."
|
|
94
148
|
},
|
|
95
|
-
"label": {
|
|
96
|
-
|
|
149
|
+
"label": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"minLength": 1
|
|
152
|
+
},
|
|
153
|
+
"icon": {
|
|
154
|
+
"type": "string"
|
|
155
|
+
},
|
|
97
156
|
"tier": {
|
|
98
157
|
"type": "string",
|
|
99
158
|
"description": "Optional logical group. Convention: CORE / ADVANCED / PRO / BUSINESS / ENTERPRISE_ONLY."
|
|
@@ -105,7 +164,6 @@
|
|
|
105
164
|
}
|
|
106
165
|
}
|
|
107
166
|
},
|
|
108
|
-
|
|
109
167
|
"PlanDef": {
|
|
110
168
|
"type": "object",
|
|
111
169
|
"required": ["id", "quotas", "features"],
|
|
@@ -116,8 +174,13 @@
|
|
|
116
174
|
"pattern": "^[A-Z][A-Z0-9_]+$",
|
|
117
175
|
"description": "Plan ID. Freely chosen by the consumer (BASIC, STANDARD, ...). Ends up as a string in Subscription.plan."
|
|
118
176
|
},
|
|
119
|
-
"name": {
|
|
120
|
-
|
|
177
|
+
"name": {
|
|
178
|
+
"type": "string",
|
|
179
|
+
"minLength": 1
|
|
180
|
+
},
|
|
181
|
+
"tagline": {
|
|
182
|
+
"type": "string"
|
|
183
|
+
},
|
|
121
184
|
"marketed": {
|
|
122
185
|
"type": "boolean",
|
|
123
186
|
"default": true,
|
|
@@ -151,7 +214,10 @@
|
|
|
151
214
|
},
|
|
152
215
|
"features": {
|
|
153
216
|
"type": "array",
|
|
154
|
-
"items": {
|
|
217
|
+
"items": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"pattern": "^[A-Z][A-Z0-9_]+$"
|
|
220
|
+
},
|
|
155
221
|
"uniqueItems": true,
|
|
156
222
|
"description": "List of included feature keys; all must exist in features[].key."
|
|
157
223
|
}
|