@spaceinvoices/react-ui 0.4.2 → 0.4.4
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.
- package/cli/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/common/autocomplete.tsx +18 -2
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +0 -1
- package/src/components/advance-invoices/create/locales/de.ts +2 -0
- package/src/components/advance-invoices/create/locales/es.ts +2 -0
- package/src/components/advance-invoices/create/locales/fr.ts +2 -0
- package/src/components/advance-invoices/create/locales/hr.ts +1 -0
- package/src/components/advance-invoices/create/locales/it.ts +2 -0
- package/src/components/advance-invoices/create/locales/nl.ts +2 -0
- package/src/components/advance-invoices/create/locales/pl.ts +1 -0
- package/src/components/advance-invoices/create/locales/pt.ts +1 -0
- package/src/components/advance-invoices/create/locales/sl.ts +1 -0
- package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +0 -1
- package/src/components/advance-invoices/list/list-table.tsx +130 -40
- package/src/components/advance-invoices/list/locales/de.ts +6 -0
- package/src/components/advance-invoices/list/locales/en.ts +5 -0
- package/src/components/advance-invoices/list/locales/es.ts +6 -0
- package/src/components/advance-invoices/list/locales/fr.ts +6 -0
- package/src/components/advance-invoices/list/locales/hr.ts +6 -0
- package/src/components/advance-invoices/list/locales/it.ts +6 -0
- package/src/components/advance-invoices/list/locales/nl.ts +6 -0
- package/src/components/advance-invoices/list/locales/pl.ts +6 -0
- package/src/components/advance-invoices/list/locales/pt.ts +6 -0
- package/src/components/advance-invoices/list/locales/sl.ts +6 -0
- package/src/components/credit-notes/create/create-credit-note-form.tsx +114 -3
- package/src/components/credit-notes/create/locales/de.ts +2 -0
- package/src/components/credit-notes/create/locales/es.ts +2 -0
- package/src/components/credit-notes/create/locales/fr.ts +2 -0
- package/src/components/credit-notes/create/locales/hr.ts +1 -0
- package/src/components/credit-notes/create/locales/it.ts +2 -0
- package/src/components/credit-notes/create/locales/nl.ts +2 -0
- package/src/components/credit-notes/create/locales/pl.ts +1 -0
- package/src/components/credit-notes/create/locales/pt.ts +1 -0
- package/src/components/credit-notes/create/locales/sl.ts +1 -0
- package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
- package/src/components/credit-notes/list/list-table.tsx +111 -36
- package/src/components/credit-notes/list/locales/de.ts +6 -1
- package/src/components/credit-notes/list/locales/en.ts +6 -0
- package/src/components/credit-notes/list/locales/es.ts +6 -1
- package/src/components/credit-notes/list/locales/fr.ts +6 -1
- package/src/components/credit-notes/list/locales/hr.ts +6 -1
- package/src/components/credit-notes/list/locales/it.ts +6 -1
- package/src/components/credit-notes/list/locales/nl.ts +6 -1
- package/src/components/credit-notes/list/locales/pl.ts +6 -1
- package/src/components/credit-notes/list/locales/pt.ts +6 -1
- package/src/components/credit-notes/list/locales/sl.ts +6 -1
- package/src/components/customers/customer-list-table/customer-list-table.tsx +0 -3
- package/src/components/customers/customer-list-table/locales/de.ts +1 -0
- package/src/components/customers/customer-list-table/locales/es.ts +1 -0
- package/src/components/customers/customer-list-table/locales/fr.ts +1 -0
- package/src/components/customers/customer-list-table/locales/hr.ts +1 -0
- package/src/components/customers/customer-list-table/locales/it.ts +1 -0
- package/src/components/customers/customer-list-table/locales/nl.ts +1 -0
- package/src/components/customers/customer-list-table/locales/pl.ts +1 -0
- package/src/components/customers/customer-list-table/locales/pt.ts +1 -0
- package/src/components/customers/customer-list-table/locales/sl.ts +1 -0
- package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +2 -2
- package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +3 -3
- package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +1 -1
- package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +1 -1
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +1 -1
- package/src/components/dashboard/shared/use-revenue-data.ts +4 -4
- package/src/components/dashboard/shared/use-stats-counts.ts +4 -4
- package/src/components/dashboard/shared/use-stats-query.ts +1 -1
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +2 -2
- package/src/components/dashboard/top-customers-chart/use-top-customers.ts +1 -1
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +332 -0
- package/src/components/delivery-notes/create/locales/de.ts +50 -0
- package/src/components/delivery-notes/create/locales/es.ts +49 -0
- package/src/components/delivery-notes/create/locales/fr.ts +50 -0
- package/src/components/delivery-notes/create/locales/hr.ts +49 -0
- package/src/components/delivery-notes/create/locales/it.ts +49 -0
- package/src/components/delivery-notes/create/locales/nl.ts +50 -0
- package/src/components/delivery-notes/create/locales/pl.ts +49 -0
- package/src/components/delivery-notes/create/locales/pt.ts +50 -0
- package/src/components/delivery-notes/create/locales/sl.ts +49 -0
- package/src/components/delivery-notes/create/prepare-delivery-note-submission.ts +38 -0
- package/src/components/delivery-notes/create/use-delivery-note-customer-form.ts +1 -0
- package/src/components/delivery-notes/delivery-notes.hooks.ts +15 -0
- package/src/components/delivery-notes/list/index.ts +3 -0
- package/src/components/delivery-notes/list/list-row-actions.tsx +122 -0
- package/src/components/delivery-notes/list/list-table.tsx +247 -0
- package/src/components/delivery-notes/list/locales/de.ts +13 -0
- package/src/components/delivery-notes/list/locales/en.ts +13 -0
- package/src/components/delivery-notes/list/locales/es.ts +13 -0
- package/src/components/delivery-notes/list/locales/fr.ts +13 -0
- package/src/components/delivery-notes/list/locales/hr.ts +13 -0
- package/src/components/delivery-notes/list/locales/it.ts +13 -0
- package/src/components/delivery-notes/list/locales/nl.ts +13 -0
- package/src/components/delivery-notes/list/locales/pl.ts +13 -0
- package/src/components/delivery-notes/list/locales/pt.ts +13 -0
- package/src/components/delivery-notes/list/locales/sl.ts +13 -0
- package/src/components/delivery-notes/list/use-delivery-note-download.ts +63 -0
- package/src/components/documents/create/document-details-section.tsx +76 -58
- package/src/components/documents/create/linked-documents-info.tsx +82 -0
- package/src/components/documents/create/live-preview.tsx +38 -11
- package/src/components/documents/create/prepare-document-submission.ts +1 -1
- package/src/components/documents/documents.hooks.ts +2 -1
- package/src/components/documents/shared/document-preview-display.tsx +12 -5
- package/src/components/documents/types.ts +12 -3
- package/src/components/documents/view/document-activities-list.tsx +65 -47
- package/src/components/documents/view/document-details-card.tsx +102 -77
- package/src/components/documents/view/document-payments-list.tsx +102 -68
- package/src/components/documents/view/document-relations-list.tsx +120 -0
- package/src/components/documents/view/document-sidebar.tsx +151 -0
- package/src/components/documents/view/index.ts +2 -0
- package/src/components/documents/view/locales/de.ts +23 -0
- package/src/components/documents/view/locales/es.ts +23 -0
- package/src/components/documents/view/locales/fr.ts +23 -0
- package/src/components/documents/view/locales/hr.ts +23 -0
- package/src/components/documents/view/locales/it.ts +23 -0
- package/src/components/documents/view/locales/nl.ts +23 -0
- package/src/components/documents/view/locales/pl.ts +23 -0
- package/src/components/documents/view/locales/pt.ts +23 -0
- package/src/components/documents/view/locales/sl.ts +23 -0
- package/src/components/documents/view/use-document-download.ts +8 -5
- package/src/components/entities/create-entity-form.tsx +165 -13
- package/src/components/entities/entity-settings-form/entity-settings-form.tsx +101 -1
- package/src/components/entities/entity-settings-form/locales/de.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/es.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/fr.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/hr.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/it.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/nl.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/pl.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/pt.ts +9 -0
- package/src/components/entities/entity-settings-form/locales/sl.ts +9 -0
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +83 -71
- package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +7 -2
- package/src/components/entities/fina-settings-form/locales/de.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/en.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/es.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/fr.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/hr.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/it.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/nl.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/pl.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/pt.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/sl.ts +2 -2
- package/src/components/entities/furs-settings-form/furs-settings-form.tsx +10 -1
- package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +7 -2
- package/src/components/entities/furs-settings-form/locales/en.ts +0 -1
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +12 -4
- package/src/components/entities/settings/pdf-template-selector/locales/de.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/es.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/fr.ts +4 -1
- package/src/components/entities/settings/pdf-template-selector/locales/hr.ts +4 -1
- package/src/components/entities/settings/pdf-template-selector/locales/it.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/nl.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/pl.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/pt.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/sl.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/pdf-template-cards.tsx +1 -0
- package/src/components/estimates/create/create-estimate-form.tsx +29 -2
- package/src/components/estimates/list/list-table.tsx +3 -6
- package/src/components/estimates/list/locales/de.ts +1 -0
- package/src/components/estimates/list/locales/es.ts +1 -0
- package/src/components/estimates/list/locales/fr.ts +1 -0
- package/src/components/estimates/list/locales/hr.ts +1 -0
- package/src/components/estimates/list/locales/it.ts +1 -0
- package/src/components/estimates/list/locales/nl.ts +1 -0
- package/src/components/estimates/list/locales/pl.ts +1 -0
- package/src/components/estimates/list/locales/pt.ts +1 -0
- package/src/components/estimates/list/locales/sl.ts +1 -0
- package/src/components/export/document-export-form.tsx +9 -2
- package/src/components/export/index.ts +2 -0
- package/src/components/export/sales-per-item-export-form.tsx +223 -0
- package/src/components/invoices/create/create-invoice-form.tsx +48 -1
- package/src/components/invoices/create/locales/de.ts +11 -0
- package/src/components/invoices/create/locales/es.ts +11 -0
- package/src/components/invoices/create/locales/fr.ts +11 -0
- package/src/components/invoices/create/locales/hr.ts +10 -0
- package/src/components/invoices/create/locales/it.ts +11 -0
- package/src/components/invoices/create/locales/nl.ts +11 -0
- package/src/components/invoices/create/locales/pl.ts +10 -0
- package/src/components/invoices/create/locales/pt.ts +10 -0
- package/src/components/invoices/create/locales/sl.ts +10 -0
- package/src/components/invoices/invoices-furs.hooks.ts +27 -9
- package/src/components/invoices/list/list-row-actions.tsx +3 -3
- package/src/components/invoices/list/list-table.tsx +132 -21
- package/src/components/invoices/list/locales/de.ts +6 -0
- package/src/components/invoices/list/locales/en.ts +5 -0
- package/src/components/invoices/list/locales/es.ts +6 -0
- package/src/components/invoices/list/locales/fr.ts +6 -0
- package/src/components/invoices/list/locales/hr.ts +6 -0
- package/src/components/invoices/list/locales/it.ts +6 -0
- package/src/components/invoices/list/locales/nl.ts +6 -0
- package/src/components/invoices/list/locales/pl.ts +6 -0
- package/src/components/invoices/list/locales/pt.ts +6 -0
- package/src/components/invoices/list/locales/sl.ts +6 -0
- package/src/components/invoices/view/fiscalization-status-card.tsx +42 -24
- package/src/components/items/item-combobox.tsx +5 -3
- package/src/components/items/item-list-table/item-list-header.tsx +4 -17
- package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -2
- package/src/components/items/item-list-table/item-list-row.tsx +3 -2
- package/src/components/items/item-list-table/item-list-table.tsx +3 -3
- package/src/components/items/item-list-table/locales/de.ts +1 -0
- package/src/components/items/item-list-table/locales/es.ts +1 -0
- package/src/components/items/item-list-table/locales/fr.ts +1 -0
- package/src/components/items/item-list-table/locales/hr.ts +1 -0
- package/src/components/items/item-list-table/locales/it.ts +1 -0
- package/src/components/items/item-list-table/locales/nl.ts +1 -0
- package/src/components/items/item-list-table/locales/pl.ts +1 -0
- package/src/components/items/item-list-table/locales/pt.ts +1 -0
- package/src/components/items/item-list-table/locales/sl.ts +1 -0
- package/src/components/payments/list/list-table.tsx +0 -4
- package/src/components/payments/list/locales/de.ts +1 -0
- package/src/components/payments/list/locales/es.ts +1 -0
- package/src/components/payments/list/locales/fr.ts +1 -0
- package/src/components/payments/list/locales/hr.ts +1 -0
- package/src/components/payments/list/locales/it.ts +1 -0
- package/src/components/payments/list/locales/nl.ts +1 -0
- package/src/components/payments/list/locales/pl.ts +1 -0
- package/src/components/payments/list/locales/pt.ts +1 -0
- package/src/components/payments/list/locales/sl.ts +1 -0
- package/src/components/recurring-invoices/list/list-table.tsx +0 -7
- package/src/components/recurring-invoices/list/locales/de.ts +1 -0
- package/src/components/recurring-invoices/list/locales/es.ts +1 -0
- package/src/components/recurring-invoices/list/locales/fr.ts +1 -0
- package/src/components/recurring-invoices/list/locales/hr.ts +1 -0
- package/src/components/recurring-invoices/list/locales/it.ts +1 -0
- package/src/components/recurring-invoices/list/locales/nl.ts +1 -0
- package/src/components/recurring-invoices/list/locales/pl.ts +1 -0
- package/src/components/recurring-invoices/list/locales/pt.ts +1 -0
- package/src/components/recurring-invoices/list/locales/sl.ts +1 -0
- package/src/components/request-logs/request-log-list-table.tsx +0 -3
- package/src/components/table/README.md +14 -121
- package/src/components/table/data-table.tsx +22 -37
- package/src/components/table/hooks/use-table-state.ts +3 -27
- package/src/components/table/index.ts +0 -2
- package/src/components/table/selection-toolbar.tsx +35 -1
- package/src/components/table/table-empty-state.tsx +6 -3
- package/src/components/table/table-no-results.tsx +10 -5
- package/src/components/table/types.ts +0 -5
- package/src/components/taxes/tax-list-table/locales/de.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/es.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/fr.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/hr.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/it.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/nl.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/pl.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/pt.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/sl.ts +1 -0
- package/src/components/taxes/tax-list-table/tax-list-header.tsx +3 -14
- package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -3
- package/src/components/ui/popover.tsx +3 -1
- package/src/components/ui/tooltip.tsx +3 -1
- package/src/generated/schemas/deliverynote.ts +134 -0
- package/src/generated/schemas/entity.ts +4 -0
- package/src/generated/schemas/index.ts +3 -0
- package/src/generated/schemas/order.ts +5 -3
- package/src/generated/schemas/payment.ts +22 -2
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +1 -0
- package/src/generated/schemas/rendercreditnotepreview_body.ts +1 -0
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +185 -0
- package/src/generated/schemas/renderestimatepreview_body.ts +1 -0
- package/src/generated/schemas/renderinvoicepreview_body.ts +1 -0
- package/src/generated/schemas/startpdfexport_body.ts +18 -2
- package/src/generated/schemas/userfinasettings.ts +19 -0
- package/src/generated/schemas/webhook.ts +12 -0
- package/src/hooks/use-duplicate-document.ts +49 -6
- package/src/hooks/use-next-document-number.ts +2 -2
- package/src/lib/fiscalization.ts +12 -0
- package/src/lib/schemas/advance-invoice.ts +0 -1
- package/src/providers/sdk-provider.tsx +5 -7
- package/src/components/table/sortable-header.tsx +0 -56
|
@@ -37,6 +37,12 @@ import sl from "./locales/sl";
|
|
|
37
37
|
import { prepareEstimateSubmission } from "./prepare-estimate-submission";
|
|
38
38
|
import { useEstimateCustomerForm } from "./use-estimate-customer-form";
|
|
39
39
|
|
|
40
|
+
function calculateDueDate(dateIso: string, days: number): string {
|
|
41
|
+
const date = new Date(dateIso);
|
|
42
|
+
date.setDate(date.getDate() + days);
|
|
43
|
+
return date.toISOString();
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
const translations = {
|
|
41
47
|
sl,
|
|
42
48
|
de,
|
|
@@ -101,6 +107,7 @@ export default function CreateEstimateForm({
|
|
|
101
107
|
|
|
102
108
|
// Get default estimate note from entity settings
|
|
103
109
|
const defaultEstimateNote = (activeEntity?.settings as any)?.default_estimate_note || "";
|
|
110
|
+
const defaultEstimateValidDays = (activeEntity?.settings as any)?.default_estimate_valid_days ?? 30;
|
|
104
111
|
|
|
105
112
|
// Fetch next estimate number
|
|
106
113
|
const { data: nextNumberData } = useNextDocumentNumber(entityId, "estimate", {
|
|
@@ -139,6 +146,9 @@ export default function CreateEstimateForm({
|
|
|
139
146
|
currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
|
|
140
147
|
note: initialValues?.note ?? defaultEstimateNote,
|
|
141
148
|
payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
|
|
149
|
+
date_valid_till:
|
|
150
|
+
initialValues?.date_valid_till ||
|
|
151
|
+
calculateDueDate(initialValues?.date || new Date().toISOString(), defaultEstimateValidDays),
|
|
142
152
|
},
|
|
143
153
|
});
|
|
144
154
|
|
|
@@ -165,13 +175,20 @@ export default function CreateEstimateForm({
|
|
|
165
175
|
}
|
|
166
176
|
}, [nextNumberData?.number, form]);
|
|
167
177
|
|
|
168
|
-
// Update default note when entity loads
|
|
178
|
+
// Update default note and valid-till date when entity loads
|
|
169
179
|
useEffect(() => {
|
|
170
180
|
const entityDefaultNote = (activeEntity?.settings as any)?.default_estimate_note;
|
|
171
181
|
if (entityDefaultNote && !form.getValues("note")) {
|
|
172
182
|
form.setValue("note", entityDefaultNote);
|
|
173
183
|
}
|
|
174
|
-
|
|
184
|
+
if (!initialValues?.date_valid_till) {
|
|
185
|
+
const validDays = (activeEntity?.settings as any)?.default_estimate_valid_days ?? 30;
|
|
186
|
+
const currentDate = form.getValues("date");
|
|
187
|
+
if (currentDate) {
|
|
188
|
+
form.setValue("date_valid_till", calculateDueDate(currentDate, validDays));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}, [activeEntity, form, initialValues?.date_valid_till]);
|
|
175
192
|
|
|
176
193
|
// Auto-add tax field for tax subject entities
|
|
177
194
|
useEffect(() => {
|
|
@@ -300,6 +317,16 @@ export default function CreateEstimateForm({
|
|
|
300
317
|
}
|
|
301
318
|
}, [activeEntity, form]);
|
|
302
319
|
|
|
320
|
+
// Recalculate valid-till date when document date changes
|
|
321
|
+
const prevDateRef = useRef(form.getValues("date"));
|
|
322
|
+
useEffect(() => {
|
|
323
|
+
const currentDate = formValues.date;
|
|
324
|
+
if (!currentDate || currentDate === prevDateRef.current) return;
|
|
325
|
+
prevDateRef.current = currentDate;
|
|
326
|
+
const validDays = (activeEntity?.settings as any)?.default_estimate_valid_days ?? 30;
|
|
327
|
+
form.setValue("date_valid_till", calculateDueDate(currentDate, validDays));
|
|
328
|
+
}, [formValues.date, activeEntity, form]);
|
|
329
|
+
|
|
303
330
|
useEffect(() => {
|
|
304
331
|
const callback = onChangeRef.current;
|
|
305
332
|
if (!callback) return;
|
|
@@ -52,6 +52,7 @@ type EstimateListTableProps = {
|
|
|
52
52
|
onDownloadSuccess?: (fileName: string) => void;
|
|
53
53
|
onDownloadError?: (error: string) => void;
|
|
54
54
|
onExportSelected?: (documentIds: string[]) => void;
|
|
55
|
+
onCreateNew?: () => void;
|
|
55
56
|
} & ListTableProps<Estimate>;
|
|
56
57
|
|
|
57
58
|
export default function EstimateListTable({
|
|
@@ -65,6 +66,7 @@ export default function EstimateListTable({
|
|
|
65
66
|
onDownloadSuccess,
|
|
66
67
|
onDownloadError,
|
|
67
68
|
onExportSelected,
|
|
69
|
+
onCreateNew,
|
|
68
70
|
...i18nProps
|
|
69
71
|
}: EstimateListTableProps) {
|
|
70
72
|
const t = createTranslation({
|
|
@@ -84,7 +86,6 @@ export default function EstimateListTable({
|
|
|
84
86
|
limit: params.limit,
|
|
85
87
|
next_cursor: params.next_cursor,
|
|
86
88
|
prev_cursor: params.prev_cursor,
|
|
87
|
-
order_by: params.order_by,
|
|
88
89
|
search: params.search,
|
|
89
90
|
query: params.query,
|
|
90
91
|
});
|
|
@@ -130,7 +131,6 @@ export default function EstimateListTable({
|
|
|
130
131
|
{
|
|
131
132
|
id: "number",
|
|
132
133
|
header: t("Number"),
|
|
133
|
-
sortable: true,
|
|
134
134
|
cell: (estimate) => (
|
|
135
135
|
<div className="flex items-center gap-2">
|
|
136
136
|
<Button variant="link" className="cursor-pointer py-0 underline" onClick={() => onRowClick?.(estimate)}>
|
|
@@ -155,26 +155,22 @@ export default function EstimateListTable({
|
|
|
155
155
|
{
|
|
156
156
|
id: "date",
|
|
157
157
|
header: t("Date"),
|
|
158
|
-
sortable: true,
|
|
159
158
|
cell: (estimate) => <FormattedDate date={estimate.date} />,
|
|
160
159
|
},
|
|
161
160
|
{
|
|
162
161
|
id: "date_valid_till",
|
|
163
162
|
header: t("Valid Until"),
|
|
164
|
-
sortable: true,
|
|
165
163
|
cell: (estimate) => <FormattedDate date={estimate.date_valid_till} />,
|
|
166
164
|
},
|
|
167
165
|
{
|
|
168
166
|
id: "total",
|
|
169
167
|
header: t("Total"),
|
|
170
|
-
sortable: true,
|
|
171
168
|
align: "right",
|
|
172
169
|
cell: (estimate) => estimate.total,
|
|
173
170
|
},
|
|
174
171
|
{
|
|
175
172
|
id: "total_with_tax",
|
|
176
173
|
header: t("Total with Tax"),
|
|
177
|
-
sortable: true,
|
|
178
174
|
align: "right",
|
|
179
175
|
cell: (estimate) => estimate.total_with_tax,
|
|
180
176
|
},
|
|
@@ -208,6 +204,7 @@ export default function EstimateListTable({
|
|
|
208
204
|
cacheKey="estimates"
|
|
209
205
|
resourceName="estimate"
|
|
210
206
|
createNewLink={entityId ? `/app/${entityId}/documents/add/estimate` : undefined}
|
|
207
|
+
onCreateNew={onCreateNew}
|
|
211
208
|
entityId={entityId}
|
|
212
209
|
filterConfig={filterConfig}
|
|
213
210
|
t={t}
|
|
@@ -32,6 +32,7 @@ export default {
|
|
|
32
32
|
"No results found": "Keine Angebote gefunden",
|
|
33
33
|
"Try adjusting your search criteria": "Versuchen Sie, Ihre Suchkriterien anzupassen",
|
|
34
34
|
"Clear search": "Suche zurücksetzen",
|
|
35
|
+
"Clear all": "Alles zurücksetzen",
|
|
35
36
|
selected: "ausgewählt",
|
|
36
37
|
"Export PDFs": "PDFs exportieren",
|
|
37
38
|
"Deselect all": "Auswahl aufheben",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "No se encontraron presupuestos",
|
|
30
30
|
"Try adjusting your search criteria": "Intente ajustar sus criterios de búsqueda",
|
|
31
31
|
"Clear search": "Limpiar búsqueda",
|
|
32
|
+
"Clear all": "Borrar todo",
|
|
32
33
|
selected: "seleccionados",
|
|
33
34
|
"Export PDFs": "Exportar PDFs",
|
|
34
35
|
"Deselect all": "Deseleccionar todo",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "Aucun devis trouvé",
|
|
30
30
|
"Try adjusting your search criteria": "Essayez d'ajuster vos critères de recherche",
|
|
31
31
|
"Clear search": "Effacer la recherche",
|
|
32
|
+
"Clear all": "Tout effacer",
|
|
32
33
|
selected: "sélectionnés",
|
|
33
34
|
"Export PDFs": "Exporter les PDF",
|
|
34
35
|
"Deselect all": "Tout désélectionner",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "Nisu pronađeni predračuni",
|
|
30
30
|
"Try adjusting your search criteria": "Pokušajte prilagoditi kriterije pretraživanja",
|
|
31
31
|
"Clear search": "Očisti pretraživanje",
|
|
32
|
+
"Clear all": "Očisti sve",
|
|
32
33
|
selected: "odabranih",
|
|
33
34
|
"Export PDFs": "Izvezi PDF-ove",
|
|
34
35
|
"Deselect all": "Poništi odabir",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "Nessun preventivo trovato",
|
|
30
30
|
"Try adjusting your search criteria": "Prova a modificare i criteri di ricerca",
|
|
31
31
|
"Clear search": "Cancella ricerca",
|
|
32
|
+
"Clear all": "Cancella tutto",
|
|
32
33
|
selected: "selezionati",
|
|
33
34
|
"Export PDFs": "Esporta PDF",
|
|
34
35
|
"Deselect all": "Deseleziona tutto",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "Geen offertes gevonden",
|
|
30
30
|
"Try adjusting your search criteria": "Probeer uw zoekcriteria aan te passen",
|
|
31
31
|
"Clear search": "Zoekopdracht wissen",
|
|
32
|
+
"Clear all": "Alles wissen",
|
|
32
33
|
selected: "geselecteerd",
|
|
33
34
|
"Export PDFs": "PDFs exporteren",
|
|
34
35
|
"Deselect all": "Alles deselecteren",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "Nie znaleziono kosztorysów",
|
|
30
30
|
"Try adjusting your search criteria": "Spróbuj dostosować kryteria wyszukiwania",
|
|
31
31
|
"Clear search": "Wyczyść wyszukiwanie",
|
|
32
|
+
"Clear all": "Wyczyść wszystko",
|
|
32
33
|
selected: "wybranych",
|
|
33
34
|
"Export PDFs": "Eksportuj PDF-y",
|
|
34
35
|
"Deselect all": "Odznacz wszystko",
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
"No results found": "Nenhum orçamento encontrado",
|
|
30
30
|
"Try adjusting your search criteria": "Tente ajustar os seus critérios de pesquisa",
|
|
31
31
|
"Clear search": "Limpar pesquisa",
|
|
32
|
+
"Clear all": "Limpar tudo",
|
|
32
33
|
selected: "selecionados",
|
|
33
34
|
"Export PDFs": "Exportar PDFs",
|
|
34
35
|
"Deselect all": "Desselecionar tudo",
|
|
@@ -32,6 +32,7 @@ export default {
|
|
|
32
32
|
"No results found": "Ni najdenih predračunov",
|
|
33
33
|
"Try adjusting your search criteria": "Poskusite prilagoditi iskalne kriterije",
|
|
34
34
|
"Clear search": "Počisti iskanje",
|
|
35
|
+
"Clear all": "Počisti vse",
|
|
35
36
|
selected: "izbranih",
|
|
36
37
|
"Export PDFs": "Izvozi PDF-je",
|
|
37
38
|
"Deselect all": "Počisti izbiro",
|
|
@@ -40,8 +40,11 @@ function isDateRangeValid(dateFrom: string, dateTo: string): boolean {
|
|
|
40
40
|
export type DocumentExportFormProps = {
|
|
41
41
|
entityId: string;
|
|
42
42
|
token: string;
|
|
43
|
+
accountId?: string | null;
|
|
43
44
|
language: string;
|
|
44
45
|
t: TFunction;
|
|
46
|
+
/** Base URL for API calls (required in embed context where relative paths don't reach the API) */
|
|
47
|
+
apiBaseUrl?: string;
|
|
45
48
|
onSuccess?: (fileName: string) => void;
|
|
46
49
|
onError?: (error: Error) => void;
|
|
47
50
|
onPdfExportStarted?: (jobId: string) => void;
|
|
@@ -51,8 +54,10 @@ export type DocumentExportFormProps = {
|
|
|
51
54
|
export function DocumentExportForm({
|
|
52
55
|
entityId,
|
|
53
56
|
token,
|
|
57
|
+
accountId,
|
|
54
58
|
language,
|
|
55
59
|
t,
|
|
60
|
+
apiBaseUrl = "",
|
|
56
61
|
onSuccess,
|
|
57
62
|
onError,
|
|
58
63
|
onPdfExportStarted,
|
|
@@ -86,11 +91,12 @@ export function DocumentExportForm({
|
|
|
86
91
|
// PDF export is async - different flow
|
|
87
92
|
if (exportFormat === "pdf_zip") {
|
|
88
93
|
try {
|
|
89
|
-
const response = await fetch(
|
|
94
|
+
const response = await fetch(`${apiBaseUrl}/documents/export/pdf`, {
|
|
90
95
|
method: "POST",
|
|
91
96
|
headers: {
|
|
92
97
|
Authorization: `Bearer ${token}`,
|
|
93
98
|
"x-entity-id": entityId,
|
|
99
|
+
...(accountId && { "x-account-id": accountId }),
|
|
94
100
|
"Content-Type": "application/json",
|
|
95
101
|
},
|
|
96
102
|
body: JSON.stringify({
|
|
@@ -132,10 +138,11 @@ export function DocumentExportForm({
|
|
|
132
138
|
queryParams.date_to = dateTo;
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
const response = await fetch(
|
|
141
|
+
const response = await fetch(`${apiBaseUrl}/documents/export?${new URLSearchParams(queryParams).toString()}`, {
|
|
136
142
|
headers: {
|
|
137
143
|
Authorization: `Bearer ${token}`,
|
|
138
144
|
"x-entity-id": entityId,
|
|
145
|
+
...(accountId && { "x-account-id": accountId }),
|
|
139
146
|
},
|
|
140
147
|
});
|
|
141
148
|
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export type { DocumentExportFormProps, DocumentType, ExportFormat } from "./document-export-form";
|
|
2
2
|
export { DocumentExportForm } from "./document-export-form";
|
|
3
|
+
export type { SalesPerItemExportFormProps } from "./sales-per-item-export-form";
|
|
4
|
+
export { SalesPerItemExportForm } from "./sales-per-item-export-form";
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
type TFunction = (key: string, options?: Record<string, unknown>) => string;
|
|
2
|
+
|
|
3
|
+
import { Calendar, Download, Loader2 } from "lucide-react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { Button } from "../ui/button";
|
|
6
|
+
import { Input } from "../ui/input";
|
|
7
|
+
import { Label } from "../ui/label";
|
|
8
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
|
9
|
+
|
|
10
|
+
type ExportFormat = "xlsx" | "csv";
|
|
11
|
+
|
|
12
|
+
// Maximum date range for export (1 year in milliseconds)
|
|
13
|
+
const MAX_DATE_RANGE_MS = 365 * 24 * 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
function formatDateLocal(date: Date): string {
|
|
16
|
+
const year = date.getFullYear();
|
|
17
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
18
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
19
|
+
return `${year}-${month}-${day}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getPreviousMonthRange(): { from: string; to: string } {
|
|
23
|
+
const now = new Date();
|
|
24
|
+
const firstDayPrevMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
|
25
|
+
const lastDayPrevMonth = new Date(now.getFullYear(), now.getMonth(), 0);
|
|
26
|
+
return {
|
|
27
|
+
from: formatDateLocal(firstDayPrevMonth),
|
|
28
|
+
to: formatDateLocal(lastDayPrevMonth),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isDateRangeValid(dateFrom: string, dateTo: string): boolean {
|
|
33
|
+
if (!dateFrom || !dateTo) return false;
|
|
34
|
+
const from = new Date(dateFrom);
|
|
35
|
+
const to = new Date(dateTo);
|
|
36
|
+
return to.getTime() - from.getTime() <= MAX_DATE_RANGE_MS && to >= from;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type SalesPerItemExportFormProps = {
|
|
40
|
+
entityId: string;
|
|
41
|
+
token: string;
|
|
42
|
+
accountId?: string | null;
|
|
43
|
+
language: string;
|
|
44
|
+
t: TFunction;
|
|
45
|
+
/** Base URL for API calls (required in embed context where relative paths don't reach the API) */
|
|
46
|
+
apiBaseUrl?: string;
|
|
47
|
+
onSuccess?: (fileName: string) => void;
|
|
48
|
+
onError?: (error: Error) => void;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export function SalesPerItemExportForm({
|
|
52
|
+
entityId,
|
|
53
|
+
token,
|
|
54
|
+
accountId,
|
|
55
|
+
language,
|
|
56
|
+
t,
|
|
57
|
+
apiBaseUrl = "",
|
|
58
|
+
onSuccess,
|
|
59
|
+
onError,
|
|
60
|
+
}: SalesPerItemExportFormProps) {
|
|
61
|
+
const defaultDates = getPreviousMonthRange();
|
|
62
|
+
const [exportFormat, setExportFormat] = useState<ExportFormat>("xlsx");
|
|
63
|
+
const [dateFrom, setDateFrom] = useState(defaultDates.from);
|
|
64
|
+
const [dateTo, setDateTo] = useState(defaultDates.to);
|
|
65
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
66
|
+
const [dateRangeError, setDateRangeError] = useState(false);
|
|
67
|
+
|
|
68
|
+
const validateDateRange = (from: string, to: string) => {
|
|
69
|
+
const isValid = isDateRangeValid(from, to);
|
|
70
|
+
setDateRangeError(!isValid && !!from && !!to);
|
|
71
|
+
return isValid;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const handleExport = async () => {
|
|
75
|
+
if (!dateFrom || !dateTo) {
|
|
76
|
+
onError?.(new Error(t("sales-per-item-export.error.dates-required")));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!validateDateRange(dateFrom, dateTo)) {
|
|
81
|
+
onError?.(new Error(t("export-page.error.date-range-exceeded")));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setIsExporting(true);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const exportLanguage = language === "sl" ? "sl" : "en";
|
|
89
|
+
const queryParams: Record<string, string> = {
|
|
90
|
+
format: exportFormat,
|
|
91
|
+
date_from: dateFrom,
|
|
92
|
+
date_to: dateTo,
|
|
93
|
+
language: exportLanguage,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const response = await fetch(
|
|
97
|
+
`${apiBaseUrl}/documents/export/sales-per-item?${new URLSearchParams(queryParams).toString()}`,
|
|
98
|
+
{
|
|
99
|
+
headers: {
|
|
100
|
+
Authorization: `Bearer ${token}`,
|
|
101
|
+
"x-entity-id": entityId,
|
|
102
|
+
...(accountId && { "x-account-id": accountId }),
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new Error(`Export failed: ${response.statusText}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const contentDisposition = response.headers.get("content-disposition");
|
|
112
|
+
let fileName = `sales-per-item.${exportFormat}`;
|
|
113
|
+
if (contentDisposition) {
|
|
114
|
+
const match = contentDisposition.match(/filename="?([^";\n]+)"?/);
|
|
115
|
+
if (match) {
|
|
116
|
+
fileName = match[1];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const blob = await response.blob();
|
|
121
|
+
const downloadUrl = window.URL.createObjectURL(blob);
|
|
122
|
+
const link = document.createElement("a");
|
|
123
|
+
link.href = downloadUrl;
|
|
124
|
+
link.download = fileName;
|
|
125
|
+
document.body.appendChild(link);
|
|
126
|
+
link.click();
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
document.body.removeChild(link);
|
|
129
|
+
window.URL.revokeObjectURL(downloadUrl);
|
|
130
|
+
}, 1000);
|
|
131
|
+
|
|
132
|
+
onSuccess?.(fileName);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
onError?.(error instanceof Error ? error : new Error("Unknown error"));
|
|
135
|
+
} finally {
|
|
136
|
+
setIsExporting(false);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const datesProvided = !!dateFrom && !!dateTo;
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div className="space-y-6">
|
|
144
|
+
{/* Export Format */}
|
|
145
|
+
<div className="grid grid-cols-2 gap-4">
|
|
146
|
+
<div className="space-y-2">
|
|
147
|
+
<Label htmlFor="sales-export-format">{t("export-page.format")}</Label>
|
|
148
|
+
<Select value={exportFormat} onValueChange={(v) => setExportFormat(v as ExportFormat)}>
|
|
149
|
+
<SelectTrigger id="sales-export-format">
|
|
150
|
+
<SelectValue />
|
|
151
|
+
</SelectTrigger>
|
|
152
|
+
<SelectContent>
|
|
153
|
+
<SelectItem value="xlsx">{t("export-page.formats.xlsx")}</SelectItem>
|
|
154
|
+
<SelectItem value="csv">{t("export-page.formats.csv")}</SelectItem>
|
|
155
|
+
</SelectContent>
|
|
156
|
+
</Select>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* Date Range */}
|
|
161
|
+
<div className="grid grid-cols-2 gap-4">
|
|
162
|
+
<div className="space-y-2">
|
|
163
|
+
<Label htmlFor="sales-date-from">{t("export-page.date-from")}</Label>
|
|
164
|
+
<div className="relative">
|
|
165
|
+
<Input
|
|
166
|
+
id="sales-date-from"
|
|
167
|
+
type="date"
|
|
168
|
+
value={dateFrom}
|
|
169
|
+
onChange={(e) => {
|
|
170
|
+
setDateFrom(e.target.value);
|
|
171
|
+
if (dateTo) validateDateRange(e.target.value, dateTo);
|
|
172
|
+
}}
|
|
173
|
+
onClick={(e) => (e.target as HTMLInputElement).showPicker?.()}
|
|
174
|
+
className="cursor-pointer pr-9 [&::-webkit-calendar-picker-indicator]:hidden"
|
|
175
|
+
/>
|
|
176
|
+
<Calendar className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div className="space-y-2">
|
|
181
|
+
<Label htmlFor="sales-date-to">{t("export-page.date-to")}</Label>
|
|
182
|
+
<div className="relative">
|
|
183
|
+
<Input
|
|
184
|
+
id="sales-date-to"
|
|
185
|
+
type="date"
|
|
186
|
+
value={dateTo}
|
|
187
|
+
onChange={(e) => {
|
|
188
|
+
setDateTo(e.target.value);
|
|
189
|
+
if (dateFrom) validateDateRange(dateFrom, e.target.value);
|
|
190
|
+
}}
|
|
191
|
+
onClick={(e) => (e.target as HTMLInputElement).showPicker?.()}
|
|
192
|
+
className={`cursor-pointer pr-9 [&::-webkit-calendar-picker-indicator]:hidden ${dateRangeError ? "border-destructive" : ""}`}
|
|
193
|
+
/>
|
|
194
|
+
<Calendar className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
{/* Date range error message */}
|
|
200
|
+
{dateRangeError && <p className="text-destructive text-sm">{t("export-page.error.date-range-exceeded")}</p>}
|
|
201
|
+
|
|
202
|
+
{/* Export Button */}
|
|
203
|
+
<Button
|
|
204
|
+
onClick={handleExport}
|
|
205
|
+
disabled={isExporting || dateRangeError || !datesProvided}
|
|
206
|
+
className="w-full"
|
|
207
|
+
size="lg"
|
|
208
|
+
>
|
|
209
|
+
{isExporting ? (
|
|
210
|
+
<>
|
|
211
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
212
|
+
{t("export-page.exporting")}
|
|
213
|
+
</>
|
|
214
|
+
) : (
|
|
215
|
+
<>
|
|
216
|
+
<Download className="mr-2 h-4 w-4" />
|
|
217
|
+
{t("export-page.export-button")}
|
|
218
|
+
</>
|
|
219
|
+
)}
|
|
220
|
+
</Button>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
} from "../../documents/create/document-details-section";
|
|
28
28
|
import { DocumentItemsSection, type PriceModesMap } from "../../documents/create/document-items-section";
|
|
29
29
|
import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
|
|
30
|
+
import { type LinkedDocumentSummary, LinkedDocumentsInfo } from "../../documents/create/linked-documents-info";
|
|
30
31
|
import { MarkAsPaidSection } from "../../documents/create/mark-as-paid-section";
|
|
31
32
|
import type { DocumentTypes } from "../../documents/types";
|
|
32
33
|
import { useFinaPremises, useFinaSettings } from "../../entities/fina-settings-form/fina-settings.hooks";
|
|
@@ -53,6 +54,12 @@ import sl from "./locales/sl";
|
|
|
53
54
|
import { prepareInvoiceSubmission } from "./prepare-invoice-submission";
|
|
54
55
|
import { useInvoiceCustomerForm } from "./use-invoice-customer-form";
|
|
55
56
|
|
|
57
|
+
function calculateDueDate(dateIso: string, days: number): string {
|
|
58
|
+
const date = new Date(dateIso);
|
|
59
|
+
date.setDate(date.getDate() + days);
|
|
60
|
+
return date.toISOString();
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
const translations = {
|
|
57
64
|
sl,
|
|
58
65
|
de,
|
|
@@ -83,6 +90,10 @@ type DocumentAddFormProps = {
|
|
|
83
90
|
onHeaderActionChange?: (action: ReactNode) => void;
|
|
84
91
|
/** Initial values for form fields (used for document duplication or editing) */
|
|
85
92
|
initialValues?: Partial<CreateInvoiceRequest> & { number?: string };
|
|
93
|
+
/** Source documents linked to this invoice (e.g., delivery notes merged into this invoice) */
|
|
94
|
+
sourceDocuments?: LinkedDocumentSummary[];
|
|
95
|
+
/** Force linking documents even if an advance invoice is already applied to another invoice */
|
|
96
|
+
forceLinkedDocuments?: boolean;
|
|
86
97
|
/** Mode: create (default) or edit */
|
|
87
98
|
mode?: "create" | "edit";
|
|
88
99
|
/** Document ID for edit mode */
|
|
@@ -98,6 +109,8 @@ export default function CreateInvoiceForm({
|
|
|
98
109
|
onAddNewTax,
|
|
99
110
|
onHeaderActionChange,
|
|
100
111
|
initialValues,
|
|
112
|
+
sourceDocuments,
|
|
113
|
+
forceLinkedDocuments,
|
|
101
114
|
mode = "create",
|
|
102
115
|
documentId,
|
|
103
116
|
t: translateProp,
|
|
@@ -118,6 +131,7 @@ export default function CreateInvoiceForm({
|
|
|
118
131
|
// Get default invoice note and payment terms from entity settings
|
|
119
132
|
const defaultInvoiceNote = (activeEntity?.settings as any)?.default_invoice_note || "";
|
|
120
133
|
const defaultPaymentTerms = (activeEntity?.settings as any)?.default_invoice_payment_terms || "";
|
|
134
|
+
const defaultInvoiceDueDays = (activeEntity?.settings as any)?.default_invoice_due_days ?? 30;
|
|
121
135
|
|
|
122
136
|
// ============================================================================
|
|
123
137
|
// FURS Settings & Premises
|
|
@@ -327,7 +341,11 @@ export default function CreateInvoiceForm({
|
|
|
327
341
|
currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
|
|
328
342
|
note: initialValues?.note ?? defaultInvoiceNote,
|
|
329
343
|
payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
|
|
344
|
+
date_due:
|
|
345
|
+
initialValues?.date_due ||
|
|
346
|
+
calculateDueDate(initialValues?.date || new Date().toISOString(), defaultInvoiceDueDays),
|
|
330
347
|
date_service: new Date().toISOString(),
|
|
348
|
+
linked_documents: (initialValues as any)?.linked_documents,
|
|
331
349
|
},
|
|
332
350
|
});
|
|
333
351
|
|
|
@@ -630,6 +648,11 @@ export default function CreateInvoiceForm({
|
|
|
630
648
|
isDraft,
|
|
631
649
|
});
|
|
632
650
|
|
|
651
|
+
// Add force_linked_documents if set (used after conflict dialog approval)
|
|
652
|
+
if (forceLinkedDocuments) {
|
|
653
|
+
(payload as any).force_linked_documents = true;
|
|
654
|
+
}
|
|
655
|
+
|
|
633
656
|
if (isEditMode && documentId) {
|
|
634
657
|
// In edit mode, use updateInvoice
|
|
635
658
|
// Remove number from payload as it's not editable
|
|
@@ -645,6 +668,7 @@ export default function CreateInvoiceForm({
|
|
|
645
668
|
updateInvoice,
|
|
646
669
|
documentId,
|
|
647
670
|
eslogValidationEnabled,
|
|
671
|
+
forceLinkedDocuments,
|
|
648
672
|
form,
|
|
649
673
|
isEditMode,
|
|
650
674
|
isEslogAvailable,
|
|
@@ -721,6 +745,15 @@ export default function CreateInvoiceForm({
|
|
|
721
745
|
form.setValue("payment_terms", entityDefaultPaymentTerms);
|
|
722
746
|
}
|
|
723
747
|
|
|
748
|
+
// Auto-populate due date from entity settings when entity loads async
|
|
749
|
+
if (!isEditMode && !initialValues?.date_due) {
|
|
750
|
+
const dueDays = (activeEntity.settings as any)?.default_invoice_due_days ?? 30;
|
|
751
|
+
const currentDate = form.getValues("date");
|
|
752
|
+
if (currentDate) {
|
|
753
|
+
form.setValue("date_due", calculateDueDate(currentDate, dueDays));
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
724
757
|
// Auto-add tax field for tax subject entities
|
|
725
758
|
if (activeEntity.is_tax_subject) {
|
|
726
759
|
const items = form.getValues("items") || [];
|
|
@@ -730,7 +763,17 @@ export default function CreateInvoiceForm({
|
|
|
730
763
|
}
|
|
731
764
|
|
|
732
765
|
initialSetupDoneRef.current = true;
|
|
733
|
-
}, [activeEntity, form]);
|
|
766
|
+
}, [activeEntity, form, isEditMode, initialValues?.date_due]);
|
|
767
|
+
|
|
768
|
+
// Recalculate due date when document date changes (skip in edit mode)
|
|
769
|
+
const prevDateRef = useRef(form.getValues("date"));
|
|
770
|
+
useEffect(() => {
|
|
771
|
+
if (isEditMode) return;
|
|
772
|
+
if (!watchedDate || watchedDate === prevDateRef.current) return;
|
|
773
|
+
prevDateRef.current = watchedDate;
|
|
774
|
+
const dueDays = (activeEntity?.settings as any)?.default_invoice_due_days ?? 30;
|
|
775
|
+
form.setValue("date_due", calculateDueDate(watchedDate, dueDays));
|
|
776
|
+
}, [watchedDate, activeEntity, isEditMode, form]);
|
|
734
777
|
|
|
735
778
|
// Use form.watch subscription for onChange callback (avoids re-render loops)
|
|
736
779
|
const prevPayloadRef = useRef<string>("");
|
|
@@ -962,6 +1005,10 @@ export default function CreateInvoiceForm({
|
|
|
962
1005
|
customer: watchedCustomer as any,
|
|
963
1006
|
}}
|
|
964
1007
|
/>
|
|
1008
|
+
|
|
1009
|
+
{sourceDocuments && sourceDocuments.length > 0 && (
|
|
1010
|
+
<LinkedDocumentsInfo documents={sourceDocuments} locale={locale || "en"} t={t} />
|
|
1011
|
+
)}
|
|
965
1012
|
</form>
|
|
966
1013
|
</Form>
|
|
967
1014
|
);
|
|
@@ -109,6 +109,17 @@ export default {
|
|
|
109
109
|
"Net price": "Nettopreis",
|
|
110
110
|
"Gross price (tax included)": "Bruttopreis (inkl. MwSt.)",
|
|
111
111
|
"Net price (before tax)": "Nettopreis (exkl. MwSt.)",
|
|
112
|
+
// Linked documents
|
|
113
|
+
"Linked documents": "Verknüpfte Dokumente",
|
|
114
|
+
Type: "Typ",
|
|
115
|
+
Total: "Gesamt",
|
|
116
|
+
"Delivery note": "Lieferschein",
|
|
117
|
+
Invoice: "Rechnung",
|
|
118
|
+
Estimate: "Angebot",
|
|
119
|
+
"Credit note": "Gutschrift",
|
|
120
|
+
"Advance invoice": "Vorausrechnung",
|
|
121
|
+
"FINA fiscalized invoices always use the current date":
|
|
122
|
+
"FINA-fiskalisierte Rechnungen verwenden immer das aktuelle Datum",
|
|
112
123
|
// Service date
|
|
113
124
|
"Service Date": "Leistungsdatum",
|
|
114
125
|
"Single Date": "Datum",
|
|
@@ -95,6 +95,17 @@ export default {
|
|
|
95
95
|
"Net price": "Precio neto",
|
|
96
96
|
"Gross price (tax included)": "Precio bruto (impuestos incluidos)",
|
|
97
97
|
"Net price (before tax)": "Precio neto (antes de impuestos)",
|
|
98
|
+
// Linked documents
|
|
99
|
+
"Linked documents": "Documentos vinculados",
|
|
100
|
+
Type: "Tipo",
|
|
101
|
+
Total: "Total",
|
|
102
|
+
"Delivery note": "Albarán",
|
|
103
|
+
Invoice: "Factura",
|
|
104
|
+
Estimate: "Presupuesto",
|
|
105
|
+
"Credit note": "Nota de crédito",
|
|
106
|
+
"Advance invoice": "Factura anticipada",
|
|
107
|
+
"FINA fiscalized invoices always use the current date":
|
|
108
|
+
"Las facturas fiscalizadas con FINA siempre usan la fecha actual",
|
|
98
109
|
"Service Date": "Fecha de servicio",
|
|
99
110
|
"Single Date": "Fecha única",
|
|
100
111
|
"Date Range": "Rango de fechas",
|