@spaceinvoices/react-ui 0.4.0 → 0.4.1

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/cli/dist/index.js CHANGED
@@ -870,7 +870,7 @@ async function list(options = {}) {
870
870
 
871
871
  // cli/src/index.ts
872
872
  var program = new Command();
873
- program.name("spaceinvoices-ui").description("CLI for adding Space Invoices React UI components to your project").version("0.4.0");
873
+ program.name("spaceinvoices-ui").description("CLI for adding Space Invoices React UI components to your project").version("0.4.1");
874
874
  program.option("--local <path>", "Use local registry from specified path (for development)");
875
875
  program.command("init").description("Initialize Space Invoices UI in your project").option("-y, --yes", "Skip prompts and use defaults").option("-f, --force", "Overwrite existing configuration").option("--cwd <path>", "Working directory (defaults to current directory)").action(async (options) => {
876
876
  const globalOpts = program.opts();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spaceinvoices/react-ui",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.4.1",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "description": "Space Invoices UI components - copy-paste distribution with CLI support",
package/registry.json CHANGED
@@ -518,35 +518,6 @@
518
518
  "providers": ["sdk-provider"],
519
519
  "utils": ["translation"]
520
520
  },
521
- "invoices/fiscalization-status-card": {
522
- "name": "Fiscalization Status Card",
523
- "category": "feature",
524
- "files": [
525
- "components/invoices/view/fiscalization-status-card.tsx"
526
- ],
527
- "dependencies": ["ui/card", "ui/badge", "ui/button"],
528
- "providers": ["sdk-provider"],
529
- "utils": ["translation"]
530
- },
531
- "documents/document-activities-list": {
532
- "name": "Document Activities List",
533
- "category": "feature",
534
- "files": [
535
- "components/documents/view/document-activities-list.tsx",
536
- "components/documents/view/locales/de.ts",
537
- "components/documents/view/locales/es.ts",
538
- "components/documents/view/locales/fr.ts",
539
- "components/documents/view/locales/hr.ts",
540
- "components/documents/view/locales/it.ts",
541
- "components/documents/view/locales/nl.ts",
542
- "components/documents/view/locales/pl.ts",
543
- "components/documents/view/locales/pt.ts",
544
- "components/documents/view/locales/sl.ts"
545
- ],
546
- "dependencies": ["ui/card", "ui/badge", "ui/button"],
547
- "providers": ["sdk-provider"],
548
- "utils": ["translation"]
549
- },
550
521
  "dashboard/collection-rate-card": {
551
522
  "name": "Collection Rate Card",
552
523
  "category": "feature",
@@ -307,6 +307,7 @@ export const PremisesManagementSection: FC<PremisesManagementSectionProps> = ({
307
307
  entity={entity}
308
308
  type={registerType}
309
309
  t={t}
310
+ existingPremiseNames={premises.map((p) => p.business_premise_name)}
310
311
  onSuccess={() => {
311
312
  setRegisterDialogOpen(false);
312
313
  onSuccess?.();
@@ -41,6 +41,7 @@ interface RegisterPremiseDialogProps {
41
41
  entity: Entity;
42
42
  type: "real-estate" | "movable";
43
43
  t: (key: string) => string;
44
+ existingPremiseNames?: string[];
44
45
  onSuccess?: () => void;
45
46
  onError?: (error: unknown) => void;
46
47
  }
@@ -51,6 +52,7 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
51
52
  entity,
52
53
  type,
53
54
  t,
55
+ existingPremiseNames = [],
54
56
  onSuccess,
55
57
  onError,
56
58
  }) => {
@@ -63,8 +65,8 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
63
65
  business_premise_name: "",
64
66
  real_estate: {
65
67
  cadastral_number: "",
66
- building_number: "",
67
- building_section: "",
68
+ building_number: "0",
69
+ building_section: "0",
68
70
  community: "",
69
71
  city: "",
70
72
  street: "",
@@ -86,12 +88,21 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
86
88
  },
87
89
  });
88
90
 
91
+ const handleMutationError = (error: unknown, form: typeof realEstateForm | typeof movableForm) => {
92
+ const err = error as { status?: number; data?: { message?: string } };
93
+ if (err?.status === 409 && err?.data?.message) {
94
+ form.setError("business_premise_name", { message: err.data.message });
95
+ } else {
96
+ onError?.(error);
97
+ }
98
+ };
99
+
89
100
  const { mutate: registerRealEstate, isPending: isRealEstatePending } = useRegisterRealEstatePremise({
90
101
  onSuccess: () => {
91
102
  realEstateForm.reset();
92
103
  onSuccess?.();
93
104
  },
94
- onError,
105
+ onError: (error) => handleMutationError(error, realEstateForm),
95
106
  });
96
107
 
97
108
  const { mutate: registerMovable, isPending: isMovablePending } = useRegisterMovablePremise({
@@ -99,10 +110,18 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
99
110
  movableForm.reset();
100
111
  onSuccess?.();
101
112
  },
102
- onError,
113
+ onError: (error) => handleMutationError(error, movableForm),
103
114
  });
104
115
 
116
+ const isDuplicateName = (name: string) => existingPremiseNames.some((n) => n.toUpperCase() === name.toUpperCase());
117
+
105
118
  const handleRealEstateSubmit = (data: RealEstatePremiseForm) => {
119
+ if (isDuplicateName(data.business_premise_name)) {
120
+ realEstateForm.setError("business_premise_name", {
121
+ message: t("A premise with this name already exists"),
122
+ });
123
+ return;
124
+ }
106
125
  registerRealEstate({
107
126
  entityId: entity.id,
108
127
  data,
@@ -110,6 +129,12 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
110
129
  };
111
130
 
112
131
  const handleMovableSubmit = (data: MovablePremiseForm) => {
132
+ if (isDuplicateName(data.business_premise_name)) {
133
+ movableForm.setError("business_premise_name", {
134
+ message: t("A premise with this name already exists"),
135
+ });
136
+ return;
137
+ }
113
138
  registerMovable({
114
139
  entityId: entity.id,
115
140
  data,
@@ -172,18 +197,11 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
172
197
  name="real_estate.building_number"
173
198
  render={({ field }) => (
174
199
  <FormItem>
175
- <FormLabel>{t("Building Number")}</FormLabel>
200
+ <FormLabel>{t("Building Number")} *</FormLabel>
176
201
  <FormControl>
177
- <Input
178
- type="text"
179
- inputMode="numeric"
180
- pattern="[0-9]*"
181
- placeholder="456"
182
- {...field}
183
- value={field.value || ""}
184
- />
202
+ <Input type="text" inputMode="numeric" pattern="[0-9]*" placeholder="456" {...field} />
185
203
  </FormControl>
186
- <FormDescription className="text-xs">{t("Must be numeric (optional)")}</FormDescription>
204
+ <FormDescription className="text-xs">{t("Numeric, use 0 if not applicable")}</FormDescription>
187
205
  <FormMessage />
188
206
  </FormItem>
189
207
  )}
@@ -193,18 +211,11 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
193
211
  name="real_estate.building_section"
194
212
  render={({ field }) => (
195
213
  <FormItem>
196
- <FormLabel>{t("Building Section")}</FormLabel>
214
+ <FormLabel>{t("Building Section")} *</FormLabel>
197
215
  <FormControl>
198
- <Input
199
- type="text"
200
- inputMode="numeric"
201
- pattern="[0-9]*"
202
- placeholder="1"
203
- {...field}
204
- value={field.value || ""}
205
- />
216
+ <Input type="text" inputMode="numeric" pattern="[0-9]*" placeholder="1" {...field} />
206
217
  </FormControl>
207
- <FormDescription className="text-xs">{t("Must be numeric (optional)")}</FormDescription>
218
+ <FormDescription className="text-xs">{t("Numeric, use 0 if not applicable")}</FormDescription>
208
219
  <FormMessage />
209
220
  </FormItem>
210
221
  )}
@@ -233,9 +244,9 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
233
244
  name="real_estate.street"
234
245
  render={({ field }) => (
235
246
  <FormItem>
236
- <FormLabel>{t("Street")}</FormLabel>
247
+ <FormLabel>{t("Street")} *</FormLabel>
237
248
  <FormControl>
238
- <Input placeholder="Dunajska cesta" {...field} value={field.value || ""} />
249
+ <Input placeholder="Dunajska cesta" {...field} />
239
250
  </FormControl>
240
251
  <FormMessage />
241
252
  </FormItem>
@@ -248,9 +259,9 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
248
259
  name="real_estate.house_number"
249
260
  render={({ field }) => (
250
261
  <FormItem>
251
- <FormLabel>{t("House Number")}</FormLabel>
262
+ <FormLabel>{t("House Number")} *</FormLabel>
252
263
  <FormControl>
253
- <Input placeholder="22" {...field} value={field.value || ""} />
264
+ <Input placeholder="22" {...field} />
254
265
  </FormControl>
255
266
  <FormMessage />
256
267
  </FormItem>
@@ -277,9 +288,9 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
277
288
  name="real_estate.city"
278
289
  render={({ field }) => (
279
290
  <FormItem>
280
- <FormLabel>{t("City")}</FormLabel>
291
+ <FormLabel>{t("City")} *</FormLabel>
281
292
  <FormControl>
282
- <Input placeholder="Ljubljana" {...field} value={field.value || ""} />
293
+ <Input placeholder="Ljubljana" {...field} />
283
294
  </FormControl>
284
295
  <FormMessage />
285
296
  </FormItem>
@@ -290,10 +301,11 @@ export const RegisterPremiseDialog: FC<RegisterPremiseDialogProps> = ({
290
301
  name="real_estate.postal_code"
291
302
  render={({ field }) => (
292
303
  <FormItem>
293
- <FormLabel>{t("Postal Code")}</FormLabel>
304
+ <FormLabel>{t("Postal Code")} *</FormLabel>
294
305
  <FormControl>
295
- <Input placeholder="1000" {...field} value={field.value || ""} />
306
+ <Input placeholder="1000" {...field} />
296
307
  </FormControl>
308
+ <FormDescription className="text-xs">{t("Exactly 4 digits")}</FormDescription>
297
309
  <FormMessage />
298
310
  </FormItem>
299
311
  )}
@@ -86,7 +86,8 @@ async function main() {
86
86
  for (const ref of constRefs) {
87
87
  const cleanRef = ref.replace(/\.(optional|nullable)\(\)/, "");
88
88
  // Check if it's actually a schema definition in the file
89
- if (fullContent.includes(`const ${cleanRef} = z.`)) {
89
+ // Handle both `const Foo = z.object(...)` and `const Foo = z\n .object(...)`
90
+ if (fullContent.includes(`const ${cleanRef} = z.`) || fullContent.includes(`const ${cleanRef} = z\n`)) {
90
91
  dependencies.push(cleanRef);
91
92
  }
92
93
  }
@@ -244,6 +245,17 @@ ${exports}
244
245
  await fs.writeFile(path.join(SCHEMAS_DIR, `${groupName}.ts`), schemaContent);
245
246
  }
246
247
 
248
+ // Add manual re-exports that can't be auto-generated
249
+ // Credit note create uses the same body as invoice create
250
+ const creditNoteFile = path.join(SCHEMAS_DIR, "creditnote.ts");
251
+ const creditNoteContent = await fs.readFile(creditNoteFile, "utf-8");
252
+ if (!creditNoteContent.includes("createCreditNoteSchema")) {
253
+ await fs.appendFile(
254
+ creditNoteFile,
255
+ "\n// Re-export invoice create schema as credit note create schema (same body structure)\nexport { createInvoiceSchema as createCreditNoteSchema, type CreateInvoiceSchema as CreateCreditNoteSchema } from './invoice';\n",
256
+ );
257
+ }
258
+
247
259
  // Create index file
248
260
  const indexContent = `/**
249
261
  * This file was automatically generated using 'bun generate-schemas'.
@@ -8,6 +8,17 @@ import { z } from 'zod';
8
8
 
9
9
  // Schemas for advanceinvoice endpoints
10
10
 
11
+ // Dependency schema for advanceinvoice
12
+ const CreateDocumentPayment = z.object({
13
+ amount: z.number().gt(0).optional(),
14
+ type: z.string().max(20),
15
+ date: z.string().optional(),
16
+ reference: z.union([z.string(), z.null()]).optional(),
17
+ note: z.union([z.string(), z.null()]).optional(),
18
+ metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
19
+ });
20
+
21
+
11
22
  // Dependency schema for advanceinvoice
12
23
  const LineDiscount = z.object({
13
24
  value: z.number().gte(0),
@@ -15,6 +26,56 @@ const LineDiscount = z.object({
15
26
  });
16
27
 
17
28
 
29
+ // Dependency schema for advanceinvoice
30
+ const DocumentItemTax = z
31
+ .object({
32
+ rate: z.number(),
33
+ tax_id: z.string(),
34
+ classification: z.string(),
35
+ reverse_charge: z.boolean(),
36
+ amount: z.number(),
37
+ })
38
+ .partial();
39
+
40
+
41
+ // Dependency schema for advanceinvoice
42
+ const DocumentEntity = z
43
+ .object({
44
+ name: z.union([z.string(), z.null()]),
45
+ email: z.union([z.string(), z.null()]),
46
+ address: z.union([z.string(), z.null()]),
47
+ address_2: z.union([z.string(), z.null()]),
48
+ post_code: z.union([z.string(), z.null()]),
49
+ city: z.union([z.string(), z.null()]),
50
+ state: z.union([z.string(), z.null()]),
51
+ country: z.union([z.string(), z.null()]),
52
+ country_code: z.union([z.string(), z.null()]),
53
+ tax_number: z.union([z.string(), z.null()]),
54
+ tax_number_2: z.union([z.string(), z.null()]),
55
+ company_number: z.union([z.string(), z.null()]),
56
+ bank_account: z.union([
57
+ z
58
+ .object({
59
+ type: z
60
+ .enum(["iban", "us_domestic", "uk_domestic", "other"])
61
+ .default("iban"),
62
+ name: z.string(),
63
+ bank_name: z.string(),
64
+ iban: z.string(),
65
+ account_number: z.string(),
66
+ bic: z.string(),
67
+ routing_number: z.string(),
68
+ sort_code: z.string(),
69
+ })
70
+ .partial()
71
+ .passthrough(),
72
+ z.null(),
73
+ ]),
74
+ })
75
+ .partial()
76
+ .passthrough();
77
+
78
+
18
79
  // Schema for create advanceinvoice operation
19
80
  const createAdvanceInvoiceSchemaDefinition = z.object({
20
81
  is_draft: z.boolean().optional(),
@@ -22,84 +83,17 @@ const createAdvanceInvoiceSchemaDefinition = z.object({
22
83
  .string()
23
84
  .regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/)
24
85
  .optional(),
25
- issuer: z
26
- .object({
27
- name: z.union([z.string(), z.null()]),
28
- email: z.union([z.string(), z.null()]),
29
- address: z.union([z.string(), z.null()]),
30
- address_2: z.union([z.string(), z.null()]),
31
- post_code: z.union([z.string(), z.null()]),
32
- city: z.union([z.string(), z.null()]),
33
- state: z.union([z.string(), z.null()]),
34
- country: z.union([z.string(), z.null()]),
35
- country_code: z.union([z.string(), z.null()]),
36
- tax_number: z.union([z.string(), z.null()]),
37
- tax_number_2: z.union([z.string(), z.null()]),
38
- company_number: z.union([z.string(), z.null()]),
39
- bank_account: z.union([
40
- z
41
- .object({
42
- type: z
43
- .enum(["iban", "us_domestic", "uk_domestic", "other"])
44
- .default("iban"),
45
- name: z.string(),
46
- bank_name: z.string(),
47
- iban: z.string(),
48
- account_number: z.string(),
49
- bic: z.string(),
50
- routing_number: z.string(),
51
- sort_code: z.string(),
52
- })
53
- .partial()
54
- .passthrough(),
55
- z.null(),
56
- ]),
57
- })
58
- .partial()
59
- .passthrough()
60
- .optional(),
86
+ issuer: DocumentEntity.optional(),
61
87
  customer_id: z.union([z.string(), z.null()]).optional(),
62
- customer: z
63
- .union([
88
+ customer: DocumentEntity.and(
89
+ z.union([
64
90
  z
65
- .object({
66
- name: z.union([z.string(), z.null()]),
67
- email: z.union([z.string(), z.null()]),
68
- address: z.union([z.string(), z.null()]),
69
- address_2: z.union([z.string(), z.null()]),
70
- post_code: z.union([z.string(), z.null()]),
71
- city: z.union([z.string(), z.null()]),
72
- state: z.union([z.string(), z.null()]),
73
- country: z.union([z.string(), z.null()]),
74
- country_code: z.union([z.string(), z.null()]),
75
- tax_number: z.union([z.string(), z.null()]),
76
- tax_number_2: z.union([z.string(), z.null()]),
77
- company_number: z.union([z.string(), z.null()]),
78
- bank_account: z.union([
79
- z
80
- .object({
81
- type: z
82
- .enum(["iban", "us_domestic", "uk_domestic", "other"])
83
- .default("iban"),
84
- name: z.string(),
85
- bank_name: z.string(),
86
- iban: z.string(),
87
- account_number: z.string(),
88
- bic: z.string(),
89
- routing_number: z.string(),
90
- sort_code: z.string(),
91
- })
92
- .partial()
93
- .passthrough(),
94
- z.null(),
95
- ]),
96
- save_customer: z.boolean().default(true),
97
- })
91
+ .object({ save_customer: z.boolean().default(true) })
98
92
  .partial()
99
93
  .passthrough(),
100
94
  z.null(),
101
95
  ])
102
- .optional(),
96
+ ).optional(),
103
97
  note: z.union([z.string(), z.null()]).optional(),
104
98
  tax_clause: z.union([z.string(), z.null()]).optional(),
105
99
  currency_code: z.string().max(3).optional(),
@@ -116,19 +110,7 @@ const createAdvanceInvoiceSchemaDefinition = z.object({
116
110
  gross_price: z.number().optional(),
117
111
  quantity: z.number().gte(-140737488355328).lte(140737488355327),
118
112
  unit: z.union([z.string(), z.null()]).optional(),
119
- taxes: z
120
- .array(
121
- z
122
- .object({
123
- rate: z.number(),
124
- tax_id: z.string(),
125
- classification: z.string(),
126
- reverse_charge: z.boolean(),
127
- amount: z.number(),
128
- })
129
- .partial()
130
- )
131
- .optional(),
113
+ taxes: z.array(DocumentItemTax).optional(),
132
114
  discounts: z.array(LineDiscount).max(5).optional(),
133
115
  metadata: z
134
116
  .union([
@@ -146,21 +128,7 @@ const createAdvanceInvoiceSchemaDefinition = z.object({
146
128
  )
147
129
  .min(1),
148
130
  linked_documents: z.array(z.string().min(1)).optional(),
149
- payments: z
150
- .union([
151
- z.array(
152
- z.object({
153
- amount: z.number().gt(0).optional(),
154
- type: z.string().max(20),
155
- date: z.string().optional(),
156
- reference: z.union([z.string(), z.null()]).optional(),
157
- note: z.union([z.string(), z.null()]).optional(),
158
- metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
159
- })
160
- ),
161
- z.null(),
162
- ])
163
- .optional(),
131
+ payments: z.union([z.array(CreateDocumentPayment), z.null()]).optional(),
164
132
  furs: z
165
133
  .union([
166
134
  z
@@ -220,81 +188,17 @@ const updateAdvanceInvoiceSchemaDefinition = z
220
188
  date: z
221
189
  .string()
222
190
  .regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/),
223
- issuer: z
224
- .object({
225
- name: z.union([z.string(), z.null()]),
226
- email: z.union([z.string(), z.null()]),
227
- address: z.union([z.string(), z.null()]),
228
- address_2: z.union([z.string(), z.null()]),
229
- post_code: z.union([z.string(), z.null()]),
230
- city: z.union([z.string(), z.null()]),
231
- state: z.union([z.string(), z.null()]),
232
- country: z.union([z.string(), z.null()]),
233
- country_code: z.union([z.string(), z.null()]),
234
- tax_number: z.union([z.string(), z.null()]),
235
- tax_number_2: z.union([z.string(), z.null()]),
236
- company_number: z.union([z.string(), z.null()]),
237
- bank_account: z.union([
238
- z
239
- .object({
240
- type: z
241
- .enum(["iban", "us_domestic", "uk_domestic", "other"])
242
- .default("iban"),
243
- name: z.string(),
244
- bank_name: z.string(),
245
- iban: z.string(),
246
- account_number: z.string(),
247
- bic: z.string(),
248
- routing_number: z.string(),
249
- sort_code: z.string(),
250
- })
251
- .partial()
252
- .passthrough(),
253
- z.null(),
254
- ]),
255
- })
256
- .partial()
257
- .passthrough(),
191
+ issuer: DocumentEntity.and(z.unknown()),
258
192
  customer_id: z.union([z.string(), z.null()]),
259
- customer: z.union([
260
- z
261
- .object({
262
- name: z.union([z.string(), z.null()]),
263
- email: z.union([z.string(), z.null()]),
264
- address: z.union([z.string(), z.null()]),
265
- address_2: z.union([z.string(), z.null()]),
266
- post_code: z.union([z.string(), z.null()]),
267
- city: z.union([z.string(), z.null()]),
268
- state: z.union([z.string(), z.null()]),
269
- country: z.union([z.string(), z.null()]),
270
- country_code: z.union([z.string(), z.null()]),
271
- tax_number: z.union([z.string(), z.null()]),
272
- tax_number_2: z.union([z.string(), z.null()]),
273
- company_number: z.union([z.string(), z.null()]),
274
- bank_account: z.union([
275
- z
276
- .object({
277
- type: z
278
- .enum(["iban", "us_domestic", "uk_domestic", "other"])
279
- .default("iban"),
280
- name: z.string(),
281
- bank_name: z.string(),
282
- iban: z.string(),
283
- account_number: z.string(),
284
- bic: z.string(),
285
- routing_number: z.string(),
286
- sort_code: z.string(),
287
- })
288
- .partial()
289
- .passthrough(),
290
- z.null(),
291
- ]),
292
- save_customer: z.boolean().default(true),
293
- })
294
- .partial()
295
- .passthrough(),
296
- z.null(),
297
- ]),
193
+ customer: DocumentEntity.and(
194
+ z.union([
195
+ z
196
+ .object({ save_customer: z.boolean().default(true) })
197
+ .partial()
198
+ .passthrough(),
199
+ z.null(),
200
+ ])
201
+ ),
298
202
  items: z
299
203
  .array(
300
204
  z.object({
@@ -304,19 +208,7 @@ const updateAdvanceInvoiceSchemaDefinition = z
304
208
  gross_price: z.number().optional(),
305
209
  quantity: z.number().gte(-140737488355328).lte(140737488355327),
306
210
  unit: z.union([z.string(), z.null()]).optional(),
307
- taxes: z
308
- .array(
309
- z
310
- .object({
311
- rate: z.number(),
312
- tax_id: z.string(),
313
- classification: z.string(),
314
- reverse_charge: z.boolean(),
315
- amount: z.number(),
316
- })
317
- .partial()
318
- )
319
- .optional(),
211
+ taxes: z.array(DocumentItemTax).optional(),
320
212
  discounts: z.array(LineDiscount).max(5).optional(),
321
213
  metadata: z
322
214
  .union([