@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
@@ -3,7 +3,7 @@
3
3
  * Server-side aggregation by month for accurate trend data.
4
4
  */
5
5
  import type { StatsQueryDataItem } from "@spaceinvoices/js-sdk";
6
- import { useQuery } from "@tanstack/react-query";
6
+ import { useQueries } from "@tanstack/react-query";
7
7
  import { useSDK } from "@/ui/providers/sdk-provider";
8
8
  import { STATS_QUERY_CACHE_KEY } from "../shared/use-stats-query";
9
9
 
@@ -37,57 +37,86 @@ export function useRevenueTrendData(entityId: string | undefined) {
37
37
 
38
38
  const { months, startDate, endDate } = getLastMonths(6);
39
39
 
40
- const query = useQuery({
41
- queryKey: [STATS_QUERY_CACHE_KEY, entityId, "revenue-trend", startDate, endDate],
42
- queryFn: async () => {
43
- if (!entityId || !sdk) throw new Error("Missing entity or SDK");
44
- return sdk.entityStats.queryEntityStats(
45
- {
46
- metrics: [{ type: "sum", field: "total_with_tax_converted", alias: "revenue" }],
47
- table: "invoices",
48
- date_from: startDate,
49
- date_to: endDate,
50
- filters: { is_draft: false, voided_at: null },
51
- group_by: ["month", "quote_currency"], // Include currency for display
52
- order_by: [{ field: "month", direction: "asc" }],
40
+ const sharedQueryParams = {
41
+ date_from: startDate,
42
+ date_to: endDate,
43
+ filters: { is_draft: false },
44
+ group_by: ["month", "quote_currency"],
45
+ order_by: [{ field: "month", direction: "asc" as const }],
46
+ };
47
+
48
+ const queries = useQueries({
49
+ queries: [
50
+ {
51
+ queryKey: [STATS_QUERY_CACHE_KEY, entityId, "revenue-trend", startDate, endDate],
52
+ queryFn: async () => {
53
+ if (!entityId || !sdk) throw new Error("Missing entity or SDK");
54
+ return sdk.entityStats.queryEntityStats(
55
+ {
56
+ metrics: [{ type: "sum", field: "total_with_tax_converted", alias: "revenue" }],
57
+ table: "invoices",
58
+ ...sharedQueryParams,
59
+ },
60
+ { entity_id: entityId },
61
+ );
62
+ },
63
+ enabled: !!entityId && !!sdk,
64
+ staleTime: 120_000,
65
+ },
66
+ {
67
+ queryKey: [STATS_QUERY_CACHE_KEY, entityId, "cn-revenue-trend", startDate, endDate],
68
+ queryFn: async () => {
69
+ if (!entityId || !sdk) throw new Error("Missing entity or SDK");
70
+ return sdk.entityStats.queryEntityStats(
71
+ {
72
+ metrics: [{ type: "sum", field: "total_with_tax_converted", alias: "revenue" }],
73
+ table: "credit_notes",
74
+ ...sharedQueryParams,
75
+ },
76
+ { entity_id: entityId },
77
+ );
53
78
  },
54
- { entity_id: entityId },
55
- );
56
- },
57
- enabled: !!entityId && !!sdk,
58
- staleTime: 120_000,
59
- select: (response) => {
60
- // Build a map of all months with 0 revenue
61
- const monthMap: Record<string, number> = {};
62
- for (const month of months) {
63
- monthMap[month] = 0;
64
- }
79
+ enabled: !!entityId && !!sdk,
80
+ staleTime: 120_000,
81
+ },
82
+ ],
83
+ });
65
84
 
66
- // Fill in the actual revenue from the API response
67
- // Sum up revenues per month (in case of multiple rows due to quote_currency grouping)
68
- const data = response.data || [];
69
- let currency = "EUR";
70
- for (const row of data as StatsQueryDataItem[]) {
71
- const month = String(row.month);
72
- if (month in monthMap) {
73
- monthMap[month] += Number(row.revenue) || 0;
74
- }
75
- // Get currency from first row with data
76
- if (row.quote_currency && currency === "EUR") {
77
- currency = String(row.quote_currency);
78
- }
79
- }
85
+ const [invoiceQuery, cnQuery] = queries;
80
86
 
81
- return {
82
- data: months.map((month) => ({ month, revenue: monthMap[month] })),
83
- currency, // Currency from document data
84
- };
85
- },
86
- });
87
+ // Build month maps
88
+ const monthMap: Record<string, number> = {};
89
+ const cnMonthMap: Record<string, number> = {};
90
+ for (const month of months) {
91
+ monthMap[month] = 0;
92
+ cnMonthMap[month] = 0;
93
+ }
94
+
95
+ // Fill invoice revenue per month
96
+ const invoiceData = (invoiceQuery.data?.data || []) as StatsQueryDataItem[];
97
+ let currency = "EUR";
98
+ for (const row of invoiceData) {
99
+ const month = String(row.month);
100
+ if (month in monthMap) {
101
+ monthMap[month] += Number(row.revenue) || 0;
102
+ }
103
+ if (row.quote_currency && currency === "EUR") {
104
+ currency = String(row.quote_currency);
105
+ }
106
+ }
107
+
108
+ // Fill credit note revenue per month
109
+ const cnData = (cnQuery.data?.data || []) as StatsQueryDataItem[];
110
+ for (const row of cnData) {
111
+ const month = String(row.month);
112
+ if (month in cnMonthMap) {
113
+ cnMonthMap[month] += Number(row.revenue) || 0;
114
+ }
115
+ }
87
116
 
88
117
  return {
89
- data: query.data?.data || [],
90
- currency: query.data?.currency || "EUR",
91
- isLoading: query.isLoading,
118
+ data: months.map((month) => ({ month, revenue: monthMap[month] - cnMonthMap[month] })),
119
+ currency,
120
+ isLoading: queries.some((q) => q.isLoading),
92
121
  };
93
122
  }
@@ -54,7 +54,7 @@ export function useRevenueData(entityId: string | undefined) {
54
54
  table: "invoices",
55
55
  date_from: monthRange.from,
56
56
  date_to: monthRange.to,
57
- filters: { is_draft: false, voided_at: null },
57
+ filters: { is_draft: false },
58
58
  group_by: ["quote_currency"], // Get the currency for display
59
59
  },
60
60
  { entity_id: entityId },
@@ -74,7 +74,7 @@ export function useRevenueData(entityId: string | undefined) {
74
74
  table: "invoices",
75
75
  date_from: yearRange.from,
76
76
  date_to: yearRange.to,
77
- filters: { is_draft: false, voided_at: null },
77
+ filters: { is_draft: false },
78
78
  },
79
79
  { entity_id: entityId },
80
80
  );
@@ -91,7 +91,7 @@ export function useRevenueData(entityId: string | undefined) {
91
91
  {
92
92
  metrics: [{ type: "sum", field: "total_due", alias: "outstanding" }],
93
93
  table: "invoices",
94
- filters: { is_draft: false, voided_at: null, paid_in_full: false },
94
+ filters: { is_draft: false, paid_in_full: false },
95
95
  },
96
96
  { entity_id: entityId },
97
97
  );
@@ -111,7 +111,7 @@ export function useRevenueData(entityId: string | undefined) {
111
111
  { type: "count", alias: "count" },
112
112
  ],
113
113
  table: "invoices",
114
- filters: { is_draft: false, voided_at: null, paid_in_full: false },
114
+ filters: { is_draft: false, paid_in_full: false },
115
115
  group_by: ["overdue_bucket"],
116
116
  },
117
117
  { entity_id: entityId },
@@ -120,10 +120,73 @@ export function useRevenueData(entityId: string | undefined) {
120
120
  enabled: !!entityId && !!sdk,
121
121
  staleTime: 120_000,
122
122
  },
123
+ // Credit notes: this month
124
+ {
125
+ queryKey: [STATS_QUERY_CACHE_KEY, entityId, "cn-this-month", monthRange.from],
126
+ queryFn: async () => {
127
+ if (!entityId || !sdk) throw new Error("Missing entity or SDK");
128
+ return sdk.entityStats.queryEntityStats(
129
+ {
130
+ metrics: [{ type: "sum", field: "total_with_tax_converted", alias: "revenue" }],
131
+ table: "credit_notes",
132
+ date_from: monthRange.from,
133
+ date_to: monthRange.to,
134
+ filters: { is_draft: false },
135
+ },
136
+ { entity_id: entityId },
137
+ );
138
+ },
139
+ enabled: !!entityId && !!sdk,
140
+ staleTime: 120_000,
141
+ },
142
+ // Credit notes: this year
143
+ {
144
+ queryKey: [STATS_QUERY_CACHE_KEY, entityId, "cn-this-year", yearRange.from],
145
+ queryFn: async () => {
146
+ if (!entityId || !sdk) throw new Error("Missing entity or SDK");
147
+ return sdk.entityStats.queryEntityStats(
148
+ {
149
+ metrics: [{ type: "sum", field: "total_with_tax_converted", alias: "revenue" }],
150
+ table: "credit_notes",
151
+ date_from: yearRange.from,
152
+ date_to: yearRange.to,
153
+ filters: { is_draft: false },
154
+ },
155
+ { entity_id: entityId },
156
+ );
157
+ },
158
+ enabled: !!entityId && !!sdk,
159
+ staleTime: 120_000,
160
+ },
161
+ // Credit notes: outstanding
162
+ {
163
+ queryKey: [STATS_QUERY_CACHE_KEY, entityId, "cn-outstanding"],
164
+ queryFn: async () => {
165
+ if (!entityId || !sdk) throw new Error("Missing entity or SDK");
166
+ return sdk.entityStats.queryEntityStats(
167
+ {
168
+ metrics: [{ type: "sum", field: "total_due", alias: "outstanding" }],
169
+ table: "credit_notes",
170
+ filters: { is_draft: false, paid_in_full: false },
171
+ },
172
+ { entity_id: entityId },
173
+ );
174
+ },
175
+ enabled: !!entityId && !!sdk,
176
+ staleTime: 120_000,
177
+ },
123
178
  ],
124
179
  });
125
180
 
126
- const [thisMonthQuery, thisYearQuery, outstandingQuery, overdueQuery] = queries;
181
+ const [
182
+ thisMonthQuery,
183
+ thisYearQuery,
184
+ outstandingQuery,
185
+ overdueQuery,
186
+ cnThisMonthQuery,
187
+ cnThisYearQuery,
188
+ cnOutstandingQuery,
189
+ ] = queries;
127
190
 
128
191
  // Extract this month revenue and currency (may have multiple rows if grouped by quote_currency)
129
192
  const thisMonthData = thisMonthQuery.data?.data || [];
@@ -134,7 +197,12 @@ export function useRevenueData(entityId: string | undefined) {
134
197
  // Get currency from first row with data
135
198
  const currency = (thisMonthData[0]?.quote_currency as string) || "EUR";
136
199
 
137
- // Extract overdue data (buckets other than "current")
200
+ // Credit note totals
201
+ const cnThisMonth = Number(cnThisMonthQuery.data?.data?.[0]?.revenue) || 0;
202
+ const cnThisYear = Number(cnThisYearQuery.data?.data?.[0]?.revenue) || 0;
203
+ const cnOutstanding = Number(cnOutstandingQuery.data?.data?.[0]?.outstanding) || 0;
204
+
205
+ // Extract overdue data (buckets other than "current") — stays invoice-only
138
206
  const overdueData = overdueQuery.data?.data || [];
139
207
  const overdueBuckets = overdueData.filter((row: StatsQueryDataItem) => row.overdue_bucket !== "current");
140
208
  const totalOverdue = overdueBuckets.reduce(
@@ -148,9 +216,9 @@ export function useRevenueData(entityId: string | undefined) {
148
216
 
149
217
  return {
150
218
  data: {
151
- thisMonth: thisMonthRevenue,
152
- thisYear: Number(thisYearQuery.data?.data?.[0]?.revenue) || 0,
153
- outstanding: Number(outstandingQuery.data?.data?.[0]?.outstanding) || 0,
219
+ thisMonth: thisMonthRevenue - cnThisMonth,
220
+ thisYear: (Number(thisYearQuery.data?.data?.[0]?.revenue) || 0) - cnThisYear,
221
+ outstanding: (Number(outstandingQuery.data?.data?.[0]?.outstanding) || 0) - cnOutstanding,
154
222
  overdue: totalOverdue,
155
223
  overdueCount,
156
224
  currency, // Currency from document data
@@ -10,12 +10,19 @@ import { Form } from "@/ui/components/ui/form";
10
10
  import { Label } from "@/ui/components/ui/label";
11
11
  import { createDeliveryNoteSchema } from "@/ui/generated/schemas";
12
12
  import { useNextDocumentNumber } from "@/ui/hooks/use-next-document-number";
13
+ import { useViesCheck } from "@/ui/hooks/use-vies-check";
13
14
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
14
15
  import { createTranslation } from "@/ui/lib/translation";
15
16
  import { useEntities } from "@/ui/providers/entities-context";
16
17
  import { useFormFooterRegistration } from "@/ui/providers/form-footer-context";
17
18
  import { CUSTOMERS_CACHE_KEY } from "../../customers/customers.hooks";
18
- import { DocumentDetailsSection, DocumentNoteField } from "../../documents/create/document-details-section";
19
+ import {
20
+ DocumentDetailsSection,
21
+ DocumentFooterField,
22
+ DocumentNoteField,
23
+ DocumentSignatureField,
24
+ DocumentTaxClauseField,
25
+ } from "../../documents/create/document-details-section";
19
26
  import { DocumentItemsSection, type PriceModesMap } from "../../documents/create/document-items-section";
20
27
  import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
21
28
  import type { DocumentTypes } from "../../documents/types";
@@ -90,6 +97,7 @@ export default function CreateDeliveryNoteForm({
90
97
 
91
98
  // Hide prices state (delivery note specific)
92
99
  const defaultHidePrices = (activeEntity?.settings as any)?.delivery_note_hide_prices ?? false;
100
+ const defaultFooter = (activeEntity?.settings as any)?.document_footer || "";
93
101
  const [hidePrices, setHidePrices] = useState<boolean>((initialValues as any)?.hide_prices ?? defaultHidePrices);
94
102
 
95
103
  // Fetch next delivery note number
@@ -106,13 +114,18 @@ export default function CreateDeliveryNoteForm({
106
114
  // Cast customer to form schema type (API type may have additional fields)
107
115
  customer: (initialValues?.customer as CreateDeliveryNoteFormValues["customer"]) ?? undefined,
108
116
  items: initialValues?.items?.length
109
- ? initialValues.items.map((item) => ({
117
+ ? initialValues.items.map((item: any) => ({
118
+ type: item.type,
110
119
  name: item.name || "",
111
120
  description: item.description || "",
112
- quantity: item.quantity ?? 1,
113
- // Use gross_price if set, otherwise use price
114
- price: item.gross_price ?? item.price,
115
- taxes: item.taxes || [],
121
+ ...(item.type !== "separator"
122
+ ? {
123
+ quantity: item.quantity ?? 1,
124
+ // Use gross_price if set, otherwise use price
125
+ price: item.gross_price ?? item.price,
126
+ taxes: item.taxes || [],
127
+ }
128
+ : {}),
116
129
  }))
117
130
  : [
118
131
  {
@@ -124,7 +137,10 @@ export default function CreateDeliveryNoteForm({
124
137
  },
125
138
  ],
126
139
  currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
140
+ reference: (initialValues as any)?.reference ?? "",
127
141
  note: initialValues?.note ?? "",
142
+ tax_clause: (initialValues as any)?.tax_clause ?? "",
143
+ footer: (initialValues as any)?.footer ?? defaultFooter,
128
144
  },
129
145
  });
130
146
 
@@ -148,6 +164,14 @@ export default function CreateDeliveryNoteForm({
148
164
  }
149
165
  }, [nextNumberData?.number, form]);
150
166
 
167
+ // Set default footer from entity settings when entity data is available
168
+ useEffect(() => {
169
+ const entityDefaultFooter = (activeEntity?.settings as any)?.document_footer;
170
+ if (entityDefaultFooter && !form.getValues("footer")) {
171
+ form.setValue("footer", entityDefaultFooter);
172
+ }
173
+ }, [activeEntity, form]);
174
+
151
175
  // Auto-add tax field for tax subject entities
152
176
  useEffect(() => {
153
177
  if (activeEntity?.is_tax_subject) {
@@ -158,10 +182,49 @@ export default function CreateDeliveryNoteForm({
158
182
  }
159
183
  }, [activeEntity?.is_tax_subject, form]);
160
184
 
185
+ // Set default signature from entity settings
186
+ useEffect(() => {
187
+ const entityDefaultSignature = (activeEntity?.settings as any)?.default_document_signature;
188
+ if (entityDefaultSignature && !form.getValues("signature")) {
189
+ form.setValue("signature", entityDefaultSignature);
190
+ }
191
+ }, [activeEntity, form]);
192
+
161
193
  const formValues = useWatch({
162
194
  control: form.control,
163
195
  });
164
196
 
197
+ // ============================================================================
198
+ // VIES Check - determine if reverse charge applies
199
+ // ============================================================================
200
+ const {
201
+ reverseChargeApplies,
202
+ transactionType,
203
+ isFetching: isViesFetching,
204
+ warning: viesWarning,
205
+ } = useViesCheck({
206
+ issuerCountryCode: activeEntity?.country_code,
207
+ isTaxSubject: activeEntity?.is_tax_subject ?? true,
208
+ customerCountry: formValues.customer?.country,
209
+ customerCountryCode: formValues.customer?.country_code,
210
+ customerTaxNumber: formValues.customer?.tax_number,
211
+ enabled: !!activeEntity,
212
+ });
213
+
214
+ // Auto-populate tax_clause from entity settings when transaction type changes
215
+ const effectiveTransactionType = transactionType ?? "domestic";
216
+ const prevTransactionTypeRef = useRef<string | undefined>(undefined);
217
+ useEffect(() => {
218
+ if (effectiveTransactionType === prevTransactionTypeRef.current) return;
219
+ prevTransactionTypeRef.current = effectiveTransactionType;
220
+
221
+ const taxClauseDefaults = (activeEntity?.settings as any)?.tax_clause_defaults;
222
+ if (!taxClauseDefaults) return;
223
+
224
+ const clause = taxClauseDefaults[effectiveTransactionType] ?? "";
225
+ form.setValue("tax_clause", clause);
226
+ }, [effectiveTransactionType, activeEntity, form]);
227
+
165
228
  // Extract customer management logic into a custom hook
166
229
  const {
167
230
  originalCustomer,
@@ -232,7 +295,7 @@ export default function CreateDeliveryNoteForm({
232
295
  useFormFooterRegistration({
233
296
  formId: "create-delivery-note-form",
234
297
  isPending,
235
- isDirty: form.formState.isDirty,
298
+ isDirty: form.formState.isDirty || !!initialValues,
236
299
  label: t("Create Delivery Note"),
237
300
  secondaryAction,
238
301
  });
@@ -260,7 +323,9 @@ export default function CreateDeliveryNoteForm({
260
323
  customer: formValues.customer,
261
324
  items: transformedItems,
262
325
  currency_code: formValues.currency_code,
326
+ reference: formValues.reference,
263
327
  note: formValues.note,
328
+ signature: formValues.signature,
264
329
  hide_prices: hidePrices,
265
330
  };
266
331
  callback(payload);
@@ -313,6 +378,10 @@ export default function CreateDeliveryNoteForm({
313
378
  maxTaxesPerItem={activeEntity?.country_rules?.max_taxes_per_item}
314
379
  priceModesRef={priceModesRef}
315
380
  initialPriceModes={initialPriceModes}
381
+ taxesDisabled={reverseChargeApplies}
382
+ taxesDisabledMessage={
383
+ reverseChargeApplies ? t("Reverse charge - tax exempt EU B2B sale") : viesWarning ? viesWarning : undefined
384
+ }
316
385
  />
317
386
 
318
387
  <DocumentNoteField
@@ -326,6 +395,44 @@ export default function CreateDeliveryNoteForm({
326
395
  customer: formValues.customer as any,
327
396
  }}
328
397
  />
398
+
399
+ <DocumentTaxClauseField
400
+ control={form.control}
401
+ t={t}
402
+ entity={activeEntity}
403
+ document={{
404
+ number: formValues.number,
405
+ date: formValues.date,
406
+ currency_code: formValues.currency_code,
407
+ customer: formValues.customer as any,
408
+ }}
409
+ transactionType={transactionType}
410
+ isTransactionTypeFetching={isViesFetching}
411
+ />
412
+
413
+ <DocumentSignatureField
414
+ control={form.control}
415
+ t={t}
416
+ entity={activeEntity}
417
+ document={{
418
+ number: formValues.number,
419
+ date: formValues.date,
420
+ currency_code: formValues.currency_code,
421
+ customer: formValues.customer as any,
422
+ }}
423
+ />
424
+
425
+ <DocumentFooterField
426
+ control={form.control}
427
+ t={t}
428
+ entity={activeEntity}
429
+ document={{
430
+ number: formValues.number,
431
+ date: formValues.date,
432
+ currency_code: formValues.currency_code,
433
+ customer: formValues.customer as any,
434
+ }}
435
+ />
329
436
  </form>
330
437
  </Form>
331
438
  );
@@ -27,6 +27,9 @@ export default {
27
27
  "Insert variable": "Variable einfügen",
28
28
  "Add payment instructions, terms, or other notes...":
29
29
  "Zahlungsanweisungen, Bedingungen oder andere Notizen hinzufügen...",
30
+ // Signature field
31
+ Signature: "Unterschrift",
32
+ "Add signature text...": "Unterschriftstext hinzufügen...",
30
33
  Quantity: "Menge",
31
34
  Price: "Preis",
32
35
  Unit: "Einheit",
@@ -47,4 +50,22 @@ export default {
47
50
  "Gross price (tax included)": "Bruttopreis (inkl. MwSt.)",
48
51
  "Net price (before tax)": "Nettopreis (exkl. MwSt.)",
49
52
  "Hide prices": "Preise ausblenden",
53
+ // Separator items
54
+ "Add separator": "Trennzeile hinzufügen",
55
+ "Section header": "Abschnittsüberschrift",
56
+ "Section title...": "Abschnittstitel...",
57
+ // Transaction type
58
+ "Transaction type": "Transaktionstyp",
59
+ Domestic: "Inland",
60
+ "EU B2B": "EU B2B",
61
+ "EU B2C": "EU B2C",
62
+ Export: "Export",
63
+ "Determining transaction type...": "Transaktionstyp wird ermittelt...",
64
+ "This invoice will not be fiscalized (non-domestic transaction)":
65
+ "Diese Rechnung wird nicht fiskalisiert (nicht-inländische Transaktion)",
66
+ "Tax Clause": "Steuerklausel",
67
+ "Add tax clause...": "Steuerklausel hinzufügen...",
68
+ Footer: "Fußzeile",
69
+ "Add document footer...": "Dokumentfußzeile hinzufügen...",
70
+ "Reverse charge - tax exempt EU B2B sale": "Umkehrung der Steuerschuldnerschaft - steuerbefreiter EU B2B Verkauf",
50
71
  } as const;
@@ -26,6 +26,9 @@ export default {
26
26
  Note: "Nota",
27
27
  "Insert variable": "Insertar variable",
28
28
  "Add payment instructions, terms, or other notes...": "Añada instrucciones de pago, condiciones u otras notas...",
29
+ // Signature field
30
+ Signature: "Firma",
31
+ "Add signature text...": "Añadir texto de firma...",
29
32
  Quantity: "Cantidad",
30
33
  Price: "Precio",
31
34
  Unit: "Unidad",
@@ -46,4 +49,22 @@ export default {
46
49
  "Gross price (tax included)": "Precio bruto (impuestos incluidos)",
47
50
  "Net price (before tax)": "Precio neto (antes de impuestos)",
48
51
  "Hide prices": "Ocultar precios",
52
+ // Separator items
53
+ "Add separator": "Añadir separador",
54
+ "Section header": "Encabezado de sección",
55
+ "Section title...": "Título de sección...",
56
+ // Transaction type
57
+ "Transaction type": "Tipo de transacción",
58
+ Domestic: "Nacional",
59
+ "EU B2B": "EU B2B",
60
+ "EU B2C": "EU B2C",
61
+ Export: "Exportación",
62
+ "Determining transaction type...": "Determinando tipo de transacción...",
63
+ "This invoice will not be fiscalized (non-domestic transaction)":
64
+ "Esta factura no será fiscalizada (transacción no nacional)",
65
+ "Tax Clause": "Cláusula fiscal",
66
+ "Add tax clause...": "Agregar cláusula fiscal...",
67
+ Footer: "Pie de página",
68
+ "Add document footer...": "Añadir pie de página del documento...",
69
+ "Reverse charge - tax exempt EU B2B sale": "Inversión del sujeto pasivo - venta EU B2B exenta de impuestos",
49
70
  } as const;
@@ -27,6 +27,9 @@ export default {
27
27
  "Insert variable": "Insérer une variable",
28
28
  "Add payment instructions, terms, or other notes...":
29
29
  "Ajoutez des instructions de paiement, des conditions ou d'autres notes...",
30
+ // Signature field
31
+ Signature: "Signature",
32
+ "Add signature text...": "Ajouter un texte de signature...",
30
33
  Quantity: "Quantité",
31
34
  Price: "Prix",
32
35
  Unit: "Unité",
@@ -47,4 +50,22 @@ export default {
47
50
  "Gross price (tax included)": "Prix brut (taxes incluses)",
48
51
  "Net price (before tax)": "Prix net (avant taxes)",
49
52
  "Hide prices": "Masquer les prix",
53
+ // Separator items
54
+ "Add separator": "Ajouter un séparateur",
55
+ "Section header": "En-tête de section",
56
+ "Section title...": "Titre de section...",
57
+ // Transaction type
58
+ "Transaction type": "Type de transaction",
59
+ Domestic: "Nationale",
60
+ "EU B2B": "EU B2B",
61
+ "EU B2C": "EU B2C",
62
+ Export: "Exportation",
63
+ "Determining transaction type...": "Détermination du type de transaction...",
64
+ "This invoice will not be fiscalized (non-domestic transaction)":
65
+ "Cette facture ne sera pas fiscalisée (transaction non nationale)",
66
+ "Tax Clause": "Clause fiscale",
67
+ "Add tax clause...": "Ajouter une clause fiscale...",
68
+ Footer: "Pied de page",
69
+ "Add document footer...": "Ajouter un pied de page...",
70
+ "Reverse charge - tax exempt EU B2B sale": "Autoliquidation - vente B2B UE exonérée de taxe",
50
71
  } as const;
@@ -26,6 +26,9 @@ export default {
26
26
  Note: "Napomena",
27
27
  "Insert variable": "Umetni varijablu",
28
28
  "Add payment instructions, terms, or other notes...": "Dodajte upute za plaćanje, uvjete ili druge napomene...",
29
+ // Signature field
30
+ Signature: "Potpis",
31
+ "Add signature text...": "Dodaj tekst potpisa...",
29
32
  Quantity: "Količina",
30
33
  Price: "Cijena",
31
34
  Unit: "Jedinica",
@@ -46,4 +49,22 @@ export default {
46
49
  "Gross price (tax included)": "Bruto cijena (s porezom)",
47
50
  "Net price (before tax)": "Neto cijena (prije poreza)",
48
51
  "Hide prices": "Sakrij cijene",
52
+ // Separator items
53
+ "Add separator": "Dodaj separator",
54
+ "Section header": "Naslov odjeljka",
55
+ "Section title...": "Naslov odjeljka...",
56
+ // Transaction type
57
+ "Transaction type": "Vrsta transakcije",
58
+ Domestic: "Domaća",
59
+ "EU B2B": "EU B2B",
60
+ "EU B2C": "EU B2C",
61
+ Export: "Izvoz",
62
+ "Determining transaction type...": "Određivanje vrste transakcije...",
63
+ "This invoice will not be fiscalized (non-domestic transaction)":
64
+ "Ovaj račun neće biti fiskaliziran (nedomaća transakcija)",
65
+ "Tax Clause": "Porezna klauzula",
66
+ "Add tax clause...": "Dodajte poreznu klauzulu...",
67
+ Footer: "Podnožje",
68
+ "Add document footer...": "Dodajte podnožje dokumenta...",
69
+ "Reverse charge - tax exempt EU B2B sale": "Prijenos porezne obveze - porezno oslobođena EU B2B prodaja",
49
70
  } as const;
@@ -26,6 +26,9 @@ export default {
26
26
  Note: "Nota",
27
27
  "Insert variable": "Inserisci variabile",
28
28
  "Add payment instructions, terms, or other notes...": "Aggiungi istruzioni di pagamento, condizioni o altre note...",
29
+ // Signature field
30
+ Signature: "Firma",
31
+ "Add signature text...": "Aggiungi testo della firma...",
29
32
  Quantity: "Quantità",
30
33
  Price: "Prezzo",
31
34
  Unit: "Unità",
@@ -46,4 +49,22 @@ export default {
46
49
  "Gross price (tax included)": "Prezzo lordo (imposte incluse)",
47
50
  "Net price (before tax)": "Prezzo netto (prima delle imposte)",
48
51
  "Hide prices": "Nascondi prezzi",
52
+ // Separator items
53
+ "Add separator": "Aggiungi separatore",
54
+ "Section header": "Intestazione sezione",
55
+ "Section title...": "Titolo sezione...",
56
+ // Transaction type
57
+ "Transaction type": "Tipo di transazione",
58
+ Domestic: "Nazionale",
59
+ "EU B2B": "EU B2B",
60
+ "EU B2C": "EU B2C",
61
+ Export: "Esportazione",
62
+ "Determining transaction type...": "Determinazione tipo di transazione...",
63
+ "This invoice will not be fiscalized (non-domestic transaction)":
64
+ "Questa fattura non sarà fiscalizzata (transazione non nazionale)",
65
+ "Tax Clause": "Clausola fiscale",
66
+ "Add tax clause...": "Aggiungi clausola fiscale...",
67
+ Footer: "Piè di pagina",
68
+ "Add document footer...": "Aggiungi piè di pagina del documento...",
69
+ "Reverse charge - tax exempt EU B2B sale": "Inversione contabile - vendita B2B UE esente da imposta",
49
70
  } as const;