@spaceinvoices/react-ui 0.4.1 → 0.4.2

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.
Files changed (191) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/registry.json +25 -0
  4. package/src/components/advance-invoices/advance-invoices.hooks.ts +32 -2
  5. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +109 -4
  6. package/src/components/advance-invoices/create/locales/de.ts +2 -0
  7. package/src/components/advance-invoices/create/locales/es.ts +2 -0
  8. package/src/components/advance-invoices/create/locales/fr.ts +2 -0
  9. package/src/components/advance-invoices/create/locales/hr.ts +2 -0
  10. package/src/components/advance-invoices/create/locales/it.ts +2 -0
  11. package/src/components/advance-invoices/create/locales/nl.ts +2 -0
  12. package/src/components/advance-invoices/create/locales/pl.ts +2 -0
  13. package/src/components/advance-invoices/create/locales/pt.ts +2 -0
  14. package/src/components/advance-invoices/create/locales/sl.ts +2 -0
  15. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +17 -0
  16. package/src/components/advance-invoices/list/list-row-actions.tsx +3 -6
  17. package/src/components/advance-invoices/list/list-table.tsx +45 -2
  18. package/src/components/advance-invoices/list/locales/de.ts +3 -0
  19. package/src/components/advance-invoices/list/locales/en.ts +3 -0
  20. package/src/components/advance-invoices/list/locales/es.ts +3 -0
  21. package/src/components/advance-invoices/list/locales/fr.ts +3 -0
  22. package/src/components/advance-invoices/list/locales/hr.ts +3 -0
  23. package/src/components/advance-invoices/list/locales/it.ts +3 -0
  24. package/src/components/advance-invoices/list/locales/nl.ts +3 -0
  25. package/src/components/advance-invoices/list/locales/pl.ts +3 -0
  26. package/src/components/advance-invoices/list/locales/pt.ts +3 -0
  27. package/src/components/advance-invoices/list/locales/sl.ts +3 -0
  28. package/src/components/credit-notes/create/create-credit-note-form.tsx +177 -6
  29. package/src/components/credit-notes/create/locales/de.ts +8 -0
  30. package/src/components/credit-notes/create/locales/es.ts +8 -0
  31. package/src/components/credit-notes/create/locales/fr.ts +7 -0
  32. package/src/components/credit-notes/create/locales/hr.ts +7 -0
  33. package/src/components/credit-notes/create/locales/it.ts +9 -0
  34. package/src/components/credit-notes/create/locales/nl.ts +7 -0
  35. package/src/components/credit-notes/create/locales/pl.ts +7 -0
  36. package/src/components/credit-notes/create/locales/pt.ts +7 -0
  37. package/src/components/credit-notes/create/locales/sl.ts +7 -0
  38. package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
  39. package/src/components/credit-notes/list/list-row-actions.tsx +3 -6
  40. package/src/components/credit-notes/list/list-table.tsx +45 -2
  41. package/src/components/credit-notes/list/locales/de.ts +3 -0
  42. package/src/components/credit-notes/list/locales/en.ts +3 -0
  43. package/src/components/credit-notes/list/locales/es.ts +3 -0
  44. package/src/components/credit-notes/list/locales/fr.ts +3 -0
  45. package/src/components/credit-notes/list/locales/hr.ts +3 -0
  46. package/src/components/credit-notes/list/locales/it.ts +3 -0
  47. package/src/components/credit-notes/list/locales/nl.ts +3 -0
  48. package/src/components/credit-notes/list/locales/pl.ts +3 -0
  49. package/src/components/credit-notes/list/locales/pt.ts +3 -0
  50. package/src/components/credit-notes/list/locales/sl.ts +3 -0
  51. package/src/components/customers/create-customer-form/create-customer-form.tsx +0 -1
  52. package/src/components/documents/create/document-details-section.tsx +67 -1
  53. package/src/components/documents/create/mark-as-paid-section.tsx +11 -2
  54. package/src/components/documents/view/document-actions-bar.tsx +30 -0
  55. package/src/components/documents/view/locales/de.ts +5 -0
  56. package/src/components/documents/view/locales/es.ts +5 -0
  57. package/src/components/documents/view/locales/fr.ts +5 -0
  58. package/src/components/documents/view/locales/hr.ts +5 -0
  59. package/src/components/documents/view/locales/it.ts +5 -0
  60. package/src/components/documents/view/locales/nl.ts +5 -0
  61. package/src/components/documents/view/locales/pl.ts +5 -0
  62. package/src/components/documents/view/locales/pt.ts +5 -0
  63. package/src/components/documents/view/locales/sl.ts +5 -0
  64. package/src/components/entities/create-entity-form.tsx +1 -1
  65. package/src/components/entities/entity-settings-form/entity-settings-form.tsx +2 -3
  66. package/src/components/entities/entity-settings-form/locales/es.ts +2 -0
  67. package/src/components/entities/entity-settings-form/locales/fr.ts +2 -0
  68. package/src/components/entities/entity-settings-form/locales/hr.ts +2 -0
  69. package/src/components/entities/entity-settings-form/locales/it.ts +2 -0
  70. package/src/components/entities/entity-settings-form/locales/nl.ts +2 -0
  71. package/src/components/entities/entity-settings-form/locales/pl.ts +2 -0
  72. package/src/components/entities/entity-settings-form/locales/pt.ts +2 -0
  73. package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +109 -0
  74. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +365 -35
  75. package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +101 -20
  76. package/src/components/entities/fina-settings-form/index.ts +1 -0
  77. package/src/components/entities/fina-settings-form/locales/de.ts +54 -34
  78. package/src/components/entities/fina-settings-form/locales/en.ts +51 -34
  79. package/src/components/entities/fina-settings-form/locales/es.ts +50 -34
  80. package/src/components/entities/fina-settings-form/locales/fr.ts +50 -34
  81. package/src/components/entities/fina-settings-form/locales/hr.ts +50 -34
  82. package/src/components/entities/fina-settings-form/locales/it.ts +50 -34
  83. package/src/components/entities/fina-settings-form/locales/nl.ts +50 -34
  84. package/src/components/entities/fina-settings-form/locales/pl.ts +50 -34
  85. package/src/components/entities/fina-settings-form/locales/pt.ts +50 -34
  86. package/src/components/entities/fina-settings-form/locales/sl.ts +50 -34
  87. package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +18 -0
  88. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +64 -89
  89. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +51 -323
  90. package/src/components/entities/furs-settings-form/furs-operator-required-dialog.tsx +106 -0
  91. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +24 -10
  92. package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +5 -9
  93. package/src/components/entities/furs-settings-form/index.ts +1 -0
  94. package/src/components/entities/furs-settings-form/locales/de.ts +27 -3
  95. package/src/components/entities/furs-settings-form/locales/en.ts +17 -3
  96. package/src/components/entities/furs-settings-form/locales/es.ts +26 -3
  97. package/src/components/entities/furs-settings-form/locales/fr.ts +26 -3
  98. package/src/components/entities/furs-settings-form/locales/hr.ts +26 -3
  99. package/src/components/entities/furs-settings-form/locales/it.ts +26 -3
  100. package/src/components/entities/furs-settings-form/locales/nl.ts +26 -3
  101. package/src/components/entities/furs-settings-form/locales/pl.ts +26 -3
  102. package/src/components/entities/furs-settings-form/locales/pt.ts +26 -3
  103. package/src/components/entities/furs-settings-form/locales/sl.ts +16 -3
  104. package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +22 -0
  105. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
  106. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +14 -2
  107. package/src/components/entities/settings/tax-rules-settings-form.tsx +4 -4
  108. package/src/components/estimates/list/list-row-actions.tsx +3 -7
  109. package/src/components/estimates/list/list-table.tsx +35 -2
  110. package/src/components/estimates/list/locales/de.ts +3 -0
  111. package/src/components/estimates/list/locales/en.ts +3 -0
  112. package/src/components/estimates/list/locales/es.ts +3 -0
  113. package/src/components/estimates/list/locales/fr.ts +3 -0
  114. package/src/components/estimates/list/locales/hr.ts +3 -0
  115. package/src/components/estimates/list/locales/it.ts +3 -0
  116. package/src/components/estimates/list/locales/nl.ts +3 -0
  117. package/src/components/estimates/list/locales/pl.ts +3 -0
  118. package/src/components/estimates/list/locales/pt.ts +3 -0
  119. package/src/components/estimates/list/locales/sl.ts +3 -0
  120. package/src/components/export/document-export-form.tsx +34 -34
  121. package/src/components/invoices/create/create-invoice-form.tsx +107 -5
  122. package/src/components/invoices/create/prepare-invoice-submission.ts +17 -0
  123. package/src/components/invoices/invoices.hooks.ts +32 -2
  124. package/src/components/invoices/list/list-row-actions.tsx +23 -8
  125. package/src/components/invoices/list/list-table.tsx +53 -2
  126. package/src/components/invoices/list/locales/de.ts +4 -0
  127. package/src/components/invoices/list/locales/en.ts +4 -0
  128. package/src/components/invoices/list/locales/es.ts +4 -0
  129. package/src/components/invoices/list/locales/fr.ts +4 -0
  130. package/src/components/invoices/list/locales/hr.ts +4 -0
  131. package/src/components/invoices/list/locales/it.ts +4 -0
  132. package/src/components/invoices/list/locales/nl.ts +4 -0
  133. package/src/components/invoices/list/locales/pl.ts +4 -0
  134. package/src/components/invoices/list/locales/pt.ts +4 -0
  135. package/src/components/invoices/list/locales/sl.ts +4 -0
  136. package/src/components/invoices/view/fiscalization-status-card.tsx +4 -1
  137. package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -7
  138. package/src/components/items/item-list-table/item-list-row.tsx +3 -2
  139. package/src/components/items/item-list-table/item-list-table.tsx +5 -1
  140. package/src/components/recurring-invoices/create-recurring-invoice-form/create-recurring-invoice-form.tsx +418 -0
  141. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/de.ts +45 -0
  142. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/es.ts +44 -0
  143. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/fr.ts +44 -0
  144. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/hr.ts +44 -0
  145. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/it.ts +44 -0
  146. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/nl.ts +44 -0
  147. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pl.ts +44 -0
  148. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pt.ts +44 -0
  149. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/sl.ts +44 -0
  150. package/src/components/recurring-invoices/index.ts +3 -0
  151. package/src/components/recurring-invoices/list/index.ts +2 -0
  152. package/src/components/recurring-invoices/list/list-row-actions.tsx +139 -0
  153. package/src/components/recurring-invoices/list/list-table.tsx +179 -0
  154. package/src/components/recurring-invoices/list/locales/de.ts +27 -0
  155. package/src/components/recurring-invoices/list/locales/en.ts +5 -0
  156. package/src/components/recurring-invoices/list/locales/es.ts +27 -0
  157. package/src/components/recurring-invoices/list/locales/fr.ts +27 -0
  158. package/src/components/recurring-invoices/list/locales/hr.ts +27 -0
  159. package/src/components/recurring-invoices/list/locales/it.ts +27 -0
  160. package/src/components/recurring-invoices/list/locales/nl.ts +27 -0
  161. package/src/components/recurring-invoices/list/locales/pl.ts +27 -0
  162. package/src/components/recurring-invoices/list/locales/pt.ts +27 -0
  163. package/src/components/recurring-invoices/list/locales/sl.ts +27 -0
  164. package/src/components/recurring-invoices/recurring-invoices.hooks.ts +28 -0
  165. package/src/components/table/data-table.tsx +122 -5
  166. package/src/components/table/selection-toolbar.tsx +36 -0
  167. package/src/components/tax-reports/kir-export-form.tsx +75 -55
  168. package/src/components/taxes/tax-list-table/tax-list-row-actions.tsx +3 -6
  169. package/src/components/taxes/tax-list-table/tax-list-row.tsx +3 -2
  170. package/src/components/taxes/tax-list-table/tax-list-table.tsx +5 -1
  171. package/src/components/ui/checkbox.tsx +5 -5
  172. package/src/generate-schemas.ts +45 -18
  173. package/src/generated/schemas/authorizeshopify_body.ts +22 -0
  174. package/src/generated/schemas/creditnote.ts +0 -2
  175. package/src/generated/schemas/entity.ts +1 -1
  176. package/src/generated/schemas/index.ts +39 -28
  177. package/src/generated/schemas/order.ts +127 -0
  178. package/src/generated/schemas/orderintegration.ts +51 -0
  179. package/src/generated/schemas/payment.ts +2 -0
  180. package/src/generated/schemas/recurringinvoice.ts +61 -0
  181. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +107 -140
  182. package/src/generated/schemas/rendercreditnotepreview_body.ts +108 -141
  183. package/src/generated/schemas/renderestimatepreview_body.ts +78 -82
  184. package/src/generated/schemas/renderinvoicepreview_body.ts +108 -141
  185. package/src/generated/schemas/webhook.ts +42 -0
  186. package/src/lib/furs-error-utils.ts +36 -0
  187. package/src/lib/schemas/advance-invoice.ts +3 -3
  188. package/src/lib/schemas/credit-note.ts +3 -3
  189. package/src/lib/schemas/estimate.ts +3 -3
  190. package/src/lib/schemas/invoice.ts +3 -3
  191. package/src/providers/white-label-provider.tsx +3 -0
@@ -29,9 +29,12 @@ import { DocumentItemsSection, type PriceModesMap } from "../../documents/create
29
29
  import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
30
30
  import { MarkAsPaidSection } from "../../documents/create/mark-as-paid-section";
31
31
  import type { DocumentTypes } from "../../documents/types";
32
+ import { useFinaPremises, useFinaSettings } from "../../entities/fina-settings-form/fina-settings.hooks";
32
33
  import { useFursPremises, useFursSettings } from "../../entities/furs-settings-form/furs-settings.hooks";
33
34
  import {
35
+ getLastUsedFinaCombo,
34
36
  getLastUsedFursCombo,
37
+ setLastUsedFinaCombo,
35
38
  setLastUsedFursCombo,
36
39
  useCreateInvoice,
37
40
  useNextInvoiceNumber,
@@ -137,6 +140,23 @@ export default function CreateInvoiceForm({
137
140
  const [selectedDeviceName, setSelectedDeviceName] = useState<string | undefined>();
138
141
  const [skipFiscalization, setSkipFiscalization] = useState(false);
139
142
 
143
+ // ============================================================================
144
+ // FINA Settings & Premises
145
+ // ============================================================================
146
+ const { data: finaSettings, isLoading: isFinaSettingsLoading } = useFinaSettings(entityId);
147
+ const { data: finaPremises, isLoading: isFinaPremisesLoading } = useFinaPremises(entityId, {
148
+ enabled: finaSettings?.enabled === true,
149
+ });
150
+
151
+ const isFinaLoading = isFinaSettingsLoading || (finaSettings?.enabled && isFinaPremisesLoading);
152
+ const isFinaEnabled = finaSettings?.enabled === true;
153
+ const activeFinaPremises = useMemo(() => finaPremises?.filter((p: any) => p.is_active) || [], [finaPremises]);
154
+ const hasFinaPremises = activeFinaPremises.length > 0;
155
+
156
+ // FINA premise/device selection state (no skip - all FINA invoices must be fiscalized)
157
+ const [selectedFinaPremiseId, setSelectedFinaPremiseId] = useState<string | undefined>();
158
+ const [selectedFinaDeviceId, setSelectedFinaDeviceId] = useState<string | undefined>();
159
+
140
160
  // UI-only state (not part of API schema)
141
161
  const [markAsPaid, setMarkAsPaid] = useState(false);
142
162
  const [paymentTypes, setPaymentTypes] = useState<string[]>(["bank_transfer"]);
@@ -232,6 +252,51 @@ export default function CreateInvoiceForm({
232
252
  }
233
253
  }, [selectedPremiseName, activePremises, selectedDeviceName]);
234
254
 
255
+ // Get active FINA devices for selected premise
256
+ const activeFinaDevices = useMemo(() => {
257
+ if (!selectedFinaPremiseId) return [];
258
+ const premise = activeFinaPremises.find((p: any) => p.premise_id === selectedFinaPremiseId);
259
+ return premise?.Devices?.filter((d: any) => d.is_active) || [];
260
+ }, [activeFinaPremises, selectedFinaPremiseId]);
261
+
262
+ // Initialize FINA selection from localStorage or first active combo
263
+ useEffect(() => {
264
+ if (!isFinaEnabled || !hasFinaPremises || selectedFinaPremiseId) return;
265
+
266
+ const lastUsed = getLastUsedFinaCombo(entityId);
267
+ if (lastUsed) {
268
+ const premise = activeFinaPremises.find((p: any) => p.premise_id === lastUsed.premise_id);
269
+ const device = premise?.Devices?.find((d: any) => d.device_id === lastUsed.device_id && d.is_active);
270
+ if (premise && device) {
271
+ setSelectedFinaPremiseId(lastUsed.premise_id);
272
+ setSelectedFinaDeviceId(lastUsed.device_id);
273
+ return;
274
+ }
275
+ }
276
+
277
+ const firstPremise = activeFinaPremises[0];
278
+ const firstDevice = firstPremise?.Devices?.find((d: any) => d.is_active);
279
+ if (firstPremise && firstDevice) {
280
+ setSelectedFinaPremiseId(firstPremise.premise_id);
281
+ setSelectedFinaDeviceId(firstDevice.device_id);
282
+ }
283
+ }, [isFinaEnabled, hasFinaPremises, activeFinaPremises, entityId, selectedFinaPremiseId]);
284
+
285
+ // When FINA premise changes, select first active device
286
+ useEffect(() => {
287
+ if (!selectedFinaPremiseId) return;
288
+ const premise = activeFinaPremises.find((p: any) => p.premise_id === selectedFinaPremiseId);
289
+ const firstDevice = premise?.Devices?.find((d: any) => d.is_active);
290
+ if (firstDevice && selectedFinaDeviceId !== firstDevice.device_id) {
291
+ const currentDeviceInPremise = premise?.Devices?.find(
292
+ (d: any) => d.device_id === selectedFinaDeviceId && d.is_active,
293
+ );
294
+ if (!currentDeviceInPremise) {
295
+ setSelectedFinaDeviceId(firstDevice.device_id);
296
+ }
297
+ }
298
+ }, [selectedFinaPremiseId, activeFinaPremises, selectedFinaDeviceId]);
299
+
235
300
  const form = useForm<CreateInvoiceFormValues>({
236
301
  // Cast resolver to accept extended form type (includes UI-only fields)
237
302
  resolver: zodResolver(createInvoiceSchema) as Resolver<CreateInvoiceFormValues>,
@@ -291,6 +356,11 @@ export default function CreateInvoiceForm({
291
356
  const isFursActive =
292
357
  isFursEnabled && hasFursPremises && selectedPremiseName && selectedDeviceName && !skipFiscalization;
293
358
 
359
+ // FINA selection ready and active checks
360
+ const isFinaSelectionReady =
361
+ !isFinaEnabled || !hasFinaPremises || (!!selectedFinaPremiseId && !!selectedFinaDeviceId);
362
+ const isFinaActive = isFinaEnabled && hasFinaPremises && selectedFinaPremiseId && selectedFinaDeviceId;
363
+
294
364
  // ============================================================================
295
365
  // Next Invoice Number Preview
296
366
  // ============================================================================
@@ -299,20 +369,21 @@ export default function CreateInvoiceForm({
299
369
  const { data: nextNumberData, isLoading: isNextNumberLoading } = useNextInvoiceNumber(entityId, {
300
370
  business_premise_name: isFursActive ? selectedPremiseName : undefined,
301
371
  electronic_device_name: isFursActive ? selectedDeviceName : undefined,
302
- enabled: !!entityId && !isFursLoading && isFursSelectionReady && !isEditMode,
372
+ enabled:
373
+ !!entityId && !isFursLoading && isFursSelectionReady && !isFinaLoading && isFinaSelectionReady && !isEditMode,
303
374
  });
304
375
 
305
- // Overall loading state - wait until we have FURS data, selection ready, and next number (only in create mode)
376
+ // Overall loading state - wait until we have FURS/FINA data, selection ready, and next number (only in create mode)
306
377
  const isFormDataLoading = isEditMode
307
378
  ? false // In edit mode, don't wait for next number
308
- : isFursLoading || !isFursSelectionReady || isNextNumberLoading;
379
+ : isFursLoading || !isFursSelectionReady || isFinaLoading || !isFinaSelectionReady || isNextNumberLoading;
309
380
 
310
381
  // Update header action with FURS and e-SLOG toggle buttons
311
382
  useEffect(() => {
312
383
  if (!onHeaderActionChange) return;
313
384
 
314
- // Don't set header action while loading or in edit mode (FURS/e-SLOG not editable)
315
- if (isFursLoading || isEditMode) {
385
+ // Don't set header action while loading or in edit mode (FURS/FINA/e-SLOG not editable)
386
+ if (isFursLoading || isFinaLoading || isEditMode) {
316
387
  onHeaderActionChange(null);
317
388
  return;
318
389
  }
@@ -406,6 +477,7 @@ export default function CreateInvoiceForm({
406
477
  }
407
478
  }, [
408
479
  isFursLoading,
480
+ isFinaLoading,
409
481
  isFursEnabled,
410
482
  hasFursPremises,
411
483
  skipFiscalization,
@@ -469,6 +541,13 @@ export default function CreateInvoiceForm({
469
541
  electronic_device_name: selectedDeviceName,
470
542
  });
471
543
  }
544
+ // Save FINA combo to localStorage on successful creation
545
+ if (isFinaActive && selectedFinaPremiseId && selectedFinaDeviceId) {
546
+ setLastUsedFinaCombo(entityId, {
547
+ premise_id: selectedFinaPremiseId,
548
+ device_id: selectedFinaDeviceId,
549
+ });
550
+ }
472
551
  // Invalidate customers cache when a customer was created/linked
473
552
  // This ensures the new customer appears in autocomplete for future documents
474
553
  if (data.customer_id) {
@@ -527,6 +606,12 @@ export default function CreateInvoiceForm({
527
606
  : undefined
528
607
  : undefined;
529
608
 
609
+ // Build FINA options (skip for drafts and edit mode; FINA can't be skipped)
610
+ const finaOptions =
611
+ !isDraft && !isEditMode && isFinaEnabled && selectedFinaPremiseId && selectedFinaDeviceId
612
+ ? { premise_id: selectedFinaPremiseId, device_id: selectedFinaDeviceId, payment_type: paymentTypes[0] }
613
+ : undefined;
614
+
530
615
  // Build e-SLOG options (skip for drafts and edit mode)
531
616
  const eslogOptions =
532
617
  !isDraft && !isEditMode && isEslogAvailable
@@ -539,6 +624,7 @@ export default function CreateInvoiceForm({
539
624
  markAsPaid: isDraft || isEditMode ? false : markAsPaid,
540
625
  paymentTypes,
541
626
  furs: fursOptions,
627
+ fina: finaOptions,
542
628
  eslog: eslogOptions,
543
629
  priceModes: priceModesRef.current,
544
630
  isDraft,
@@ -563,11 +649,14 @@ export default function CreateInvoiceForm({
563
649
  isEditMode,
564
650
  isEslogAvailable,
565
651
  isFursEnabled,
652
+ isFinaEnabled,
566
653
  markAsPaid,
567
654
  originalCustomer,
568
655
  paymentTypes,
569
656
  selectedDeviceName,
570
657
  selectedPremiseName,
658
+ selectedFinaPremiseId,
659
+ selectedFinaDeviceId,
571
660
  showCustomerForm,
572
661
  skipFiscalization,
573
662
  ],
@@ -798,6 +887,18 @@ export default function CreateInvoiceForm({
798
887
  }
799
888
  : undefined
800
889
  }
890
+ finaInline={
891
+ !isEditMode && isFinaEnabled && hasFinaPremises
892
+ ? {
893
+ premises: activeFinaPremises.map((p: any) => ({ id: p.id, premise_id: p.premise_id })),
894
+ devices: activeFinaDevices.map((d: any) => ({ id: d.id, device_id: d.device_id })),
895
+ selectedPremise: selectedFinaPremiseId,
896
+ selectedDevice: selectedFinaDeviceId,
897
+ onPremiseChange: setSelectedFinaPremiseId,
898
+ onDeviceChange: setSelectedFinaDeviceId,
899
+ }
900
+ : undefined
901
+ }
801
902
  serviceDate={{
802
903
  dateType: serviceDateType,
803
904
  onDateTypeChange: setServiceDateType,
@@ -812,6 +913,7 @@ export default function CreateInvoiceForm({
812
913
  paymentTypes={paymentTypes}
813
914
  onPaymentTypesChange={setPaymentTypes}
814
915
  t={t}
916
+ alwaysShowPaymentType={!!isFinaActive}
815
917
  />
816
918
  )}
817
919
  </DocumentDetailsSection>
@@ -8,6 +8,12 @@ type FursData = {
8
8
  skip?: boolean;
9
9
  };
10
10
 
11
+ type FinaData = {
12
+ premise_id?: string;
13
+ device_id?: string;
14
+ payment_type?: string;
15
+ };
16
+
11
17
  type EslogData = {
12
18
  validation_enabled?: boolean;
13
19
  };
@@ -22,6 +28,8 @@ type PrepareOptions = {
22
28
  paymentTypes?: string[];
23
29
  /** FURS fiscalization data (for Slovenia) */
24
30
  furs?: FursData;
31
+ /** FINA fiscalization data (for Croatia) */
32
+ fina?: FinaData;
25
33
  /** e-SLOG validation data (for Slovenia) */
26
34
  eslog?: EslogData;
27
35
  /** Map of item index to gross price mode (collected from component state) */
@@ -68,6 +76,15 @@ export function prepareInvoiceSubmission(values: CreateInvoiceSchema, options: P
68
76
  }
69
77
  }
70
78
 
79
+ // Add FINA data if provided (FINA can't be skipped - all invoices must be fiscalized)
80
+ if (options.fina?.premise_id && options.fina.device_id) {
81
+ (payload as any).fina = {
82
+ premise_id: options.fina.premise_id,
83
+ device_id: options.fina.device_id,
84
+ ...(options.fina.payment_type && { payment_type: options.fina.payment_type }),
85
+ };
86
+ }
87
+
71
88
  // Add e-SLOG data if provided
72
89
  if (options.eslog !== undefined) {
73
90
  (payload as any).eslog = {
@@ -1,4 +1,4 @@
1
- import type { CreateInvoice201, CreateInvoiceBody } from "@spaceinvoices/js-sdk";
1
+ import type { CreateInvoice, Invoice } from "@spaceinvoices/js-sdk";
2
2
  import { useQuery } from "@tanstack/react-query";
3
3
 
4
4
  import { createResourceHooks } from "@/ui/hooks/create-resource-hooks";
@@ -13,7 +13,7 @@ const {
13
13
  useCreateResource: useCreateInvoice,
14
14
  useUpdateResource: useUpdateInvoice,
15
15
  useDeleteResource: useDeleteInvoice,
16
- } = createResourceHooks<CreateInvoice201, CreateInvoiceBody>("invoices", INVOICES_CACHE_KEY);
16
+ } = createResourceHooks<Invoice, CreateInvoice>("invoices", INVOICES_CACHE_KEY);
17
17
 
18
18
  export { useCreateInvoice, useUpdateInvoice, useDeleteInvoice };
19
19
 
@@ -108,3 +108,33 @@ export function setLastUsedFursCombo(entityId: string, combo: FursCombo): void {
108
108
  // Ignore localStorage errors (quota exceeded, etc.)
109
109
  }
110
110
  }
111
+
112
+ // ============================================================================
113
+ // FINA Last-Used Combo (localStorage)
114
+ // ============================================================================
115
+
116
+ const FINA_LAST_USED_KEY = "hr:fina:last-used";
117
+
118
+ export type FinaCombo = {
119
+ premise_id: string;
120
+ device_id: string;
121
+ };
122
+
123
+ export function getLastUsedFinaCombo(entityId: string): FinaCombo | null {
124
+ if (typeof window === "undefined") return null;
125
+ try {
126
+ const stored = localStorage.getItem(`${FINA_LAST_USED_KEY}:${entityId}`);
127
+ return stored ? JSON.parse(stored) : null;
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+
133
+ export function setLastUsedFinaCombo(entityId: string, combo: FinaCombo): void {
134
+ if (typeof window === "undefined") return;
135
+ try {
136
+ localStorage.setItem(`${FINA_LAST_USED_KEY}:${entityId}`, JSON.stringify(combo));
137
+ } catch {
138
+ // Ignore localStorage errors (quota exceeded, etc.)
139
+ }
140
+ }
@@ -1,6 +1,6 @@
1
1
  import type { Invoice } from "@spaceinvoices/js-sdk";
2
2
 
3
- import { Copy, Download, Eye, Link2Off, Loader2, Mail, MoreHorizontal, Plus } from "lucide-react";
3
+ import { Ban, Copy, Download, Eye, Link2Off, Loader2, Mail, MoreHorizontal, Plus } from "lucide-react";
4
4
  import { useState } from "react";
5
5
  import { Button } from "@/ui/components/ui/button";
6
6
  import {
@@ -19,6 +19,7 @@ import { useInvoiceDownload } from "./use-invoice-download";
19
19
 
20
20
  type InvoiceListRowActionsProps = {
21
21
  invoice: Invoice;
22
+ onView?: (invoice: Invoice) => void;
22
23
  onAddPayment?: (invoice: Invoice) => void;
23
24
  onDuplicate?: (invoice: Invoice) => void;
24
25
  onDownloadStart?: () => void;
@@ -26,10 +27,13 @@ type InvoiceListRowActionsProps = {
26
27
  onDownloadError?: (error: string) => void;
27
28
  onUnshare?: (invoice: Invoice) => Promise<void>;
28
29
  isUnsharing?: boolean;
30
+ onVoid?: (invoice: Invoice) => void;
31
+ isVoiding?: boolean;
29
32
  } & ComponentTranslationProps;
30
33
 
31
34
  export default function InvoiceListRowActions({
32
35
  invoice,
36
+ onView,
33
37
  onAddPayment,
34
38
  onDuplicate,
35
39
  onDownloadStart,
@@ -37,6 +41,8 @@ export default function InvoiceListRowActions({
37
41
  onDownloadError,
38
42
  onUnshare,
39
43
  isUnsharing,
44
+ onVoid,
45
+ isVoiding,
40
46
  ...i18nProps
41
47
  }: InvoiceListRowActionsProps) {
42
48
  const t = createTranslation(i18nProps);
@@ -67,13 +73,7 @@ export default function InvoiceListRowActions({
67
73
  </DropdownMenuGroup>
68
74
  <DropdownMenuSeparator />
69
75
  <DropdownMenuGroup>
70
- <DropdownMenuItem
71
- className="cursor-pointer"
72
- onClick={() => {
73
- // TODO: Use router
74
- window.location.href = `/app/documents/view/${invoice.id}`;
75
- }}
76
- >
76
+ <DropdownMenuItem className="cursor-pointer" onClick={() => onView?.(invoice)}>
77
77
  <Eye className="h-4 w-4" />
78
78
  {t("View invoice")}
79
79
  </DropdownMenuItem>
@@ -118,6 +118,21 @@ export default function InvoiceListRowActions({
118
118
  </DropdownMenuGroup>
119
119
  </>
120
120
  )}
121
+ {onVoid && !invoice.voided_at && !(invoice as any).is_draft && (
122
+ <>
123
+ <DropdownMenuSeparator />
124
+ <DropdownMenuGroup>
125
+ <DropdownMenuItem
126
+ className="cursor-pointer text-destructive focus:text-destructive"
127
+ onClick={() => onVoid(invoice)}
128
+ disabled={isVoiding}
129
+ >
130
+ {isVoiding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Ban className="h-4 w-4" />}
131
+ {t("Void")}
132
+ </DropdownMenuItem>
133
+ </DropdownMenuGroup>
134
+ </>
135
+ )}
121
136
  </DropdownMenuContent>
122
137
  </DropdownMenu>
123
138
  <SendEmailDialog
@@ -1,8 +1,9 @@
1
1
  import type { Invoice } from "@spaceinvoices/js-sdk";
2
- import { useMemo } from "react";
2
+ import { useCallback, useMemo, useState } from "react";
3
3
  import { DataTable } from "@/ui/components/table/data-table";
4
4
  import { FormattedDate } from "@/ui/components/table/date-cell";
5
5
  import { useTableFetch } from "@/ui/components/table/hooks/use-table-fetch";
6
+ import { SelectionToolbar } from "@/ui/components/table/selection-toolbar";
6
7
  import type {
7
8
  Column,
8
9
  FilterConfig,
@@ -44,16 +45,21 @@ type InvoiceListTableProps = {
44
45
  namespace?: string;
45
46
  locale?: string;
46
47
  entityId?: string;
48
+ onView?: (invoice: Invoice) => void;
47
49
  onAddPayment?: (invoice: Invoice) => void;
48
50
  onDuplicate?: (invoice: Invoice) => void;
49
51
  onDownloadStart?: () => void;
50
52
  onDownloadSuccess?: (fileName: string) => void;
51
53
  onDownloadError?: (error: string) => void;
54
+ onExportSelected?: (documentIds: string[]) => void;
55
+ onVoid?: (invoice: Invoice) => void;
56
+ isVoiding?: boolean;
52
57
  } & ListTableProps<Invoice>;
53
58
 
54
59
  export default function InvoiceListTable({
55
60
  queryParams,
56
61
  onRowClick,
62
+ onView,
57
63
  onAddPayment,
58
64
  onDuplicate,
59
65
  onChangeParams,
@@ -62,6 +68,9 @@ export default function InvoiceListTable({
62
68
  onDownloadStart,
63
69
  onDownloadSuccess,
64
70
  onDownloadError,
71
+ onExportSelected,
72
+ onVoid,
73
+ isVoiding,
65
74
  ...i18nProps
66
75
  }: InvoiceListTableProps) {
67
76
  const t = createTranslation({
@@ -70,6 +79,7 @@ export default function InvoiceListTable({
70
79
  });
71
80
 
72
81
  const { sdk } = useSDK();
82
+ const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
73
83
 
74
84
  const handleFetch = useTableFetch(async (params: TableQueryParams) => {
75
85
  if (!sdk) throw new Error("SDK not initialized");
@@ -99,6 +109,28 @@ export default function InvoiceListTable({
99
109
  [t],
100
110
  );
101
111
 
112
+ const handleExportPdfs = useCallback(() => {
113
+ if (selectedIds.size > 0 && onExportSelected) {
114
+ onExportSelected(Array.from(selectedIds));
115
+ }
116
+ }, [selectedIds, onExportSelected]);
117
+
118
+ const handleDeselectAll = useCallback(() => {
119
+ setSelectedIds(new Set());
120
+ }, []);
121
+
122
+ const selectionToolbar = useCallback(
123
+ (count: number) => (
124
+ <SelectionToolbar
125
+ selectedCount={count}
126
+ onExportPdfs={onExportSelected ? handleExportPdfs : undefined}
127
+ onDeselectAll={handleDeselectAll}
128
+ t={t}
129
+ />
130
+ ),
131
+ [handleExportPdfs, handleDeselectAll, onExportSelected, t],
132
+ );
133
+
102
134
  const columns: Column<Invoice>[] = useMemo(
103
135
  () => [
104
136
  {
@@ -149,18 +181,33 @@ export default function InvoiceListTable({
149
181
  cell: (invoice) => (
150
182
  <InvoiceListRowActions
151
183
  invoice={invoice}
184
+ onView={onView}
152
185
  onAddPayment={onAddPayment}
153
186
  onDuplicate={onDuplicate}
154
187
  onDownloadStart={onDownloadStart}
155
188
  onDownloadSuccess={onDownloadSuccess}
156
189
  onDownloadError={onDownloadError}
190
+ onVoid={onVoid}
191
+ isVoiding={isVoiding}
157
192
  t={t}
158
193
  locale={i18nProps.locale}
159
194
  />
160
195
  ),
161
196
  },
162
197
  ],
163
- [t, onRowClick, onAddPayment, onDuplicate, onDownloadStart, onDownloadSuccess, onDownloadError, i18nProps.locale],
198
+ [
199
+ t,
200
+ onRowClick,
201
+ onView,
202
+ onAddPayment,
203
+ onDuplicate,
204
+ onDownloadStart,
205
+ onDownloadSuccess,
206
+ onDownloadError,
207
+ onVoid,
208
+ isVoiding,
209
+ i18nProps.locale,
210
+ ],
164
211
  );
165
212
 
166
213
  return (
@@ -177,6 +224,10 @@ export default function InvoiceListTable({
177
224
  filterConfig={filterConfig}
178
225
  t={t}
179
226
  locale={i18nProps.locale}
227
+ selectable={!!onExportSelected}
228
+ selectedIds={selectedIds}
229
+ onSelectionChange={setSelectedIds}
230
+ selectionToolbar={selectionToolbar}
180
231
  />
181
232
  );
182
233
  }
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Keine Rechnungen gefunden",
40
40
  "Try adjusting your search criteria": "Versuchen Sie, Ihre Suchkriterien anzupassen",
41
41
  "Clear search": "Suche zurücksetzen",
42
+ selected: "ausgewählt",
43
+ "Export PDFs": "PDFs exportieren",
44
+ "Deselect all": "Auswahl aufheben",
45
+ Void: "Stornieren",
42
46
  } as const;
@@ -5,4 +5,8 @@ export default {
5
5
  "Create new": "Create invoice",
6
6
  // No results translations
7
7
  "No results found": "No invoices found",
8
+ selected: "selected",
9
+ "Export PDFs": "Export PDFs",
10
+ "Deselect all": "Deselect all",
11
+ Void: "Void",
8
12
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "No se encontraron facturas",
40
40
  "Try adjusting your search criteria": "Intente ajustar sus criterios de búsqueda",
41
41
  "Clear search": "Limpiar búsqueda",
42
+ selected: "seleccionados",
43
+ "Export PDFs": "Exportar PDFs",
44
+ "Deselect all": "Deseleccionar todo",
45
+ Void: "Anular",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Aucune facture trouvée",
40
40
  "Try adjusting your search criteria": "Essayez de modifier vos critères de recherche",
41
41
  "Clear search": "Effacer la recherche",
42
+ selected: "sélectionnés",
43
+ "Export PDFs": "Exporter les PDF",
44
+ "Deselect all": "Tout désélectionner",
45
+ Void: "Annuler",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Nisu pronađeni računi",
40
40
  "Try adjusting your search criteria": "Pokušajte prilagoditi kriterije pretrage",
41
41
  "Clear search": "Očisti pretragu",
42
+ selected: "odabranih",
43
+ "Export PDFs": "Izvezi PDF-ove",
44
+ "Deselect all": "Poništi odabir",
45
+ Void: "Storniraj",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Nessuna fattura trovata",
40
40
  "Try adjusting your search criteria": "Prova a modificare i criteri di ricerca",
41
41
  "Clear search": "Cancella ricerca",
42
+ selected: "selezionati",
43
+ "Export PDFs": "Esporta PDF",
44
+ "Deselect all": "Deseleziona tutto",
45
+ Void: "Annulla",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Geen facturen gevonden",
40
40
  "Try adjusting your search criteria": "Probeer uw zoekcriteria aan te passen",
41
41
  "Clear search": "Zoekopdracht wissen",
42
+ selected: "geselecteerd",
43
+ "Export PDFs": "PDFs exporteren",
44
+ "Deselect all": "Alles deselecteren",
45
+ Void: "Nietig verklaren",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Nie znaleziono faktur",
40
40
  "Try adjusting your search criteria": "Spróbuj dostosować kryteria wyszukiwania",
41
41
  "Clear search": "Wyczyść wyszukiwanie",
42
+ selected: "wybranych",
43
+ "Export PDFs": "Eksportuj PDF-y",
44
+ "Deselect all": "Odznacz wszystko",
45
+ Void: "Anuluj",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Nenhuma fatura encontrada",
40
40
  "Try adjusting your search criteria": "Tente ajustar os seus critérios de pesquisa",
41
41
  "Clear search": "Limpar pesquisa",
42
+ selected: "selecionados",
43
+ "Export PDFs": "Exportar PDFs",
44
+ "Deselect all": "Desselecionar tudo",
45
+ Void: "Anular",
42
46
  } as const;
@@ -39,4 +39,8 @@ export default {
39
39
  "No results found": "Ni najdenih računov",
40
40
  "Try adjusting your search criteria": "Poskusite prilagoditi iskalne kriterije",
41
41
  "Clear search": "Počisti iskanje",
42
+ selected: "izbranih",
43
+ "Export PDFs": "Izvozi PDF-je",
44
+ "Deselect all": "Počisti izbiro",
45
+ Void: "Storniraj",
42
46
  } as const;
@@ -18,7 +18,10 @@ import sl from "../../documents/view/locales/sl";
18
18
 
19
19
  const translations = { de, es, fr, hr, it, nl, pl, pt, sl } as const;
20
20
 
21
- type FiscalizationData = FursFiscalizationResponse | FinaFiscalizationResponse;
21
+ type FiscalizationStatus = "pending" | "success" | "failed" | "skipped";
22
+ type FiscalizationData = (Exclude<FursFiscalizationResponse, null> | Exclude<FinaFiscalizationResponse, null>) & {
23
+ status: FiscalizationStatus;
24
+ };
22
25
 
23
26
  interface FiscalizationStatusCardProps extends ComponentTranslationProps {
24
27
  fiscalizationType: "furs" | "fina";
@@ -15,9 +15,10 @@ import { createTranslation } from "@/ui/lib/translation";
15
15
 
16
16
  type ItemListRowActionsProps = {
17
17
  item: Item;
18
+ onView?: (item: Item) => void;
18
19
  } & ComponentTranslationProps;
19
20
 
20
- export default function ItemListRowActions({ item, ...i18nProps }: ItemListRowActionsProps) {
21
+ export default function ItemListRowActions({ item, onView, ...i18nProps }: ItemListRowActionsProps) {
21
22
  const t = createTranslation(i18nProps);
22
23
 
23
24
  return (
@@ -34,12 +35,7 @@ export default function ItemListRowActions({ item, ...i18nProps }: ItemListRowAc
34
35
  {t("Copy item ID")}
35
36
  </DropdownMenuItem>
36
37
  <DropdownMenuSeparator />
37
- <DropdownMenuItem
38
- className="cursor-pointer"
39
- onClick={() => {
40
- window.location.href = `/app/items/${item.id}`;
41
- }}
42
- >
38
+ <DropdownMenuItem className="cursor-pointer" onClick={() => onView?.(item)}>
43
39
  {t("View item")}
44
40
  </DropdownMenuItem>
45
41
  </DropdownMenuContent>
@@ -9,9 +9,10 @@ import ItemListRowActions from "./item-list-row-actions";
9
9
  type ItemListRowProps = {
10
10
  item: Item;
11
11
  onRowClick?: (item: Item) => void;
12
+ onView?: (item: Item) => void;
12
13
  } & ComponentTranslationProps;
13
14
 
14
- export default function ItemListRow({ item, onRowClick, ...i18nProps }: ItemListRowProps) {
15
+ export default function ItemListRow({ item, onRowClick, onView, ...i18nProps }: ItemListRowProps) {
15
16
  const t = createTranslation(i18nProps);
16
17
 
17
18
  return (
@@ -25,7 +26,7 @@ export default function ItemListRow({ item, onRowClick, ...i18nProps }: ItemList
25
26
  <TableCell>{item.description}</TableCell>
26
27
  <TableCell className="text-right">{item.price}</TableCell>
27
28
  <TableCell className="text-right">
28
- <ItemListRowActions item={item} t={t} />
29
+ <ItemListRowActions item={item} onView={onView} t={t} />
29
30
  </TableCell>
30
31
  </TableRow>
31
32
  );