@spaceinvoices/react-ui 0.4.8 → 0.4.11

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 (276) hide show
  1. package/README.md +24 -8
  2. package/cli/dist/index.js +89 -26
  3. package/package.json +4 -1
  4. package/spaceinvoices.schema.json +6 -1
  5. package/src/common/autocomplete.tsx +69 -6
  6. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +124 -285
  7. package/src/components/advance-invoices/list/list-table.tsx +10 -3
  8. package/src/components/advance-invoices/list/locales/de.ts +2 -0
  9. package/src/components/advance-invoices/list/locales/en.ts +1 -0
  10. package/src/components/advance-invoices/list/locales/es.ts +1 -0
  11. package/src/components/advance-invoices/list/locales/fr.ts +1 -0
  12. package/src/components/advance-invoices/list/locales/hr.ts +1 -0
  13. package/src/components/advance-invoices/list/locales/it.ts +1 -0
  14. package/src/components/advance-invoices/list/locales/nl.ts +1 -0
  15. package/src/components/advance-invoices/list/locales/pl.ts +1 -0
  16. package/src/components/advance-invoices/list/locales/pt.ts +1 -0
  17. package/src/components/advance-invoices/list/locales/sl.ts +1 -0
  18. package/src/components/advance-invoices/list/use-advance-invoice-download.ts +1 -12
  19. package/src/components/credit-notes/create/create-credit-note-form.tsx +116 -238
  20. package/src/components/credit-notes/list/list-table.tsx +6 -3
  21. package/src/components/credit-notes/list/use-credit-note-download.ts +1 -12
  22. package/src/components/customers/customer-autocomplete.tsx +64 -11
  23. package/src/components/customers/customer-list-table/customer-list-table.tsx +3 -2
  24. package/src/components/dashboard/collection-rate-card/collection-rate-card.tsx +9 -1
  25. package/src/components/dashboard/collection-rate-card/locales/bg.ts +3 -0
  26. package/src/components/dashboard/collection-rate-card/locales/cs.ts +3 -0
  27. package/src/components/dashboard/collection-rate-card/locales/et.ts +3 -0
  28. package/src/components/dashboard/collection-rate-card/locales/fi.ts +3 -0
  29. package/src/components/dashboard/collection-rate-card/locales/is.ts +3 -0
  30. package/src/components/dashboard/collection-rate-card/locales/nb.ts +3 -0
  31. package/src/components/dashboard/collection-rate-card/locales/sk.ts +3 -0
  32. package/src/components/dashboard/collection-rate-card/locales/sv.ts +3 -0
  33. package/src/components/dashboard/invoice-status-chart/invoice-status-chart.tsx +10 -2
  34. package/src/components/dashboard/invoice-status-chart/locales/bg.ts +10 -0
  35. package/src/components/dashboard/invoice-status-chart/locales/cs.ts +10 -0
  36. package/src/components/dashboard/invoice-status-chart/locales/de.ts +1 -0
  37. package/src/components/dashboard/invoice-status-chart/locales/es.ts +1 -0
  38. package/src/components/dashboard/invoice-status-chart/locales/et.ts +10 -0
  39. package/src/components/dashboard/invoice-status-chart/locales/fi.ts +10 -0
  40. package/src/components/dashboard/invoice-status-chart/locales/fr.ts +1 -0
  41. package/src/components/dashboard/invoice-status-chart/locales/hr.ts +1 -0
  42. package/src/components/dashboard/invoice-status-chart/locales/is.ts +10 -0
  43. package/src/components/dashboard/invoice-status-chart/locales/it.ts +1 -0
  44. package/src/components/dashboard/invoice-status-chart/locales/nb.ts +10 -0
  45. package/src/components/dashboard/invoice-status-chart/locales/nl.ts +1 -0
  46. package/src/components/dashboard/invoice-status-chart/locales/pl.ts +1 -0
  47. package/src/components/dashboard/invoice-status-chart/locales/pt.ts +1 -0
  48. package/src/components/dashboard/invoice-status-chart/locales/sk.ts +10 -0
  49. package/src/components/dashboard/invoice-status-chart/locales/sl.ts +1 -0
  50. package/src/components/dashboard/invoice-status-chart/locales/sv.ts +10 -0
  51. package/src/components/dashboard/payment-methods-chart/locales/bg.ts +12 -0
  52. package/src/components/dashboard/payment-methods-chart/locales/cs.ts +12 -0
  53. package/src/components/dashboard/payment-methods-chart/locales/et.ts +12 -0
  54. package/src/components/dashboard/payment-methods-chart/locales/fi.ts +12 -0
  55. package/src/components/dashboard/payment-methods-chart/locales/is.ts +12 -0
  56. package/src/components/dashboard/payment-methods-chart/locales/nb.ts +12 -0
  57. package/src/components/dashboard/payment-methods-chart/locales/sk.ts +12 -0
  58. package/src/components/dashboard/payment-methods-chart/locales/sv.ts +12 -0
  59. package/src/components/dashboard/payment-methods-chart/payment-methods-chart.tsx +9 -1
  60. package/src/components/dashboard/payment-trend-chart/locales/bg.ts +6 -0
  61. package/src/components/dashboard/payment-trend-chart/locales/cs.ts +6 -0
  62. package/src/components/dashboard/payment-trend-chart/locales/de.ts +1 -0
  63. package/src/components/dashboard/payment-trend-chart/locales/es.ts +1 -0
  64. package/src/components/dashboard/payment-trend-chart/locales/et.ts +6 -0
  65. package/src/components/dashboard/payment-trend-chart/locales/fi.ts +6 -0
  66. package/src/components/dashboard/payment-trend-chart/locales/fr.ts +1 -0
  67. package/src/components/dashboard/payment-trend-chart/locales/hr.ts +1 -0
  68. package/src/components/dashboard/payment-trend-chart/locales/is.ts +6 -0
  69. package/src/components/dashboard/payment-trend-chart/locales/it.ts +1 -0
  70. package/src/components/dashboard/payment-trend-chart/locales/nb.ts +6 -0
  71. package/src/components/dashboard/payment-trend-chart/locales/nl.ts +1 -0
  72. package/src/components/dashboard/payment-trend-chart/locales/pl.ts +1 -0
  73. package/src/components/dashboard/payment-trend-chart/locales/pt.ts +1 -0
  74. package/src/components/dashboard/payment-trend-chart/locales/sk.ts +6 -0
  75. package/src/components/dashboard/payment-trend-chart/locales/sl.ts +1 -0
  76. package/src/components/dashboard/payment-trend-chart/locales/sv.ts +6 -0
  77. package/src/components/dashboard/payment-trend-chart/payment-trend-chart.tsx +15 -8
  78. package/src/components/dashboard/revenue-trend-chart/locales/bg.ts +6 -0
  79. package/src/components/dashboard/revenue-trend-chart/locales/cs.ts +6 -0
  80. package/src/components/dashboard/revenue-trend-chart/locales/de.ts +1 -0
  81. package/src/components/dashboard/revenue-trend-chart/locales/es.ts +1 -0
  82. package/src/components/dashboard/revenue-trend-chart/locales/et.ts +6 -0
  83. package/src/components/dashboard/revenue-trend-chart/locales/fi.ts +6 -0
  84. package/src/components/dashboard/revenue-trend-chart/locales/fr.ts +1 -0
  85. package/src/components/dashboard/revenue-trend-chart/locales/hr.ts +1 -0
  86. package/src/components/dashboard/revenue-trend-chart/locales/is.ts +6 -0
  87. package/src/components/dashboard/revenue-trend-chart/locales/it.ts +1 -0
  88. package/src/components/dashboard/revenue-trend-chart/locales/nb.ts +6 -0
  89. package/src/components/dashboard/revenue-trend-chart/locales/nl.ts +1 -0
  90. package/src/components/dashboard/revenue-trend-chart/locales/pl.ts +1 -0
  91. package/src/components/dashboard/revenue-trend-chart/locales/pt.ts +1 -0
  92. package/src/components/dashboard/revenue-trend-chart/locales/sk.ts +6 -0
  93. package/src/components/dashboard/revenue-trend-chart/locales/sl.ts +1 -0
  94. package/src/components/dashboard/revenue-trend-chart/locales/sv.ts +6 -0
  95. package/src/components/dashboard/revenue-trend-chart/revenue-trend-chart.tsx +15 -8
  96. package/src/components/dashboard/tax-collected-card/locales.ts +110 -0
  97. package/src/components/dashboard/tax-collected-card/tax-collected-card.tsx +8 -2
  98. package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +4 -4
  99. package/src/components/dashboard/top-customers-chart/locales/bg.ts +7 -0
  100. package/src/components/dashboard/top-customers-chart/locales/cs.ts +7 -0
  101. package/src/components/dashboard/top-customers-chart/locales/de.ts +2 -0
  102. package/src/components/dashboard/top-customers-chart/locales/es.ts +2 -0
  103. package/src/components/dashboard/top-customers-chart/locales/et.ts +7 -0
  104. package/src/components/dashboard/top-customers-chart/locales/fi.ts +7 -0
  105. package/src/components/dashboard/top-customers-chart/locales/fr.ts +2 -0
  106. package/src/components/dashboard/top-customers-chart/locales/hr.ts +2 -0
  107. package/src/components/dashboard/top-customers-chart/locales/is.ts +7 -0
  108. package/src/components/dashboard/top-customers-chart/locales/it.ts +2 -0
  109. package/src/components/dashboard/top-customers-chart/locales/nb.ts +7 -0
  110. package/src/components/dashboard/top-customers-chart/locales/nl.ts +2 -0
  111. package/src/components/dashboard/top-customers-chart/locales/pl.ts +2 -0
  112. package/src/components/dashboard/top-customers-chart/locales/pt.ts +2 -0
  113. package/src/components/dashboard/top-customers-chart/locales/sk.ts +7 -0
  114. package/src/components/dashboard/top-customers-chart/locales/sl.ts +2 -0
  115. package/src/components/dashboard/top-customers-chart/locales/sv.ts +7 -0
  116. package/src/components/dashboard/top-customers-chart/top-customers-chart.tsx +23 -12
  117. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +33 -20
  118. package/src/components/delivery-notes/list/list-table.tsx +22 -13
  119. package/src/components/delivery-notes/list/locales/de.ts +2 -0
  120. package/src/components/delivery-notes/list/locales/en.ts +1 -0
  121. package/src/components/delivery-notes/list/locales/es.ts +1 -0
  122. package/src/components/delivery-notes/list/locales/fr.ts +1 -0
  123. package/src/components/delivery-notes/list/locales/hr.ts +1 -0
  124. package/src/components/delivery-notes/list/locales/it.ts +1 -0
  125. package/src/components/delivery-notes/list/locales/nl.ts +1 -0
  126. package/src/components/delivery-notes/list/locales/pl.ts +1 -0
  127. package/src/components/delivery-notes/list/locales/pt.ts +1 -0
  128. package/src/components/delivery-notes/list/locales/sl.ts +1 -0
  129. package/src/components/delivery-notes/list/use-delivery-note-download.ts +1 -12
  130. package/src/components/documents/create/document-add-item-form.tsx +28 -16
  131. package/src/components/documents/create/document-add-item-tax-rate-field.tsx +12 -2
  132. package/src/components/documents/create/document-items-section.tsx +70 -39
  133. package/src/components/documents/create/document-recipient-section.tsx +10 -1
  134. package/src/components/documents/create/live-preview.tsx +113 -15
  135. package/src/components/documents/create/prepare-document-submission.ts +35 -16
  136. package/src/components/documents/create/use-document-customer-form.ts +14 -3
  137. package/src/components/documents/documents.hooks.ts +7 -2
  138. package/src/components/documents/shared/document-preview-display.tsx +136 -67
  139. package/src/components/documents/shared/scaled-document-preview.tsx +45 -5
  140. package/src/components/documents/view/document-actions-bar.tsx +284 -182
  141. package/src/components/documents/view/document-activities-list.tsx +3 -0
  142. package/src/components/documents/view/document-payments-list.tsx +3 -0
  143. package/src/components/documents/view/locales/de.ts +8 -0
  144. package/src/components/documents/view/locales/es.ts +8 -0
  145. package/src/components/documents/view/locales/fr.ts +8 -0
  146. package/src/components/documents/view/locales/hr.ts +8 -0
  147. package/src/components/documents/view/locales/it.ts +8 -0
  148. package/src/components/documents/view/locales/nl.ts +8 -0
  149. package/src/components/documents/view/locales/pl.ts +8 -0
  150. package/src/components/documents/view/locales/pt.ts +8 -0
  151. package/src/components/documents/view/locales/sl.ts +8 -0
  152. package/src/components/documents/view/use-document-download.ts +14 -25
  153. package/src/components/entities/create-entity-form.tsx +101 -16
  154. package/src/components/entities/entity-settings-form/entity-settings-form.tsx +10 -10
  155. package/src/components/entities/entity-settings-form/locales/de.ts +10 -0
  156. package/src/components/entities/entity-settings-form/locales/es.ts +10 -0
  157. package/src/components/entities/entity-settings-form/locales/fr.ts +10 -0
  158. package/src/components/entities/entity-settings-form/locales/hr.ts +10 -0
  159. package/src/components/entities/entity-settings-form/locales/it.ts +10 -0
  160. package/src/components/entities/entity-settings-form/locales/nl.ts +10 -0
  161. package/src/components/entities/entity-settings-form/locales/pl.ts +10 -0
  162. package/src/components/entities/entity-settings-form/locales/pt.ts +10 -0
  163. package/src/components/entities/entity-settings-form/locales/sl.ts +10 -0
  164. package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +3 -3
  165. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +78 -124
  166. package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +8 -1
  167. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +14 -2
  168. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +7 -2
  169. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +56 -130
  170. package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +8 -1
  171. package/src/components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx +1 -0
  172. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
  173. package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +20 -3
  174. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +38 -12
  175. package/src/components/entities/settings/defaults-settings-form.tsx +6 -6
  176. package/src/components/entities/settings/eslog-settings-form.tsx +13 -1
  177. package/src/components/entities/settings/pdf-template-selector/demo-invoice-data.ts +3 -22
  178. package/src/components/entities/shared/fiscalization-step-flow.ts +77 -0
  179. package/src/components/entities/shared/fiscalization-step-tabs.tsx +71 -0
  180. package/src/components/estimates/create/create-estimate-form.tsx +34 -21
  181. package/src/components/estimates/list/list-table.tsx +23 -14
  182. package/src/components/estimates/list/locales/de.ts +2 -0
  183. package/src/components/estimates/list/locales/en.ts +1 -0
  184. package/src/components/estimates/list/locales/es.ts +1 -0
  185. package/src/components/estimates/list/locales/fr.ts +1 -0
  186. package/src/components/estimates/list/locales/hr.ts +1 -0
  187. package/src/components/estimates/list/locales/it.ts +1 -0
  188. package/src/components/estimates/list/locales/nl.ts +1 -0
  189. package/src/components/estimates/list/locales/pl.ts +1 -0
  190. package/src/components/estimates/list/locales/pt.ts +1 -0
  191. package/src/components/estimates/list/locales/sl.ts +1 -0
  192. package/src/components/estimates/list/use-estimate-download.ts +1 -12
  193. package/src/components/export/document-export-form.tsx +33 -7
  194. package/src/components/export/sales-per-item-export-form.tsx +23 -7
  195. package/src/components/invoices/create/create-invoice-form.tsx +295 -329
  196. package/src/components/invoices/create/prepare-invoice-submission.ts +0 -8
  197. package/src/components/invoices/list/list-table.tsx +7 -4
  198. package/src/components/invoices/list/use-invoice-download.ts +1 -11
  199. package/src/components/invoices/send-email-dialog/locales/de.ts +20 -0
  200. package/src/components/invoices/send-email-dialog/locales/es.ts +20 -0
  201. package/src/components/invoices/send-email-dialog/locales/fr.ts +20 -0
  202. package/src/components/invoices/send-email-dialog/locales/hr.ts +20 -0
  203. package/src/components/invoices/send-email-dialog/locales/it.ts +20 -0
  204. package/src/components/invoices/send-email-dialog/locales/nl.ts +20 -0
  205. package/src/components/invoices/send-email-dialog/locales/pl.ts +20 -0
  206. package/src/components/invoices/send-email-dialog/locales/pt.ts +20 -0
  207. package/src/components/invoices/send-email-dialog/locales/sl.ts +20 -0
  208. package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +77 -8
  209. package/src/components/invoices/view/eslog-info-display.tsx +17 -1
  210. package/src/components/invoices/view/fiscalization-status-card.tsx +7 -3
  211. package/src/components/items/item-combobox.tsx +26 -6
  212. package/src/components/items/item-list-table/item-list-table.tsx +5 -2
  213. package/src/components/payments/create-payment-form/index.ts +1 -0
  214. package/src/components/payments/list/list-table.tsx +14 -4
  215. package/src/components/recurring-invoices/list/list-table.tsx +7 -4
  216. package/src/components/request-logs/locales.ts +412 -0
  217. package/src/components/request-logs/request-log-detail.tsx +37 -21
  218. package/src/components/request-logs/request-log-list-table.tsx +57 -11
  219. package/src/components/table/data-table.tsx +5 -2
  220. package/src/components/table/date-cell.tsx +3 -1
  221. package/src/components/table/filter-bar.tsx +14 -2
  222. package/src/components/table/hooks/use-table-query.ts +1 -1
  223. package/src/components/table/locales.ts +1116 -0
  224. package/src/components/table/search-input.tsx +12 -3
  225. package/src/components/table/selection-toolbar.tsx +23 -6
  226. package/src/components/table/table-empty-state.tsx +43 -3
  227. package/src/components/table/table-no-results.tsx +3 -3
  228. package/src/components/table/table-pagination.tsx +4 -3
  229. package/src/components/table/types.ts +1 -0
  230. package/src/components/tax-reports/index.ts +1 -0
  231. package/src/components/tax-reports/kir-export-form.tsx +46 -8
  232. package/src/components/tax-reports/slovenia-tax-profile-step.tsx +191 -0
  233. package/src/components/tax-reports/slovenia-yearly-export-form.tsx +509 -0
  234. package/src/components/tax-reports/slovenia-yearly-review-step.tsx +253 -0
  235. package/src/components/tax-reports/slovenia-yearly-summary.tsx +19 -0
  236. package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -2
  237. package/src/components/ui/sidebar.tsx +3 -2
  238. package/src/components/ui/sticky-form-footer.tsx +7 -1
  239. package/src/components/webhook-logs/index.ts +6 -0
  240. package/src/components/webhook-logs/locales.ts +392 -0
  241. package/src/components/webhook-logs/webhook-delivery-detail.tsx +255 -0
  242. package/src/components/webhook-logs/webhook-delivery-list-table.tsx +278 -0
  243. package/src/components/wl-subscription/index.ts +1 -0
  244. package/src/components/wl-subscription/locked-feature.tsx +1 -0
  245. package/src/components/wl-subscription/paywall.tsx +193 -0
  246. package/src/components/wl-subscription/upgrade-modal.tsx +93 -29
  247. package/src/generate-schemas.ts +12 -7
  248. package/src/generated/schemas/customer.ts +2 -0
  249. package/src/generated/schemas/entity.ts +134 -0
  250. package/src/generated/schemas/exportsloveniayearlynormiranireport_body.ts +27 -0
  251. package/src/generated/schemas/index.ts +2 -0
  252. package/src/generated/schemas/me.ts +20 -1
  253. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +40 -34
  254. package/src/generated/schemas/rendercreditnotepreview_body.ts +42 -36
  255. package/src/generated/schemas/renderdeliverynotepreview_body.ts +23 -13
  256. package/src/generated/schemas/renderestimatepreview_body.ts +23 -13
  257. package/src/generated/schemas/renderinvoicepreview_body.ts +40 -34
  258. package/src/generated/schemas/sendemail_body.ts +44 -0
  259. package/src/generated/schemas/sloveniataxprofile.ts +42 -0
  260. package/src/generated/schemas/startpdfexport_body.ts +91 -1
  261. package/src/generated/schemas/webhook.ts +10 -0
  262. package/src/hooks/use-duplicate-document.ts +51 -13
  263. package/src/hooks/use-eslog-validation.ts +59 -0
  264. package/src/hooks/use-premise-selection.ts +186 -0
  265. package/src/lib/browser-cookies.ts +4 -4
  266. package/src/lib/date-fns-locale.ts +48 -0
  267. package/src/lib/fiscalization-options.ts +81 -0
  268. package/src/lib/locale.ts +38 -0
  269. package/src/lib/template-variables.tsx +1 -1
  270. package/src/lib/translation.ts +14 -3
  271. package/src/providers/entities-context.tsx +1 -0
  272. package/src/providers/entities-provider.tsx +102 -3
  273. package/src/providers/form-footer-context.tsx +37 -4
  274. package/src/providers/sdk-provider.tsx +7 -2
  275. package/src/providers/white-label-provider.tsx +4 -1
  276. package/src/providers/wl-subscription-provider.tsx +90 -3
@@ -13,7 +13,10 @@ import { Form } from "@/ui/components/ui/form";
13
13
  import { Skeleton } from "@/ui/components/ui/skeleton";
14
14
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/ui/components/ui/tooltip";
15
15
  import { createInvoiceSchema } from "@/ui/generated/schemas";
16
+ import { useEslogValidation } from "@/ui/hooks/use-eslog-validation";
17
+ import { usePremiseSelection } from "@/ui/hooks/use-premise-selection";
16
18
  import { useTransactionTypeCheck } from "@/ui/hooks/use-transaction-type-check";
19
+ import { buildEslogOptions, buildFinaOptions, buildFursOptions } from "@/ui/lib/fiscalization-options";
17
20
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
18
21
  import { createTranslation } from "@/ui/lib/translation";
19
22
  import { cn } from "@/ui/lib/utils";
@@ -33,17 +36,7 @@ import { DocumentRecipientSection } from "../../documents/create/document-recipi
33
36
  import { type LinkedDocumentSummary, LinkedDocumentsInfo } from "../../documents/create/linked-documents-info";
34
37
  import { MarkAsPaidSection } from "../../documents/create/mark-as-paid-section";
35
38
  import type { DocumentTypes } from "../../documents/types";
36
- import { useFinaPremises, useFinaSettings } from "../../entities/fina-settings-form/fina-settings.hooks";
37
- import { useFursPremises, useFursSettings } from "../../entities/furs-settings-form/furs-settings.hooks";
38
- import {
39
- getLastUsedFinaCombo,
40
- getLastUsedFursCombo,
41
- setLastUsedFinaCombo,
42
- setLastUsedFursCombo,
43
- useCreateInvoice,
44
- useNextInvoiceNumber,
45
- useUpdateInvoice,
46
- } from "../invoices.hooks";
39
+ import { useCreateInvoice, useNextInvoiceNumber, useUpdateInvoice } from "../invoices.hooks";
47
40
  import { getEntityErrors, getFormFieldErrors, validateEslogForm } from "./eslog-validation";
48
41
  import de from "./locales/de";
49
42
  import es from "./locales/es";
@@ -77,6 +70,13 @@ const translations = {
77
70
  hr,
78
71
  } as const;
79
72
 
73
+ const DUPLICATE_PREVIEW_SETTLE_MS = 120;
74
+ const DUPLICATE_PREVIEW_MIN_DELAY_MS = 260;
75
+
76
+ function emitInvoiceCreateDebug(_detail: Record<string, unknown>) {
77
+ if (!import.meta.env.DEV || typeof window === "undefined") return;
78
+ }
79
+
80
80
  // Form values: extend schema with local-only fields (number is for display, not sent to API)
81
81
  type CreateInvoiceFormValues = z.infer<typeof createInvoiceSchema> & {
82
82
  number?: string;
@@ -105,6 +105,62 @@ type DocumentAddFormProps = {
105
105
  documentId?: string;
106
106
  } & ComponentTranslationProps;
107
107
 
108
+ function buildInvoiceFormValues({
109
+ initialValues,
110
+ currencyCode,
111
+ defaultInvoiceNote,
112
+ defaultPaymentTerms,
113
+ defaultFooter,
114
+ defaultInvoiceDueDays,
115
+ }: {
116
+ initialValues?: Partial<CreateInvoiceRequest> & { number?: string };
117
+ currencyCode?: string;
118
+ defaultInvoiceNote: string;
119
+ defaultPaymentTerms: string;
120
+ defaultFooter: string;
121
+ defaultInvoiceDueDays: number;
122
+ }): CreateInvoiceFormValues {
123
+ return {
124
+ number: initialValues?.number || "",
125
+ date: initialValues?.date || new Date().toISOString(),
126
+ customer_id: initialValues?.customer_id ?? undefined,
127
+ customer: (initialValues?.customer as CreateInvoiceFormValues["customer"]) ?? undefined,
128
+ items: initialValues?.items?.length
129
+ ? initialValues.items.map((item: any) => ({
130
+ type: item.type,
131
+ name: item.name || "",
132
+ description: item.description || "",
133
+ ...(item.type !== "separator"
134
+ ? {
135
+ quantity: item.quantity ?? 1,
136
+ price: item.gross_price ?? item.price,
137
+ taxes: item.taxes || [],
138
+ }
139
+ : {}),
140
+ }))
141
+ : [
142
+ {
143
+ name: "",
144
+ description: "",
145
+ quantity: 1,
146
+ price: undefined,
147
+ taxes: [],
148
+ },
149
+ ],
150
+ currency_code: initialValues?.currency_code || currencyCode || "EUR",
151
+ reference: (initialValues as any)?.reference ?? "",
152
+ note: initialValues?.note ?? defaultInvoiceNote,
153
+ tax_clause: (initialValues as any)?.tax_clause ?? "",
154
+ payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
155
+ footer: (initialValues as any)?.footer ?? defaultFooter,
156
+ date_due:
157
+ initialValues?.date_due ||
158
+ calculateDueDate(initialValues?.date || new Date().toISOString(), defaultInvoiceDueDays),
159
+ date_service: (initialValues as any)?.date_service || new Date().toISOString(),
160
+ linked_documents: (initialValues as any)?.linked_documents,
161
+ };
162
+ }
163
+
108
164
  export default function CreateInvoiceForm({
109
165
  type: _type,
110
166
  entityId,
@@ -140,42 +196,16 @@ export default function CreateInvoiceForm({
140
196
  const defaultInvoiceDueDays = (activeEntity?.settings as any)?.default_invoice_due_days ?? 30;
141
197
 
142
198
  // ============================================================================
143
- // FURS Settings & Premises
199
+ // FURS & FINA Premise Selection (shared hook)
144
200
  // ============================================================================
145
- const { data: fursSettings, isLoading: isFursSettingsLoading } = useFursSettings(entityId);
146
- const { data: fursPremises, isLoading: isFursPremisesLoading } = useFursPremises(entityId, {
147
- enabled: fursSettings?.enabled === true,
148
- });
149
-
150
- // Loading state for FURS - don't render form until we know if FURS is active
151
- const isFursLoading = isFursSettingsLoading || (fursSettings?.enabled && isFursPremisesLoading);
152
-
153
- // Check if FURS is enabled and has active premises
154
- const isFursEnabled = fursSettings?.enabled === true;
155
- const activePremises = useMemo(() => fursPremises?.filter((p) => p.is_active) || [], [fursPremises]);
156
- const hasFursPremises = activePremises.length > 0;
157
-
158
- // FURS premise/device selection state
159
- const [selectedPremiseName, setSelectedPremiseName] = useState<string | undefined>();
160
- const [selectedDeviceName, setSelectedDeviceName] = useState<string | undefined>();
201
+ const furs = usePremiseSelection({ entityId, type: "furs" });
202
+ const fina = usePremiseSelection({ entityId, type: "fina" });
161
203
  const [skipFiscalization, setSkipFiscalization] = useState(false);
162
204
 
163
205
  // ============================================================================
164
- // FINA Settings & Premises
206
+ // e-SLOG Validation (shared hook)
165
207
  // ============================================================================
166
- const { data: finaSettings, isLoading: isFinaSettingsLoading } = useFinaSettings(entityId);
167
- const { data: finaPremises, isLoading: isFinaPremisesLoading } = useFinaPremises(entityId, {
168
- enabled: finaSettings?.enabled === true,
169
- });
170
-
171
- const isFinaLoading = isFinaSettingsLoading || (finaSettings?.enabled && isFinaPremisesLoading);
172
- const isFinaEnabled = finaSettings?.enabled === true;
173
- const activeFinaPremises = useMemo(() => finaPremises?.filter((p: any) => p.is_active) || [], [finaPremises]);
174
- const hasFinaPremises = activeFinaPremises.length > 0;
175
-
176
- // FINA premise/device selection state (no skip - all FINA invoices must be fiscalized)
177
- const [selectedFinaBusinessPremiseName, setSelectedFinaBusinessPremiseName] = useState<string | undefined>();
178
- const [selectedFinaElectronicDeviceName, setSelectedFinaElectronicDeviceName] = useState<string | undefined>();
208
+ const eslog = useEslogValidation(activeEntity);
179
209
 
180
210
  // UI-only state (not part of API schema)
181
211
  const [markAsPaid, setMarkAsPaid] = useState(false);
@@ -183,7 +213,9 @@ export default function CreateInvoiceForm({
183
213
  const [isDraftPending, setIsDraftPending] = useState(false);
184
214
 
185
215
  // Service date type state (single date or range)
186
- const [serviceDateType, setServiceDateType] = useState<"single" | "range">("single");
216
+ const [serviceDateType, setServiceDateType] = useState<"single" | "range">(
217
+ initialValues && (initialValues as any).date_service_to ? "range" : "single",
218
+ );
187
219
 
188
220
  // Due days type state for invoice due date selector
189
221
  const [dueDaysType, setDueDaysType] = useState<number | "custom">(() => {
@@ -202,173 +234,30 @@ export default function CreateInvoiceForm({
202
234
  }, [initialValues?.items]);
203
235
  const priceModesRef = useRef<PriceModesMap>(initialPriceModes);
204
236
 
205
- // ============================================================================
206
- // e-SLOG Settings (Slovenian e-Invoice)
207
- // ============================================================================
208
- const isSloenianEntity = activeEntity?.country_code === "SI";
209
- const entityEslogEnabled = !!(activeEntity?.settings as any)?.eslog_validation_enabled;
210
- const isEslogAvailable = isSloenianEntity && entityEslogEnabled;
211
-
212
- // e-SLOG validation state - defaults to entity setting
213
- const [eslogValidationEnabled, setEslogValidationEnabled] = useState<boolean | undefined>(undefined);
214
- // e-SLOG entity-level errors (require settings update, can't be fixed in form)
215
- const [eslogEntityErrors, setEslogEntityErrors] = useState<Array<{ field: string; message: string }>>([]);
216
-
217
- // Initialize e-SLOG state from entity settings
218
- useEffect(() => {
219
- if (isEslogAvailable && eslogValidationEnabled === undefined) {
220
- setEslogValidationEnabled(true);
221
- }
222
- }, [isEslogAvailable, eslogValidationEnabled]);
223
-
224
- // Clear entity errors when eslog validation is disabled
225
- useEffect(() => {
226
- if (!eslogValidationEnabled) {
227
- setEslogEntityErrors([]);
228
- }
229
- }, [eslogValidationEnabled]);
230
-
231
- // Get active devices for selected premise
232
- const activeDevices = useMemo(() => {
233
- if (!selectedPremiseName) return [];
234
- const premise = activePremises.find((p) => p.business_premise_name === selectedPremiseName);
235
- return premise?.Devices?.filter((d) => d.is_active) || [];
236
- }, [activePremises, selectedPremiseName]);
237
-
238
- // Initialize FURS selection from localStorage or first active combo
239
- useEffect(() => {
240
- if (!isFursEnabled || !hasFursPremises || selectedPremiseName) return;
241
-
242
- const lastUsed = getLastUsedFursCombo(entityId);
243
- if (lastUsed) {
244
- // Verify the last-used combo is still valid (premise/device still exist and active)
245
- const premise = activePremises.find((p) => p.business_premise_name === lastUsed.business_premise_name);
246
- const device = premise?.Devices?.find(
247
- (d) => d.electronic_device_name === lastUsed.electronic_device_name && d.is_active,
248
- );
249
- if (premise && device) {
250
- setSelectedPremiseName(lastUsed.business_premise_name);
251
- setSelectedDeviceName(lastUsed.electronic_device_name);
252
- return;
253
- }
254
- }
255
-
256
- // Fall back to first active premise/device
257
- const firstPremise = activePremises[0];
258
- const firstDevice = firstPremise?.Devices?.find((d) => d.is_active);
259
- if (firstPremise && firstDevice) {
260
- setSelectedPremiseName(firstPremise.business_premise_name);
261
- setSelectedDeviceName(firstDevice.electronic_device_name);
262
- }
263
- }, [isFursEnabled, hasFursPremises, activePremises, entityId, selectedPremiseName]);
264
-
265
- // When premise changes, select first active device
266
- useEffect(() => {
267
- if (!selectedPremiseName) return;
268
- const premise = activePremises.find((p) => p.business_premise_name === selectedPremiseName);
269
- const firstDevice = premise?.Devices?.find((d) => d.is_active);
270
- if (firstDevice && selectedDeviceName !== firstDevice.electronic_device_name) {
271
- // Only update if the current device is not in this premise
272
- const currentDeviceInPremise = premise?.Devices?.find(
273
- (d) => d.electronic_device_name === selectedDeviceName && d.is_active,
274
- );
275
- if (!currentDeviceInPremise) {
276
- setSelectedDeviceName(firstDevice.electronic_device_name);
277
- }
278
- }
279
- }, [selectedPremiseName, activePremises, selectedDeviceName]);
280
-
281
- // Get active FINA devices for selected premise
282
- const activeFinaDevices = useMemo(() => {
283
- if (!selectedFinaBusinessPremiseName) return [];
284
- const premise = activeFinaPremises.find((p: any) => p.business_premise_name === selectedFinaBusinessPremiseName);
285
- return premise?.Devices?.filter((d: any) => d.is_active) || [];
286
- }, [activeFinaPremises, selectedFinaBusinessPremiseName]);
287
-
288
- // Initialize FINA selection from localStorage or first active combo
289
- useEffect(() => {
290
- if (!isFinaEnabled || !hasFinaPremises || selectedFinaBusinessPremiseName) return;
291
-
292
- const lastUsed = getLastUsedFinaCombo(entityId);
293
- if (lastUsed) {
294
- const premise = activeFinaPremises.find((p: any) => p.business_premise_name === lastUsed.business_premise_name);
295
- const device = premise?.Devices?.find(
296
- (d: any) => d.electronic_device_name === lastUsed.electronic_device_name && d.is_active,
297
- );
298
- if (premise && device) {
299
- setSelectedFinaBusinessPremiseName(lastUsed.business_premise_name);
300
- setSelectedFinaElectronicDeviceName(lastUsed.electronic_device_name);
301
- return;
302
- }
303
- }
304
-
305
- const firstPremise = activeFinaPremises[0];
306
- const firstDevice = firstPremise?.Devices?.find((d: any) => d.is_active);
307
- if (firstPremise && firstDevice) {
308
- setSelectedFinaBusinessPremiseName(firstPremise.business_premise_name);
309
- setSelectedFinaElectronicDeviceName(firstDevice.electronic_device_name);
310
- }
311
- }, [isFinaEnabled, hasFinaPremises, activeFinaPremises, entityId, selectedFinaBusinessPremiseName]);
312
-
313
- // When FINA premise changes, select first active device
314
- useEffect(() => {
315
- if (!selectedFinaBusinessPremiseName) return;
316
- const premise = activeFinaPremises.find((p: any) => p.business_premise_name === selectedFinaBusinessPremiseName);
317
- const firstDevice = premise?.Devices?.find((d: any) => d.is_active);
318
- if (firstDevice && selectedFinaElectronicDeviceName !== firstDevice.electronic_device_name) {
319
- const currentDeviceInPremise = premise?.Devices?.find(
320
- (d: any) => d.electronic_device_name === selectedFinaElectronicDeviceName && d.is_active,
321
- );
322
- if (!currentDeviceInPremise) {
323
- setSelectedFinaElectronicDeviceName(firstDevice.electronic_device_name);
324
- }
325
- }
326
- }, [selectedFinaBusinessPremiseName, activeFinaPremises, selectedFinaElectronicDeviceName]);
237
+ const formDefaultValues = useMemo(
238
+ () =>
239
+ buildInvoiceFormValues({
240
+ initialValues,
241
+ currencyCode: activeEntity?.currency_code ?? undefined,
242
+ defaultInvoiceNote,
243
+ defaultPaymentTerms,
244
+ defaultFooter,
245
+ defaultInvoiceDueDays,
246
+ }),
247
+ [
248
+ activeEntity?.currency_code,
249
+ defaultFooter,
250
+ defaultInvoiceDueDays,
251
+ defaultInvoiceNote,
252
+ defaultPaymentTerms,
253
+ initialValues,
254
+ ],
255
+ );
327
256
 
328
257
  const form = useForm<CreateInvoiceFormValues>({
329
258
  // Cast resolver to accept extended form type (includes UI-only fields)
330
259
  resolver: zodResolver(createInvoiceSchema) as Resolver<CreateInvoiceFormValues>,
331
- defaultValues: {
332
- number: initialValues?.number || "", // Edit mode uses initialValues, create mode uses useNextInvoiceNumber
333
- date: initialValues?.date || new Date().toISOString(),
334
- customer_id: initialValues?.customer_id ?? undefined,
335
- // Cast customer to form schema type (API type may have additional fields)
336
- customer: (initialValues?.customer as CreateInvoiceFormValues["customer"]) ?? undefined,
337
- items: initialValues?.items?.length
338
- ? initialValues.items.map((item: any) => ({
339
- type: item.type,
340
- name: item.name || "",
341
- description: item.description || "",
342
- ...(item.type !== "separator"
343
- ? {
344
- quantity: item.quantity ?? 1,
345
- // Use gross_price if set, otherwise use price
346
- price: item.gross_price ?? item.price,
347
- taxes: item.taxes || [],
348
- }
349
- : {}),
350
- }))
351
- : [
352
- {
353
- name: "",
354
- description: "",
355
- quantity: 1,
356
- price: undefined,
357
- taxes: [],
358
- },
359
- ],
360
- currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
361
- reference: (initialValues as any)?.reference ?? "",
362
- note: initialValues?.note ?? defaultInvoiceNote,
363
- tax_clause: (initialValues as any)?.tax_clause ?? "",
364
- payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
365
- footer: (initialValues as any)?.footer ?? defaultFooter,
366
- date_due:
367
- initialValues?.date_due ||
368
- calculateDueDate(initialValues?.date || new Date().toISOString(), defaultInvoiceDueDays),
369
- date_service: new Date().toISOString(),
370
- linked_documents: (initialValues as any)?.linked_documents,
371
- },
260
+ defaultValues: formDefaultValues,
372
261
  });
373
262
 
374
263
  // Skip fiscalization is only allowed for bank transfers or unpaid invoices
@@ -402,19 +291,8 @@ export default function CreateInvoiceForm({
402
291
  [form],
403
292
  );
404
293
 
405
- // Check if FURS selection is ready (needed to prevent number flashing)
406
- // Selection is ready when: FURS not enabled, OR no premises, OR we have a valid selection
407
- const isFursSelectionReady = !isFursEnabled || !hasFursPremises || (!!selectedPremiseName && !!selectedDeviceName);
408
-
409
294
  // FURS is "active" for this invoice if enabled and we have a valid selection (and not skipped)
410
- const isFursActive =
411
- isFursEnabled && hasFursPremises && selectedPremiseName && selectedDeviceName && !skipFiscalization;
412
-
413
- // FINA selection ready and active checks
414
- const isFinaSelectionReady =
415
- !isFinaEnabled || !hasFinaPremises || (!!selectedFinaBusinessPremiseName && !!selectedFinaElectronicDeviceName);
416
- const isFinaActive =
417
- isFinaEnabled && hasFinaPremises && selectedFinaBusinessPremiseName && selectedFinaElectronicDeviceName;
295
+ const isFursActive = furs.isActive && !skipFiscalization;
418
296
 
419
297
  // ============================================================================
420
298
  // VIES Check - determine transaction type early (needed for number preview)
@@ -441,10 +319,10 @@ export default function CreateInvoiceForm({
441
319
  });
442
320
 
443
321
  // FINA numbering guard: use FINA numbering for domestic transactions (or all if unified numbering is on)
444
- const finaUnifiedNumbering = finaSettings?.unified_numbering !== false;
322
+ const finaUnifiedNumbering = fina.settings?.unified_numbering !== false;
445
323
  const useFinaNumbering =
446
- !!isFinaActive && (finaUnifiedNumbering || transactionType == null || transactionType === "domestic");
447
- const isFinaNonDomestic = !!isFinaActive && !useFinaNumbering;
324
+ !!fina.isActive && (finaUnifiedNumbering || transactionType == null || transactionType === "domestic");
325
+ const isFinaNonDomestic = !!fina.isActive && !useFinaNumbering;
448
326
 
449
327
  // ============================================================================
450
328
  // Next Invoice Number Preview
@@ -453,44 +331,44 @@ export default function CreateInvoiceForm({
453
331
  // Skip in edit mode - we use the existing document number
454
332
  // Use the same premise/device params for both FURS and FINA (an entity is either one, never both)
455
333
  const activePremiseName = isFursActive
456
- ? selectedPremiseName
334
+ ? furs.selectedPremiseName
457
335
  : useFinaNumbering
458
- ? selectedFinaBusinessPremiseName
336
+ ? fina.selectedPremiseName
459
337
  : undefined;
460
338
  const activeDeviceNameForNumber = isFursActive
461
- ? selectedDeviceName
339
+ ? furs.selectedDeviceName
462
340
  : useFinaNumbering
463
- ? selectedFinaElectronicDeviceName
341
+ ? fina.selectedDeviceName
464
342
  : undefined;
465
343
 
466
344
  const { data: nextNumberData, isLoading: isNextNumberLoading } = useNextInvoiceNumber(entityId, {
467
345
  business_premise_name: activePremiseName,
468
346
  electronic_device_name: activeDeviceNameForNumber,
469
347
  enabled:
470
- !!entityId && !isFursLoading && isFursSelectionReady && !isFinaLoading && isFinaSelectionReady && !isEditMode,
348
+ !!entityId && !furs.isLoading && furs.isSelectionReady && !fina.isLoading && fina.isSelectionReady && !isEditMode,
471
349
  });
472
350
 
473
351
  // Overall loading state - wait until we have FURS/FINA data, selection ready, and next number (only in create mode)
474
352
  const isFormDataLoading = isEditMode
475
353
  ? false // In edit mode, don't wait for next number
476
- : isFursLoading || !isFursSelectionReady || isFinaLoading || !isFinaSelectionReady || isNextNumberLoading;
354
+ : furs.isLoading || !furs.isSelectionReady || fina.isLoading || !fina.isSelectionReady || isNextNumberLoading;
477
355
 
478
356
  // Update header action with FURS and e-SLOG toggle buttons
479
357
  useEffect(() => {
480
358
  if (!onHeaderActionChange) return;
481
359
 
482
360
  // Don't set header action while loading or in edit mode (FURS/FINA/e-SLOG not editable)
483
- if (isFursLoading || isFinaLoading || isEditMode) {
361
+ if (furs.isLoading || fina.isLoading || isEditMode) {
484
362
  onHeaderActionChange(null);
485
363
  return;
486
364
  }
487
365
 
488
- const showFursToggle = isFursEnabled && hasFursPremises;
489
- const showEslogToggle = isEslogAvailable;
366
+ const showFursToggle = furs.isEnabled && furs.hasPremises;
367
+ const showEslogToggle = eslog.isAvailable;
490
368
 
491
369
  if (showFursToggle || showEslogToggle) {
492
370
  const isFursChecked = !skipFiscalization;
493
- const isEslogChecked = eslogValidationEnabled === true;
371
+ const isEslogChecked = eslog.isEnabled === true;
494
372
 
495
373
  onHeaderActionChange(
496
374
  <div className="flex items-center gap-2">
@@ -504,7 +382,7 @@ export default function CreateInvoiceForm({
504
382
  variant={isEslogChecked ? "outline" : "ghost"}
505
383
  size="sm"
506
384
  className={cn("h-8 cursor-pointer gap-2", !isEslogChecked && "text-muted-foreground")}
507
- onClick={() => setEslogValidationEnabled(!eslogValidationEnabled)}
385
+ onClick={() => eslog.setEnabled(!eslog.isEnabled)}
508
386
  >
509
387
  <div
510
388
  className={cn(
@@ -573,14 +451,15 @@ export default function CreateInvoiceForm({
573
451
  onHeaderActionChange(null);
574
452
  }
575
453
  }, [
576
- isFursLoading,
577
- isFinaLoading,
578
- isFursEnabled,
579
- hasFursPremises,
454
+ furs.isLoading,
455
+ fina.isLoading,
456
+ furs.isEnabled,
457
+ furs.hasPremises,
580
458
  skipFiscalization,
581
459
  canSkipFiscalization,
582
- isEslogAvailable,
583
- eslogValidationEnabled,
460
+ eslog.isAvailable,
461
+ eslog.isEnabled,
462
+ eslog.setEnabled,
584
463
  isEditMode,
585
464
  onHeaderActionChange,
586
465
  t,
@@ -634,7 +513,7 @@ export default function CreateInvoiceForm({
634
513
  if (isDomesticTransaction && customerHasTaxNumber && !customerIsEndConsumerWatch) {
635
514
  return t("Domestic B2B invoicing in Croatia is not supported");
636
515
  }
637
- if (!isFinaEnabled) {
516
+ if (!fina.isEnabled) {
638
517
  return t("FINA fiscalization must be enabled for domestic invoices");
639
518
  }
640
519
  return undefined;
@@ -668,20 +547,9 @@ export default function CreateInvoiceForm({
668
547
  const { mutate: createInvoice, isPending: isCreatePending } = useCreateInvoice({
669
548
  entityId,
670
549
  onSuccess: (data) => {
671
- // Save FURS combo to localStorage on successful creation
672
- if (isFursActive && selectedPremiseName && selectedDeviceName) {
673
- setLastUsedFursCombo(entityId, {
674
- business_premise_name: selectedPremiseName,
675
- electronic_device_name: selectedDeviceName,
676
- });
677
- }
678
- // Save FINA combo to localStorage on successful creation
679
- if (isFinaActive && selectedFinaBusinessPremiseName && selectedFinaElectronicDeviceName) {
680
- setLastUsedFinaCombo(entityId, {
681
- business_premise_name: selectedFinaBusinessPremiseName,
682
- electronic_device_name: selectedFinaElectronicDeviceName,
683
- });
684
- }
550
+ // Save FURS/FINA combos to localStorage on successful creation
551
+ furs.saveCombo();
552
+ fina.saveCombo();
685
553
  // Invalidate customers cache when a customer was created/linked
686
554
  // This ensures the new customer appears in autocomplete for future documents
687
555
  if (data.customer_id) {
@@ -715,13 +583,13 @@ export default function CreateInvoiceForm({
715
583
  if (finaValidationError) return;
716
584
 
717
585
  // Skip e-SLOG validation for drafts and edit mode
718
- if (!isDraft && !isEditMode && eslogValidationEnabled) {
586
+ if (!isDraft && !isEditMode && eslog.isEnabled) {
719
587
  const validationErrors = validateEslogForm(values as any, activeEntity);
720
588
 
721
589
  if (validationErrors.length > 0) {
722
590
  const entityErrors = getEntityErrors(validationErrors);
723
591
  const formErrors = getFormFieldErrors(validationErrors);
724
- setEslogEntityErrors(entityErrors);
592
+ eslog.setEntityErrors(entityErrors);
725
593
  for (const error of formErrors) {
726
594
  form.setError(error.field as any, {
727
595
  type: "eslog",
@@ -730,38 +598,36 @@ export default function CreateInvoiceForm({
730
598
  }
731
599
  return;
732
600
  }
733
- setEslogEntityErrors([]);
601
+ eslog.setEntityErrors([]);
734
602
  }
735
603
 
736
604
  // Build FURS options (skip for drafts and edit mode)
737
- const fursOptions =
738
- !isDraft && !isEditMode && isFursEnabled
739
- ? skipFiscalization
740
- ? { skip: true }
741
- : selectedPremiseName && selectedDeviceName
742
- ? { business_premise_name: selectedPremiseName, electronic_device_name: selectedDeviceName }
743
- : undefined
744
- : undefined;
605
+ const fursOptions = buildFursOptions({
606
+ isDraft,
607
+ isEnabled: furs.isEnabled,
608
+ isEditMode,
609
+ skipFiscalization,
610
+ premiseName: furs.selectedPremiseName,
611
+ deviceName: furs.selectedDeviceName,
612
+ });
745
613
 
746
614
  // Build FINA options (skip for drafts, edit mode, and non-domestic transactions)
747
- const finaOptions =
748
- !isDraft &&
749
- !isEditMode &&
750
- useFinaNumbering &&
751
- selectedFinaBusinessPremiseName &&
752
- selectedFinaElectronicDeviceName
753
- ? {
754
- business_premise_name: selectedFinaBusinessPremiseName,
755
- electronic_device_name: selectedFinaElectronicDeviceName,
756
- payment_type: paymentTypes[0],
757
- }
758
- : undefined;
615
+ const finaOptions = buildFinaOptions({
616
+ isDraft,
617
+ useFinaNumbering,
618
+ isEditMode,
619
+ premiseName: fina.selectedPremiseName,
620
+ deviceName: fina.selectedDeviceName,
621
+ paymentType: paymentTypes[0],
622
+ });
759
623
 
760
624
  // Build e-SLOG options (skip for drafts and edit mode)
761
- const eslogOptions =
762
- !isDraft && !isEditMode && isEslogAvailable
763
- ? { validation_enabled: eslogValidationEnabled === true }
764
- : undefined;
625
+ const eslogOptions = buildEslogOptions({
626
+ isDraft,
627
+ isEditMode,
628
+ isAvailable: eslog.isAvailable,
629
+ isEnabled: eslog.isEnabled,
630
+ });
765
631
 
766
632
  const payload = prepareInvoiceSubmission(values as any, {
767
633
  originalCustomer,
@@ -794,21 +660,17 @@ export default function CreateInvoiceForm({
794
660
  createInvoice,
795
661
  updateInvoice,
796
662
  documentId,
797
- eslogValidationEnabled,
663
+ eslog,
798
664
  finaValidationError,
665
+ fina,
799
666
  forceLinkedDocuments,
800
667
  form,
668
+ furs,
801
669
  isEditMode,
802
- isEslogAvailable,
803
- isFursEnabled,
804
670
  useFinaNumbering,
805
671
  markAsPaid,
806
672
  originalCustomer,
807
673
  paymentTypes,
808
- selectedDeviceName,
809
- selectedPremiseName,
810
- selectedFinaBusinessPremiseName,
811
- selectedFinaElectronicDeviceName,
812
674
  showCustomerForm,
813
675
  skipFiscalization,
814
676
  ],
@@ -858,6 +720,24 @@ export default function CreateInvoiceForm({
858
720
 
859
721
  // Track if initial setup has been done
860
722
  const initialSetupDoneRef = useRef(false);
723
+ const hasInitialValues = !!initialValues;
724
+ const duplicateHydrationStartedAtRef = useRef<number | null>(hasInitialValues ? performance.now() : null);
725
+ const duplicateHydrationLoggedRef = useRef(false);
726
+ const appliedInitialValuesSignatureRef = useRef<string | null>(null);
727
+
728
+ useEffect(() => {
729
+ if (!hasInitialValues) return;
730
+
731
+ const nextSignature = JSON.stringify(formDefaultValues);
732
+ if (appliedInitialValuesSignatureRef.current === nextSignature) return;
733
+
734
+ appliedInitialValuesSignatureRef.current = nextSignature;
735
+ initialSetupDoneRef.current = false;
736
+ duplicateHydrationStartedAtRef.current = performance.now();
737
+ duplicateHydrationLoggedRef.current = false;
738
+ form.reset(formDefaultValues);
739
+ priceModesRef.current = initialPriceModes;
740
+ }, [form, formDefaultValues, hasInitialValues, initialPriceModes]);
861
741
 
862
742
  // Set default note and payment terms from entity settings when entity data is available
863
743
  // This handles the case where activeEntity loads asynchronously
@@ -901,7 +781,15 @@ export default function CreateInvoiceForm({
901
781
  }
902
782
 
903
783
  initialSetupDoneRef.current = true;
904
- }, [activeEntity, form, isEditMode, initialValues?.date_due]);
784
+ if (hasInitialValues && duplicateHydrationStartedAtRef.current && !duplicateHydrationLoggedRef.current) {
785
+ duplicateHydrationLoggedRef.current = true;
786
+ emitInvoiceCreateDebug({
787
+ stage: "entity_defaults_applied",
788
+ hasInitialValues: true,
789
+ elapsedMs: Number((performance.now() - duplicateHydrationStartedAtRef.current).toFixed(1)),
790
+ });
791
+ }
792
+ }, [activeEntity, form, hasInitialValues, isEditMode, initialValues]);
905
793
 
906
794
  // Recalculate due date when document date changes (skip in edit mode and custom due days)
907
795
  const prevDateRef = useRef(form.getValues("date"));
@@ -916,48 +804,119 @@ export default function CreateInvoiceForm({
916
804
 
917
805
  // Use form.watch subscription for onChange callback (avoids re-render loops)
918
806
  const prevPayloadRef = useRef<string>("");
807
+ const initialPreviewTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
808
+ const hasEmittedSettledInitialPreviewRef = useRef(!hasInitialValues);
809
+ const pendingInitialPreviewPayloadRef = useRef<InvoicePreviewPayload | null>(null);
810
+
811
+ useEffect(() => {
812
+ if (!hasInitialValues) return;
813
+
814
+ hasEmittedSettledInitialPreviewRef.current = false;
815
+ prevPayloadRef.current = "";
816
+ pendingInitialPreviewPayloadRef.current = null;
817
+ if (initialPreviewTimeoutRef.current) {
818
+ clearTimeout(initialPreviewTimeoutRef.current);
819
+ initialPreviewTimeoutRef.current = null;
820
+ }
821
+ }, [hasInitialValues]);
822
+
823
+ const buildPreviewPayload = useCallback((formValues: any): InvoicePreviewPayload => {
824
+ const currentItems = formValues.items || [];
825
+ const transformedItems = currentItems.map((item: any, index: number) => {
826
+ const { price, ...rest } = item;
827
+ const isGross = priceModesRef.current[index] ?? false;
828
+ return isGross ? { ...rest, gross_price: price } : { ...rest, price };
829
+ });
830
+ return {
831
+ number: formValues.number,
832
+ date: formValues.date,
833
+ date_service: formValues.date_service,
834
+ date_service_to: formValues.date_service_to,
835
+ customer_id: formValues.customer_id,
836
+ customer: formValues.customer,
837
+ items: transformedItems,
838
+ currency_code: formValues.currency_code,
839
+ reference: formValues.reference,
840
+ note: formValues.note,
841
+ payment_terms: formValues.payment_terms,
842
+ signature: formValues.signature,
843
+ };
844
+ }, []);
845
+
846
+ const emitCurrentPreviewPayload = useCallback(() => {
847
+ if (!onChange) return;
848
+ onChange(buildPreviewPayload(form.getValues()));
849
+ }, [buildPreviewPayload, form, onChange]);
919
850
 
920
851
  useEffect(() => {
921
852
  if (!onChange) return;
922
853
 
923
- const buildPayload = (formValues: any): InvoicePreviewPayload => {
924
- const currentItems = formValues.items || [];
925
- const transformedItems = currentItems.map((item: any, index: number) => {
926
- const { price, ...rest } = item;
927
- const isGross = priceModesRef.current[index] ?? false;
928
- return isGross ? { ...rest, gross_price: price } : { ...rest, price };
929
- });
930
- return {
931
- number: formValues.number,
932
- date: formValues.date,
933
- customer_id: formValues.customer_id,
934
- customer: formValues.customer,
935
- items: transformedItems,
936
- currency_code: formValues.currency_code,
937
- reference: formValues.reference,
938
- note: formValues.note,
939
- payment_terms: formValues.payment_terms,
940
- signature: formValues.signature,
941
- };
854
+ const emitSettledInitialPreview = (payload: InvoicePreviewPayload) => {
855
+ pendingInitialPreviewPayloadRef.current = payload;
856
+
857
+ if (initialPreviewTimeoutRef.current) return;
858
+
859
+ const hydrationStartedAt = duplicateHydrationStartedAtRef.current ?? performance.now();
860
+ const elapsedMs = performance.now() - hydrationStartedAt;
861
+ const delayMs = Math.max(DUPLICATE_PREVIEW_SETTLE_MS, DUPLICATE_PREVIEW_MIN_DELAY_MS - elapsedMs);
862
+ initialPreviewTimeoutRef.current = setTimeout(() => {
863
+ initialPreviewTimeoutRef.current = null;
864
+ hasEmittedSettledInitialPreviewRef.current = true;
865
+ const settledPayload = pendingInitialPreviewPayloadRef.current ?? payload;
866
+ if (hasInitialValues && duplicateHydrationStartedAtRef.current && !duplicateHydrationLoggedRef.current) {
867
+ duplicateHydrationLoggedRef.current = true;
868
+ emitInvoiceCreateDebug({
869
+ stage: "initial_payload_emitted",
870
+ hasInitialValues: true,
871
+ itemCount: settledPayload.items?.length ?? 0,
872
+ elapsedMs: Number((performance.now() - duplicateHydrationStartedAtRef.current).toFixed(1)),
873
+ });
874
+ }
875
+ onChange(settledPayload);
876
+ pendingInitialPreviewPayloadRef.current = null;
877
+ }, delayMs);
942
878
  };
943
879
 
944
880
  // Initial call
945
- const initialPayload = buildPayload(form.getValues());
881
+ const initialPayload = buildPreviewPayload(form.getValues());
946
882
  prevPayloadRef.current = JSON.stringify(initialPayload);
947
- onChange(initialPayload);
883
+ if (hasInitialValues) {
884
+ emitSettledInitialPreview(initialPayload);
885
+ } else {
886
+ onChange(initialPayload);
887
+ }
948
888
 
949
889
  // Subscribe to changes
950
890
  const subscription = form.watch((formValues) => {
951
- const payload = buildPayload(formValues);
891
+ const payload = buildPreviewPayload(formValues);
952
892
  const payloadStr = JSON.stringify(payload);
953
893
  if (payloadStr !== prevPayloadRef.current) {
954
894
  prevPayloadRef.current = payloadStr;
955
- onChange(payload);
895
+ if (hasInitialValues && duplicateHydrationStartedAtRef.current) {
896
+ emitInvoiceCreateDebug({
897
+ stage: "payload_changed",
898
+ hasInitialValues: true,
899
+ itemCount: payload.items?.length ?? 0,
900
+ elapsedMs: Number((performance.now() - duplicateHydrationStartedAtRef.current).toFixed(1)),
901
+ });
902
+ }
903
+ if (hasInitialValues && !hasEmittedSettledInitialPreviewRef.current) {
904
+ emitSettledInitialPreview(payload);
905
+ } else {
906
+ onChange(payload);
907
+ }
956
908
  }
957
909
  });
958
910
 
959
- return () => subscription.unsubscribe();
960
- }, [onChange, form]);
911
+ return () => {
912
+ subscription.unsubscribe();
913
+ if (initialPreviewTimeoutRef.current) {
914
+ clearTimeout(initialPreviewTimeoutRef.current);
915
+ initialPreviewTimeoutRef.current = null;
916
+ }
917
+ pendingInitialPreviewPayloadRef.current = null;
918
+ };
919
+ }, [buildPreviewPayload, form, hasInitialValues, onChange]);
961
920
 
962
921
  const onSubmit = (values: CreateInvoiceFormValues) => {
963
922
  submitInvoice(values, false);
@@ -1040,14 +999,14 @@ export default function CreateInvoiceForm({
1040
999
  )}
1041
1000
 
1042
1001
  {/* e-SLOG entity-level validation errors */}
1043
- {eslogEntityErrors.length > 0 && (
1002
+ {eslog.entityErrors.length > 0 && (
1044
1003
  <Alert variant="destructive">
1045
1004
  <AlertCircle className="h-4 w-4" />
1046
1005
  <AlertTitle>{t("e-SLOG Validation Failed")}</AlertTitle>
1047
1006
  <AlertDescription>
1048
1007
  <p className="mb-2">{t("The following entity settings need to be updated:")}</p>
1049
1008
  <ul className="list-disc space-y-1 pl-4">
1050
- {eslogEntityErrors.map((error) => (
1009
+ {eslog.entityErrors.map((error) => (
1051
1010
  <li key={error.field} className="text-sm">
1052
1011
  {error.message}
1053
1012
  </li>
@@ -1076,14 +1035,20 @@ export default function CreateInvoiceForm({
1076
1035
  t={t}
1077
1036
  fursInline={
1078
1037
  // Hide FURS selector in edit mode - fiscalization is set at creation only
1079
- !isEditMode && isFursEnabled && hasFursPremises
1038
+ !isEditMode && furs.isEnabled && furs.hasPremises
1080
1039
  ? {
1081
- premises: activePremises.map((p) => ({ id: p.id, business_premise_name: p.business_premise_name })),
1082
- devices: activeDevices.map((d) => ({ id: d.id, electronic_device_name: d.electronic_device_name })),
1083
- selectedPremise: selectedPremiseName,
1084
- selectedDevice: selectedDeviceName,
1085
- onPremiseChange: setSelectedPremiseName,
1086
- onDeviceChange: setSelectedDeviceName,
1040
+ premises: furs.activePremises.map((p) => ({
1041
+ id: p.id,
1042
+ business_premise_name: p.business_premise_name,
1043
+ })),
1044
+ devices: furs.activeDevices.map((d: any) => ({
1045
+ id: d.id,
1046
+ electronic_device_name: d.electronic_device_name,
1047
+ })),
1048
+ selectedPremise: furs.selectedPremiseName,
1049
+ selectedDevice: furs.selectedDeviceName,
1050
+ onPremiseChange: furs.setSelectedPremiseName,
1051
+ onDeviceChange: furs.setSelectedDeviceName,
1087
1052
  isSkipped: skipFiscalization,
1088
1053
  }
1089
1054
  : undefined
@@ -1091,18 +1056,18 @@ export default function CreateInvoiceForm({
1091
1056
  finaInline={
1092
1057
  !isEditMode && useFinaNumbering
1093
1058
  ? {
1094
- premises: activeFinaPremises.map((p: any) => ({
1059
+ premises: fina.activePremises.map((p: any) => ({
1095
1060
  id: p.id,
1096
1061
  business_premise_name: p.business_premise_name,
1097
1062
  })),
1098
- devices: activeFinaDevices.map((d: any) => ({
1063
+ devices: fina.activeDevices.map((d: any) => ({
1099
1064
  id: d.id,
1100
1065
  electronic_device_name: d.electronic_device_name,
1101
1066
  })),
1102
- selectedPremise: selectedFinaBusinessPremiseName,
1103
- selectedDevice: selectedFinaElectronicDeviceName,
1104
- onPremiseChange: setSelectedFinaBusinessPremiseName,
1105
- onDeviceChange: setSelectedFinaElectronicDeviceName,
1067
+ selectedPremise: fina.selectedPremiseName,
1068
+ selectedDevice: fina.selectedDeviceName,
1069
+ onPremiseChange: fina.setSelectedPremiseName,
1070
+ onDeviceChange: fina.setSelectedDeviceName,
1106
1071
  }
1107
1072
  : undefined
1108
1073
  }
@@ -1124,7 +1089,7 @@ export default function CreateInvoiceForm({
1124
1089
  paymentTypes={paymentTypes}
1125
1090
  onPaymentTypesChange={setPaymentTypes}
1126
1091
  t={t}
1127
- alwaysShowPaymentType={!!isFinaActive && requiresFinaFiscalization}
1092
+ alwaysShowPaymentType={!!fina.isActive && requiresFinaFiscalization}
1128
1093
  />
1129
1094
  )}
1130
1095
  </DocumentDetailsSection>
@@ -1146,6 +1111,7 @@ export default function CreateInvoiceForm({
1146
1111
  maxTaxesPerItem={activeEntity?.country_rules?.max_taxes_per_item}
1147
1112
  priceModesRef={priceModesRef}
1148
1113
  initialPriceModes={initialPriceModes}
1114
+ onItemsStateChange={emitCurrentPreviewPayload}
1149
1115
  />
1150
1116
 
1151
1117
  <DocumentNoteField