@spaceinvoices/react-ui 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +61 -12
- package/src/components/advance-invoices/create/locales/de.ts +17 -0
- package/src/components/advance-invoices/create/locales/es.ts +17 -0
- package/src/components/advance-invoices/create/locales/fr.ts +17 -0
- package/src/components/advance-invoices/create/locales/hr.ts +16 -0
- package/src/components/advance-invoices/create/locales/it.ts +17 -0
- package/src/components/advance-invoices/create/locales/nl.ts +17 -0
- package/src/components/advance-invoices/create/locales/pl.ts +16 -0
- package/src/components/advance-invoices/create/locales/pt.ts +16 -0
- package/src/components/advance-invoices/create/locales/sl.ts +16 -0
- package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +0 -1
- package/src/components/advance-invoices/list/list-row-actions.tsx +48 -1
- package/src/components/advance-invoices/list/list-table.tsx +100 -50
- package/src/components/advance-invoices/list/locales/de.ts +9 -0
- package/src/components/advance-invoices/list/locales/en.ts +7 -0
- package/src/components/advance-invoices/list/locales/es.ts +9 -0
- package/src/components/advance-invoices/list/locales/fr.ts +9 -0
- package/src/components/advance-invoices/list/locales/hr.ts +8 -0
- package/src/components/advance-invoices/list/locales/it.ts +9 -0
- package/src/components/advance-invoices/list/locales/nl.ts +9 -0
- package/src/components/advance-invoices/list/locales/pl.ts +8 -0
- package/src/components/advance-invoices/list/locales/pt.ts +9 -0
- package/src/components/advance-invoices/list/locales/sl.ts +8 -0
- package/src/components/credit-notes/create/create-credit-note-form.tsx +185 -11
- package/src/components/credit-notes/create/locales/de.ts +18 -0
- package/src/components/credit-notes/create/locales/es.ts +18 -0
- package/src/components/credit-notes/create/locales/fr.ts +18 -0
- package/src/components/credit-notes/create/locales/hr.ts +17 -0
- package/src/components/credit-notes/create/locales/it.ts +18 -0
- package/src/components/credit-notes/create/locales/nl.ts +18 -0
- package/src/components/credit-notes/create/locales/pl.ts +17 -0
- package/src/components/credit-notes/create/locales/pt.ts +17 -0
- package/src/components/credit-notes/create/locales/sl.ts +18 -1
- package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
- package/src/components/credit-notes/list/list-row-actions.tsx +44 -1
- package/src/components/credit-notes/list/list-table.tsx +93 -32
- package/src/components/credit-notes/list/locales/de.ts +7 -0
- package/src/components/credit-notes/list/locales/en.ts +6 -0
- package/src/components/credit-notes/list/locales/es.ts +7 -0
- package/src/components/credit-notes/list/locales/fr.ts +7 -0
- package/src/components/credit-notes/list/locales/hr.ts +7 -0
- package/src/components/credit-notes/list/locales/it.ts +7 -0
- package/src/components/credit-notes/list/locales/nl.ts +7 -0
- package/src/components/credit-notes/list/locales/pl.ts +7 -0
- package/src/components/credit-notes/list/locales/pt.ts +7 -0
- package/src/components/credit-notes/list/locales/sl.ts +7 -0
- 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 +48 -9
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +77 -48
- package/src/components/dashboard/shared/use-revenue-data.ts +77 -9
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +67 -7
- package/src/components/delivery-notes/create/locales/de.ts +16 -0
- package/src/components/delivery-notes/create/locales/es.ts +16 -0
- package/src/components/delivery-notes/create/locales/fr.ts +16 -0
- package/src/components/delivery-notes/create/locales/hr.ts +16 -0
- package/src/components/delivery-notes/create/locales/it.ts +16 -0
- package/src/components/delivery-notes/create/locales/nl.ts +16 -0
- package/src/components/delivery-notes/create/locales/pl.ts +16 -0
- package/src/components/delivery-notes/create/locales/pt.ts +16 -0
- package/src/components/delivery-notes/create/locales/sl.ts +17 -1
- package/src/components/delivery-notes/list/list-row-actions.tsx +20 -1
- package/src/components/delivery-notes/list/list-table.tsx +50 -8
- package/src/components/delivery-notes/list/locales/de.ts +35 -0
- package/src/components/delivery-notes/list/locales/en.ts +33 -0
- package/src/components/delivery-notes/list/locales/es.ts +35 -0
- package/src/components/delivery-notes/list/locales/fr.ts +35 -0
- package/src/components/delivery-notes/list/locales/hr.ts +35 -0
- package/src/components/delivery-notes/list/locales/it.ts +35 -0
- package/src/components/delivery-notes/list/locales/nl.ts +35 -0
- package/src/components/delivery-notes/list/locales/pl.ts +35 -0
- package/src/components/delivery-notes/list/locales/pt.ts +35 -0
- package/src/components/delivery-notes/list/locales/sl.ts +35 -0
- package/src/components/documents/create/document-add-item-form.tsx +70 -0
- package/src/components/documents/create/document-details-section.tsx +163 -29
- package/src/components/documents/create/document-items-section.tsx +21 -4
- package/src/components/documents/create/linked-documents-info.tsx +82 -0
- package/src/components/documents/create/live-preview.tsx +25 -5
- package/src/components/documents/create/mark-as-paid-section.tsx +29 -20
- package/src/components/documents/create/prepare-document-submission.ts +19 -7
- package/src/components/documents/shared/document-preview-display.tsx +3 -4
- package/src/components/documents/types.ts +2 -2
- package/src/components/documents/view/document-actions-bar.tsx +7 -27
- package/src/components/documents/view/document-activities-list.tsx +65 -47
- package/src/components/documents/view/document-details-card.tsx +118 -76
- package/src/components/documents/view/document-payments-list.tsx +99 -65
- package/src/components/documents/view/document-relations-list.tsx +43 -28
- 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 +4 -0
- package/src/components/documents/view/locales/es.ts +4 -0
- package/src/components/documents/view/locales/fr.ts +4 -0
- package/src/components/documents/view/locales/hr.ts +4 -0
- package/src/components/documents/view/locales/it.ts +4 -0
- package/src/components/documents/view/locales/nl.ts +4 -0
- package/src/components/documents/view/locales/pl.ts +4 -0
- package/src/components/documents/view/locales/pt.ts +4 -0
- package/src/components/documents/view/locales/sl.ts +5 -1
- package/src/components/documents/view/use-document-download.ts +7 -3
- package/src/components/entities/create-entity-form.tsx +166 -13
- package/src/components/entities/entity-settings-form/entity-settings-form.tsx +101 -1
- package/src/components/entities/entity-settings-form/input-with-preview.tsx +30 -2
- package/src/components/entities/entity-settings-form/locales/de.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/es.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/fr.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/hr.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/it.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/nl.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/pl.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/pt.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/sl.ts +10 -0
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +6 -6
- 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/locales/en.ts +0 -1
- package/src/components/entities/settings/company-settings-form.tsx +173 -20
- 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 +93 -8
- package/src/components/estimates/create/locales/de.ts +16 -0
- package/src/components/estimates/create/locales/es.ts +16 -0
- package/src/components/estimates/create/locales/fr.ts +16 -0
- package/src/components/estimates/create/locales/hr.ts +16 -0
- package/src/components/estimates/create/locales/it.ts +16 -0
- package/src/components/estimates/create/locales/nl.ts +16 -0
- package/src/components/estimates/create/locales/pl.ts +16 -0
- package/src/components/estimates/create/locales/pt.ts +16 -0
- package/src/components/estimates/create/locales/sl.ts +17 -1
- package/src/components/estimates/list/list-table.tsx +14 -8
- package/src/components/estimates/list/locales/de.ts +2 -0
- package/src/components/estimates/list/locales/en.ts +1 -0
- package/src/components/estimates/list/locales/es.ts +2 -0
- package/src/components/estimates/list/locales/fr.ts +2 -0
- package/src/components/estimates/list/locales/hr.ts +2 -0
- package/src/components/estimates/list/locales/it.ts +2 -0
- package/src/components/estimates/list/locales/nl.ts +2 -0
- package/src/components/estimates/list/locales/pl.ts +2 -0
- package/src/components/estimates/list/locales/pt.ts +2 -0
- package/src/components/estimates/list/locales/sl.ts +2 -0
- package/src/components/export/document-export-form.tsx +55 -14
- 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 +106 -11
- package/src/components/invoices/create/locales/de.ts +26 -0
- package/src/components/invoices/create/locales/es.ts +26 -0
- package/src/components/invoices/create/locales/fr.ts +26 -0
- package/src/components/invoices/create/locales/hr.ts +25 -0
- package/src/components/invoices/create/locales/it.ts +26 -0
- package/src/components/invoices/create/locales/nl.ts +26 -0
- package/src/components/invoices/create/locales/pl.ts +25 -0
- package/src/components/invoices/create/locales/pt.ts +25 -0
- package/src/components/invoices/create/locales/sl.ts +26 -1
- package/src/components/invoices/invoices-furs.hooks.ts +4 -1
- package/src/components/invoices/list/list-table.tsx +77 -31
- package/src/components/invoices/list/locales/de.ts +5 -0
- package/src/components/invoices/list/locales/en.ts +4 -0
- package/src/components/invoices/list/locales/es.ts +5 -0
- package/src/components/invoices/list/locales/fr.ts +5 -0
- package/src/components/invoices/list/locales/hr.ts +5 -0
- package/src/components/invoices/list/locales/it.ts +5 -0
- package/src/components/invoices/list/locales/nl.ts +5 -0
- package/src/components/invoices/list/locales/pl.ts +5 -0
- package/src/components/invoices/list/locales/pt.ts +5 -0
- package/src/components/invoices/list/locales/sl.ts +5 -0
- package/src/components/invoices/view/fiscalization-status-card.tsx +44 -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-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/search-input.tsx +17 -0
- 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/advanceinvoice.ts +4 -2
- package/src/generated/schemas/creditnote.ts +2 -1
- package/src/generated/schemas/deliverynote.ts +2 -1
- package/src/generated/schemas/estimate.ts +4 -2
- package/src/generated/schemas/index.ts +1 -1
- package/src/generated/schemas/invoice.ts +2 -1
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -23
- package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -23
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +17 -23
- package/src/generated/schemas/renderestimatepreview_body.ts +17 -23
- package/src/generated/schemas/renderinvoicepreview_body.ts +19 -23
- package/src/hooks/use-duplicate-document.ts +56 -14
- package/src/hooks/use-vies-check.ts +3 -0
- package/src/lib/fiscalization.ts +12 -0
- package/src/lib/schemas/advance-invoice.ts +0 -1
- package/src/components/table/sortable-header.tsx +0 -56
|
@@ -3,13 +3,16 @@ type TFunction = (key: string, options?: Record<string, unknown>) => string;
|
|
|
3
3
|
import { Calendar, Download, Loader2 } from "lucide-react";
|
|
4
4
|
import { useRef, useState } from "react";
|
|
5
5
|
import { Button } from "../ui/button";
|
|
6
|
+
import { Checkbox } from "../ui/checkbox";
|
|
6
7
|
import { Input } from "../ui/input";
|
|
7
8
|
import { Label } from "../ui/label";
|
|
8
9
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
|
9
10
|
|
|
10
|
-
export type DocumentType = "invoice" | "estimate" | "credit_note";
|
|
11
|
+
export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice" | "delivery_note";
|
|
11
12
|
export type ExportFormat = "xlsx" | "csv" | "pdf_zip";
|
|
12
13
|
|
|
14
|
+
const ALL_DOCUMENT_TYPES: DocumentType[] = ["invoice", "estimate", "credit_note", "advance_invoice", "delivery_note"];
|
|
15
|
+
|
|
13
16
|
// Maximum date range for export (1 year in milliseconds)
|
|
14
17
|
const MAX_DATE_RANGE_MS = 365 * 24 * 60 * 60 * 1000;
|
|
15
18
|
|
|
@@ -40,8 +43,11 @@ function isDateRangeValid(dateFrom: string, dateTo: string): boolean {
|
|
|
40
43
|
export type DocumentExportFormProps = {
|
|
41
44
|
entityId: string;
|
|
42
45
|
token: string;
|
|
46
|
+
accountId?: string | null;
|
|
43
47
|
language: string;
|
|
44
48
|
t: TFunction;
|
|
49
|
+
/** Base URL for API calls (required in embed context where relative paths don't reach the API) */
|
|
50
|
+
apiBaseUrl?: string;
|
|
45
51
|
onSuccess?: (fileName: string) => void;
|
|
46
52
|
onError?: (error: Error) => void;
|
|
47
53
|
onPdfExportStarted?: (jobId: string) => void;
|
|
@@ -51,8 +57,10 @@ export type DocumentExportFormProps = {
|
|
|
51
57
|
export function DocumentExportForm({
|
|
52
58
|
entityId,
|
|
53
59
|
token,
|
|
60
|
+
accountId,
|
|
54
61
|
language,
|
|
55
62
|
t,
|
|
63
|
+
apiBaseUrl = "",
|
|
56
64
|
onSuccess,
|
|
57
65
|
onError,
|
|
58
66
|
onPdfExportStarted,
|
|
@@ -60,6 +68,7 @@ export function DocumentExportForm({
|
|
|
60
68
|
}: DocumentExportFormProps) {
|
|
61
69
|
const defaultDates = getPreviousMonthRange();
|
|
62
70
|
const [documentType, setDocumentType] = useState<DocumentType>("invoice");
|
|
71
|
+
const [selectedTypes, setSelectedTypes] = useState<DocumentType[]>(["invoice"]);
|
|
63
72
|
const [exportFormat, setExportFormat] = useState<ExportFormat>("xlsx");
|
|
64
73
|
const [dateFrom, setDateFrom] = useState(defaultDates.from);
|
|
65
74
|
const [dateTo, setDateTo] = useState(defaultDates.to);
|
|
@@ -68,6 +77,17 @@ export function DocumentExportForm({
|
|
|
68
77
|
|
|
69
78
|
const toastIdRef = useRef<string | number | null>(null);
|
|
70
79
|
|
|
80
|
+
const toggleType = (type: DocumentType) => {
|
|
81
|
+
setSelectedTypes((prev) => {
|
|
82
|
+
if (prev.includes(type)) {
|
|
83
|
+
// Don't allow deselecting the last type
|
|
84
|
+
if (prev.length === 1) return prev;
|
|
85
|
+
return prev.filter((t) => t !== type);
|
|
86
|
+
}
|
|
87
|
+
return [...prev, type];
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
71
91
|
const validateDateRange = (from: string, to: string) => {
|
|
72
92
|
const isValid = isDateRangeValid(from, to);
|
|
73
93
|
setDateRangeError(!isValid);
|
|
@@ -86,15 +106,16 @@ export function DocumentExportForm({
|
|
|
86
106
|
// PDF export is async - different flow
|
|
87
107
|
if (exportFormat === "pdf_zip") {
|
|
88
108
|
try {
|
|
89
|
-
const response = await fetch(
|
|
109
|
+
const response = await fetch(`${apiBaseUrl}/documents/export/pdf`, {
|
|
90
110
|
method: "POST",
|
|
91
111
|
headers: {
|
|
92
112
|
Authorization: `Bearer ${token}`,
|
|
93
113
|
"x-entity-id": entityId,
|
|
114
|
+
...(accountId && { "x-account-id": accountId }),
|
|
94
115
|
"Content-Type": "application/json",
|
|
95
116
|
},
|
|
96
117
|
body: JSON.stringify({
|
|
97
|
-
|
|
118
|
+
types: selectedTypes,
|
|
98
119
|
date_from: dateFrom || undefined,
|
|
99
120
|
date_to: dateTo || undefined,
|
|
100
121
|
}),
|
|
@@ -132,10 +153,11 @@ export function DocumentExportForm({
|
|
|
132
153
|
queryParams.date_to = dateTo;
|
|
133
154
|
}
|
|
134
155
|
|
|
135
|
-
const response = await fetch(
|
|
156
|
+
const response = await fetch(`${apiBaseUrl}/documents/export?${new URLSearchParams(queryParams).toString()}`, {
|
|
136
157
|
headers: {
|
|
137
158
|
Authorization: `Bearer ${token}`,
|
|
138
159
|
"x-entity-id": entityId,
|
|
160
|
+
...(accountId && { "x-account-id": accountId }),
|
|
139
161
|
},
|
|
140
162
|
});
|
|
141
163
|
|
|
@@ -180,16 +202,35 @@ export function DocumentExportForm({
|
|
|
180
202
|
<div className="grid grid-cols-2 gap-4">
|
|
181
203
|
<div className="space-y-2">
|
|
182
204
|
<Label htmlFor="document-type">{t("export-page.document-type")}</Label>
|
|
183
|
-
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
205
|
+
{exportFormat === "pdf_zip" ? (
|
|
206
|
+
<fieldset className="space-y-2 rounded-md border p-3">
|
|
207
|
+
{ALL_DOCUMENT_TYPES.map((type) => (
|
|
208
|
+
<div key={type} className="flex items-center gap-2">
|
|
209
|
+
<Checkbox
|
|
210
|
+
id={`type-${type}`}
|
|
211
|
+
checked={selectedTypes.includes(type)}
|
|
212
|
+
onCheckedChange={() => toggleType(type)}
|
|
213
|
+
/>
|
|
214
|
+
<Label htmlFor={`type-${type}`} className="cursor-pointer font-normal">
|
|
215
|
+
{t(`export-page.types.${type}`)}
|
|
216
|
+
</Label>
|
|
217
|
+
</div>
|
|
218
|
+
))}
|
|
219
|
+
</fieldset>
|
|
220
|
+
) : (
|
|
221
|
+
<Select value={documentType} onValueChange={(v) => setDocumentType(v as DocumentType)}>
|
|
222
|
+
<SelectTrigger id="document-type">
|
|
223
|
+
<SelectValue />
|
|
224
|
+
</SelectTrigger>
|
|
225
|
+
<SelectContent>
|
|
226
|
+
<SelectItem value="invoice">{t("export-page.types.invoice")}</SelectItem>
|
|
227
|
+
<SelectItem value="estimate">{t("export-page.types.estimate")}</SelectItem>
|
|
228
|
+
<SelectItem value="credit_note">{t("export-page.types.credit_note")}</SelectItem>
|
|
229
|
+
<SelectItem value="advance_invoice">{t("export-page.types.advance_invoice")}</SelectItem>
|
|
230
|
+
<SelectItem value="delivery_note">{t("export-page.types.delivery_note")}</SelectItem>
|
|
231
|
+
</SelectContent>
|
|
232
|
+
</Select>
|
|
233
|
+
)}
|
|
193
234
|
</div>
|
|
194
235
|
|
|
195
236
|
<div className="space-y-2">
|
|
@@ -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
|
+
}
|
|
@@ -24,9 +24,11 @@ import {
|
|
|
24
24
|
DocumentDetailsSection,
|
|
25
25
|
DocumentNoteField,
|
|
26
26
|
DocumentPaymentTermsField,
|
|
27
|
+
DocumentTaxClauseField,
|
|
27
28
|
} from "../../documents/create/document-details-section";
|
|
28
29
|
import { DocumentItemsSection, type PriceModesMap } from "../../documents/create/document-items-section";
|
|
29
30
|
import { DocumentRecipientSection } from "../../documents/create/document-recipient-section";
|
|
31
|
+
import { type LinkedDocumentSummary, LinkedDocumentsInfo } from "../../documents/create/linked-documents-info";
|
|
30
32
|
import { MarkAsPaidSection } from "../../documents/create/mark-as-paid-section";
|
|
31
33
|
import type { DocumentTypes } from "../../documents/types";
|
|
32
34
|
import { useFinaPremises, useFinaSettings } from "../../entities/fina-settings-form/fina-settings.hooks";
|
|
@@ -53,6 +55,12 @@ import sl from "./locales/sl";
|
|
|
53
55
|
import { prepareInvoiceSubmission } from "./prepare-invoice-submission";
|
|
54
56
|
import { useInvoiceCustomerForm } from "./use-invoice-customer-form";
|
|
55
57
|
|
|
58
|
+
function calculateDueDate(dateIso: string, days: number): string {
|
|
59
|
+
const date = new Date(dateIso);
|
|
60
|
+
date.setDate(date.getDate() + days);
|
|
61
|
+
return date.toISOString();
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
const translations = {
|
|
57
65
|
sl,
|
|
58
66
|
de,
|
|
@@ -83,6 +91,10 @@ type DocumentAddFormProps = {
|
|
|
83
91
|
onHeaderActionChange?: (action: ReactNode) => void;
|
|
84
92
|
/** Initial values for form fields (used for document duplication or editing) */
|
|
85
93
|
initialValues?: Partial<CreateInvoiceRequest> & { number?: string };
|
|
94
|
+
/** Source documents linked to this invoice (e.g., delivery notes merged into this invoice) */
|
|
95
|
+
sourceDocuments?: LinkedDocumentSummary[];
|
|
96
|
+
/** Force linking documents even if an advance invoice is already applied to another invoice */
|
|
97
|
+
forceLinkedDocuments?: boolean;
|
|
86
98
|
/** Mode: create (default) or edit */
|
|
87
99
|
mode?: "create" | "edit";
|
|
88
100
|
/** Document ID for edit mode */
|
|
@@ -98,6 +110,8 @@ export default function CreateInvoiceForm({
|
|
|
98
110
|
onAddNewTax,
|
|
99
111
|
onHeaderActionChange,
|
|
100
112
|
initialValues,
|
|
113
|
+
sourceDocuments,
|
|
114
|
+
forceLinkedDocuments,
|
|
101
115
|
mode = "create",
|
|
102
116
|
documentId,
|
|
103
117
|
t: translateProp,
|
|
@@ -118,6 +132,7 @@ export default function CreateInvoiceForm({
|
|
|
118
132
|
// Get default invoice note and payment terms from entity settings
|
|
119
133
|
const defaultInvoiceNote = (activeEntity?.settings as any)?.default_invoice_note || "";
|
|
120
134
|
const defaultPaymentTerms = (activeEntity?.settings as any)?.default_invoice_payment_terms || "";
|
|
135
|
+
const defaultInvoiceDueDays = (activeEntity?.settings as any)?.default_invoice_due_days ?? 30;
|
|
121
136
|
|
|
122
137
|
// ============================================================================
|
|
123
138
|
// FURS Settings & Premises
|
|
@@ -307,13 +322,18 @@ export default function CreateInvoiceForm({
|
|
|
307
322
|
// Cast customer to form schema type (API type may have additional fields)
|
|
308
323
|
customer: (initialValues?.customer as CreateInvoiceFormValues["customer"]) ?? undefined,
|
|
309
324
|
items: initialValues?.items?.length
|
|
310
|
-
? initialValues.items.map((item) => ({
|
|
325
|
+
? initialValues.items.map((item: any) => ({
|
|
326
|
+
type: item.type,
|
|
311
327
|
name: item.name || "",
|
|
312
328
|
description: item.description || "",
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
329
|
+
...(item.type !== "separator"
|
|
330
|
+
? {
|
|
331
|
+
quantity: item.quantity ?? 1,
|
|
332
|
+
// Use gross_price if set, otherwise use price
|
|
333
|
+
price: item.gross_price ?? item.price,
|
|
334
|
+
taxes: item.taxes || [],
|
|
335
|
+
}
|
|
336
|
+
: {}),
|
|
317
337
|
}))
|
|
318
338
|
: [
|
|
319
339
|
{
|
|
@@ -326,8 +346,13 @@ export default function CreateInvoiceForm({
|
|
|
326
346
|
],
|
|
327
347
|
currency_code: initialValues?.currency_code || activeEntity?.currency_code || "EUR",
|
|
328
348
|
note: initialValues?.note ?? defaultInvoiceNote,
|
|
349
|
+
tax_clause: (initialValues as any)?.tax_clause ?? "",
|
|
329
350
|
payment_terms: initialValues?.payment_terms ?? defaultPaymentTerms,
|
|
351
|
+
date_due:
|
|
352
|
+
initialValues?.date_due ||
|
|
353
|
+
calculateDueDate(initialValues?.date || new Date().toISOString(), defaultInvoiceDueDays),
|
|
330
354
|
date_service: new Date().toISOString(),
|
|
355
|
+
linked_documents: (initialValues as any)?.linked_documents,
|
|
331
356
|
},
|
|
332
357
|
});
|
|
333
358
|
|
|
@@ -511,7 +536,13 @@ export default function CreateInvoiceForm({
|
|
|
511
536
|
// ============================================================================
|
|
512
537
|
// VIES Check - determine if reverse charge applies
|
|
513
538
|
// ============================================================================
|
|
514
|
-
const {
|
|
539
|
+
const {
|
|
540
|
+
reverseChargeApplies,
|
|
541
|
+
transactionType,
|
|
542
|
+
customerCountryCode: viesCustomerCountryCode,
|
|
543
|
+
isFetching: isViesFetching,
|
|
544
|
+
warning: viesWarning,
|
|
545
|
+
} = useViesCheck({
|
|
515
546
|
issuerCountryCode: activeEntity?.country_code,
|
|
516
547
|
isTaxSubject: activeEntity?.is_tax_subject ?? true,
|
|
517
548
|
customerCountry,
|
|
@@ -520,6 +551,23 @@ export default function CreateInvoiceForm({
|
|
|
520
551
|
enabled: !!activeEntity,
|
|
521
552
|
});
|
|
522
553
|
|
|
554
|
+
// FINA non-domestic guard: hide FINA selectors for non-domestic transactions
|
|
555
|
+
const isFinaNonDomestic = isFinaEnabled && viesCustomerCountryCode != null && viesCustomerCountryCode !== "HR";
|
|
556
|
+
|
|
557
|
+
// Auto-populate tax_clause from entity settings when transaction type changes
|
|
558
|
+
const effectiveTransactionType = transactionType ?? "domestic";
|
|
559
|
+
const prevTransactionTypeRef = useRef<string | undefined>(undefined);
|
|
560
|
+
useEffect(() => {
|
|
561
|
+
if (effectiveTransactionType === prevTransactionTypeRef.current) return;
|
|
562
|
+
prevTransactionTypeRef.current = effectiveTransactionType;
|
|
563
|
+
|
|
564
|
+
const taxClauseDefaults = (activeEntity?.settings as any)?.tax_clause_defaults;
|
|
565
|
+
if (!taxClauseDefaults) return;
|
|
566
|
+
|
|
567
|
+
const clause = taxClauseDefaults[effectiveTransactionType] ?? "";
|
|
568
|
+
form.setValue("tax_clause", clause);
|
|
569
|
+
}, [effectiveTransactionType, activeEntity, form]);
|
|
570
|
+
|
|
523
571
|
// Extract customer management logic into a custom hook
|
|
524
572
|
const {
|
|
525
573
|
originalCustomer,
|
|
@@ -606,9 +654,9 @@ export default function CreateInvoiceForm({
|
|
|
606
654
|
: undefined
|
|
607
655
|
: undefined;
|
|
608
656
|
|
|
609
|
-
// Build FINA options (skip for drafts
|
|
657
|
+
// Build FINA options (skip for drafts, edit mode, and non-domestic transactions)
|
|
610
658
|
const finaOptions =
|
|
611
|
-
!isDraft && !isEditMode && isFinaEnabled && selectedFinaPremiseId && selectedFinaDeviceId
|
|
659
|
+
!isDraft && !isEditMode && isFinaEnabled && !isFinaNonDomestic && selectedFinaPremiseId && selectedFinaDeviceId
|
|
612
660
|
? { premise_id: selectedFinaPremiseId, device_id: selectedFinaDeviceId, payment_type: paymentTypes[0] }
|
|
613
661
|
: undefined;
|
|
614
662
|
|
|
@@ -630,6 +678,11 @@ export default function CreateInvoiceForm({
|
|
|
630
678
|
isDraft,
|
|
631
679
|
});
|
|
632
680
|
|
|
681
|
+
// Add force_linked_documents if set (used after conflict dialog approval)
|
|
682
|
+
if (forceLinkedDocuments) {
|
|
683
|
+
(payload as any).force_linked_documents = true;
|
|
684
|
+
}
|
|
685
|
+
|
|
633
686
|
if (isEditMode && documentId) {
|
|
634
687
|
// In edit mode, use updateInvoice
|
|
635
688
|
// Remove number from payload as it's not editable
|
|
@@ -645,11 +698,13 @@ export default function CreateInvoiceForm({
|
|
|
645
698
|
updateInvoice,
|
|
646
699
|
documentId,
|
|
647
700
|
eslogValidationEnabled,
|
|
701
|
+
forceLinkedDocuments,
|
|
648
702
|
form,
|
|
649
703
|
isEditMode,
|
|
650
704
|
isEslogAvailable,
|
|
651
705
|
isFursEnabled,
|
|
652
706
|
isFinaEnabled,
|
|
707
|
+
isFinaNonDomestic,
|
|
653
708
|
markAsPaid,
|
|
654
709
|
originalCustomer,
|
|
655
710
|
paymentTypes,
|
|
@@ -693,7 +748,8 @@ export default function CreateInvoiceForm({
|
|
|
693
748
|
);
|
|
694
749
|
|
|
695
750
|
// Watch isDirty to get stable reference
|
|
696
|
-
|
|
751
|
+
// When form is pre-populated via initialValues (duplicate/merge), treat as dirty immediately
|
|
752
|
+
const isDirty = form.formState.isDirty || !!initialValues;
|
|
697
753
|
|
|
698
754
|
useFormFooterRegistration({
|
|
699
755
|
formId: "create-invoice-form",
|
|
@@ -721,6 +777,15 @@ export default function CreateInvoiceForm({
|
|
|
721
777
|
form.setValue("payment_terms", entityDefaultPaymentTerms);
|
|
722
778
|
}
|
|
723
779
|
|
|
780
|
+
// Auto-populate due date from entity settings when entity loads async
|
|
781
|
+
if (!isEditMode && !initialValues?.date_due) {
|
|
782
|
+
const dueDays = (activeEntity.settings as any)?.default_invoice_due_days ?? 30;
|
|
783
|
+
const currentDate = form.getValues("date");
|
|
784
|
+
if (currentDate) {
|
|
785
|
+
form.setValue("date_due", calculateDueDate(currentDate, dueDays));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
724
789
|
// Auto-add tax field for tax subject entities
|
|
725
790
|
if (activeEntity.is_tax_subject) {
|
|
726
791
|
const items = form.getValues("items") || [];
|
|
@@ -730,7 +795,17 @@ export default function CreateInvoiceForm({
|
|
|
730
795
|
}
|
|
731
796
|
|
|
732
797
|
initialSetupDoneRef.current = true;
|
|
733
|
-
}, [activeEntity, form]);
|
|
798
|
+
}, [activeEntity, form, isEditMode, initialValues?.date_due]);
|
|
799
|
+
|
|
800
|
+
// Recalculate due date when document date changes (skip in edit mode)
|
|
801
|
+
const prevDateRef = useRef(form.getValues("date"));
|
|
802
|
+
useEffect(() => {
|
|
803
|
+
if (isEditMode) return;
|
|
804
|
+
if (!watchedDate || watchedDate === prevDateRef.current) return;
|
|
805
|
+
prevDateRef.current = watchedDate;
|
|
806
|
+
const dueDays = (activeEntity?.settings as any)?.default_invoice_due_days ?? 30;
|
|
807
|
+
form.setValue("date_due", calculateDueDate(watchedDate, dueDays));
|
|
808
|
+
}, [watchedDate, activeEntity, isEditMode, form]);
|
|
734
809
|
|
|
735
810
|
// Use form.watch subscription for onChange callback (avoids re-render loops)
|
|
736
811
|
const prevPayloadRef = useRef<string>("");
|
|
@@ -888,7 +963,7 @@ export default function CreateInvoiceForm({
|
|
|
888
963
|
: undefined
|
|
889
964
|
}
|
|
890
965
|
finaInline={
|
|
891
|
-
!isEditMode && isFinaEnabled && hasFinaPremises
|
|
966
|
+
!isEditMode && isFinaEnabled && hasFinaPremises && !isFinaNonDomestic
|
|
892
967
|
? {
|
|
893
968
|
premises: activeFinaPremises.map((p: any) => ({ id: p.id, premise_id: p.premise_id })),
|
|
894
969
|
devices: activeFinaDevices.map((d: any) => ({ id: d.id, device_id: d.device_id })),
|
|
@@ -950,6 +1025,22 @@ export default function CreateInvoiceForm({
|
|
|
950
1025
|
}}
|
|
951
1026
|
/>
|
|
952
1027
|
|
|
1028
|
+
<DocumentTaxClauseField
|
|
1029
|
+
control={form.control}
|
|
1030
|
+
t={t}
|
|
1031
|
+
entity={activeEntity}
|
|
1032
|
+
document={{
|
|
1033
|
+
number: watchedNumber,
|
|
1034
|
+
date: watchedDate,
|
|
1035
|
+
date_due: watchedDateDue,
|
|
1036
|
+
currency_code: watchedCurrencyCode,
|
|
1037
|
+
customer: watchedCustomer as any,
|
|
1038
|
+
}}
|
|
1039
|
+
transactionType={transactionType}
|
|
1040
|
+
isTransactionTypeFetching={isViesFetching}
|
|
1041
|
+
isFinaNonDomestic={isFinaNonDomestic}
|
|
1042
|
+
/>
|
|
1043
|
+
|
|
953
1044
|
<DocumentPaymentTermsField
|
|
954
1045
|
control={form.control}
|
|
955
1046
|
t={t}
|
|
@@ -962,6 +1053,10 @@ export default function CreateInvoiceForm({
|
|
|
962
1053
|
customer: watchedCustomer as any,
|
|
963
1054
|
}}
|
|
964
1055
|
/>
|
|
1056
|
+
|
|
1057
|
+
{sourceDocuments && sourceDocuments.length > 0 && (
|
|
1058
|
+
<LinkedDocumentsInfo documents={sourceDocuments} locale={locale || "en"} t={t} />
|
|
1059
|
+
)}
|
|
965
1060
|
</form>
|
|
966
1061
|
</Form>
|
|
967
1062
|
);
|
|
@@ -109,10 +109,36 @@ 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",
|
|
115
126
|
"Date Range": "Zeitraum",
|
|
116
127
|
From: "Von",
|
|
117
128
|
To: "Bis",
|
|
129
|
+
// Separator items
|
|
130
|
+
"Add separator": "Trennzeile hinzufügen",
|
|
131
|
+
"Section header": "Abschnittsüberschrift",
|
|
132
|
+
"Section title...": "Abschnittstitel...",
|
|
133
|
+
// Transaction type
|
|
134
|
+
"Transaction type": "Transaktionstyp",
|
|
135
|
+
Domestic: "Inland",
|
|
136
|
+
"EU B2B": "EU B2B",
|
|
137
|
+
"EU B2C": "EU B2C",
|
|
138
|
+
Export: "Export",
|
|
139
|
+
"Determining transaction type...": "Transaktionstyp wird ermittelt...",
|
|
140
|
+
"This invoice will not be fiscalized (non-domestic transaction)":
|
|
141
|
+
"Diese Rechnung wird nicht fiskalisiert (nicht-inländische Transaktion)",
|
|
142
|
+
"Tax Clause": "Steuerklausel",
|
|
143
|
+
"Add tax clause...": "Steuerklausel hinzufügen...",
|
|
118
144
|
} as const;
|