@spaceinvoices/react-ui 0.4.3 → 0.4.5

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 (263) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/common/autocomplete.tsx +18 -2
  4. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +61 -12
  5. package/src/components/advance-invoices/create/locales/de.ts +17 -0
  6. package/src/components/advance-invoices/create/locales/es.ts +17 -0
  7. package/src/components/advance-invoices/create/locales/fr.ts +17 -0
  8. package/src/components/advance-invoices/create/locales/hr.ts +16 -0
  9. package/src/components/advance-invoices/create/locales/it.ts +17 -0
  10. package/src/components/advance-invoices/create/locales/nl.ts +17 -0
  11. package/src/components/advance-invoices/create/locales/pl.ts +16 -0
  12. package/src/components/advance-invoices/create/locales/pt.ts +16 -0
  13. package/src/components/advance-invoices/create/locales/sl.ts +16 -0
  14. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +0 -1
  15. package/src/components/advance-invoices/list/list-row-actions.tsx +48 -1
  16. package/src/components/advance-invoices/list/list-table.tsx +100 -50
  17. package/src/components/advance-invoices/list/locales/de.ts +9 -0
  18. package/src/components/advance-invoices/list/locales/en.ts +7 -0
  19. package/src/components/advance-invoices/list/locales/es.ts +9 -0
  20. package/src/components/advance-invoices/list/locales/fr.ts +9 -0
  21. package/src/components/advance-invoices/list/locales/hr.ts +8 -0
  22. package/src/components/advance-invoices/list/locales/it.ts +9 -0
  23. package/src/components/advance-invoices/list/locales/nl.ts +9 -0
  24. package/src/components/advance-invoices/list/locales/pl.ts +8 -0
  25. package/src/components/advance-invoices/list/locales/pt.ts +9 -0
  26. package/src/components/advance-invoices/list/locales/sl.ts +8 -0
  27. package/src/components/credit-notes/create/create-credit-note-form.tsx +185 -11
  28. package/src/components/credit-notes/create/locales/de.ts +18 -0
  29. package/src/components/credit-notes/create/locales/es.ts +18 -0
  30. package/src/components/credit-notes/create/locales/fr.ts +18 -0
  31. package/src/components/credit-notes/create/locales/hr.ts +17 -0
  32. package/src/components/credit-notes/create/locales/it.ts +18 -0
  33. package/src/components/credit-notes/create/locales/nl.ts +18 -0
  34. package/src/components/credit-notes/create/locales/pl.ts +17 -0
  35. package/src/components/credit-notes/create/locales/pt.ts +17 -0
  36. package/src/components/credit-notes/create/locales/sl.ts +18 -1
  37. package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
  38. package/src/components/credit-notes/list/list-row-actions.tsx +44 -1
  39. package/src/components/credit-notes/list/list-table.tsx +93 -32
  40. package/src/components/credit-notes/list/locales/de.ts +7 -0
  41. package/src/components/credit-notes/list/locales/en.ts +6 -0
  42. package/src/components/credit-notes/list/locales/es.ts +7 -0
  43. package/src/components/credit-notes/list/locales/fr.ts +7 -0
  44. package/src/components/credit-notes/list/locales/hr.ts +7 -0
  45. package/src/components/credit-notes/list/locales/it.ts +7 -0
  46. package/src/components/credit-notes/list/locales/nl.ts +7 -0
  47. package/src/components/credit-notes/list/locales/pl.ts +7 -0
  48. package/src/components/credit-notes/list/locales/pt.ts +7 -0
  49. package/src/components/credit-notes/list/locales/sl.ts +7 -0
  50. package/src/components/customers/customer-list-table/customer-list-table.tsx +0 -3
  51. package/src/components/customers/customer-list-table/locales/de.ts +1 -0
  52. package/src/components/customers/customer-list-table/locales/es.ts +1 -0
  53. package/src/components/customers/customer-list-table/locales/fr.ts +1 -0
  54. package/src/components/customers/customer-list-table/locales/hr.ts +1 -0
  55. package/src/components/customers/customer-list-table/locales/it.ts +1 -0
  56. package/src/components/customers/customer-list-table/locales/nl.ts +1 -0
  57. package/src/components/customers/customer-list-table/locales/pl.ts +1 -0
  58. package/src/components/customers/customer-list-table/locales/pt.ts +1 -0
  59. package/src/components/customers/customer-list-table/locales/sl.ts +1 -0
  60. package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -9
  61. package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +77 -48
  62. package/src/components/dashboard/shared/use-revenue-data.ts +77 -9
  63. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +67 -7
  64. package/src/components/delivery-notes/create/locales/de.ts +16 -0
  65. package/src/components/delivery-notes/create/locales/es.ts +16 -0
  66. package/src/components/delivery-notes/create/locales/fr.ts +16 -0
  67. package/src/components/delivery-notes/create/locales/hr.ts +16 -0
  68. package/src/components/delivery-notes/create/locales/it.ts +16 -0
  69. package/src/components/delivery-notes/create/locales/nl.ts +16 -0
  70. package/src/components/delivery-notes/create/locales/pl.ts +16 -0
  71. package/src/components/delivery-notes/create/locales/pt.ts +16 -0
  72. package/src/components/delivery-notes/create/locales/sl.ts +17 -1
  73. package/src/components/delivery-notes/list/list-row-actions.tsx +20 -1
  74. package/src/components/delivery-notes/list/list-table.tsx +50 -8
  75. package/src/components/delivery-notes/list/locales/de.ts +35 -0
  76. package/src/components/delivery-notes/list/locales/en.ts +33 -0
  77. package/src/components/delivery-notes/list/locales/es.ts +35 -0
  78. package/src/components/delivery-notes/list/locales/fr.ts +35 -0
  79. package/src/components/delivery-notes/list/locales/hr.ts +35 -0
  80. package/src/components/delivery-notes/list/locales/it.ts +35 -0
  81. package/src/components/delivery-notes/list/locales/nl.ts +35 -0
  82. package/src/components/delivery-notes/list/locales/pl.ts +35 -0
  83. package/src/components/delivery-notes/list/locales/pt.ts +35 -0
  84. package/src/components/delivery-notes/list/locales/sl.ts +35 -0
  85. package/src/components/documents/create/document-add-item-form.tsx +70 -0
  86. package/src/components/documents/create/document-details-section.tsx +163 -29
  87. package/src/components/documents/create/document-items-section.tsx +21 -4
  88. package/src/components/documents/create/linked-documents-info.tsx +82 -0
  89. package/src/components/documents/create/live-preview.tsx +25 -5
  90. package/src/components/documents/create/mark-as-paid-section.tsx +29 -20
  91. package/src/components/documents/create/prepare-document-submission.ts +19 -7
  92. package/src/components/documents/shared/document-preview-display.tsx +3 -4
  93. package/src/components/documents/types.ts +2 -2
  94. package/src/components/documents/view/document-actions-bar.tsx +7 -27
  95. package/src/components/documents/view/document-activities-list.tsx +65 -47
  96. package/src/components/documents/view/document-details-card.tsx +118 -76
  97. package/src/components/documents/view/document-payments-list.tsx +99 -65
  98. package/src/components/documents/view/document-relations-list.tsx +43 -28
  99. package/src/components/documents/view/document-sidebar.tsx +151 -0
  100. package/src/components/documents/view/index.ts +2 -0
  101. package/src/components/documents/view/locales/de.ts +4 -0
  102. package/src/components/documents/view/locales/es.ts +4 -0
  103. package/src/components/documents/view/locales/fr.ts +4 -0
  104. package/src/components/documents/view/locales/hr.ts +4 -0
  105. package/src/components/documents/view/locales/it.ts +4 -0
  106. package/src/components/documents/view/locales/nl.ts +4 -0
  107. package/src/components/documents/view/locales/pl.ts +4 -0
  108. package/src/components/documents/view/locales/pt.ts +4 -0
  109. package/src/components/documents/view/locales/sl.ts +5 -1
  110. package/src/components/documents/view/use-document-download.ts +7 -3
  111. package/src/components/entities/create-entity-form.tsx +166 -13
  112. package/src/components/entities/entity-settings-form/entity-settings-form.tsx +101 -1
  113. package/src/components/entities/entity-settings-form/input-with-preview.tsx +30 -2
  114. package/src/components/entities/entity-settings-form/locales/de.ts +10 -0
  115. package/src/components/entities/entity-settings-form/locales/es.ts +10 -0
  116. package/src/components/entities/entity-settings-form/locales/fr.ts +10 -0
  117. package/src/components/entities/entity-settings-form/locales/hr.ts +10 -0
  118. package/src/components/entities/entity-settings-form/locales/it.ts +10 -0
  119. package/src/components/entities/entity-settings-form/locales/nl.ts +10 -0
  120. package/src/components/entities/entity-settings-form/locales/pl.ts +10 -0
  121. package/src/components/entities/entity-settings-form/locales/pt.ts +10 -0
  122. package/src/components/entities/entity-settings-form/locales/sl.ts +10 -0
  123. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +6 -6
  124. package/src/components/entities/fina-settings-form/locales/de.ts +2 -2
  125. package/src/components/entities/fina-settings-form/locales/en.ts +2 -2
  126. package/src/components/entities/fina-settings-form/locales/es.ts +2 -2
  127. package/src/components/entities/fina-settings-form/locales/fr.ts +2 -2
  128. package/src/components/entities/fina-settings-form/locales/hr.ts +2 -2
  129. package/src/components/entities/fina-settings-form/locales/it.ts +2 -2
  130. package/src/components/entities/fina-settings-form/locales/nl.ts +2 -2
  131. package/src/components/entities/fina-settings-form/locales/pl.ts +2 -2
  132. package/src/components/entities/fina-settings-form/locales/pt.ts +2 -2
  133. package/src/components/entities/fina-settings-form/locales/sl.ts +2 -2
  134. package/src/components/entities/furs-settings-form/locales/en.ts +0 -1
  135. package/src/components/entities/settings/company-settings-form.tsx +173 -20
  136. package/src/components/entities/settings/pdf-template-selector/locales/de.ts +3 -1
  137. package/src/components/entities/settings/pdf-template-selector/locales/es.ts +3 -1
  138. package/src/components/entities/settings/pdf-template-selector/locales/fr.ts +4 -1
  139. package/src/components/entities/settings/pdf-template-selector/locales/hr.ts +4 -1
  140. package/src/components/entities/settings/pdf-template-selector/locales/it.ts +3 -1
  141. package/src/components/entities/settings/pdf-template-selector/locales/nl.ts +3 -1
  142. package/src/components/entities/settings/pdf-template-selector/locales/pl.ts +3 -1
  143. package/src/components/entities/settings/pdf-template-selector/locales/pt.ts +3 -1
  144. package/src/components/entities/settings/pdf-template-selector/locales/sl.ts +3 -1
  145. package/src/components/entities/settings/pdf-template-selector/pdf-template-cards.tsx +1 -0
  146. package/src/components/estimates/create/create-estimate-form.tsx +93 -8
  147. package/src/components/estimates/create/locales/de.ts +16 -0
  148. package/src/components/estimates/create/locales/es.ts +16 -0
  149. package/src/components/estimates/create/locales/fr.ts +16 -0
  150. package/src/components/estimates/create/locales/hr.ts +16 -0
  151. package/src/components/estimates/create/locales/it.ts +16 -0
  152. package/src/components/estimates/create/locales/nl.ts +16 -0
  153. package/src/components/estimates/create/locales/pl.ts +16 -0
  154. package/src/components/estimates/create/locales/pt.ts +16 -0
  155. package/src/components/estimates/create/locales/sl.ts +17 -1
  156. package/src/components/estimates/list/list-table.tsx +14 -8
  157. package/src/components/estimates/list/locales/de.ts +2 -0
  158. package/src/components/estimates/list/locales/en.ts +1 -0
  159. package/src/components/estimates/list/locales/es.ts +2 -0
  160. package/src/components/estimates/list/locales/fr.ts +2 -0
  161. package/src/components/estimates/list/locales/hr.ts +2 -0
  162. package/src/components/estimates/list/locales/it.ts +2 -0
  163. package/src/components/estimates/list/locales/nl.ts +2 -0
  164. package/src/components/estimates/list/locales/pl.ts +2 -0
  165. package/src/components/estimates/list/locales/pt.ts +2 -0
  166. package/src/components/estimates/list/locales/sl.ts +2 -0
  167. package/src/components/export/document-export-form.tsx +55 -14
  168. package/src/components/export/index.ts +2 -0
  169. package/src/components/export/sales-per-item-export-form.tsx +223 -0
  170. package/src/components/invoices/create/create-invoice-form.tsx +106 -11
  171. package/src/components/invoices/create/locales/de.ts +26 -0
  172. package/src/components/invoices/create/locales/es.ts +26 -0
  173. package/src/components/invoices/create/locales/fr.ts +26 -0
  174. package/src/components/invoices/create/locales/hr.ts +25 -0
  175. package/src/components/invoices/create/locales/it.ts +26 -0
  176. package/src/components/invoices/create/locales/nl.ts +26 -0
  177. package/src/components/invoices/create/locales/pl.ts +25 -0
  178. package/src/components/invoices/create/locales/pt.ts +25 -0
  179. package/src/components/invoices/create/locales/sl.ts +26 -1
  180. package/src/components/invoices/invoices-furs.hooks.ts +4 -1
  181. package/src/components/invoices/list/list-table.tsx +77 -31
  182. package/src/components/invoices/list/locales/de.ts +5 -0
  183. package/src/components/invoices/list/locales/en.ts +4 -0
  184. package/src/components/invoices/list/locales/es.ts +5 -0
  185. package/src/components/invoices/list/locales/fr.ts +5 -0
  186. package/src/components/invoices/list/locales/hr.ts +5 -0
  187. package/src/components/invoices/list/locales/it.ts +5 -0
  188. package/src/components/invoices/list/locales/nl.ts +5 -0
  189. package/src/components/invoices/list/locales/pl.ts +5 -0
  190. package/src/components/invoices/list/locales/pt.ts +5 -0
  191. package/src/components/invoices/list/locales/sl.ts +5 -0
  192. package/src/components/invoices/view/fiscalization-status-card.tsx +44 -24
  193. package/src/components/items/item-combobox.tsx +5 -3
  194. package/src/components/items/item-list-table/item-list-header.tsx +4 -17
  195. package/src/components/items/item-list-table/item-list-table.tsx +3 -3
  196. package/src/components/items/item-list-table/locales/de.ts +1 -0
  197. package/src/components/items/item-list-table/locales/es.ts +1 -0
  198. package/src/components/items/item-list-table/locales/fr.ts +1 -0
  199. package/src/components/items/item-list-table/locales/hr.ts +1 -0
  200. package/src/components/items/item-list-table/locales/it.ts +1 -0
  201. package/src/components/items/item-list-table/locales/nl.ts +1 -0
  202. package/src/components/items/item-list-table/locales/pl.ts +1 -0
  203. package/src/components/items/item-list-table/locales/pt.ts +1 -0
  204. package/src/components/items/item-list-table/locales/sl.ts +1 -0
  205. package/src/components/payments/list/list-table.tsx +0 -4
  206. package/src/components/payments/list/locales/de.ts +1 -0
  207. package/src/components/payments/list/locales/es.ts +1 -0
  208. package/src/components/payments/list/locales/fr.ts +1 -0
  209. package/src/components/payments/list/locales/hr.ts +1 -0
  210. package/src/components/payments/list/locales/it.ts +1 -0
  211. package/src/components/payments/list/locales/nl.ts +1 -0
  212. package/src/components/payments/list/locales/pl.ts +1 -0
  213. package/src/components/payments/list/locales/pt.ts +1 -0
  214. package/src/components/payments/list/locales/sl.ts +1 -0
  215. package/src/components/recurring-invoices/list/list-table.tsx +0 -7
  216. package/src/components/recurring-invoices/list/locales/de.ts +1 -0
  217. package/src/components/recurring-invoices/list/locales/es.ts +1 -0
  218. package/src/components/recurring-invoices/list/locales/fr.ts +1 -0
  219. package/src/components/recurring-invoices/list/locales/hr.ts +1 -0
  220. package/src/components/recurring-invoices/list/locales/it.ts +1 -0
  221. package/src/components/recurring-invoices/list/locales/nl.ts +1 -0
  222. package/src/components/recurring-invoices/list/locales/pl.ts +1 -0
  223. package/src/components/recurring-invoices/list/locales/pt.ts +1 -0
  224. package/src/components/recurring-invoices/list/locales/sl.ts +1 -0
  225. package/src/components/request-logs/request-log-list-table.tsx +0 -3
  226. package/src/components/table/README.md +14 -121
  227. package/src/components/table/data-table.tsx +22 -37
  228. package/src/components/table/hooks/use-table-state.ts +3 -27
  229. package/src/components/table/index.ts +0 -2
  230. package/src/components/table/search-input.tsx +17 -0
  231. package/src/components/table/selection-toolbar.tsx +35 -1
  232. package/src/components/table/table-empty-state.tsx +6 -3
  233. package/src/components/table/table-no-results.tsx +10 -5
  234. package/src/components/table/types.ts +0 -5
  235. package/src/components/taxes/tax-list-table/locales/de.ts +1 -0
  236. package/src/components/taxes/tax-list-table/locales/es.ts +1 -0
  237. package/src/components/taxes/tax-list-table/locales/fr.ts +1 -0
  238. package/src/components/taxes/tax-list-table/locales/hr.ts +1 -0
  239. package/src/components/taxes/tax-list-table/locales/it.ts +1 -0
  240. package/src/components/taxes/tax-list-table/locales/nl.ts +1 -0
  241. package/src/components/taxes/tax-list-table/locales/pl.ts +1 -0
  242. package/src/components/taxes/tax-list-table/locales/pt.ts +1 -0
  243. package/src/components/taxes/tax-list-table/locales/sl.ts +1 -0
  244. package/src/components/taxes/tax-list-table/tax-list-header.tsx +3 -14
  245. package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -3
  246. package/src/components/ui/popover.tsx +3 -1
  247. package/src/components/ui/tooltip.tsx +3 -1
  248. package/src/generated/schemas/advanceinvoice.ts +4 -2
  249. package/src/generated/schemas/creditnote.ts +2 -1
  250. package/src/generated/schemas/deliverynote.ts +2 -1
  251. package/src/generated/schemas/estimate.ts +4 -2
  252. package/src/generated/schemas/index.ts +1 -1
  253. package/src/generated/schemas/invoice.ts +2 -1
  254. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -23
  255. package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -23
  256. package/src/generated/schemas/renderdeliverynotepreview_body.ts +17 -23
  257. package/src/generated/schemas/renderestimatepreview_body.ts +17 -23
  258. package/src/generated/schemas/renderinvoicepreview_body.ts +19 -23
  259. package/src/hooks/use-duplicate-document.ts +56 -14
  260. package/src/hooks/use-vies-check.ts +3 -0
  261. package/src/lib/fiscalization.ts +12 -0
  262. package/src/lib/schemas/advance-invoice.ts +0 -1
  263. package/src/components/table/sortable-header.tsx +0 -56
@@ -9,6 +9,7 @@ import { Form } from "@/ui/components/ui/form";
9
9
  import { Skeleton } from "@/ui/components/ui/skeleton";
10
10
  import { createCreditNoteSchema } from "@/ui/generated/schemas";
11
11
  import { useNextDocumentNumber } from "@/ui/hooks/use-next-document-number";
12
+ import { useViesCheck } from "@/ui/hooks/use-vies-check";
12
13
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
13
14
  import { createTranslation } from "@/ui/lib/translation";
14
15
  import { useEntities } from "@/ui/providers/entities-context";
@@ -18,6 +19,7 @@ import {
18
19
  DocumentDetailsSection,
19
20
  DocumentNoteField,
20
21
  DocumentPaymentTermsField,
22
+ DocumentTaxClauseField,
21
23
  } from "../../documents/create/document-details-section";
22
24
  import { DocumentItemsSection, type PriceModesMap } from "../../documents/create/document-items-section";
23
25
  import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
@@ -26,7 +28,14 @@ import { prepareDocumentSubmission } from "../../documents/create/prepare-docume
26
28
  import { useDocumentCustomerForm } from "../../documents/create/use-document-customer-form";
27
29
  import type { DocumentTypes } from "../../documents/types";
28
30
  import { useFinaPremises, useFinaSettings } from "../../entities/fina-settings-form/fina-settings.hooks";
29
- import { getLastUsedFinaCombo, setLastUsedFinaCombo, useCreateCreditNote } from "../credit-notes.hooks";
31
+ import { useFursPremises, useFursSettings } from "../../entities/furs-settings-form/furs-settings.hooks";
32
+ import {
33
+ getLastUsedFinaCombo,
34
+ getLastUsedFursCombo,
35
+ setLastUsedFinaCombo,
36
+ setLastUsedFursCombo,
37
+ useCreateCreditNote,
38
+ } from "../credit-notes.hooks";
30
39
  import de from "./locales/de";
31
40
  import es from "./locales/es";
32
41
  import fr from "./locales/fr";
@@ -90,6 +99,23 @@ export default function CreateCreditNoteForm({
90
99
  const { activeEntity } = useEntities();
91
100
  const queryClient = useQueryClient();
92
101
 
102
+ // ============================================================================
103
+ // FURS Settings & Premises
104
+ // ============================================================================
105
+ const { data: fursSettings, isLoading: isFursSettingsLoading } = useFursSettings(entityId);
106
+ const { data: fursPremises, isLoading: isFursPremisesLoading } = useFursPremises(entityId, {
107
+ enabled: fursSettings?.enabled === true,
108
+ });
109
+
110
+ const isFursLoading = isFursSettingsLoading || (fursSettings?.enabled && isFursPremisesLoading);
111
+ const isFursEnabled = fursSettings?.enabled === true;
112
+ const activePremises = useMemo(() => fursPremises?.filter((p) => p.is_active) || [], [fursPremises]);
113
+ const hasFursPremises = activePremises.length > 0;
114
+
115
+ // FURS premise/device selection state
116
+ const [selectedPremiseName, setSelectedPremiseName] = useState<string | undefined>();
117
+ const [selectedDeviceName, setSelectedDeviceName] = useState<string | undefined>();
118
+
93
119
  // ============================================================================
94
120
  // FINA Settings & Premises
95
121
  // ============================================================================
@@ -122,6 +148,57 @@ export default function CreateCreditNoteForm({
122
148
  }, [initialValues?.items]);
123
149
  const priceModesRef = useRef<PriceModesMap>(initialPriceModes);
124
150
 
151
+ // Get active FURS devices for selected premise
152
+ const activeDevices = useMemo(() => {
153
+ if (!selectedPremiseName) return [];
154
+ const premise = activePremises.find((p) => p.business_premise_name === selectedPremiseName);
155
+ return premise?.Devices?.filter((d) => d.is_active) || [];
156
+ }, [activePremises, selectedPremiseName]);
157
+
158
+ // Initialize FURS selection from localStorage or first active combo
159
+ useEffect(() => {
160
+ if (!isFursEnabled || !hasFursPremises || selectedPremiseName) return;
161
+
162
+ const lastUsed = getLastUsedFursCombo(entityId);
163
+ if (lastUsed) {
164
+ const premise = activePremises.find((p) => p.business_premise_name === lastUsed.business_premise_name);
165
+ const device = premise?.Devices?.find(
166
+ (d) => d.electronic_device_name === lastUsed.electronic_device_name && d.is_active,
167
+ );
168
+ if (premise && device) {
169
+ setSelectedPremiseName(lastUsed.business_premise_name);
170
+ setSelectedDeviceName(lastUsed.electronic_device_name);
171
+ return;
172
+ }
173
+ }
174
+
175
+ const firstPremise = activePremises[0];
176
+ const firstDevice = firstPremise?.Devices?.find((d) => d.is_active);
177
+ if (firstPremise && firstDevice) {
178
+ setSelectedPremiseName(firstPremise.business_premise_name);
179
+ setSelectedDeviceName(firstDevice.electronic_device_name);
180
+ }
181
+ }, [isFursEnabled, hasFursPremises, activePremises, entityId, selectedPremiseName]);
182
+
183
+ // When FURS premise changes, select first active device
184
+ useEffect(() => {
185
+ if (!selectedPremiseName) return;
186
+ const premise = activePremises.find((p) => p.business_premise_name === selectedPremiseName);
187
+ const firstDevice = premise?.Devices?.find((d) => d.is_active);
188
+ if (firstDevice && selectedDeviceName !== firstDevice.electronic_device_name) {
189
+ const currentDeviceInPremise = premise?.Devices?.find(
190
+ (d) => d.electronic_device_name === selectedDeviceName && d.is_active,
191
+ );
192
+ if (!currentDeviceInPremise) {
193
+ setSelectedDeviceName(firstDevice.electronic_device_name);
194
+ }
195
+ }
196
+ }, [selectedPremiseName, activePremises, selectedDeviceName]);
197
+
198
+ // FURS selection ready and active checks
199
+ const isFursSelectionReady = !isFursEnabled || !hasFursPremises || (!!selectedPremiseName && !!selectedDeviceName);
200
+ const isFursActive = isFursEnabled && hasFursPremises && selectedPremiseName && selectedDeviceName;
201
+
125
202
  // Get active FINA devices for selected premise
126
203
  const activeFinaDevices = useMemo(() => {
127
204
  if (!selectedFinaPremiseId) return [];
@@ -176,11 +253,14 @@ export default function CreateCreditNoteForm({
176
253
  // Next Credit Note Number Preview
177
254
  // ============================================================================
178
255
  const { data: nextNumberData, isLoading: isNextNumberLoading } = useNextDocumentNumber(entityId, "credit_note", {
179
- enabled: !!entityId && !isFinaLoading && isFinaSelectionReady,
256
+ businessPremiseName: isFursActive ? selectedPremiseName : undefined,
257
+ electronicDeviceName: isFursActive ? selectedDeviceName : undefined,
258
+ enabled: !!entityId && !isFursLoading && isFursSelectionReady && !isFinaLoading && isFinaSelectionReady,
180
259
  });
181
260
 
182
261
  // Overall loading state
183
- const isFormDataLoading = isFinaLoading || !isFinaSelectionReady || isNextNumberLoading;
262
+ const isFormDataLoading =
263
+ isFursLoading || !isFursSelectionReady || isFinaLoading || !isFinaSelectionReady || isNextNumberLoading;
184
264
 
185
265
  // No header action for credit notes - FINA can't be skipped
186
266
 
@@ -197,13 +277,18 @@ export default function CreateCreditNoteForm({
197
277
  // Cast customer to form schema type (API type may have additional fields)
198
278
  customer: (initialValues?.customer as CreateCreditNoteFormValues["customer"]) ?? undefined,
199
279
  items: initialValues?.items?.length
200
- ? initialValues.items.map((item) => ({
280
+ ? initialValues.items.map((item: any) => ({
281
+ type: item.type,
201
282
  name: item.name || "",
202
283
  description: item.description || "",
203
- quantity: item.quantity ?? 1,
204
- // Use gross_price if set, otherwise use price
205
- price: item.gross_price ?? item.price,
206
- taxes: item.taxes || [],
284
+ ...(item.type !== "separator"
285
+ ? {
286
+ quantity: item.quantity ?? 1,
287
+ // Use gross_price if set, otherwise use price
288
+ price: item.gross_price ?? item.price,
289
+ taxes: item.taxes || [],
290
+ }
291
+ : {}),
207
292
  }))
208
293
  : [
209
294
  {
@@ -216,6 +301,7 @@ export default function CreateCreditNoteForm({
216
301
  ],
217
302
  currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
218
303
  note: initialValues?.note ?? "",
304
+ tax_clause: (initialValues as any)?.tax_clause ?? "",
219
305
  payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
220
306
  },
221
307
  });
@@ -224,6 +310,41 @@ export default function CreateCreditNoteForm({
224
310
  control: form.control,
225
311
  });
226
312
 
313
+ // ============================================================================
314
+ // VIES Check - determine if reverse charge applies
315
+ // ============================================================================
316
+ const {
317
+ reverseChargeApplies,
318
+ transactionType,
319
+ customerCountryCode: viesCustomerCountryCode,
320
+ isFetching: isViesFetching,
321
+ warning: viesWarning,
322
+ } = useViesCheck({
323
+ issuerCountryCode: activeEntity?.country_code,
324
+ isTaxSubject: activeEntity?.is_tax_subject ?? true,
325
+ customerCountry: formValues.customer?.country,
326
+ customerCountryCode: formValues.customer?.country_code,
327
+ customerTaxNumber: formValues.customer?.tax_number,
328
+ enabled: !!activeEntity,
329
+ });
330
+
331
+ // FINA non-domestic guard: hide FINA selectors for non-domestic transactions
332
+ const isFinaNonDomestic = isFinaEnabled && viesCustomerCountryCode != null && viesCustomerCountryCode !== "HR";
333
+
334
+ // Auto-populate tax_clause from entity settings when transaction type changes
335
+ const effectiveTransactionType = transactionType ?? "domestic";
336
+ const prevTransactionTypeRef = useRef<string | undefined>(undefined);
337
+ useEffect(() => {
338
+ if (effectiveTransactionType === prevTransactionTypeRef.current) return;
339
+ prevTransactionTypeRef.current = effectiveTransactionType;
340
+
341
+ const taxClauseDefaults = (activeEntity?.settings as any)?.tax_clause_defaults;
342
+ if (!taxClauseDefaults) return;
343
+
344
+ const clause = taxClauseDefaults[effectiveTransactionType] ?? "";
345
+ form.setValue("tax_clause", clause);
346
+ }, [effectiveTransactionType, activeEntity, form]);
347
+
227
348
  // Extract customer management logic into a shared hook
228
349
  const {
229
350
  originalCustomer,
@@ -245,6 +366,13 @@ export default function CreateCreditNoteForm({
245
366
  const { mutate: createCreditNote, isPending } = useCreateCreditNote({
246
367
  entityId,
247
368
  onSuccess: (data) => {
369
+ // Save FURS combo to localStorage on successful creation
370
+ if (isFursActive && selectedPremiseName && selectedDeviceName) {
371
+ setLastUsedFursCombo(entityId, {
372
+ business_premise_name: selectedPremiseName,
373
+ electronic_device_name: selectedDeviceName,
374
+ });
375
+ }
248
376
  // Save FINA combo to localStorage on successful creation
249
377
  if (isFinaActive && selectedFinaPremiseId && selectedFinaDeviceId) {
250
378
  setLastUsedFinaCombo(entityId, {
@@ -264,9 +392,15 @@ export default function CreateCreditNoteForm({
264
392
  // Shared submit logic for both regular save and save as draft
265
393
  const submitCreditNote = useCallback(
266
394
  (values: CreateCreditNoteFormValues, isDraft: boolean) => {
395
+ // Build FURS options (skip for drafts; no skip toggle for credit notes)
396
+ const fursOptions =
397
+ !isDraft && isFursEnabled && selectedPremiseName && selectedDeviceName
398
+ ? { business_premise_name: selectedPremiseName, electronic_device_name: selectedDeviceName }
399
+ : undefined;
400
+
267
401
  // Build FINA options (skip for drafts; FINA can't be skipped)
268
402
  const finaOptions =
269
- !isDraft && isFinaEnabled && selectedFinaPremiseId && selectedFinaDeviceId
403
+ !isDraft && isFinaEnabled && !isFinaNonDomestic && selectedFinaPremiseId && selectedFinaDeviceId
270
404
  ? { premise_id: selectedFinaPremiseId, device_id: selectedFinaDeviceId, payment_type: paymentTypes[0] }
271
405
  : undefined;
272
406
 
@@ -280,6 +414,11 @@ export default function CreateCreditNoteForm({
280
414
  isDraft,
281
415
  });
282
416
 
417
+ // Add FURS data to payload
418
+ if (fursOptions) {
419
+ (payload as any).furs = fursOptions;
420
+ }
421
+
283
422
  // Add FINA data to payload
284
423
  if (finaOptions) {
285
424
  (payload as any).fina = finaOptions;
@@ -289,12 +428,16 @@ export default function CreateCreditNoteForm({
289
428
  },
290
429
  [
291
430
  createCreditNote,
431
+ isFursEnabled,
292
432
  isFinaEnabled,
433
+ isFinaNonDomestic,
293
434
  markAsPaid,
294
435
  originalCustomer,
295
436
  paymentTypes,
437
+ selectedDeviceName,
296
438
  selectedFinaDeviceId,
297
439
  selectedFinaPremiseId,
440
+ selectedPremiseName,
298
441
  showCustomerForm,
299
442
  ],
300
443
  );
@@ -316,7 +459,7 @@ export default function CreateCreditNoteForm({
316
459
  useFormFooterRegistration({
317
460
  formId: "create-credit-note-form",
318
461
  isPending,
319
- isDirty: form.formState.isDirty,
462
+ isDirty: form.formState.isDirty || !!initialValues,
320
463
  label: t("Save"),
321
464
  secondaryAction: {
322
465
  label: t("Save as Draft"),
@@ -449,8 +592,20 @@ export default function CreateCreditNoteForm({
449
592
  control={form.control}
450
593
  documentType={_type}
451
594
  t={t}
595
+ fursInline={
596
+ isFursEnabled && hasFursPremises
597
+ ? {
598
+ premises: activePremises.map((p) => ({ id: p.id, business_premise_name: p.business_premise_name })),
599
+ devices: activeDevices.map((d) => ({ id: d.id, electronic_device_name: d.electronic_device_name })),
600
+ selectedPremise: selectedPremiseName,
601
+ selectedDevice: selectedDeviceName,
602
+ onPremiseChange: setSelectedPremiseName,
603
+ onDeviceChange: setSelectedDeviceName,
604
+ }
605
+ : undefined
606
+ }
452
607
  finaInline={
453
- isFinaEnabled && hasFinaPremises
608
+ isFinaEnabled && hasFinaPremises && !isFinaNonDomestic
454
609
  ? {
455
610
  premises: activeFinaPremises.map((p: any) => ({ id: p.id, premise_id: p.premise_id })),
456
611
  devices: activeFinaDevices.map((d: any) => ({ id: d.id, device_id: d.device_id })),
@@ -486,6 +641,10 @@ export default function CreateCreditNoteForm({
486
641
  maxTaxesPerItem={activeEntity?.country_rules?.max_taxes_per_item}
487
642
  priceModesRef={priceModesRef}
488
643
  initialPriceModes={initialPriceModes}
644
+ taxesDisabled={reverseChargeApplies}
645
+ taxesDisabledMessage={
646
+ reverseChargeApplies ? t("Reverse charge - tax exempt EU B2B sale") : viesWarning ? viesWarning : undefined
647
+ }
489
648
  />
490
649
 
491
650
  <DocumentNoteField
@@ -500,6 +659,21 @@ export default function CreateCreditNoteForm({
500
659
  }}
501
660
  />
502
661
 
662
+ <DocumentTaxClauseField
663
+ control={form.control}
664
+ t={t}
665
+ entity={activeEntity}
666
+ document={{
667
+ number: formValues.number,
668
+ date: formValues.date,
669
+ currency_code: formValues.currency_code,
670
+ customer: formValues.customer as any,
671
+ }}
672
+ transactionType={transactionType}
673
+ isTransactionTypeFetching={isViesFetching}
674
+ isFinaNonDomestic={isFinaNonDomestic}
675
+ />
676
+
503
677
  <DocumentPaymentTermsField
504
678
  control={form.control}
505
679
  t={t}
@@ -74,4 +74,22 @@ export default {
74
74
  "Cannot skip fiscalization for cash payments": "Fiskalisierung kann für Barzahlungen nicht übersprungen werden",
75
75
  Premise: "Geschäftsräume",
76
76
  Device: "Gerät",
77
+ "FINA fiscalized invoices always use the current date":
78
+ "FINA-fiskalisierte Dokumente verwenden immer das aktuelle Datum",
79
+ // Separator items
80
+ "Add separator": "Trennzeile hinzufügen",
81
+ "Section header": "Abschnittsüberschrift",
82
+ "Section title...": "Abschnittstitel...",
83
+ // Transaction type
84
+ "Transaction type": "Transaktionstyp",
85
+ Domestic: "Inland",
86
+ "EU B2B": "EU B2B",
87
+ "EU B2C": "EU B2C",
88
+ Export: "Export",
89
+ "Determining transaction type...": "Transaktionstyp wird ermittelt...",
90
+ "This invoice will not be fiscalized (non-domestic transaction)":
91
+ "Diese Rechnung wird nicht fiskalisiert (nicht-inländische Transaktion)",
92
+ "Tax Clause": "Steuerklausel",
93
+ "Add tax clause...": "Steuerklausel hinzufügen...",
94
+ "Reverse charge - tax exempt EU B2B sale": "Umkehrung der Steuerschuldnerschaft - steuerbefreiter EU B2B Verkauf",
77
95
  } as const;
@@ -73,4 +73,22 @@ export default {
73
73
  "Cannot skip fiscalization for cash payments": "No se puede omitir la fiscalizacion para pagos en efectivo",
74
74
  Premise: "Local",
75
75
  Device: "Dispositivo",
76
+ "FINA fiscalized invoices always use the current date":
77
+ "Los documentos fiscalizados con FINA siempre usan la fecha actual",
78
+ // Separator items
79
+ "Add separator": "Añadir separador",
80
+ "Section header": "Encabezado de sección",
81
+ "Section title...": "Título de sección...",
82
+ // Transaction type
83
+ "Transaction type": "Tipo de transacción",
84
+ Domestic: "Nacional",
85
+ "EU B2B": "EU B2B",
86
+ "EU B2C": "EU B2C",
87
+ Export: "Exportación",
88
+ "Determining transaction type...": "Determinando tipo de transacción...",
89
+ "This invoice will not be fiscalized (non-domestic transaction)":
90
+ "Esta factura no será fiscalizada (transacción no nacional)",
91
+ "Tax Clause": "Cláusula fiscal",
92
+ "Add tax clause...": "Agregar cláusula fiscal...",
93
+ "Reverse charge - tax exempt EU B2B sale": "Inversión del sujeto pasivo - venta EU B2B exenta de impuestos",
76
94
  } as const;
@@ -73,4 +73,22 @@ export default {
73
73
  "Cannot skip fiscalization for cash payments": "Impossible d'ignorer la fiscalisation pour les paiements en especes",
74
74
  Premise: "Local",
75
75
  Device: "Appareil",
76
+ "FINA fiscalized invoices always use the current date":
77
+ "Les documents fiscalisés FINA utilisent toujours la date actuelle",
78
+ // Separator items
79
+ "Add separator": "Ajouter un séparateur",
80
+ "Section header": "En-tête de section",
81
+ "Section title...": "Titre de section...",
82
+ // Transaction type
83
+ "Transaction type": "Type de transaction",
84
+ Domestic: "Nationale",
85
+ "EU B2B": "EU B2B",
86
+ "EU B2C": "EU B2C",
87
+ Export: "Exportation",
88
+ "Determining transaction type...": "Détermination du type de transaction...",
89
+ "This invoice will not be fiscalized (non-domestic transaction)":
90
+ "Cette facture ne sera pas fiscalisée (transaction non nationale)",
91
+ "Tax Clause": "Clause fiscale",
92
+ "Add tax clause...": "Ajouter une clause fiscale...",
93
+ "Reverse charge - tax exempt EU B2B sale": "Autoliquidation - vente B2B UE exonérée de taxe",
76
94
  } as const;
@@ -72,4 +72,21 @@ export default {
72
72
  "Cannot skip fiscalization for cash payments": "Za gotovinska placanja nije moguce preskociti fiskalizaciju",
73
73
  Premise: "Prostor",
74
74
  Device: "Uredaj",
75
+ "FINA fiscalized invoices always use the current date": "FINA fiskalizirani dokumenti uvijek koriste trenutni datum",
76
+ // Separator items
77
+ "Add separator": "Dodaj separator",
78
+ "Section header": "Naslov odjeljka",
79
+ "Section title...": "Naslov odjeljka...",
80
+ // Transaction type
81
+ "Transaction type": "Vrsta transakcije",
82
+ Domestic: "Domaća",
83
+ "EU B2B": "EU B2B",
84
+ "EU B2C": "EU B2C",
85
+ Export: "Izvoz",
86
+ "Determining transaction type...": "Određivanje vrste transakcije...",
87
+ "This invoice will not be fiscalized (non-domestic transaction)":
88
+ "Ovaj račun neće biti fiskaliziran (nedomaća transakcija)",
89
+ "Tax Clause": "Porezna klauzula",
90
+ "Add tax clause...": "Dodajte poreznu klauzulu...",
91
+ "Reverse charge - tax exempt EU B2B sale": "Prijenos porezne obveze - porezno oslobođena EU B2B prodaja",
75
92
  } as const;
@@ -74,4 +74,22 @@ export default {
74
74
  "Non e possibile saltare la fiscalizzazione per i pagamenti in contanti",
75
75
  Premise: "Locale",
76
76
  Device: "Dispositivo",
77
+ "FINA fiscalized invoices always use the current date":
78
+ "I documenti fiscalizzati FINA utilizzano sempre la data corrente",
79
+ // Separator items
80
+ "Add separator": "Aggiungi separatore",
81
+ "Section header": "Intestazione sezione",
82
+ "Section title...": "Titolo sezione...",
83
+ // Transaction type
84
+ "Transaction type": "Tipo di transazione",
85
+ Domestic: "Nazionale",
86
+ "EU B2B": "EU B2B",
87
+ "EU B2C": "EU B2C",
88
+ Export: "Esportazione",
89
+ "Determining transaction type...": "Determinazione tipo di transazione...",
90
+ "This invoice will not be fiscalized (non-domestic transaction)":
91
+ "Questa fattura non sarà fiscalizzata (transazione non nazionale)",
92
+ "Tax Clause": "Clausola fiscale",
93
+ "Add tax clause...": "Aggiungi clausola fiscale...",
94
+ "Reverse charge - tax exempt EU B2B sale": "Inversione contabile - vendita B2B UE esente da imposta",
77
95
  } as const;
@@ -73,4 +73,22 @@ export default {
73
73
  "Cannot skip fiscalization for cash payments": "Fiscalisatie kan niet worden overgeslagen voor contante betalingen",
74
74
  Premise: "Bedrijfsruimte",
75
75
  Device: "Apparaat",
76
+ "FINA fiscalized invoices always use the current date":
77
+ "FINA-gefiscaliseerde documenten gebruiken altijd de huidige datum",
78
+ // Separator items
79
+ "Add separator": "Scheidingslijn toevoegen",
80
+ "Section header": "Sectiekop",
81
+ "Section title...": "Sectietitel...",
82
+ // Transaction type
83
+ "Transaction type": "Transactietype",
84
+ Domestic: "Binnenland",
85
+ "EU B2B": "EU B2B",
86
+ "EU B2C": "EU B2C",
87
+ Export: "Export",
88
+ "Determining transaction type...": "Transactietype bepalen...",
89
+ "This invoice will not be fiscalized (non-domestic transaction)":
90
+ "Deze factuur wordt niet gefiscaliseerd (niet-binnenlandse transactie)",
91
+ "Tax Clause": "Belastingclausule",
92
+ "Add tax clause...": "Belastingclausule toevoegen...",
93
+ "Reverse charge - tax exempt EU B2B sale": "Verlegging - belastingvrijgestelde EU B2B verkoop",
76
94
  } as const;
@@ -72,4 +72,21 @@ export default {
72
72
  "Cannot skip fiscalization for cash payments": "Nie mozna pominac fiskalizacji dla platnosci gotowkowych",
73
73
  Premise: "Lokal",
74
74
  Device: "Urządzenie",
75
+ "FINA fiscalized invoices always use the current date": "Dokumenty fiskalizowane FINA zawsze używają bieżącej daty",
76
+ // Separator items
77
+ "Add separator": "Dodaj separator",
78
+ "Section header": "Nagłówek sekcji",
79
+ "Section title...": "Tytuł sekcji...",
80
+ // Transaction type
81
+ "Transaction type": "Typ transakcji",
82
+ Domestic: "Krajowa",
83
+ "EU B2B": "EU B2B",
84
+ "EU B2C": "EU B2C",
85
+ Export: "Eksport",
86
+ "Determining transaction type...": "Określanie typu transakcji...",
87
+ "This invoice will not be fiscalized (non-domestic transaction)":
88
+ "Ta faktura nie będzie fiskalizowana (transakcja niekrajowa)",
89
+ "Tax Clause": "Klauzula podatkowa",
90
+ "Add tax clause...": "Dodaj klauzulę podatkową...",
91
+ "Reverse charge - tax exempt EU B2B sale": "Odwrotne obciążenie - zwolniona z podatku sprzedaż EU B2B",
75
92
  } as const;
@@ -72,4 +72,21 @@ export default {
72
72
  "Cannot skip fiscalization for cash payments": "Nao e possivel ignorar a fiscalizacao para pagamentos em dinheiro",
73
73
  Premise: "Estabelecimento",
74
74
  Device: "Dispositivo",
75
+ "FINA fiscalized invoices always use the current date": "Os documentos fiscalizados FINA usam sempre a data atual",
76
+ // Separator items
77
+ "Add separator": "Adicionar separador",
78
+ "Section header": "Cabeçalho da secção",
79
+ "Section title...": "Título da secção...",
80
+ // Transaction type
81
+ "Transaction type": "Tipo de transação",
82
+ Domestic: "Nacional",
83
+ "EU B2B": "EU B2B",
84
+ "EU B2C": "EU B2C",
85
+ Export: "Exportação",
86
+ "Determining transaction type...": "Determinando tipo de transação...",
87
+ "This invoice will not be fiscalized (non-domestic transaction)":
88
+ "Esta fatura não será fiscalizada (transação não nacional)",
89
+ "Tax Clause": "Cláusula fiscal",
90
+ "Add tax clause...": "Adicionar cláusula fiscal...",
91
+ "Reverse charge - tax exempt EU B2B sale": "Autoliquidação - venda B2B UE isenta de imposto",
75
92
  } as const;
@@ -15,7 +15,7 @@ export default {
15
15
  Price: "Cena",
16
16
  Unit: "Enota",
17
17
  Discount: "Popust",
18
- Tax: "Davek",
18
+ Tax: "DDV",
19
19
  "Tax rate": "Davčna stopnja",
20
20
  "Add tax": "Dodaj davek",
21
21
  "Select tax...": "Izberi davek...",
@@ -71,4 +71,21 @@ export default {
71
71
  "Cannot skip fiscalization for cash payments": "Za gotovinska plačila ni mogoče preskočiti davčnega potrjevanja",
72
72
  Premise: "Poslovni prostor",
73
73
  Device: "Naprava",
74
+ "FINA fiscalized invoices always use the current date": "FINA fiskalizirani dokumenti vedno uporabijo trenutni datum",
75
+ // Separator items
76
+ "Add separator": "Dodaj ločilnik",
77
+ "Section header": "Naslov razdelka",
78
+ "Section title...": "Naslov razdelka...",
79
+ // Transaction type
80
+ "Transaction type": "Vrsta transakcije",
81
+ Domestic: "Domača",
82
+ "EU B2B": "EU B2B",
83
+ "EU B2C": "EU B2C",
84
+ Export: "Izvoz",
85
+ "Determining transaction type...": "Določanje vrste transakcije...",
86
+ "This invoice will not be fiscalized (non-domestic transaction)":
87
+ "Ta račun ne bo fiskaliziran (nedomača transakcija)",
88
+ "Tax Clause": "Davčna klavzula",
89
+ "Add tax clause...": "Dodajte davčno klavzulo...",
90
+ "Reverse charge - tax exempt EU B2B sale": "Obrnjena davčna obveznost - davka oproščena EU B2B prodaja",
74
91
  } as const;
@@ -21,6 +21,36 @@ const {
21
21
 
22
22
  export { useCreateCreditNote, useUpdateCreditNote, useDeleteCreditNote };
23
23
 
24
+ // ============================================================================
25
+ // FURS Last-Used Combo (localStorage) for credit notes
26
+ // ============================================================================
27
+
28
+ const FURS_CN_LAST_USED_KEY = "si:furs:cn:last-used";
29
+
30
+ export type FursCombo = {
31
+ business_premise_name: string;
32
+ electronic_device_name: string;
33
+ };
34
+
35
+ export function getLastUsedFursCombo(entityId: string): FursCombo | null {
36
+ if (typeof window === "undefined") return null;
37
+ try {
38
+ const stored = localStorage.getItem(`${FURS_CN_LAST_USED_KEY}:${entityId}`);
39
+ return stored ? JSON.parse(stored) : null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ export function setLastUsedFursCombo(entityId: string, combo: FursCombo): void {
46
+ if (typeof window === "undefined") return;
47
+ try {
48
+ localStorage.setItem(`${FURS_CN_LAST_USED_KEY}:${entityId}`, JSON.stringify(combo));
49
+ } catch {
50
+ // Ignore localStorage errors
51
+ }
52
+ }
53
+
24
54
  // ============================================================================
25
55
  // FINA Last-Used Combo (localStorage) for credit notes
26
56
  // ============================================================================
@@ -1,4 +1,4 @@
1
- import { Copy, Download, Eye, Link2Off, Loader2, MoreHorizontal, Plus } from "lucide-react";
1
+ import { Ban, Copy, Download, Eye, Link2Off, Loader2, MoreHorizontal, Plus } from "lucide-react";
2
2
  import { Button } from "@/ui/components/ui/button";
3
3
  import {
4
4
  DropdownMenu,
@@ -9,6 +9,7 @@ import {
9
9
  DropdownMenuSeparator,
10
10
  DropdownMenuTrigger,
11
11
  } from "@/ui/components/ui/dropdown-menu";
12
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
12
13
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
13
14
  import { createTranslation } from "@/ui/lib/translation";
14
15
  import { useCreditNoteDownload } from "./use-credit-note-download";
@@ -26,6 +27,8 @@ type CreditNoteListRowActionsProps = {
26
27
  onDownloadError?: (error: string) => void;
27
28
  onUnshare?: (creditNote: CreditNote) => Promise<void>;
28
29
  isUnsharing?: boolean;
30
+ onVoid?: (creditNote: CreditNote) => void;
31
+ isVoiding?: boolean;
29
32
  } & ComponentTranslationProps;
30
33
 
31
34
  export default function CreditNoteListRowActions({
@@ -38,6 +41,8 @@ export default function CreditNoteListRowActions({
38
41
  onDownloadError,
39
42
  onUnshare,
40
43
  isUnsharing,
44
+ onVoid,
45
+ isVoiding,
41
46
  ...i18nProps
42
47
  }: CreditNoteListRowActionsProps) {
43
48
  const t = createTranslation(i18nProps);
@@ -107,6 +112,44 @@ export default function CreditNoteListRowActions({
107
112
  </DropdownMenuGroup>
108
113
  </>
109
114
  )}
115
+ {onVoid &&
116
+ !creditNote.voided_at &&
117
+ !creditNote.is_draft &&
118
+ (() => {
119
+ const isFiscalized = !!(creditNote.furs || creditNote.fina);
120
+ const voidDisabled = isVoiding || isFiscalized;
121
+ const voidItem = (
122
+ <DropdownMenuItem
123
+ className={
124
+ voidDisabled
125
+ ? "text-destructive opacity-50"
126
+ : "cursor-pointer text-destructive focus:text-destructive"
127
+ }
128
+ onClick={() => !voidDisabled && onVoid(creditNote)}
129
+ disabled={voidDisabled}
130
+ >
131
+ {isVoiding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Ban className="h-4 w-4" />}
132
+ {t("Void")}
133
+ </DropdownMenuItem>
134
+ );
135
+ return (
136
+ <>
137
+ <DropdownMenuSeparator />
138
+ <DropdownMenuGroup>
139
+ {isFiscalized ? (
140
+ <Tooltip>
141
+ <TooltipTrigger asChild>
142
+ <div>{voidItem}</div>
143
+ </TooltipTrigger>
144
+ <TooltipContent side="left">{t("Cannot void a fiscalized credit note")}</TooltipContent>
145
+ </Tooltip>
146
+ ) : (
147
+ voidItem
148
+ )}
149
+ </DropdownMenuGroup>
150
+ </>
151
+ );
152
+ })()}
110
153
  </DropdownMenuContent>
111
154
  </DropdownMenu>
112
155
  );