@spaceinvoices/react-ui 0.4.0 → 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 (200) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/registry.json +23 -27
  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/premises-management-section.tsx +1 -0
  107. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +58 -34
  108. package/src/components/entities/settings/tax-rules-settings-form.tsx +4 -4
  109. package/src/components/estimates/list/list-row-actions.tsx +3 -7
  110. package/src/components/estimates/list/list-table.tsx +35 -2
  111. package/src/components/estimates/list/locales/de.ts +3 -0
  112. package/src/components/estimates/list/locales/en.ts +3 -0
  113. package/src/components/estimates/list/locales/es.ts +3 -0
  114. package/src/components/estimates/list/locales/fr.ts +3 -0
  115. package/src/components/estimates/list/locales/hr.ts +3 -0
  116. package/src/components/estimates/list/locales/it.ts +3 -0
  117. package/src/components/estimates/list/locales/nl.ts +3 -0
  118. package/src/components/estimates/list/locales/pl.ts +3 -0
  119. package/src/components/estimates/list/locales/pt.ts +3 -0
  120. package/src/components/estimates/list/locales/sl.ts +3 -0
  121. package/src/components/export/document-export-form.tsx +34 -34
  122. package/src/components/invoices/create/create-invoice-form.tsx +107 -5
  123. package/src/components/invoices/create/prepare-invoice-submission.ts +17 -0
  124. package/src/components/invoices/invoices.hooks.ts +32 -2
  125. package/src/components/invoices/list/list-row-actions.tsx +23 -8
  126. package/src/components/invoices/list/list-table.tsx +53 -2
  127. package/src/components/invoices/list/locales/de.ts +4 -0
  128. package/src/components/invoices/list/locales/en.ts +4 -0
  129. package/src/components/invoices/list/locales/es.ts +4 -0
  130. package/src/components/invoices/list/locales/fr.ts +4 -0
  131. package/src/components/invoices/list/locales/hr.ts +4 -0
  132. package/src/components/invoices/list/locales/it.ts +4 -0
  133. package/src/components/invoices/list/locales/nl.ts +4 -0
  134. package/src/components/invoices/list/locales/pl.ts +4 -0
  135. package/src/components/invoices/list/locales/pt.ts +4 -0
  136. package/src/components/invoices/list/locales/sl.ts +4 -0
  137. package/src/components/invoices/view/fiscalization-status-card.tsx +4 -1
  138. package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -7
  139. package/src/components/items/item-list-table/item-list-row.tsx +3 -2
  140. package/src/components/items/item-list-table/item-list-table.tsx +5 -1
  141. package/src/components/recurring-invoices/create-recurring-invoice-form/create-recurring-invoice-form.tsx +418 -0
  142. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/de.ts +45 -0
  143. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/es.ts +44 -0
  144. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/fr.ts +44 -0
  145. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/hr.ts +44 -0
  146. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/it.ts +44 -0
  147. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/nl.ts +44 -0
  148. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pl.ts +44 -0
  149. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pt.ts +44 -0
  150. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/sl.ts +44 -0
  151. package/src/components/recurring-invoices/index.ts +3 -0
  152. package/src/components/recurring-invoices/list/index.ts +2 -0
  153. package/src/components/recurring-invoices/list/list-row-actions.tsx +139 -0
  154. package/src/components/recurring-invoices/list/list-table.tsx +179 -0
  155. package/src/components/recurring-invoices/list/locales/de.ts +27 -0
  156. package/src/components/recurring-invoices/list/locales/en.ts +5 -0
  157. package/src/components/recurring-invoices/list/locales/es.ts +27 -0
  158. package/src/components/recurring-invoices/list/locales/fr.ts +27 -0
  159. package/src/components/recurring-invoices/list/locales/hr.ts +27 -0
  160. package/src/components/recurring-invoices/list/locales/it.ts +27 -0
  161. package/src/components/recurring-invoices/list/locales/nl.ts +27 -0
  162. package/src/components/recurring-invoices/list/locales/pl.ts +27 -0
  163. package/src/components/recurring-invoices/list/locales/pt.ts +27 -0
  164. package/src/components/recurring-invoices/list/locales/sl.ts +27 -0
  165. package/src/components/recurring-invoices/recurring-invoices.hooks.ts +28 -0
  166. package/src/components/table/data-table.tsx +122 -5
  167. package/src/components/table/selection-toolbar.tsx +36 -0
  168. package/src/components/tax-reports/kir-export-form.tsx +75 -55
  169. package/src/components/taxes/tax-list-table/tax-list-row-actions.tsx +3 -6
  170. package/src/components/taxes/tax-list-table/tax-list-row.tsx +3 -2
  171. package/src/components/taxes/tax-list-table/tax-list-table.tsx +5 -1
  172. package/src/components/ui/checkbox.tsx +5 -5
  173. package/src/generate-schemas.ts +46 -7
  174. package/src/generated/schemas/advanceinvoice.ts +79 -187
  175. package/src/generated/schemas/authorizeshopify_body.ts +22 -0
  176. package/src/generated/schemas/creditnote.ts +60 -88
  177. package/src/generated/schemas/customadvanceinvoice.ts +70 -97
  178. package/src/generated/schemas/customcreditnote.ts +70 -97
  179. package/src/generated/schemas/customestimate.ts +68 -97
  180. package/src/generated/schemas/custominvoice.ts +70 -97
  181. package/src/generated/schemas/entity.ts +1 -1
  182. package/src/generated/schemas/estimate.ts +67 -172
  183. package/src/generated/schemas/index.ts +39 -28
  184. package/src/generated/schemas/invoice.ts +79 -187
  185. package/src/generated/schemas/order.ts +127 -0
  186. package/src/generated/schemas/orderintegration.ts +51 -0
  187. package/src/generated/schemas/payment.ts +2 -0
  188. package/src/generated/schemas/recurringinvoice.ts +61 -0
  189. package/src/generated/schemas/registerfursrealestatepremise_body.ts +11 -7
  190. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +140 -269
  191. package/src/generated/schemas/rendercreditnotepreview_body.ts +141 -270
  192. package/src/generated/schemas/renderestimatepreview_body.ts +112 -212
  193. package/src/generated/schemas/renderinvoicepreview_body.ts +141 -270
  194. package/src/generated/schemas/webhook.ts +42 -0
  195. package/src/lib/furs-error-utils.ts +36 -0
  196. package/src/lib/schemas/advance-invoice.ts +3 -3
  197. package/src/lib/schemas/credit-note.ts +3 -3
  198. package/src/lib/schemas/estimate.ts +3 -3
  199. package/src/lib/schemas/invoice.ts +3 -3
  200. package/src/providers/white-label-provider.tsx +3 -0
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Geen voorschotfacturen gevonden",
39
39
  "Try adjusting your search criteria": "Probeer uw zoekcriteria aan te passen",
40
40
  "Clear search": "Zoekopdracht wissen",
41
+ selected: "geselecteerd",
42
+ "Export PDFs": "PDFs exporteren",
43
+ "Deselect all": "Alles deselecteren",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Nie znaleziono faktur zaliczkowych",
39
39
  "Try adjusting your search criteria": "Sprobuj dostosowac kryteria wyszukiwania",
40
40
  "Clear search": "Wyczysc wyszukiwanie",
41
+ selected: "wybranych",
42
+ "Export PDFs": "Eksportuj PDF-y",
43
+ "Deselect all": "Odznacz wszystko",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Nenhuma fatura antecipada encontrada",
39
39
  "Try adjusting your search criteria": "Tente ajustar os seus criterios de pesquisa",
40
40
  "Clear search": "Limpar pesquisa",
41
+ selected: "selecionados",
42
+ "Export PDFs": "Exportar PDFs",
43
+ "Deselect all": "Desselecionar tudo",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Ni najdenih avansnih računov",
39
39
  "Try adjusting your search criteria": "Poskusite prilagoditi iskalne kriterije",
40
40
  "Clear search": "Počisti iskanje",
41
+ selected: "izbranih",
42
+ "Export PDFs": "Izvozi PDF-je",
43
+ "Deselect all": "Počisti izbiro",
41
44
  } as const;
@@ -6,6 +6,7 @@ import type { Resolver } from "react-hook-form";
6
6
  import { useForm, useWatch } from "react-hook-form";
7
7
  import type { z } from "zod";
8
8
  import { Form } from "@/ui/components/ui/form";
9
+ import { Skeleton } from "@/ui/components/ui/skeleton";
9
10
  import { createCreditNoteSchema } from "@/ui/generated/schemas";
10
11
  import { useNextDocumentNumber } from "@/ui/hooks/use-next-document-number";
11
12
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
@@ -24,7 +25,8 @@ import { MarkAsPaidSection } from "../../documents/create/mark-as-paid-section";
24
25
  import { prepareDocumentSubmission } from "../../documents/create/prepare-document-submission";
25
26
  import { useDocumentCustomerForm } from "../../documents/create/use-document-customer-form";
26
27
  import type { DocumentTypes } from "../../documents/types";
27
- import { useCreateCreditNote } from "../credit-notes.hooks";
28
+ import { useFinaPremises, useFinaSettings } from "../../entities/fina-settings-form/fina-settings.hooks";
29
+ import { getLastUsedFinaCombo, setLastUsedFinaCombo, useCreateCreditNote } from "../credit-notes.hooks";
28
30
  import de from "./locales/de";
29
31
  import es from "./locales/es";
30
32
  import fr from "./locales/fr";
@@ -88,11 +90,23 @@ export default function CreateCreditNoteForm({
88
90
  const { activeEntity } = useEntities();
89
91
  const queryClient = useQueryClient();
90
92
 
91
- // Fetch next credit note number
92
- const { data: nextNumberData } = useNextDocumentNumber(entityId, "credit_note", {
93
- enabled: !!entityId,
93
+ // ============================================================================
94
+ // FINA Settings & Premises
95
+ // ============================================================================
96
+ const { data: finaSettings, isLoading: isFinaSettingsLoading } = useFinaSettings(entityId);
97
+ const { data: finaPremises, isLoading: isFinaPremisesLoading } = useFinaPremises(entityId, {
98
+ enabled: finaSettings?.enabled === true,
94
99
  });
95
100
 
101
+ const isFinaLoading = isFinaSettingsLoading || (finaSettings?.enabled && isFinaPremisesLoading);
102
+ const isFinaEnabled = finaSettings?.enabled === true;
103
+ const activeFinaPremises = useMemo(() => finaPremises?.filter((p: any) => p.is_active) || [], [finaPremises]);
104
+ const hasFinaPremises = activeFinaPremises.length > 0;
105
+
106
+ // FINA premise/device selection state (no skip - all FINA credit notes must be fiscalized)
107
+ const [selectedFinaPremiseId, setSelectedFinaPremiseId] = useState<string | undefined>();
108
+ const [selectedFinaDeviceId, setSelectedFinaDeviceId] = useState<string | undefined>();
109
+
96
110
  // UI-only state (not part of API schema)
97
111
  const [markAsPaid, setMarkAsPaid] = useState(false);
98
112
  const [paymentTypes, setPaymentTypes] = useState<string[]>(["bank_transfer"]);
@@ -108,6 +122,68 @@ export default function CreateCreditNoteForm({
108
122
  }, [initialValues?.items]);
109
123
  const priceModesRef = useRef<PriceModesMap>(initialPriceModes);
110
124
 
125
+ // Get active FINA devices for selected premise
126
+ const activeFinaDevices = useMemo(() => {
127
+ if (!selectedFinaPremiseId) return [];
128
+ const premise = activeFinaPremises.find((p: any) => p.premise_id === selectedFinaPremiseId);
129
+ return premise?.Devices?.filter((d: any) => d.is_active) || [];
130
+ }, [activeFinaPremises, selectedFinaPremiseId]);
131
+
132
+ // Initialize FINA selection from localStorage or first active combo
133
+ useEffect(() => {
134
+ if (!isFinaEnabled || !hasFinaPremises || selectedFinaPremiseId) return;
135
+
136
+ const lastUsed = getLastUsedFinaCombo(entityId);
137
+ if (lastUsed) {
138
+ const premise = activeFinaPremises.find((p: any) => p.premise_id === lastUsed.premise_id);
139
+ const device = premise?.Devices?.find((d: any) => d.device_id === lastUsed.device_id && d.is_active);
140
+ if (premise && device) {
141
+ setSelectedFinaPremiseId(lastUsed.premise_id);
142
+ setSelectedFinaDeviceId(lastUsed.device_id);
143
+ return;
144
+ }
145
+ }
146
+
147
+ const firstPremise = activeFinaPremises[0];
148
+ const firstDevice = firstPremise?.Devices?.find((d: any) => d.is_active);
149
+ if (firstPremise && firstDevice) {
150
+ setSelectedFinaPremiseId(firstPremise.premise_id);
151
+ setSelectedFinaDeviceId(firstDevice.device_id);
152
+ }
153
+ }, [isFinaEnabled, hasFinaPremises, activeFinaPremises, entityId, selectedFinaPremiseId]);
154
+
155
+ // When FINA premise changes, select first active device
156
+ useEffect(() => {
157
+ if (!selectedFinaPremiseId) return;
158
+ const premise = activeFinaPremises.find((p: any) => p.premise_id === selectedFinaPremiseId);
159
+ const firstDevice = premise?.Devices?.find((d: any) => d.is_active);
160
+ if (firstDevice && selectedFinaDeviceId !== firstDevice.device_id) {
161
+ const currentDeviceInPremise = premise?.Devices?.find(
162
+ (d: any) => d.device_id === selectedFinaDeviceId && d.is_active,
163
+ );
164
+ if (!currentDeviceInPremise) {
165
+ setSelectedFinaDeviceId(firstDevice.device_id);
166
+ }
167
+ }
168
+ }, [selectedFinaPremiseId, activeFinaPremises, selectedFinaDeviceId]);
169
+
170
+ // FINA selection ready and active checks
171
+ const isFinaSelectionReady =
172
+ !isFinaEnabled || !hasFinaPremises || (!!selectedFinaPremiseId && !!selectedFinaDeviceId);
173
+ const isFinaActive = isFinaEnabled && hasFinaPremises && selectedFinaPremiseId && selectedFinaDeviceId;
174
+
175
+ // ============================================================================
176
+ // Next Credit Note Number Preview
177
+ // ============================================================================
178
+ const { data: nextNumberData, isLoading: isNextNumberLoading } = useNextDocumentNumber(entityId, "credit_note", {
179
+ enabled: !!entityId && !isFinaLoading && isFinaSelectionReady,
180
+ });
181
+
182
+ // Overall loading state
183
+ const isFormDataLoading = isFinaLoading || !isFinaSelectionReady || isNextNumberLoading;
184
+
185
+ // No header action for credit notes - FINA can't be skipped
186
+
111
187
  // Get default payment terms from entity settings
112
188
  const defaultPaymentTerms = (activeEntity?.settings as any)?.default_credit_note_payment_terms || "";
113
189
 
@@ -169,6 +245,13 @@ export default function CreateCreditNoteForm({
169
245
  const { mutate: createCreditNote, isPending } = useCreateCreditNote({
170
246
  entityId,
171
247
  onSuccess: (data) => {
248
+ // Save FINA combo to localStorage on successful creation
249
+ if (isFinaActive && selectedFinaPremiseId && selectedFinaDeviceId) {
250
+ setLastUsedFinaCombo(entityId, {
251
+ premise_id: selectedFinaPremiseId,
252
+ device_id: selectedFinaDeviceId,
253
+ });
254
+ }
172
255
  // Invalidate customers cache when a customer was created/linked
173
256
  if (data.customer_id) {
174
257
  queryClient.invalidateQueries({ queryKey: [CUSTOMERS_CACHE_KEY] });
@@ -181,6 +264,12 @@ export default function CreateCreditNoteForm({
181
264
  // Shared submit logic for both regular save and save as draft
182
265
  const submitCreditNote = useCallback(
183
266
  (values: CreateCreditNoteFormValues, isDraft: boolean) => {
267
+ // Build FINA options (skip for drafts; FINA can't be skipped)
268
+ const finaOptions =
269
+ !isDraft && isFinaEnabled && selectedFinaPremiseId && selectedFinaDeviceId
270
+ ? { premise_id: selectedFinaPremiseId, device_id: selectedFinaDeviceId, payment_type: paymentTypes[0] }
271
+ : undefined;
272
+
184
273
  const payload = prepareDocumentSubmission(values, {
185
274
  originalCustomer,
186
275
  wasCustomerFormShown: showCustomerForm,
@@ -190,9 +279,24 @@ export default function CreateCreditNoteForm({
190
279
  priceModes: priceModesRef.current,
191
280
  isDraft,
192
281
  });
282
+
283
+ // Add FINA data to payload
284
+ if (finaOptions) {
285
+ (payload as any).fina = finaOptions;
286
+ }
287
+
193
288
  createCreditNote(payload as CreateCreditNoteRequest);
194
289
  },
195
- [createCreditNote, markAsPaid, originalCustomer, paymentTypes, showCustomerForm],
290
+ [
291
+ createCreditNote,
292
+ isFinaEnabled,
293
+ markAsPaid,
294
+ originalCustomer,
295
+ paymentTypes,
296
+ selectedFinaDeviceId,
297
+ selectedFinaPremiseId,
298
+ showCustomerForm,
299
+ ],
196
300
  );
197
301
 
198
302
  // Handle save as draft
@@ -276,6 +380,56 @@ export default function CreateCreditNoteForm({
276
380
  submitCreditNote(values, false);
277
381
  };
278
382
 
383
+ // Show skeleton while loading
384
+ if (isFormDataLoading) {
385
+ return (
386
+ <div className="space-y-8">
387
+ <div className="flex w-full flex-col md:flex-row md:gap-6">
388
+ <div className="flex-1 space-y-4">
389
+ <Skeleton className="h-7 w-24" />
390
+ <Skeleton className="h-10 w-full" />
391
+ </div>
392
+ <div className="flex-1 space-y-4">
393
+ <Skeleton className="h-7 w-20" />
394
+ <Skeleton className="h-5 w-16" />
395
+ <Skeleton className="h-10 w-full" />
396
+ <Skeleton className="h-5 w-12" />
397
+ <Skeleton className="h-10 w-full" />
398
+ <Skeleton className="h-5 w-16" />
399
+ <Skeleton className="h-10 w-full" />
400
+ <Skeleton className="h-5 w-20" />
401
+ <Skeleton className="h-10 w-full" />
402
+ <div className="space-y-3 rounded-md border p-4">
403
+ <div className="flex items-center gap-3">
404
+ <Skeleton className="h-4 w-4 rounded" />
405
+ <Skeleton className="h-5 w-28" />
406
+ </div>
407
+ </div>
408
+ </div>
409
+ </div>
410
+
411
+ <div className="space-y-4">
412
+ <Skeleton className="h-7 w-16" />
413
+ <div className="space-y-4 rounded-lg border p-4">
414
+ <Skeleton className="h-10 w-full" />
415
+ <div className="flex gap-4">
416
+ <Skeleton className="h-10 w-24" />
417
+ <Skeleton className="h-10 flex-1" />
418
+ </div>
419
+ </div>
420
+ <Skeleton className="h-9 w-24" />
421
+ </div>
422
+
423
+ <div className="space-y-2">
424
+ <Skeleton className="h-5 w-12" />
425
+ <Skeleton className="h-24 w-full" />
426
+ </div>
427
+
428
+ <Skeleton className="h-10 w-24" />
429
+ </div>
430
+ );
431
+ }
432
+
279
433
  return (
280
434
  <Form {...form}>
281
435
  <form id="create-credit-note-form" onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
@@ -291,7 +445,23 @@ export default function CreateCreditNoteForm({
291
445
  initialCustomerName={initialCustomerName}
292
446
  t={t}
293
447
  />
294
- <DocumentDetailsSection control={form.control} documentType={_type} t={t}>
448
+ <DocumentDetailsSection
449
+ control={form.control}
450
+ documentType={_type}
451
+ t={t}
452
+ finaInline={
453
+ isFinaEnabled && hasFinaPremises
454
+ ? {
455
+ premises: activeFinaPremises.map((p: any) => ({ id: p.id, premise_id: p.premise_id })),
456
+ devices: activeFinaDevices.map((d: any) => ({ id: d.id, device_id: d.device_id })),
457
+ selectedPremise: selectedFinaPremiseId,
458
+ selectedDevice: selectedFinaDeviceId,
459
+ onPremiseChange: setSelectedFinaPremiseId,
460
+ onDeviceChange: setSelectedFinaDeviceId,
461
+ }
462
+ : undefined
463
+ }
464
+ >
295
465
  {/* Credit note specific: Mark as paid section (UI-only state, not in form schema) */}
296
466
  <MarkAsPaidSection
297
467
  checked={markAsPaid}
@@ -299,6 +469,7 @@ export default function CreateCreditNoteForm({
299
469
  paymentTypes={paymentTypes}
300
470
  onPaymentTypesChange={setPaymentTypes}
301
471
  t={t}
472
+ alwaysShowPaymentType={!!isFinaActive}
302
473
  />
303
474
  </DocumentDetailsSection>
304
475
  </div>
@@ -66,4 +66,12 @@ export default {
66
66
  "Net price": "Nettopreis",
67
67
  "Gross price (tax included)": "Bruttopreis (inkl. MwSt.)",
68
68
  "Net price (before tax)": "Nettopreis (exkl. MwSt.)",
69
+ // FINA fiscalization
70
+ "Fiscally verify": "Steuerlich bestätigen",
71
+ "Click to skip fiscalization for this credit note":
72
+ "Klicken, um die Fiskalisierung für diese Gutschrift zu überspringen",
73
+ "Click to enable fiscalization": "Klicken, um die Fiskalisierung zu aktivieren",
74
+ "Cannot skip fiscalization for cash payments": "Fiskalisierung kann für Barzahlungen nicht übersprungen werden",
75
+ Premise: "Geschäftsräume",
76
+ Device: "Gerät",
69
77
  } as const;
@@ -65,4 +65,12 @@ export default {
65
65
  "Net price": "Precio neto",
66
66
  "Gross price (tax included)": "Precio bruto (impuestos incluidos)",
67
67
  "Net price (before tax)": "Precio neto (antes de impuestos)",
68
+ // FINA fiscalization
69
+ "Fiscally verify": "Verificar fiscalmente",
70
+ "Click to skip fiscalization for this credit note":
71
+ "Haga clic para omitir la fiscalizacion para esta nota de credito",
72
+ "Click to enable fiscalization": "Haga clic para habilitar la fiscalizacion",
73
+ "Cannot skip fiscalization for cash payments": "No se puede omitir la fiscalizacion para pagos en efectivo",
74
+ Premise: "Local",
75
+ Device: "Dispositivo",
68
76
  } as const;
@@ -66,4 +66,11 @@ export default {
66
66
  "Net price": "Prix net",
67
67
  "Gross price (tax included)": "Prix brut (taxes comprises)",
68
68
  "Net price (before tax)": "Prix net (hors taxes)",
69
+ // FINA fiscalization
70
+ "Fiscally verify": "Verification fiscale",
71
+ "Click to skip fiscalization for this credit note": "Cliquez pour ignorer la fiscalisation pour cet avoir",
72
+ "Click to enable fiscalization": "Cliquez pour activer la fiscalisation",
73
+ "Cannot skip fiscalization for cash payments": "Impossible d'ignorer la fiscalisation pour les paiements en especes",
74
+ Premise: "Local",
75
+ Device: "Appareil",
69
76
  } as const;
@@ -65,4 +65,11 @@ export default {
65
65
  "Net price": "Neto cijena",
66
66
  "Gross price (tax included)": "Bruto cijena (s porezom)",
67
67
  "Net price (before tax)": "Neto cijena (bez poreza)",
68
+ // FINA fiscalization
69
+ "Fiscally verify": "Fiskalno potvrdi",
70
+ "Click to skip fiscalization for this credit note": "Kliknite za preskakanje fiskalizacije za ovo odobrenje",
71
+ "Click to enable fiscalization": "Kliknite za ukljucivanje fiskalizacije",
72
+ "Cannot skip fiscalization for cash payments": "Za gotovinska placanja nije moguce preskociti fiskalizaciju",
73
+ Premise: "Prostor",
74
+ Device: "Uredaj",
68
75
  } as const;
@@ -65,4 +65,13 @@ export default {
65
65
  "Net price": "Prezzo netto",
66
66
  "Gross price (tax included)": "Prezzo lordo (imposte incluse)",
67
67
  "Net price (before tax)": "Prezzo netto (prima delle imposte)",
68
+ // FINA fiscalization
69
+ "Fiscally verify": "Verifica fiscale",
70
+ "Click to skip fiscalization for this credit note":
71
+ "Clicca per saltare la fiscalizzazione per questa nota di credito",
72
+ "Click to enable fiscalization": "Clicca per abilitare la fiscalizzazione",
73
+ "Cannot skip fiscalization for cash payments":
74
+ "Non e possibile saltare la fiscalizzazione per i pagamenti in contanti",
75
+ Premise: "Locale",
76
+ Device: "Dispositivo",
68
77
  } as const;
@@ -66,4 +66,11 @@ export default {
66
66
  "Net price": "Nettoprijs",
67
67
  "Gross price (tax included)": "Brutoprijs (btw inbegrepen)",
68
68
  "Net price (before tax)": "Nettoprijs (exclusief btw)",
69
+ // FINA fiscalization
70
+ "Fiscally verify": "Fiscaal verifieren",
71
+ "Click to skip fiscalization for this credit note": "Klik om fiscalisatie voor deze creditnota over te slaan",
72
+ "Click to enable fiscalization": "Klik om fiscalisatie in te schakelen",
73
+ "Cannot skip fiscalization for cash payments": "Fiscalisatie kan niet worden overgeslagen voor contante betalingen",
74
+ Premise: "Bedrijfsruimte",
75
+ Device: "Apparaat",
69
76
  } as const;
@@ -65,4 +65,11 @@ export default {
65
65
  "Net price": "Cena netto",
66
66
  "Gross price (tax included)": "Cena brutto (z podatkiem)",
67
67
  "Net price (before tax)": "Cena netto (przed podatkiem)",
68
+ // FINA fiscalization
69
+ "Fiscally verify": "Zweryfikuj fiskalnie",
70
+ "Click to skip fiscalization for this credit note": "Kliknij, aby pominac fiskalizacje dla tej noty kredytowej",
71
+ "Click to enable fiscalization": "Kliknij, aby wlaczyc fiskalizacje",
72
+ "Cannot skip fiscalization for cash payments": "Nie mozna pominac fiskalizacji dla platnosci gotowkowych",
73
+ Premise: "Lokal",
74
+ Device: "Urządzenie",
68
75
  } as const;
@@ -65,4 +65,11 @@ export default {
65
65
  "Net price": "Preco liquido",
66
66
  "Gross price (tax included)": "Preco bruto (com imposto)",
67
67
  "Net price (before tax)": "Preco liquido (sem imposto)",
68
+ // FINA fiscalization
69
+ "Fiscally verify": "Verificar fiscalmente",
70
+ "Click to skip fiscalization for this credit note": "Clique para ignorar a fiscalizacao para esta nota de credito",
71
+ "Click to enable fiscalization": "Clique para ativar a fiscalizacao",
72
+ "Cannot skip fiscalization for cash payments": "Nao e possivel ignorar a fiscalizacao para pagamentos em dinheiro",
73
+ Premise: "Estabelecimento",
74
+ Device: "Dispositivo",
68
75
  } as const;
@@ -64,4 +64,11 @@ export default {
64
64
  "Net price": "Neto cena",
65
65
  "Gross price (tax included)": "Bruto cena (z davkom)",
66
66
  "Net price (before tax)": "Neto cena (brez davka)",
67
+ // FINA fiscalization
68
+ "Fiscally verify": "Davčno potrdi",
69
+ "Click to skip fiscalization for this credit note": "Klikni za preskok davčnega potrjevanja za ta dobropis",
70
+ "Click to enable fiscalization": "Klikni za vključitev davčnega potrjevanja",
71
+ "Cannot skip fiscalization for cash payments": "Za gotovinska plačila ni mogoče preskočiti davčnega potrjevanja",
72
+ Premise: "Poslovni prostor",
73
+ Device: "Naprava",
67
74
  } as const;
@@ -20,3 +20,33 @@ const {
20
20
  } = createResourceHooks<CreditNote, CreateCreditNoteRequest>("creditNotes", CREDIT_NOTES_CACHE_KEY);
21
21
 
22
22
  export { useCreateCreditNote, useUpdateCreditNote, useDeleteCreditNote };
23
+
24
+ // ============================================================================
25
+ // FINA Last-Used Combo (localStorage) for credit notes
26
+ // ============================================================================
27
+
28
+ const FINA_CN_LAST_USED_KEY = "hr:fina:cn:last-used";
29
+
30
+ export type FinaCombo = {
31
+ premise_id: string;
32
+ device_id: string;
33
+ };
34
+
35
+ export function getLastUsedFinaCombo(entityId: string): FinaCombo | null {
36
+ if (typeof window === "undefined") return null;
37
+ try {
38
+ const stored = localStorage.getItem(`${FINA_CN_LAST_USED_KEY}:${entityId}`);
39
+ return stored ? JSON.parse(stored) : null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ export function setLastUsedFinaCombo(entityId: string, combo: FinaCombo): void {
46
+ if (typeof window === "undefined") return;
47
+ try {
48
+ localStorage.setItem(`${FINA_CN_LAST_USED_KEY}:${entityId}`, JSON.stringify(combo));
49
+ } catch {
50
+ // Ignore localStorage errors
51
+ }
52
+ }
@@ -18,6 +18,7 @@ type CreditNote = any;
18
18
 
19
19
  type CreditNoteListRowActionsProps = {
20
20
  creditNote: CreditNote;
21
+ onView?: (creditNote: CreditNote) => void;
21
22
  onAddPayment?: (creditNote: CreditNote) => void;
22
23
  onDuplicate?: (creditNote: CreditNote) => void;
23
24
  onDownloadStart?: () => void;
@@ -29,6 +30,7 @@ type CreditNoteListRowActionsProps = {
29
30
 
30
31
  export default function CreditNoteListRowActions({
31
32
  creditNote,
33
+ onView,
32
34
  onAddPayment,
33
35
  onDuplicate,
34
36
  onDownloadStart,
@@ -64,12 +66,7 @@ export default function CreditNoteListRowActions({
64
66
  </DropdownMenuGroup>
65
67
  <DropdownMenuSeparator />
66
68
  <DropdownMenuGroup>
67
- <DropdownMenuItem
68
- className="cursor-pointer"
69
- onClick={() => {
70
- window.location.href = `/app/documents/view/${creditNote.id}`;
71
- }}
72
- >
69
+ <DropdownMenuItem className="cursor-pointer" onClick={() => onView?.(creditNote)}>
73
70
  <Eye className="h-4 w-4" />
74
71
  {t("View credit note")}
75
72
  </DropdownMenuItem>
@@ -1,8 +1,9 @@
1
1
  import type { CreditNote } 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,
@@ -45,16 +46,19 @@ type CreditNoteListTableProps = {
45
46
  namespace?: string;
46
47
  locale?: string;
47
48
  entityId?: string;
49
+ onView?: (creditNote: CreditNote) => void;
48
50
  onAddPayment?: (creditNote: CreditNote) => void;
49
51
  onDuplicate?: (creditNote: CreditNote) => void;
50
52
  onDownloadStart?: () => void;
51
53
  onDownloadSuccess?: (fileName: string) => void;
52
54
  onDownloadError?: (error: string) => void;
55
+ onExportSelected?: (documentIds: string[]) => void;
53
56
  } & ListTableProps<CreditNote>;
54
57
 
55
58
  export default function CreditNoteListTable({
56
59
  queryParams,
57
60
  onRowClick,
61
+ onView,
58
62
  onAddPayment,
59
63
  onDuplicate,
60
64
  onChangeParams,
@@ -62,6 +66,7 @@ export default function CreditNoteListTable({
62
66
  onDownloadStart,
63
67
  onDownloadSuccess,
64
68
  onDownloadError,
69
+ onExportSelected,
65
70
  ...i18nProps
66
71
  }: CreditNoteListTableProps) {
67
72
  const t = createTranslation({
@@ -70,6 +75,7 @@ export default function CreditNoteListTable({
70
75
  });
71
76
 
72
77
  const { sdk } = useSDK();
78
+ const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
73
79
 
74
80
  const handleFetch = useTableFetch(async (params: TableQueryParams) => {
75
81
  if (!sdk) throw new Error("SDK not initialized");
@@ -98,6 +104,28 @@ export default function CreditNoteListTable({
98
104
  [t],
99
105
  );
100
106
 
107
+ const handleExportPdfs = useCallback(() => {
108
+ if (selectedIds.size > 0 && onExportSelected) {
109
+ onExportSelected(Array.from(selectedIds));
110
+ }
111
+ }, [selectedIds, onExportSelected]);
112
+
113
+ const handleDeselectAll = useCallback(() => {
114
+ setSelectedIds(new Set());
115
+ }, []);
116
+
117
+ const selectionToolbar = useCallback(
118
+ (count: number) => (
119
+ <SelectionToolbar
120
+ selectedCount={count}
121
+ onExportPdfs={onExportSelected ? handleExportPdfs : undefined}
122
+ onDeselectAll={handleDeselectAll}
123
+ t={t}
124
+ />
125
+ ),
126
+ [handleExportPdfs, handleDeselectAll, onExportSelected, t],
127
+ );
128
+
101
129
  const columns: Column<CreditNote>[] = useMemo(
102
130
  () => [
103
131
  {
@@ -157,6 +185,7 @@ export default function CreditNoteListTable({
157
185
  cell: (creditNote) => (
158
186
  <CreditNoteListRowActions
159
187
  creditNote={creditNote}
188
+ onView={onView}
160
189
  onAddPayment={onAddPayment}
161
190
  onDuplicate={onDuplicate}
162
191
  onDownloadStart={onDownloadStart}
@@ -168,7 +197,17 @@ export default function CreditNoteListTable({
168
197
  ),
169
198
  },
170
199
  ],
171
- [t, onRowClick, onAddPayment, onDuplicate, onDownloadStart, onDownloadSuccess, onDownloadError, i18nProps.locale],
200
+ [
201
+ t,
202
+ onRowClick,
203
+ onView,
204
+ onAddPayment,
205
+ onDuplicate,
206
+ onDownloadStart,
207
+ onDownloadSuccess,
208
+ onDownloadError,
209
+ i18nProps.locale,
210
+ ],
172
211
  );
173
212
 
174
213
  return (
@@ -184,6 +223,10 @@ export default function CreditNoteListTable({
184
223
  filterConfig={filterConfig}
185
224
  t={t}
186
225
  locale={i18nProps.locale}
226
+ selectable={!!onExportSelected}
227
+ selectedIds={selectedIds}
228
+ onSelectionChange={setSelectedIds}
229
+ selectionToolbar={selectionToolbar}
187
230
  />
188
231
  );
189
232
  }
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Keine Gutschriften gefunden",
39
39
  "Try adjusting your search criteria": "Versuchen Sie, Ihre Suchkriterien anzupassen",
40
40
  "Clear search": "Suche zurücksetzen",
41
+ selected: "ausgewählt",
42
+ "Export PDFs": "PDFs exportieren",
43
+ "Deselect all": "Auswahl aufheben",
41
44
  } as const;
@@ -5,4 +5,7 @@ export default {
5
5
  "Create new": "Create credit note",
6
6
  // No results translations
7
7
  "No results found": "No credit notes found",
8
+ selected: "selected",
9
+ "Export PDFs": "Export PDFs",
10
+ "Deselect all": "Deselect all",
8
11
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "No se encontraron notas de credito",
39
39
  "Try adjusting your search criteria": "Intente ajustar sus criterios de busqueda",
40
40
  "Clear search": "Limpiar busqueda",
41
+ selected: "seleccionados",
42
+ "Export PDFs": "Exportar PDFs",
43
+ "Deselect all": "Deseleccionar todo",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Aucun avoir trouve",
39
39
  "Try adjusting your search criteria": "Essayez de modifier vos criteres de recherche",
40
40
  "Clear search": "Effacer la recherche",
41
+ selected: "sélectionnés",
42
+ "Export PDFs": "Exporter les PDF",
43
+ "Deselect all": "Tout désélectionner",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Nije pronadjeno nijedno odobrenje",
39
39
  "Try adjusting your search criteria": "Pokusajte prilagoditi kriterije pretrazivanja",
40
40
  "Clear search": "Ocisti pretragu",
41
+ selected: "odabranih",
42
+ "Export PDFs": "Izvezi PDF-ove",
43
+ "Deselect all": "Poništi odabir",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Nessuna nota di credito trovata",
39
39
  "Try adjusting your search criteria": "Prova a modificare i criteri di ricerca",
40
40
  "Clear search": "Cancella ricerca",
41
+ selected: "selezionati",
42
+ "Export PDFs": "Esporta PDF",
43
+ "Deselect all": "Deseleziona tutto",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Geen creditnota's gevonden",
39
39
  "Try adjusting your search criteria": "Probeer uw zoekcriteria aan te passen",
40
40
  "Clear search": "Zoekopdracht wissen",
41
+ selected: "geselecteerd",
42
+ "Export PDFs": "PDFs exporteren",
43
+ "Deselect all": "Alles deselecteren",
41
44
  } as const;
@@ -38,4 +38,7 @@ export default {
38
38
  "No results found": "Nie znaleziono not kredytowych",
39
39
  "Try adjusting your search criteria": "Sprobuj dostosowac kryteria wyszukiwania",
40
40
  "Clear search": "Wyczysc wyszukiwanie",
41
+ selected: "wybranych",
42
+ "Export PDFs": "Eksportuj PDF-y",
43
+ "Deselect all": "Odznacz wszystko",
41
44
  } as const;