@spaceinvoices/react-ui 0.4.4 → 0.4.6

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 (178) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/components/advance-invoices/advance-invoices.hooks.ts +2 -2
  4. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +151 -45
  5. package/src/components/advance-invoices/create/locales/de.ts +20 -0
  6. package/src/components/advance-invoices/create/locales/es.ts +20 -0
  7. package/src/components/advance-invoices/create/locales/fr.ts +20 -0
  8. package/src/components/advance-invoices/create/locales/hr.ts +20 -0
  9. package/src/components/advance-invoices/create/locales/it.ts +20 -0
  10. package/src/components/advance-invoices/create/locales/nl.ts +20 -0
  11. package/src/components/advance-invoices/create/locales/pl.ts +20 -0
  12. package/src/components/advance-invoices/create/locales/pt.ts +20 -0
  13. package/src/components/advance-invoices/create/locales/sl.ts +20 -0
  14. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +5 -5
  15. package/src/components/advance-invoices/list/list-row-actions.tsx +48 -1
  16. package/src/components/advance-invoices/list/list-table.tsx +21 -1
  17. package/src/components/advance-invoices/list/locales/de.ts +4 -0
  18. package/src/components/advance-invoices/list/locales/en.ts +3 -0
  19. package/src/components/advance-invoices/list/locales/es.ts +4 -0
  20. package/src/components/advance-invoices/list/locales/fr.ts +4 -0
  21. package/src/components/advance-invoices/list/locales/hr.ts +3 -0
  22. package/src/components/advance-invoices/list/locales/it.ts +4 -0
  23. package/src/components/advance-invoices/list/locales/nl.ts +4 -0
  24. package/src/components/advance-invoices/list/locales/pl.ts +3 -0
  25. package/src/components/advance-invoices/list/locales/pt.ts +4 -0
  26. package/src/components/advance-invoices/list/locales/sl.ts +3 -0
  27. package/src/components/credit-notes/create/create-credit-note-form.tsx +161 -42
  28. package/src/components/credit-notes/create/locales/de.ts +21 -0
  29. package/src/components/credit-notes/create/locales/es.ts +21 -0
  30. package/src/components/credit-notes/create/locales/fr.ts +21 -0
  31. package/src/components/credit-notes/create/locales/hr.ts +21 -0
  32. package/src/components/credit-notes/create/locales/it.ts +21 -0
  33. package/src/components/credit-notes/create/locales/nl.ts +21 -0
  34. package/src/components/credit-notes/create/locales/pl.ts +21 -0
  35. package/src/components/credit-notes/create/locales/pt.ts +21 -0
  36. package/src/components/credit-notes/create/locales/sl.ts +22 -1
  37. package/src/components/credit-notes/credit-notes.hooks.ts +2 -2
  38. package/src/components/credit-notes/list/list-row-actions.tsx +44 -1
  39. package/src/components/credit-notes/list/list-table.tsx +16 -2
  40. package/src/components/credit-notes/list/locales/de.ts +2 -0
  41. package/src/components/credit-notes/list/locales/en.ts +2 -0
  42. package/src/components/credit-notes/list/locales/es.ts +2 -0
  43. package/src/components/credit-notes/list/locales/fr.ts +2 -0
  44. package/src/components/credit-notes/list/locales/hr.ts +2 -0
  45. package/src/components/credit-notes/list/locales/it.ts +2 -0
  46. package/src/components/credit-notes/list/locales/nl.ts +2 -0
  47. package/src/components/credit-notes/list/locales/pl.ts +2 -0
  48. package/src/components/credit-notes/list/locales/pt.ts +2 -0
  49. package/src/components/credit-notes/list/locales/sl.ts +2 -0
  50. package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -9
  51. package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +77 -48
  52. package/src/components/dashboard/shared/use-revenue-data.ts +77 -9
  53. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +114 -7
  54. package/src/components/delivery-notes/create/locales/de.ts +21 -0
  55. package/src/components/delivery-notes/create/locales/es.ts +21 -0
  56. package/src/components/delivery-notes/create/locales/fr.ts +21 -0
  57. package/src/components/delivery-notes/create/locales/hr.ts +21 -0
  58. package/src/components/delivery-notes/create/locales/it.ts +21 -0
  59. package/src/components/delivery-notes/create/locales/nl.ts +21 -0
  60. package/src/components/delivery-notes/create/locales/pl.ts +21 -0
  61. package/src/components/delivery-notes/create/locales/pt.ts +21 -0
  62. package/src/components/delivery-notes/create/locales/sl.ts +22 -1
  63. package/src/components/delivery-notes/list/list-table.tsx +17 -8
  64. package/src/components/delivery-notes/list/locales/de.ts +32 -1
  65. package/src/components/delivery-notes/list/locales/en.ts +31 -0
  66. package/src/components/delivery-notes/list/locales/es.ts +32 -1
  67. package/src/components/delivery-notes/list/locales/fr.ts +32 -1
  68. package/src/components/delivery-notes/list/locales/hr.ts +32 -1
  69. package/src/components/delivery-notes/list/locales/it.ts +32 -1
  70. package/src/components/delivery-notes/list/locales/nl.ts +32 -1
  71. package/src/components/delivery-notes/list/locales/pl.ts +32 -1
  72. package/src/components/delivery-notes/list/locales/pt.ts +32 -1
  73. package/src/components/delivery-notes/list/locales/sl.ts +32 -1
  74. package/src/components/documents/create/document-add-item-form.tsx +70 -0
  75. package/src/components/documents/create/document-details-section.tsx +590 -344
  76. package/src/components/documents/create/document-items-section.tsx +21 -4
  77. package/src/components/documents/create/live-preview.tsx +24 -4
  78. package/src/components/documents/create/mark-as-paid-section.tsx +29 -20
  79. package/src/components/documents/create/prepare-document-submission.ts +22 -8
  80. package/src/components/documents/create/smart-code-insert-button.tsx +6 -0
  81. package/src/components/documents/shared/document-preview-display.tsx +3 -4
  82. package/src/components/documents/view/document-actions-bar.tsx +7 -27
  83. package/src/components/documents/view/document-details-card.tsx +32 -9
  84. package/src/components/documents/view/locales/de.ts +3 -0
  85. package/src/components/documents/view/locales/es.ts +3 -0
  86. package/src/components/documents/view/locales/fr.ts +3 -0
  87. package/src/components/documents/view/locales/hr.ts +3 -0
  88. package/src/components/documents/view/locales/it.ts +3 -0
  89. package/src/components/documents/view/locales/nl.ts +3 -0
  90. package/src/components/documents/view/locales/pl.ts +3 -0
  91. package/src/components/documents/view/locales/pt.ts +3 -0
  92. package/src/components/documents/view/locales/sl.ts +4 -1
  93. package/src/components/documents/view/use-document-download.ts +6 -3
  94. package/src/components/entities/create-entity-form.tsx +2 -1
  95. package/src/components/entities/entity-settings-form/email-template-variables-info.tsx +6 -0
  96. package/src/components/entities/entity-settings-form/input-with-preview.tsx +2 -117
  97. package/src/components/entities/entity-settings-form/locales/de.ts +5 -0
  98. package/src/components/entities/entity-settings-form/locales/es.ts +5 -0
  99. package/src/components/entities/entity-settings-form/locales/fr.ts +5 -0
  100. package/src/components/entities/entity-settings-form/locales/hr.ts +5 -0
  101. package/src/components/entities/entity-settings-form/locales/it.ts +5 -0
  102. package/src/components/entities/entity-settings-form/locales/nl.ts +5 -0
  103. package/src/components/entities/entity-settings-form/locales/pl.ts +5 -0
  104. package/src/components/entities/entity-settings-form/locales/pt.ts +5 -0
  105. package/src/components/entities/entity-settings-form/locales/sl.ts +5 -0
  106. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +15 -0
  107. package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +5 -1
  108. package/src/components/entities/fina-settings-form/locales/de.ts +3 -0
  109. package/src/components/entities/fina-settings-form/locales/en.ts +3 -0
  110. package/src/components/entities/fina-settings-form/locales/es.ts +3 -0
  111. package/src/components/entities/fina-settings-form/locales/fr.ts +3 -0
  112. package/src/components/entities/fina-settings-form/locales/hr.ts +3 -0
  113. package/src/components/entities/fina-settings-form/locales/it.ts +3 -0
  114. package/src/components/entities/fina-settings-form/locales/nl.ts +3 -0
  115. package/src/components/entities/fina-settings-form/locales/pl.ts +3 -0
  116. package/src/components/entities/fina-settings-form/locales/pt.ts +3 -0
  117. package/src/components/entities/fina-settings-form/locales/sl.ts +3 -0
  118. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +4 -4
  119. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +3 -3
  120. package/src/components/entities/settings/company-settings-form.tsx +173 -20
  121. package/src/components/entities/settings/defaults-settings-form.tsx +38 -1
  122. package/src/components/entities/settings/tax-rules-settings-form.tsx +1 -2
  123. package/src/components/estimates/create/create-estimate-form.tsx +107 -8
  124. package/src/components/estimates/create/locales/de.ts +21 -0
  125. package/src/components/estimates/create/locales/es.ts +21 -0
  126. package/src/components/estimates/create/locales/fr.ts +21 -0
  127. package/src/components/estimates/create/locales/hr.ts +21 -0
  128. package/src/components/estimates/create/locales/it.ts +21 -0
  129. package/src/components/estimates/create/locales/nl.ts +21 -0
  130. package/src/components/estimates/create/locales/pl.ts +21 -0
  131. package/src/components/estimates/create/locales/pt.ts +21 -0
  132. package/src/components/estimates/create/locales/sl.ts +22 -1
  133. package/src/components/estimates/list/list-table.tsx +11 -2
  134. package/src/components/estimates/list/locales/de.ts +1 -0
  135. package/src/components/estimates/list/locales/en.ts +1 -0
  136. package/src/components/estimates/list/locales/es.ts +1 -0
  137. package/src/components/estimates/list/locales/fr.ts +1 -0
  138. package/src/components/estimates/list/locales/hr.ts +1 -0
  139. package/src/components/estimates/list/locales/it.ts +1 -0
  140. package/src/components/estimates/list/locales/nl.ts +1 -0
  141. package/src/components/estimates/list/locales/pl.ts +1 -0
  142. package/src/components/estimates/list/locales/pt.ts +1 -0
  143. package/src/components/estimates/list/locales/sl.ts +1 -0
  144. package/src/components/export/document-export-form.tsx +46 -12
  145. package/src/components/invoices/create/create-invoice-form.tsx +186 -48
  146. package/src/components/invoices/create/locales/de.ts +28 -0
  147. package/src/components/invoices/create/locales/es.ts +28 -0
  148. package/src/components/invoices/create/locales/fr.ts +28 -0
  149. package/src/components/invoices/create/locales/hr.ts +28 -0
  150. package/src/components/invoices/create/locales/it.ts +28 -0
  151. package/src/components/invoices/create/locales/nl.ts +28 -0
  152. package/src/components/invoices/create/locales/pl.ts +28 -0
  153. package/src/components/invoices/create/locales/pt.ts +28 -0
  154. package/src/components/invoices/create/locales/sl.ts +29 -1
  155. package/src/components/invoices/create/prepare-invoice-submission.ts +5 -5
  156. package/src/components/invoices/invoices.hooks.ts +2 -2
  157. package/src/components/invoices/view/fiscalization-status-card.tsx +10 -8
  158. package/src/components/table/search-input.tsx +17 -0
  159. package/src/components/table/table-pagination.tsx +1 -1
  160. package/src/generated/schemas/advanceinvoice.ts +6 -2
  161. package/src/generated/schemas/creditnote.ts +3 -1
  162. package/src/generated/schemas/deliverynote.ts +3 -1
  163. package/src/generated/schemas/entity.ts +4 -4
  164. package/src/generated/schemas/entityapikey.ts +19 -0
  165. package/src/generated/schemas/estimate.ts +6 -2
  166. package/src/generated/schemas/index.ts +1 -0
  167. package/src/generated/schemas/invoice.ts +4 -1
  168. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -23
  169. package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -23
  170. package/src/generated/schemas/renderdeliverynotepreview_body.ts +17 -23
  171. package/src/generated/schemas/renderestimatepreview_body.ts +17 -23
  172. package/src/generated/schemas/renderinvoicepreview_body.ts +19 -23
  173. package/src/generated/schemas/startpdfexport_body.ts +14 -2
  174. package/src/generated/schemas/webhook.ts +4 -0
  175. package/src/hooks/use-duplicate-document.ts +16 -9
  176. package/src/hooks/use-vies-check.ts +3 -0
  177. package/src/lib/template-variables.tsx +167 -0
  178. package/src/providers/entities-context.tsx +2 -2
@@ -1,6 +1,6 @@
1
1
  import type { AdvanceInvoice } from "@spaceinvoices/js-sdk";
2
2
 
3
- import { Copy, Download, Eye, Link2Off, Loader2, MoreHorizontal, Plus } from "lucide-react";
3
+ import { Ban, Copy, Download, Eye, Link2Off, Loader2, MoreHorizontal, Plus } from "lucide-react";
4
4
  import { Button } from "@/ui/components/ui/button";
5
5
  import {
6
6
  DropdownMenu,
@@ -11,6 +11,7 @@ import {
11
11
  DropdownMenuSeparator,
12
12
  DropdownMenuTrigger,
13
13
  } from "@/ui/components/ui/dropdown-menu";
14
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
14
15
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
15
16
  import { createTranslation } from "@/ui/lib/translation";
16
17
  import { useAdvanceInvoiceDownload } from "./use-advance-invoice-download";
@@ -25,6 +26,8 @@ type AdvanceInvoiceListRowActionsProps = {
25
26
  onDownloadError?: (error: string) => void;
26
27
  onUnshare?: (advanceInvoice: AdvanceInvoice) => Promise<void>;
27
28
  isUnsharing?: boolean;
29
+ onVoid?: (advanceInvoice: AdvanceInvoice) => void;
30
+ isVoiding?: boolean;
28
31
  } & ComponentTranslationProps;
29
32
 
30
33
  export default function AdvanceInvoiceListRowActions({
@@ -37,6 +40,8 @@ export default function AdvanceInvoiceListRowActions({
37
40
  onDownloadError,
38
41
  onUnshare,
39
42
  isUnsharing,
43
+ onVoid,
44
+ isVoiding,
40
45
  ...i18nProps
41
46
  }: AdvanceInvoiceListRowActionsProps) {
42
47
  const t = createTranslation(i18nProps);
@@ -110,6 +115,48 @@ export default function AdvanceInvoiceListRowActions({
110
115
  </DropdownMenuGroup>
111
116
  </>
112
117
  )}
118
+ {onVoid &&
119
+ !(advanceInvoice as any).voided_at &&
120
+ !(advanceInvoice as any).is_draft &&
121
+ (() => {
122
+ const isLinked = (advanceInvoice as any).document_relations?.some(
123
+ (rel: any) => rel.relation_type === "advance_applied" || rel.relation_type === "applied_to",
124
+ );
125
+ const voidDisabled = isVoiding || isLinked;
126
+ const voidItem = (
127
+ <DropdownMenuItem
128
+ className={
129
+ voidDisabled
130
+ ? "text-destructive opacity-50"
131
+ : "cursor-pointer text-destructive focus:text-destructive"
132
+ }
133
+ onClick={() => !voidDisabled && onVoid(advanceInvoice)}
134
+ disabled={voidDisabled}
135
+ >
136
+ {isVoiding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Ban className="h-4 w-4" />}
137
+ {t("Void")}
138
+ </DropdownMenuItem>
139
+ );
140
+ return (
141
+ <>
142
+ <DropdownMenuSeparator />
143
+ <DropdownMenuGroup>
144
+ {isLinked ? (
145
+ <Tooltip>
146
+ <TooltipTrigger asChild>
147
+ <div>{voidItem}</div>
148
+ </TooltipTrigger>
149
+ <TooltipContent side="left">
150
+ {t("Cannot void an advance invoice linked to an invoice")}
151
+ </TooltipContent>
152
+ </Tooltip>
153
+ ) : (
154
+ voidItem
155
+ )}
156
+ </DropdownMenuGroup>
157
+ </>
158
+ );
159
+ })()}
113
160
  </DropdownMenuContent>
114
161
  </DropdownMenu>
115
162
  );
@@ -56,9 +56,12 @@ type AdvanceInvoiceListTableProps = {
56
56
  onDownloadSuccess?: (fileName: string) => void;
57
57
  onDownloadError?: (error: string) => void;
58
58
  onExportSelected?: (documentIds: string[]) => void;
59
+ onCopyToInvoice?: (documentIds: string[]) => void;
59
60
  onRetryFiscalization?: (documentIds: string[]) => void;
60
61
  fiscalizationFeatures?: ("furs" | "fina")[];
61
62
  onCreateNew?: () => void;
63
+ onVoid?: (advanceInvoice: AdvanceInvoice) => void;
64
+ isVoiding?: boolean;
62
65
  } & ListTableProps<AdvanceInvoice>;
63
66
 
64
67
  export default function AdvanceInvoiceListTable({
@@ -73,9 +76,12 @@ export default function AdvanceInvoiceListTable({
73
76
  onDownloadSuccess,
74
77
  onDownloadError,
75
78
  onExportSelected,
79
+ onCopyToInvoice,
76
80
  onRetryFiscalization,
77
81
  fiscalizationFeatures,
78
82
  onCreateNew,
83
+ onVoid,
84
+ isVoiding,
79
85
  ...i18nProps
80
86
  }: AdvanceInvoiceListTableProps) {
81
87
  const t = createTranslation({
@@ -97,6 +103,7 @@ export default function AdvanceInvoiceListTable({
97
103
  prev_cursor: params.prev_cursor,
98
104
  search: params.search,
99
105
  query: params.query,
106
+ include: "document_relations",
100
107
  });
101
108
  return response as unknown as TableQueryResponse<AdvanceInvoice>;
102
109
  }, entityId);
@@ -118,6 +125,12 @@ export default function AdvanceInvoiceListTable({
118
125
  }
119
126
  }, [selectedIds, onExportSelected]);
120
127
 
128
+ const handleCopyToInvoice = useCallback(() => {
129
+ if (selectedIds.size > 0 && onCopyToInvoice) {
130
+ onCopyToInvoice(Array.from(selectedIds));
131
+ }
132
+ }, [selectedIds, onCopyToInvoice]);
133
+
121
134
  const handleDeselectAll = useCallback(() => {
122
135
  setSelectedIds(new Set());
123
136
  }, []);
@@ -140,6 +153,7 @@ export default function AdvanceInvoiceListTable({
140
153
  <SelectionToolbar
141
154
  selectedCount={count}
142
155
  onExportPdfs={onExportSelected ? handleExportPdfs : undefined}
156
+ onCopyToInvoice={onCopyToInvoice ? handleCopyToInvoice : undefined}
143
157
  onRetryFiscalization={showRetry ? () => onRetryFiscalization(failedDocs.map((d) => d.id)) : undefined}
144
158
  retryFiscalizationDisabled={someFailed && !allFailed}
145
159
  retryFiscalizationTooltip={
@@ -152,8 +166,10 @@ export default function AdvanceInvoiceListTable({
152
166
  },
153
167
  [
154
168
  handleExportPdfs,
169
+ handleCopyToInvoice,
155
170
  handleDeselectAll,
156
171
  onExportSelected,
172
+ onCopyToInvoice,
157
173
  onRetryFiscalization,
158
174
  fiscalizationFeatures,
159
175
  selectedIds,
@@ -240,6 +256,8 @@ export default function AdvanceInvoiceListTable({
240
256
  onDownloadStart={onDownloadStart}
241
257
  onDownloadSuccess={onDownloadSuccess}
242
258
  onDownloadError={onDownloadError}
259
+ onVoid={onVoid}
260
+ isVoiding={isVoiding}
243
261
  t={t}
244
262
  locale={i18nProps.locale}
245
263
  />
@@ -255,6 +273,8 @@ export default function AdvanceInvoiceListTable({
255
273
  onDownloadStart,
256
274
  onDownloadSuccess,
257
275
  onDownloadError,
276
+ onVoid,
277
+ isVoiding,
258
278
  i18nProps.locale,
259
279
  fiscalizationFeatures,
260
280
  ],
@@ -274,7 +294,7 @@ export default function AdvanceInvoiceListTable({
274
294
  filterConfig={filterConfig}
275
295
  t={t}
276
296
  locale={i18nProps.locale}
277
- selectable={!!(onExportSelected || onRetryFiscalization)}
297
+ selectable={!!(onExportSelected || onCopyToInvoice || onRetryFiscalization)}
278
298
  selectedIds={selectedIds}
279
299
  onSelectionChange={setSelectedIds}
280
300
  selectionToolbar={selectionToolbar}
@@ -47,4 +47,8 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "In Rechnung kopieren",
51
+ Void: "Stornieren",
52
+ "Cannot void an advance invoice linked to an invoice":
53
+ "Stornierung einer mit einer Rechnung verknüpften Anzahlungsrechnung nicht möglich",
50
54
  } as const;
@@ -13,4 +13,7 @@ export default {
13
13
  "FINA fiscalization failed": "FINA fiscalization failed",
14
14
  "Retry Fiscalization": "Retry Fiscalization",
15
15
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
16
+ "Copy to Invoice": "Copy to Invoice",
17
+ Void: "Void",
18
+ "Cannot void an advance invoice linked to an invoice": "Cannot void an advance invoice linked to an invoice",
16
19
  } as const;
@@ -47,4 +47,8 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "Copiar a factura",
51
+ Void: "Anular",
52
+ "Cannot void an advance invoice linked to an invoice":
53
+ "No se puede anular una factura anticipada vinculada a una factura",
50
54
  } as const;
@@ -47,4 +47,8 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "Copier vers facture",
51
+ Void: "Annuler",
52
+ "Cannot void an advance invoice linked to an invoice":
53
+ "Impossible d'annuler une facture d'acompte liée à une facture",
50
54
  } as const;
@@ -47,4 +47,7 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiskalizacija nije uspjela",
48
48
  "Retry Fiscalization": "Ponovi fiskalizaciju",
49
49
  "Some selected documents don't have failed fiscalization": "Neki odabrani dokumenti nemaju neuspjelu fiskalizaciju",
50
+ "Copy to Invoice": "Kopiraj u račun",
51
+ Void: "Storniraj",
52
+ "Cannot void an advance invoice linked to an invoice": "Nije moguće poništiti avansni račun povezan s računom",
50
53
  } as const;
@@ -47,4 +47,8 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "Copia in fattura",
51
+ Void: "Annulla",
52
+ "Cannot void an advance invoice linked to an invoice":
53
+ "Impossibile annullare una fattura anticipata collegata a una fattura",
50
54
  } as const;
@@ -47,4 +47,8 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "Kopieer naar factuur",
51
+ Void: "Nietig verklaren",
52
+ "Cannot void an advance invoice linked to an invoice":
53
+ "Kan een voorschotfactuur gekoppeld aan een factuur niet ongeldig maken",
50
54
  } as const;
@@ -47,4 +47,7 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "Kopiuj do faktury",
51
+ Void: "Anuluj",
52
+ "Cannot void an advance invoice linked to an invoice": "Nie można anulować faktury zaliczkowej powiązanej z fakturą",
50
53
  } as const;
@@ -47,4 +47,8 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiscalization failed",
48
48
  "Retry Fiscalization": "Retry Fiscalization",
49
49
  "Some selected documents don't have failed fiscalization": "Some selected documents don't have failed fiscalization",
50
+ "Copy to Invoice": "Copiar para fatura",
51
+ Void: "Anular",
52
+ "Cannot void an advance invoice linked to an invoice":
53
+ "Não é possível anular uma fatura antecipada vinculada a uma fatura",
50
54
  } as const;
@@ -47,4 +47,7 @@ export default {
47
47
  "FINA fiscalization failed": "FINA fiskalizacija ni uspela",
48
48
  "Retry Fiscalization": "Ponovi fiskalizacijo",
49
49
  "Some selected documents don't have failed fiscalization": "Nekateri izbrani dokumenti nimajo neuspele fiskalizacije",
50
+ "Copy to Invoice": "Kopiraj v račun",
51
+ Void: "Storniraj",
52
+ "Cannot void an advance invoice linked to an invoice": "Ni mogoče razveljaviti predračuna, ki je povezan z računom",
50
53
  } as const;
@@ -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";
@@ -16,8 +17,11 @@ import { useFormFooterRegistration } from "@/ui/providers/form-footer-context";
16
17
  import { CUSTOMERS_CACHE_KEY } from "../../customers/customers.hooks";
17
18
  import {
18
19
  DocumentDetailsSection,
20
+ DocumentFooterField,
19
21
  DocumentNoteField,
20
22
  DocumentPaymentTermsField,
23
+ DocumentSignatureField,
24
+ DocumentTaxClauseField,
21
25
  } from "../../documents/create/document-details-section";
22
26
  import { DocumentItemsSection, type PriceModesMap } from "../../documents/create/document-items-section";
23
27
  import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
@@ -128,8 +132,8 @@ export default function CreateCreditNoteForm({
128
132
  const hasFinaPremises = activeFinaPremises.length > 0;
129
133
 
130
134
  // FINA premise/device selection state (no skip - all FINA credit notes must be fiscalized)
131
- const [selectedFinaPremiseId, setSelectedFinaPremiseId] = useState<string | undefined>();
132
- const [selectedFinaDeviceId, setSelectedFinaDeviceId] = useState<string | undefined>();
135
+ const [selectedFinaBusinessPremiseName, setSelectedFinaBusinessPremiseName] = useState<string | undefined>();
136
+ const [selectedFinaElectronicDeviceName, setSelectedFinaElectronicDeviceName] = useState<string | undefined>();
133
137
 
134
138
  // UI-only state (not part of API schema)
135
139
  const [markAsPaid, setMarkAsPaid] = useState(false);
@@ -199,22 +203,24 @@ export default function CreateCreditNoteForm({
199
203
 
200
204
  // Get active FINA devices for selected premise
201
205
  const activeFinaDevices = useMemo(() => {
202
- if (!selectedFinaPremiseId) return [];
203
- const premise = activeFinaPremises.find((p: any) => p.premise_id === selectedFinaPremiseId);
206
+ if (!selectedFinaBusinessPremiseName) return [];
207
+ const premise = activeFinaPremises.find((p: any) => p.business_premise_name === selectedFinaBusinessPremiseName);
204
208
  return premise?.Devices?.filter((d: any) => d.is_active) || [];
205
- }, [activeFinaPremises, selectedFinaPremiseId]);
209
+ }, [activeFinaPremises, selectedFinaBusinessPremiseName]);
206
210
 
207
211
  // Initialize FINA selection from localStorage or first active combo
208
212
  useEffect(() => {
209
- if (!isFinaEnabled || !hasFinaPremises || selectedFinaPremiseId) return;
213
+ if (!isFinaEnabled || !hasFinaPremises || selectedFinaBusinessPremiseName) return;
210
214
 
211
215
  const lastUsed = getLastUsedFinaCombo(entityId);
212
216
  if (lastUsed) {
213
- const premise = activeFinaPremises.find((p: any) => p.premise_id === lastUsed.premise_id);
214
- const device = premise?.Devices?.find((d: any) => d.device_id === lastUsed.device_id && d.is_active);
217
+ const premise = activeFinaPremises.find((p: any) => p.business_premise_name === lastUsed.business_premise_name);
218
+ const device = premise?.Devices?.find(
219
+ (d: any) => d.electronic_device_name === lastUsed.electronic_device_name && d.is_active,
220
+ );
215
221
  if (premise && device) {
216
- setSelectedFinaPremiseId(lastUsed.premise_id);
217
- setSelectedFinaDeviceId(lastUsed.device_id);
222
+ setSelectedFinaBusinessPremiseName(lastUsed.business_premise_name);
223
+ setSelectedFinaElectronicDeviceName(lastUsed.electronic_device_name);
218
224
  return;
219
225
  }
220
226
  }
@@ -222,30 +228,31 @@ export default function CreateCreditNoteForm({
222
228
  const firstPremise = activeFinaPremises[0];
223
229
  const firstDevice = firstPremise?.Devices?.find((d: any) => d.is_active);
224
230
  if (firstPremise && firstDevice) {
225
- setSelectedFinaPremiseId(firstPremise.premise_id);
226
- setSelectedFinaDeviceId(firstDevice.device_id);
231
+ setSelectedFinaBusinessPremiseName(firstPremise.business_premise_name);
232
+ setSelectedFinaElectronicDeviceName(firstDevice.electronic_device_name);
227
233
  }
228
- }, [isFinaEnabled, hasFinaPremises, activeFinaPremises, entityId, selectedFinaPremiseId]);
234
+ }, [isFinaEnabled, hasFinaPremises, activeFinaPremises, entityId, selectedFinaBusinessPremiseName]);
229
235
 
230
236
  // When FINA premise changes, select first active device
231
237
  useEffect(() => {
232
- if (!selectedFinaPremiseId) return;
233
- const premise = activeFinaPremises.find((p: any) => p.premise_id === selectedFinaPremiseId);
238
+ if (!selectedFinaBusinessPremiseName) return;
239
+ const premise = activeFinaPremises.find((p: any) => p.business_premise_name === selectedFinaBusinessPremiseName);
234
240
  const firstDevice = premise?.Devices?.find((d: any) => d.is_active);
235
- if (firstDevice && selectedFinaDeviceId !== firstDevice.device_id) {
241
+ if (firstDevice && selectedFinaElectronicDeviceName !== firstDevice.electronic_device_name) {
236
242
  const currentDeviceInPremise = premise?.Devices?.find(
237
- (d: any) => d.device_id === selectedFinaDeviceId && d.is_active,
243
+ (d: any) => d.electronic_device_name === selectedFinaElectronicDeviceName && d.is_active,
238
244
  );
239
245
  if (!currentDeviceInPremise) {
240
- setSelectedFinaDeviceId(firstDevice.device_id);
246
+ setSelectedFinaElectronicDeviceName(firstDevice.electronic_device_name);
241
247
  }
242
248
  }
243
- }, [selectedFinaPremiseId, activeFinaPremises, selectedFinaDeviceId]);
249
+ }, [selectedFinaBusinessPremiseName, activeFinaPremises, selectedFinaElectronicDeviceName]);
244
250
 
245
251
  // FINA selection ready and active checks
246
252
  const isFinaSelectionReady =
247
- !isFinaEnabled || !hasFinaPremises || (!!selectedFinaPremiseId && !!selectedFinaDeviceId);
248
- const isFinaActive = isFinaEnabled && hasFinaPremises && selectedFinaPremiseId && selectedFinaDeviceId;
253
+ !isFinaEnabled || !hasFinaPremises || (!!selectedFinaBusinessPremiseName && !!selectedFinaElectronicDeviceName);
254
+ const isFinaActive =
255
+ isFinaEnabled && hasFinaPremises && selectedFinaBusinessPremiseName && selectedFinaElectronicDeviceName;
249
256
 
250
257
  // ============================================================================
251
258
  // Next Credit Note Number Preview
@@ -262,8 +269,9 @@ export default function CreateCreditNoteForm({
262
269
 
263
270
  // No header action for credit notes - FINA can't be skipped
264
271
 
265
- // Get default payment terms from entity settings
272
+ // Get default payment terms and footer from entity settings
266
273
  const defaultPaymentTerms = (activeEntity?.settings as any)?.default_credit_note_payment_terms || "";
274
+ const defaultFooter = (activeEntity?.settings as any)?.document_footer || "";
267
275
 
268
276
  const form = useForm<CreateCreditNoteFormValues>({
269
277
  // Cast resolver to accept extended form type (includes UI-only fields)
@@ -275,13 +283,18 @@ export default function CreateCreditNoteForm({
275
283
  // Cast customer to form schema type (API type may have additional fields)
276
284
  customer: (initialValues?.customer as CreateCreditNoteFormValues["customer"]) ?? undefined,
277
285
  items: initialValues?.items?.length
278
- ? initialValues.items.map((item) => ({
286
+ ? initialValues.items.map((item: any) => ({
287
+ type: item.type,
279
288
  name: item.name || "",
280
289
  description: item.description || "",
281
- quantity: item.quantity ?? 1,
282
- // Use gross_price if set, otherwise use price
283
- price: item.gross_price ?? item.price,
284
- taxes: item.taxes || [],
290
+ ...(item.type !== "separator"
291
+ ? {
292
+ quantity: item.quantity ?? 1,
293
+ // Use gross_price if set, otherwise use price
294
+ price: item.gross_price ?? item.price,
295
+ taxes: item.taxes || [],
296
+ }
297
+ : {}),
285
298
  }))
286
299
  : [
287
300
  {
@@ -293,8 +306,11 @@ export default function CreateCreditNoteForm({
293
306
  },
294
307
  ],
295
308
  currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
309
+ reference: (initialValues as any)?.reference ?? "",
296
310
  note: initialValues?.note ?? "",
311
+ tax_clause: (initialValues as any)?.tax_clause ?? "",
297
312
  payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
313
+ footer: (initialValues as any)?.footer ?? defaultFooter,
298
314
  },
299
315
  });
300
316
 
@@ -302,6 +318,41 @@ export default function CreateCreditNoteForm({
302
318
  control: form.control,
303
319
  });
304
320
 
321
+ // ============================================================================
322
+ // VIES Check - determine if reverse charge applies
323
+ // ============================================================================
324
+ const {
325
+ reverseChargeApplies,
326
+ transactionType,
327
+ customerCountryCode: viesCustomerCountryCode,
328
+ isFetching: isViesFetching,
329
+ warning: viesWarning,
330
+ } = useViesCheck({
331
+ issuerCountryCode: activeEntity?.country_code,
332
+ isTaxSubject: activeEntity?.is_tax_subject ?? true,
333
+ customerCountry: formValues.customer?.country,
334
+ customerCountryCode: formValues.customer?.country_code,
335
+ customerTaxNumber: formValues.customer?.tax_number,
336
+ enabled: !!activeEntity,
337
+ });
338
+
339
+ // FINA non-domestic guard: hide FINA selectors for non-domestic transactions
340
+ const isFinaNonDomestic = isFinaEnabled && viesCustomerCountryCode != null && viesCustomerCountryCode !== "HR";
341
+
342
+ // Auto-populate tax_clause from entity settings when transaction type changes
343
+ const effectiveTransactionType = transactionType ?? "domestic";
344
+ const prevTransactionTypeRef = useRef<string | undefined>(undefined);
345
+ useEffect(() => {
346
+ if (effectiveTransactionType === prevTransactionTypeRef.current) return;
347
+ prevTransactionTypeRef.current = effectiveTransactionType;
348
+
349
+ const taxClauseDefaults = (activeEntity?.settings as any)?.tax_clause_defaults;
350
+ if (!taxClauseDefaults) return;
351
+
352
+ const clause = taxClauseDefaults[effectiveTransactionType] ?? "";
353
+ form.setValue("tax_clause", clause);
354
+ }, [effectiveTransactionType, activeEntity, form]);
355
+
305
356
  // Extract customer management logic into a shared hook
306
357
  const {
307
358
  originalCustomer,
@@ -331,10 +382,10 @@ export default function CreateCreditNoteForm({
331
382
  });
332
383
  }
333
384
  // Save FINA combo to localStorage on successful creation
334
- if (isFinaActive && selectedFinaPremiseId && selectedFinaDeviceId) {
385
+ if (isFinaActive && selectedFinaBusinessPremiseName && selectedFinaElectronicDeviceName) {
335
386
  setLastUsedFinaCombo(entityId, {
336
- premise_id: selectedFinaPremiseId,
337
- device_id: selectedFinaDeviceId,
387
+ business_premise_name: selectedFinaBusinessPremiseName,
388
+ electronic_device_name: selectedFinaElectronicDeviceName,
338
389
  });
339
390
  }
340
391
  // Invalidate customers cache when a customer was created/linked
@@ -357,8 +408,16 @@ export default function CreateCreditNoteForm({
357
408
 
358
409
  // Build FINA options (skip for drafts; FINA can't be skipped)
359
410
  const finaOptions =
360
- !isDraft && isFinaEnabled && selectedFinaPremiseId && selectedFinaDeviceId
361
- ? { premise_id: selectedFinaPremiseId, device_id: selectedFinaDeviceId, payment_type: paymentTypes[0] }
411
+ !isDraft &&
412
+ isFinaEnabled &&
413
+ !isFinaNonDomestic &&
414
+ selectedFinaBusinessPremiseName &&
415
+ selectedFinaElectronicDeviceName
416
+ ? {
417
+ business_premise_name: selectedFinaBusinessPremiseName,
418
+ electronic_device_name: selectedFinaElectronicDeviceName,
419
+ payment_type: paymentTypes[0],
420
+ }
362
421
  : undefined;
363
422
 
364
423
  const payload = prepareDocumentSubmission(values, {
@@ -387,12 +446,13 @@ export default function CreateCreditNoteForm({
387
446
  createCreditNote,
388
447
  isFursEnabled,
389
448
  isFinaEnabled,
449
+ isFinaNonDomestic,
390
450
  markAsPaid,
391
451
  originalCustomer,
392
452
  paymentTypes,
393
453
  selectedDeviceName,
394
- selectedFinaDeviceId,
395
- selectedFinaPremiseId,
454
+ selectedFinaElectronicDeviceName,
455
+ selectedFinaBusinessPremiseName,
396
456
  selectedPremiseName,
397
457
  showCustomerForm,
398
458
  ],
@@ -415,7 +475,7 @@ export default function CreateCreditNoteForm({
415
475
  useFormFooterRegistration({
416
476
  formId: "create-credit-note-form",
417
477
  isPending,
418
- isDirty: form.formState.isDirty,
478
+ isDirty: form.formState.isDirty || !!initialValues,
419
479
  label: t("Save"),
420
480
  secondaryAction: {
421
481
  label: t("Save as Draft"),
@@ -434,6 +494,14 @@ export default function CreateCreditNoteForm({
434
494
  if (entityDefaultPaymentTerms && !form.getValues("payment_terms")) {
435
495
  form.setValue("payment_terms", entityDefaultPaymentTerms);
436
496
  }
497
+ const entityDefaultFooter = (activeEntity?.settings as any)?.document_footer;
498
+ if (entityDefaultFooter && !form.getValues("footer")) {
499
+ form.setValue("footer", entityDefaultFooter);
500
+ }
501
+ const entityDefaultSignature = (activeEntity?.settings as any)?.default_document_signature;
502
+ if (entityDefaultSignature && !form.getValues("signature")) {
503
+ form.setValue("signature", entityDefaultSignature);
504
+ }
437
505
  }, [activeEntity, form]);
438
506
 
439
507
  // Auto-add tax field for tax subject entities
@@ -468,8 +536,10 @@ export default function CreateCreditNoteForm({
468
536
  customer: formValues.customer,
469
537
  items: transformedItems,
470
538
  currency_code: formValues.currency_code,
539
+ reference: formValues.reference,
471
540
  note: formValues.note,
472
541
  payment_terms: formValues.payment_terms,
542
+ signature: formValues.signature,
473
543
  };
474
544
  onChange(payload);
475
545
  }
@@ -561,14 +631,20 @@ export default function CreateCreditNoteForm({
561
631
  : undefined
562
632
  }
563
633
  finaInline={
564
- isFinaEnabled && hasFinaPremises
634
+ isFinaEnabled && hasFinaPremises && !isFinaNonDomestic
565
635
  ? {
566
- premises: activeFinaPremises.map((p: any) => ({ id: p.id, premise_id: p.premise_id })),
567
- devices: activeFinaDevices.map((d: any) => ({ id: d.id, device_id: d.device_id })),
568
- selectedPremise: selectedFinaPremiseId,
569
- selectedDevice: selectedFinaDeviceId,
570
- onPremiseChange: setSelectedFinaPremiseId,
571
- onDeviceChange: setSelectedFinaDeviceId,
636
+ premises: activeFinaPremises.map((p: any) => ({
637
+ id: p.id,
638
+ business_premise_name: p.business_premise_name,
639
+ })),
640
+ devices: activeFinaDevices.map((d: any) => ({
641
+ id: d.id,
642
+ electronic_device_name: d.electronic_device_name,
643
+ })),
644
+ selectedPremise: selectedFinaBusinessPremiseName,
645
+ selectedDevice: selectedFinaElectronicDeviceName,
646
+ onPremiseChange: setSelectedFinaBusinessPremiseName,
647
+ onDeviceChange: setSelectedFinaElectronicDeviceName,
572
648
  }
573
649
  : undefined
574
650
  }
@@ -597,6 +673,10 @@ export default function CreateCreditNoteForm({
597
673
  maxTaxesPerItem={activeEntity?.country_rules?.max_taxes_per_item}
598
674
  priceModesRef={priceModesRef}
599
675
  initialPriceModes={initialPriceModes}
676
+ taxesDisabled={reverseChargeApplies}
677
+ taxesDisabledMessage={
678
+ reverseChargeApplies ? t("Reverse charge - tax exempt EU B2B sale") : viesWarning ? viesWarning : undefined
679
+ }
600
680
  />
601
681
 
602
682
  <DocumentNoteField
@@ -611,6 +691,21 @@ export default function CreateCreditNoteForm({
611
691
  }}
612
692
  />
613
693
 
694
+ <DocumentTaxClauseField
695
+ control={form.control}
696
+ t={t}
697
+ entity={activeEntity}
698
+ document={{
699
+ number: formValues.number,
700
+ date: formValues.date,
701
+ currency_code: formValues.currency_code,
702
+ customer: formValues.customer as any,
703
+ }}
704
+ transactionType={transactionType}
705
+ isTransactionTypeFetching={isViesFetching}
706
+ isFinaNonDomestic={isFinaNonDomestic}
707
+ />
708
+
614
709
  <DocumentPaymentTermsField
615
710
  control={form.control}
616
711
  t={t}
@@ -622,6 +717,30 @@ export default function CreateCreditNoteForm({
622
717
  customer: formValues.customer as any,
623
718
  }}
624
719
  />
720
+
721
+ <DocumentSignatureField
722
+ control={form.control}
723
+ t={t}
724
+ entity={activeEntity}
725
+ document={{
726
+ number: formValues.number,
727
+ date: formValues.date,
728
+ currency_code: formValues.currency_code,
729
+ customer: formValues.customer as any,
730
+ }}
731
+ />
732
+
733
+ <DocumentFooterField
734
+ control={form.control}
735
+ t={t}
736
+ entity={activeEntity}
737
+ document={{
738
+ number: formValues.number,
739
+ date: formValues.date,
740
+ currency_code: formValues.currency_code,
741
+ customer: formValues.customer as any,
742
+ }}
743
+ />
625
744
  </form>
626
745
  </Form>
627
746
  );