@spaceinvoices/react-ui 0.4.6 → 0.4.7
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/components/advance-invoices/create/create-advance-invoice-form.tsx +60 -44
- package/src/components/credit-notes/create/create-credit-note-form.tsx +52 -42
- package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -92
- package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +48 -82
- package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +22 -31
- package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +33 -48
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +56 -76
- package/src/components/dashboard/shared/index.ts +1 -1
- package/src/components/dashboard/shared/use-revenue-data.ts +106 -182
- package/src/components/dashboard/shared/use-stats-counts.ts +18 -68
- package/src/components/dashboard/shared/use-stats-query.ts +35 -5
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +57 -75
- package/src/components/dashboard/top-customers-chart/use-top-customers.ts +38 -49
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +3 -2
- package/src/components/documents/create/document-details-section.tsx +6 -4
- package/src/components/documents/create/document-recipient-section.tsx +30 -1
- package/src/components/documents/create/live-preview.tsx +15 -28
- package/src/components/documents/create/prepare-document-submission.ts +1 -0
- package/src/components/documents/create/use-document-customer-form.ts +4 -0
- package/src/components/documents/shared/document-preview-skeleton.tsx +63 -0
- package/src/components/documents/shared/index.ts +1 -0
- package/src/components/documents/view/document-actions-bar.tsx +29 -7
- package/src/components/entities/settings/tax-rules-settings-form.tsx +31 -13
- package/src/components/estimates/create/create-estimate-form.tsx +3 -2
- package/src/components/invoices/create/create-invoice-form.tsx +134 -62
- package/src/components/invoices/create/locales/de.ts +6 -0
- package/src/components/invoices/create/locales/es.ts +6 -0
- package/src/components/invoices/create/locales/fr.ts +6 -0
- package/src/components/invoices/create/locales/hr.ts +6 -0
- package/src/components/invoices/create/locales/it.ts +6 -0
- package/src/components/invoices/create/locales/nl.ts +6 -0
- package/src/components/invoices/create/locales/pl.ts +6 -0
- package/src/components/invoices/create/locales/pt.ts +6 -0
- package/src/components/invoices/create/locales/sl.ts +6 -0
- package/src/components/invoices/invoices.hooks.ts +1 -1
- package/src/components/ui/progress.tsx +27 -0
- package/src/generate-schemas.ts +15 -2
- package/src/generated/schemas/advanceinvoice.ts +2 -0
- package/src/generated/schemas/creditnote.ts +2 -0
- package/src/generated/schemas/customer.ts +2 -0
- package/src/generated/schemas/deliverynote.ts +2 -0
- package/src/generated/schemas/entity.ts +10 -0
- package/src/generated/schemas/estimate.ts +2 -0
- package/src/generated/schemas/finasettings.ts +4 -3
- package/src/generated/schemas/invoice.ts +2 -0
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +16 -10
- package/src/generated/schemas/rendercreditnotepreview_body.ts +16 -10
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +14 -7
- package/src/generated/schemas/renderestimatepreview_body.ts +14 -7
- package/src/generated/schemas/renderinvoicepreview_body.ts +16 -10
- package/src/generated/schemas/startpdfexport_body.ts +12 -17
- package/src/hooks/use-transaction-type-check.ts +152 -0
- package/src/hooks/use-vies-check.ts +7 -131
|
@@ -13,7 +13,7 @@ import { Form } from "@/ui/components/ui/form";
|
|
|
13
13
|
import { Skeleton } from "@/ui/components/ui/skeleton";
|
|
14
14
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/ui/components/ui/tooltip";
|
|
15
15
|
import { createInvoiceSchema } from "@/ui/generated/schemas";
|
|
16
|
-
import {
|
|
16
|
+
import { useTransactionTypeCheck } from "@/ui/hooks/use-transaction-type-check";
|
|
17
17
|
import type { ComponentTranslationProps } from "@/ui/lib/translation";
|
|
18
18
|
import { createTranslation } from "@/ui/lib/translation";
|
|
19
19
|
import { cn } from "@/ui/lib/utils";
|
|
@@ -416,14 +416,56 @@ export default function CreateInvoiceForm({
|
|
|
416
416
|
const isFinaActive =
|
|
417
417
|
isFinaEnabled && hasFinaPremises && selectedFinaBusinessPremiseName && selectedFinaElectronicDeviceName;
|
|
418
418
|
|
|
419
|
+
// ============================================================================
|
|
420
|
+
// VIES Check - determine transaction type early (needed for number preview)
|
|
421
|
+
// ============================================================================
|
|
422
|
+
const customerCountry = useWatch({ control: form.control, name: "customer.country" });
|
|
423
|
+
const customerCountryCode = useWatch({ control: form.control, name: "customer.country_code" });
|
|
424
|
+
const customerTaxNumber = useWatch({ control: form.control, name: "customer.tax_number" });
|
|
425
|
+
const customerIsEndConsumerWatch =
|
|
426
|
+
useWatch({ control: form.control, name: "customer.is_end_consumer" as any }) === true;
|
|
427
|
+
|
|
428
|
+
const {
|
|
429
|
+
reverseChargeApplies,
|
|
430
|
+
transactionType,
|
|
431
|
+
isFetching: isViesFetching,
|
|
432
|
+
warning: viesWarning,
|
|
433
|
+
} = useTransactionTypeCheck({
|
|
434
|
+
issuerCountryCode: activeEntity?.country_code,
|
|
435
|
+
isTaxSubject: activeEntity?.is_tax_subject ?? true,
|
|
436
|
+
customerCountry,
|
|
437
|
+
customerCountryCode,
|
|
438
|
+
customerTaxNumber,
|
|
439
|
+
customerIsEndConsumer: customerIsEndConsumerWatch,
|
|
440
|
+
enabled: !!activeEntity,
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// FINA numbering guard: use FINA numbering for domestic transactions (or all if unified numbering is on)
|
|
444
|
+
const finaUnifiedNumbering = finaSettings?.unified_numbering !== false;
|
|
445
|
+
const useFinaNumbering =
|
|
446
|
+
!!isFinaActive && (finaUnifiedNumbering || transactionType == null || transactionType === "domestic");
|
|
447
|
+
const isFinaNonDomestic = !!isFinaActive && !useFinaNumbering;
|
|
448
|
+
|
|
419
449
|
// ============================================================================
|
|
420
450
|
// Next Invoice Number Preview
|
|
421
451
|
// ============================================================================
|
|
422
452
|
// Wait for FURS selection to be ready before querying to prevent number flashing
|
|
423
453
|
// Skip in edit mode - we use the existing document number
|
|
454
|
+
// Use the same premise/device params for both FURS and FINA (an entity is either one, never both)
|
|
455
|
+
const activePremiseName = isFursActive
|
|
456
|
+
? selectedPremiseName
|
|
457
|
+
: useFinaNumbering
|
|
458
|
+
? selectedFinaBusinessPremiseName
|
|
459
|
+
: undefined;
|
|
460
|
+
const activeDeviceNameForNumber = isFursActive
|
|
461
|
+
? selectedDeviceName
|
|
462
|
+
: useFinaNumbering
|
|
463
|
+
? selectedFinaElectronicDeviceName
|
|
464
|
+
: undefined;
|
|
465
|
+
|
|
424
466
|
const { data: nextNumberData, isLoading: isNextNumberLoading } = useNextInvoiceNumber(entityId, {
|
|
425
|
-
business_premise_name:
|
|
426
|
-
electronic_device_name:
|
|
467
|
+
business_premise_name: activePremiseName,
|
|
468
|
+
electronic_device_name: activeDeviceNameForNumber,
|
|
427
469
|
enabled:
|
|
428
470
|
!!entityId && !isFursLoading && isFursSelectionReady && !isFinaLoading && isFinaSelectionReady && !isEditMode,
|
|
429
471
|
});
|
|
@@ -551,11 +593,6 @@ export default function CreateInvoiceForm({
|
|
|
551
593
|
}
|
|
552
594
|
}, [nextNumberData?.number, form]);
|
|
553
595
|
|
|
554
|
-
// Watch specific fields for VIES check (stable references)
|
|
555
|
-
const customerCountry = useWatch({ control: form.control, name: "customer.country" });
|
|
556
|
-
const customerCountryCode = useWatch({ control: form.control, name: "customer.country_code" });
|
|
557
|
-
const customerTaxNumber = useWatch({ control: form.control, name: "customer.tax_number" });
|
|
558
|
-
|
|
559
596
|
// Watch fields needed for document note/payment terms preview
|
|
560
597
|
const watchedNumber = useWatch({ control: form.control, name: "number" });
|
|
561
598
|
const watchedDate = useWatch({ control: form.control, name: "date" });
|
|
@@ -563,29 +600,45 @@ export default function CreateInvoiceForm({
|
|
|
563
600
|
const watchedCurrencyCode = useWatch({ control: form.control, name: "currency_code" });
|
|
564
601
|
const watchedCustomer = useWatch({ control: form.control, name: "customer" });
|
|
565
602
|
|
|
566
|
-
//
|
|
567
|
-
//
|
|
568
|
-
//
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
customerTaxNumber
|
|
581
|
-
|
|
582
|
-
|
|
603
|
+
// Croatian invoice validation:
|
|
604
|
+
// - Domestic/3w B2C requires FINA
|
|
605
|
+
// - Domestic B2B (customer has tax number and NOT end consumer) is blocked
|
|
606
|
+
const isCroatianEntity = activeEntity?.country_code === "HR";
|
|
607
|
+
const customerHasTaxNumber = !!customerTaxNumber?.trim();
|
|
608
|
+
const isDomesticTransaction = transactionType === "domestic";
|
|
609
|
+
const requiresFinaFiscalization = isDomesticTransaction || transactionType === "3w_b2c";
|
|
610
|
+
const is3wTransaction = transactionType === "3w_b2b" || transactionType === "3w_b2c";
|
|
611
|
+
|
|
612
|
+
// Auto-toggle is_end_consumer based on tax number for Croatian domestic/3w customers
|
|
613
|
+
// Default: checked (end consumer). When tax number is entered: uncheck (business). User can override.
|
|
614
|
+
const prevAutoSetTaxRef = useRef<string | undefined>(undefined);
|
|
615
|
+
useEffect(() => {
|
|
616
|
+
if (!isCroatianEntity || !(isDomesticTransaction || is3wTransaction)) return;
|
|
617
|
+
const hasTaxNumber = !!customerTaxNumber?.trim();
|
|
618
|
+
const hadTaxNumber = !!prevAutoSetTaxRef.current?.trim();
|
|
619
|
+
prevAutoSetTaxRef.current = customerTaxNumber ?? undefined;
|
|
620
|
+
|
|
621
|
+
// Auto-uncheck when tax number goes from empty to filled (likely a business)
|
|
622
|
+
if (hasTaxNumber && !hadTaxNumber && customerIsEndConsumerWatch) {
|
|
623
|
+
form.setValue("customer.is_end_consumer" as any, false);
|
|
624
|
+
}
|
|
625
|
+
// Auto-check when tax number goes from filled to empty (likely an individual)
|
|
626
|
+
if (!hasTaxNumber && hadTaxNumber && !customerIsEndConsumerWatch) {
|
|
627
|
+
form.setValue("customer.is_end_consumer" as any, true);
|
|
628
|
+
}
|
|
629
|
+
}, [customerTaxNumber, isCroatianEntity, isDomesticTransaction, is3wTransaction, customerIsEndConsumerWatch, form]);
|
|
583
630
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
631
|
+
const finaValidationError = (() => {
|
|
632
|
+
if (!isCroatianEntity || !requiresFinaFiscalization) return undefined;
|
|
633
|
+
// Domestic B2B is always blocked (3w B2B never reaches here since requiresFinaFiscalization is false for 3w B2B)
|
|
634
|
+
if (isDomesticTransaction && customerHasTaxNumber && !customerIsEndConsumerWatch) {
|
|
635
|
+
return t("Domestic B2B invoicing in Croatia is not supported");
|
|
636
|
+
}
|
|
637
|
+
if (!isFinaEnabled) {
|
|
638
|
+
return t("FINA fiscalization must be enabled for domestic invoices");
|
|
639
|
+
}
|
|
640
|
+
return undefined;
|
|
641
|
+
})();
|
|
589
642
|
|
|
590
643
|
// Auto-populate tax_clause from entity settings when transaction type changes
|
|
591
644
|
const effectiveTransactionType = transactionType ?? "domestic";
|
|
@@ -658,6 +711,9 @@ export default function CreateInvoiceForm({
|
|
|
658
711
|
// Shared submit logic for both regular save and save as draft
|
|
659
712
|
const submitInvoice = useCallback(
|
|
660
713
|
(values: CreateInvoiceFormValues, isDraft: boolean) => {
|
|
714
|
+
// Block Croatian domestic B2B and domestic B2C without FINA
|
|
715
|
+
if (finaValidationError) return;
|
|
716
|
+
|
|
661
717
|
// Skip e-SLOG validation for drafts and edit mode
|
|
662
718
|
if (!isDraft && !isEditMode && eslogValidationEnabled) {
|
|
663
719
|
const validationErrors = validateEslogForm(values as any, activeEntity);
|
|
@@ -691,8 +747,7 @@ export default function CreateInvoiceForm({
|
|
|
691
747
|
const finaOptions =
|
|
692
748
|
!isDraft &&
|
|
693
749
|
!isEditMode &&
|
|
694
|
-
|
|
695
|
-
!isFinaNonDomestic &&
|
|
750
|
+
useFinaNumbering &&
|
|
696
751
|
selectedFinaBusinessPremiseName &&
|
|
697
752
|
selectedFinaElectronicDeviceName
|
|
698
753
|
? {
|
|
@@ -740,13 +795,13 @@ export default function CreateInvoiceForm({
|
|
|
740
795
|
updateInvoice,
|
|
741
796
|
documentId,
|
|
742
797
|
eslogValidationEnabled,
|
|
798
|
+
finaValidationError,
|
|
743
799
|
forceLinkedDocuments,
|
|
744
800
|
form,
|
|
745
801
|
isEditMode,
|
|
746
802
|
isEslogAvailable,
|
|
747
803
|
isFursEnabled,
|
|
748
|
-
|
|
749
|
-
isFinaNonDomestic,
|
|
804
|
+
useFinaNumbering,
|
|
750
805
|
markAsPaid,
|
|
751
806
|
originalCustomer,
|
|
752
807
|
paymentTypes,
|
|
@@ -916,25 +971,36 @@ export default function CreateInvoiceForm({
|
|
|
916
971
|
<div className="flex w-full flex-col md:flex-row md:gap-6">
|
|
917
972
|
{/* Recipient section skeleton */}
|
|
918
973
|
<div className="flex-1 space-y-4">
|
|
919
|
-
<Skeleton className="h-7 w-24" />
|
|
920
|
-
<Skeleton className="h-10 w-full" />
|
|
974
|
+
<Skeleton className="h-7 w-24" />
|
|
975
|
+
<Skeleton className="h-10 w-full" />
|
|
921
976
|
</div>
|
|
922
|
-
{/* Details section skeleton */}
|
|
923
|
-
<div className="flex-1 space-y-
|
|
924
|
-
<Skeleton className="h-7 w-20" />
|
|
925
|
-
<
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
<
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
977
|
+
{/* Details section skeleton — inline label rows */}
|
|
978
|
+
<div className="flex-1 space-y-3">
|
|
979
|
+
<Skeleton className="h-7 w-20" />
|
|
980
|
+
<div className="flex items-center gap-3">
|
|
981
|
+
<Skeleton className="h-5 w-[6.5rem] shrink-0" />
|
|
982
|
+
<Skeleton className="h-10 flex-1" />
|
|
983
|
+
</div>
|
|
984
|
+
<div className="flex items-center gap-3">
|
|
985
|
+
<Skeleton className="h-5 w-[6.5rem] shrink-0" />
|
|
986
|
+
<Skeleton className="h-10 flex-1" />
|
|
987
|
+
</div>
|
|
988
|
+
<div className="flex items-center gap-3">
|
|
989
|
+
<Skeleton className="h-5 w-[6.5rem] shrink-0" />
|
|
990
|
+
<Skeleton className="h-10 flex-1" />
|
|
991
|
+
</div>
|
|
992
|
+
<div className="flex items-center gap-3">
|
|
993
|
+
<Skeleton className="h-5 w-[6.5rem] shrink-0" />
|
|
994
|
+
<Skeleton className="h-10 flex-1" />
|
|
995
|
+
</div>
|
|
996
|
+
<div className="flex items-center gap-3">
|
|
997
|
+
<Skeleton className="h-5 w-[6.5rem] shrink-0" />
|
|
998
|
+
<Skeleton className="h-10 flex-1" />
|
|
999
|
+
</div>
|
|
934
1000
|
<div className="space-y-3 rounded-md border p-4">
|
|
935
1001
|
<div className="flex items-center gap-3">
|
|
936
|
-
<Skeleton className="h-4 w-4 rounded" />
|
|
937
|
-
<Skeleton className="h-5 w-28" />
|
|
1002
|
+
<Skeleton className="h-4 w-4 rounded" />
|
|
1003
|
+
<Skeleton className="h-5 w-28" />
|
|
938
1004
|
</div>
|
|
939
1005
|
</div>
|
|
940
1006
|
</div>
|
|
@@ -942,25 +1008,22 @@ export default function CreateInvoiceForm({
|
|
|
942
1008
|
|
|
943
1009
|
{/* Items section skeleton */}
|
|
944
1010
|
<div className="space-y-4">
|
|
945
|
-
<Skeleton className="h-7 w-16" />
|
|
1011
|
+
<Skeleton className="h-7 w-16" />
|
|
946
1012
|
<div className="space-y-4 rounded-lg border p-4">
|
|
947
|
-
<Skeleton className="h-10 w-full" />
|
|
1013
|
+
<Skeleton className="h-10 w-full" />
|
|
948
1014
|
<div className="flex gap-4">
|
|
949
|
-
<Skeleton className="h-10 w-24" />
|
|
950
|
-
<Skeleton className="h-10 flex-1" />
|
|
1015
|
+
<Skeleton className="h-10 w-24" />
|
|
1016
|
+
<Skeleton className="h-10 flex-1" />
|
|
951
1017
|
</div>
|
|
952
1018
|
</div>
|
|
953
|
-
<Skeleton className="h-9 w-24" />
|
|
1019
|
+
<Skeleton className="h-9 w-24" />
|
|
954
1020
|
</div>
|
|
955
1021
|
|
|
956
1022
|
{/* Note field skeleton */}
|
|
957
1023
|
<div className="space-y-2">
|
|
958
|
-
<Skeleton className="h-5 w-12" />
|
|
959
|
-
<Skeleton className="h-24 w-full" />
|
|
1024
|
+
<Skeleton className="h-5 w-12" />
|
|
1025
|
+
<Skeleton className="h-24 w-full" />
|
|
960
1026
|
</div>
|
|
961
|
-
|
|
962
|
-
{/* Save button skeleton */}
|
|
963
|
-
<Skeleton className="h-10 w-24" />
|
|
964
1027
|
</div>
|
|
965
1028
|
);
|
|
966
1029
|
}
|
|
@@ -968,6 +1031,14 @@ export default function CreateInvoiceForm({
|
|
|
968
1031
|
return (
|
|
969
1032
|
<Form {...form}>
|
|
970
1033
|
<form id="create-invoice-form" onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
1034
|
+
{/* Croatian domestic invoice validation errors */}
|
|
1035
|
+
{finaValidationError && (
|
|
1036
|
+
<Alert variant="destructive">
|
|
1037
|
+
<AlertCircle className="h-4 w-4" />
|
|
1038
|
+
<AlertTitle>{finaValidationError}</AlertTitle>
|
|
1039
|
+
</Alert>
|
|
1040
|
+
)}
|
|
1041
|
+
|
|
971
1042
|
{/* e-SLOG entity-level validation errors */}
|
|
972
1043
|
{eslogEntityErrors.length > 0 && (
|
|
973
1044
|
<Alert variant="destructive">
|
|
@@ -996,6 +1067,7 @@ export default function CreateInvoiceForm({
|
|
|
996
1067
|
shouldFocusName={shouldFocusName}
|
|
997
1068
|
selectedCustomerId={selectedCustomerId}
|
|
998
1069
|
initialCustomerName={initialCustomerName}
|
|
1070
|
+
showEndConsumerToggle={isCroatianEntity && (isDomesticTransaction || is3wTransaction)}
|
|
999
1071
|
t={t}
|
|
1000
1072
|
/>
|
|
1001
1073
|
<DocumentDetailsSection
|
|
@@ -1017,7 +1089,7 @@ export default function CreateInvoiceForm({
|
|
|
1017
1089
|
: undefined
|
|
1018
1090
|
}
|
|
1019
1091
|
finaInline={
|
|
1020
|
-
!isEditMode &&
|
|
1092
|
+
!isEditMode && useFinaNumbering
|
|
1021
1093
|
? {
|
|
1022
1094
|
premises: activeFinaPremises.map((p: any) => ({
|
|
1023
1095
|
id: p.id,
|
|
@@ -1052,7 +1124,7 @@ export default function CreateInvoiceForm({
|
|
|
1052
1124
|
paymentTypes={paymentTypes}
|
|
1053
1125
|
onPaymentTypesChange={setPaymentTypes}
|
|
1054
1126
|
t={t}
|
|
1055
|
-
alwaysShowPaymentType={!!isFinaActive}
|
|
1127
|
+
alwaysShowPaymentType={!!isFinaActive && requiresFinaFiscalization}
|
|
1056
1128
|
/>
|
|
1057
1129
|
)}
|
|
1058
1130
|
</DocumentDetailsSection>
|
|
@@ -154,4 +154,10 @@ export default {
|
|
|
154
154
|
"Add tax clause...": "Steuerklausel hinzufügen...",
|
|
155
155
|
Footer: "Fußzeile",
|
|
156
156
|
"Add document footer...": "Dokumentfußzeile hinzufügen...",
|
|
157
|
+
// Croatian domestic invoice validation
|
|
158
|
+
"End consumer": "Endverbraucher",
|
|
159
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
160
|
+
"Inländische B2B-Rechnungsstellung in Kroatien wird nicht unterstützt. Kroatien erfordert die Einhaltung von Fiskalisierung 2.0 für B2B-Rechnungsstellung.",
|
|
161
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
162
|
+
"FINA-Fiskalisierung muss für inländische Rechnungen aktiviert sein. Aktivieren Sie sie in den Unternehmenseinstellungen.",
|
|
157
163
|
} as const;
|
|
@@ -139,4 +139,10 @@ export default {
|
|
|
139
139
|
"Add tax clause...": "Agregar cláusula fiscal...",
|
|
140
140
|
Footer: "Pie de página",
|
|
141
141
|
"Add document footer...": "Añadir pie de página del documento...",
|
|
142
|
+
// Croatian domestic invoice validation
|
|
143
|
+
"End consumer": "Consumidor final",
|
|
144
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
145
|
+
"La facturación B2B doméstica en Croacia no está soportada. Croacia requiere conformidad con Fiskalizacija 2.0 para facturación B2B.",
|
|
146
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
147
|
+
"La fiscalización FINA debe estar habilitada para facturas domésticas. Habilítela en la configuración de la entidad.",
|
|
142
148
|
} as const;
|
|
@@ -141,4 +141,10 @@ export default {
|
|
|
141
141
|
"Add tax clause...": "Ajouter une clause fiscale...",
|
|
142
142
|
Footer: "Pied de page",
|
|
143
143
|
"Add document footer...": "Ajouter un pied de page...",
|
|
144
|
+
// Croatian domestic invoice validation
|
|
145
|
+
"End consumer": "Consommateur final",
|
|
146
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
147
|
+
"La facturation B2B domestique en Croatie n'est pas prise en charge. La Croatie exige la conformité à Fiskalizacija 2.0 pour la facturation B2B.",
|
|
148
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
149
|
+
"La fiscalisation FINA doit être activée pour les factures domestiques. Activez-la dans les paramètres de l'entité.",
|
|
144
150
|
} as const;
|
|
@@ -138,4 +138,10 @@ export default {
|
|
|
138
138
|
"Add tax clause...": "Dodajte poreznu klauzulu...",
|
|
139
139
|
Footer: "Podnožje",
|
|
140
140
|
"Add document footer...": "Dodajte podnožje dokumenta...",
|
|
141
|
+
// Croatian domestic invoice validation
|
|
142
|
+
"End consumer": "Krajnji potrošač",
|
|
143
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
144
|
+
"Domaće B2B fakturiranje u Hrvatskoj nije podržano. Hrvatska zahtijeva usklađenost s Fiskalizacijom 2.0 za B2B fakturiranje.",
|
|
145
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
146
|
+
"FINA fiskalizacija mora biti omogućena za domaće račune. Omogućite je u postavkama tvrtke.",
|
|
141
147
|
} as const;
|
|
@@ -141,4 +141,10 @@ export default {
|
|
|
141
141
|
"Add tax clause...": "Aggiungi clausola fiscale...",
|
|
142
142
|
Footer: "Piè di pagina",
|
|
143
143
|
"Add document footer...": "Aggiungi piè di pagina del documento...",
|
|
144
|
+
// Croatian domestic invoice validation
|
|
145
|
+
"End consumer": "Consumatore finale",
|
|
146
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
147
|
+
"La fatturazione B2B domestica in Croazia non è supportata. La Croazia richiede la conformità a Fiskalizacija 2.0 per la fatturazione B2B.",
|
|
148
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
149
|
+
"La fiscalizzazione FINA deve essere abilitata per le fatture domestiche. Abilitarla nelle impostazioni dell'entità.",
|
|
144
150
|
} as const;
|
|
@@ -140,4 +140,10 @@ export default {
|
|
|
140
140
|
"Add tax clause...": "Belastingclausule toevoegen...",
|
|
141
141
|
Footer: "Voettekst",
|
|
142
142
|
"Add document footer...": "Documentvoettekst toevoegen...",
|
|
143
|
+
// Croatian domestic invoice validation
|
|
144
|
+
"End consumer": "Eindconsument",
|
|
145
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
146
|
+
"Binnenlandse B2B-facturering in Kroatië wordt niet ondersteund. Kroatië vereist naleving van Fiskalizacija 2.0 voor B2B-facturering.",
|
|
147
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
148
|
+
"FINA-fiscalisering moet zijn ingeschakeld voor binnenlandse facturen. Schakel deze in via de bedrijfsinstellingen.",
|
|
143
149
|
} as const;
|
|
@@ -139,4 +139,10 @@ export default {
|
|
|
139
139
|
"Add tax clause...": "Dodaj klauzulę podatkową...",
|
|
140
140
|
Footer: "Stopka",
|
|
141
141
|
"Add document footer...": "Dodaj stopkę dokumentu...",
|
|
142
|
+
// Croatian domestic invoice validation
|
|
143
|
+
"End consumer": "Konsument końcowy",
|
|
144
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
145
|
+
"Krajowe fakturowanie B2B w Chorwacji nie jest obsługiwane. Chorwacja wymaga zgodności z Fiskalizacija 2.0 dla fakturowania B2B.",
|
|
146
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
147
|
+
"Fiskalizacja FINA musi być włączona dla faktur krajowych. Włącz ją w ustawieniach podmiotu.",
|
|
142
148
|
} as const;
|
|
@@ -140,4 +140,10 @@ export default {
|
|
|
140
140
|
"Add tax clause...": "Adicionar cláusula fiscal...",
|
|
141
141
|
Footer: "Rodapé",
|
|
142
142
|
"Add document footer...": "Adicionar rodapé do documento...",
|
|
143
|
+
// Croatian domestic invoice validation
|
|
144
|
+
"End consumer": "Consumidor final",
|
|
145
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
146
|
+
"A faturação B2B doméstica na Croácia não é suportada. A Croácia exige conformidade com Fiskalizacija 2.0 para faturação B2B.",
|
|
147
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
148
|
+
"A fiscalização FINA deve estar ativada para faturas domésticas. Ative-a nas configurações da entidade.",
|
|
143
149
|
} as const;
|
|
@@ -149,4 +149,10 @@ export default {
|
|
|
149
149
|
"Add tax clause...": "Dodajte davčno klavzulo...",
|
|
150
150
|
Footer: "Noga dokumenta",
|
|
151
151
|
"Add document footer...": "Dodajte nogo dokumenta...",
|
|
152
|
+
// Croatian domestic invoice validation
|
|
153
|
+
"End consumer": "Končni potrošnik",
|
|
154
|
+
"Domestic B2B invoicing in Croatia is not supported":
|
|
155
|
+
"Domače B2B fakturiranje na Hrvaškem ni podprto. Hrvška zahteva skladnost s Fiskalizacijo 2.0 za B2B fakturiranje.",
|
|
156
|
+
"FINA fiscalization must be enabled for domestic invoices":
|
|
157
|
+
"FINA fiskalizacija mora biti omogočena za domače račune. Omogočite jo v nastavitvah podjetja.",
|
|
152
158
|
} as const;
|
|
@@ -66,7 +66,7 @@ export function useNextInvoiceNumber(
|
|
|
66
66
|
return response as NextInvoiceNumberResponse;
|
|
67
67
|
},
|
|
68
68
|
enabled: options?.enabled !== false && !!entityId && !!sdk?.documents,
|
|
69
|
-
staleTime:
|
|
69
|
+
staleTime: 0, // Always refetch when form opens or params change
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/ui/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Progress({
|
|
6
|
+
value,
|
|
7
|
+
className,
|
|
8
|
+
...props
|
|
9
|
+
}: ProgressPrimitive.Root.Props & { className?: string }) {
|
|
10
|
+
return (
|
|
11
|
+
<ProgressPrimitive.Root
|
|
12
|
+
data-slot="progress"
|
|
13
|
+
value={value}
|
|
14
|
+
className={cn("relative w-full overflow-hidden rounded-full bg-primary/20", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
<ProgressPrimitive.Track>
|
|
18
|
+
<ProgressPrimitive.Indicator
|
|
19
|
+
className="h-full bg-primary transition-all duration-500 ease-in-out"
|
|
20
|
+
style={{ width: `${value ?? 0}%` }}
|
|
21
|
+
/>
|
|
22
|
+
</ProgressPrimitive.Track>
|
|
23
|
+
</ProgressPrimitive.Root>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { Progress }
|
package/src/generate-schemas.ts
CHANGED
|
@@ -26,9 +26,21 @@ async function main() {
|
|
|
26
26
|
await fs.mkdir(SCHEMAS_DIR, { recursive: true });
|
|
27
27
|
await fs.mkdir(GENERATED_DIR, { recursive: true });
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// Fetch OpenAPI spec from running API and generate schemas
|
|
30
|
+
const API_URL = "http://localhost:3000/openapi.json";
|
|
31
|
+
const openApiPath = path.resolve(GENERATED_DIR, "openapi.json");
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch(API_URL);
|
|
35
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
36
|
+
await fs.writeFile(openApiPath, await res.text());
|
|
37
|
+
console.log(`Fetched OpenAPI spec from ${API_URL}`);
|
|
38
|
+
} catch (_error) {
|
|
39
|
+
console.error(`Failed to fetch OpenAPI spec from ${API_URL}. Is the API running?`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
try {
|
|
31
|
-
const openApiPath = path.resolve(__dirname, "../../../apps/api/openapi.json");
|
|
32
44
|
execSync(
|
|
33
45
|
`bunx openapi-zod-client ${openApiPath} ` +
|
|
34
46
|
"--output " +
|
|
@@ -299,6 +311,7 @@ export { createInvoiceSchema as createCreditNoteSchema, type CreateInvoiceSchema
|
|
|
299
311
|
|
|
300
312
|
// Clean up temporary files
|
|
301
313
|
await fs.unlink(`${GENERATED_DIR}/schemas.ts`);
|
|
314
|
+
await fs.unlink(`${GENERATED_DIR}/openapi.json`);
|
|
302
315
|
}
|
|
303
316
|
|
|
304
317
|
main().catch(console.error);
|
|
@@ -97,6 +97,8 @@ const createAdvanceInvoiceSchemaDefinition = z.object({
|
|
|
97
97
|
note: z.union([z.string(), z.null()]).optional(),
|
|
98
98
|
tax_clause: z.union([z.string(), z.null()]).optional(),
|
|
99
99
|
footer: z.union([z.string(), z.null()]).optional(),
|
|
100
|
+
signature: z.union([z.string(), z.null()]).optional(),
|
|
101
|
+
reference: z.union([z.string(), z.null()]).optional(),
|
|
100
102
|
currency_code: z.string().max(3).optional(),
|
|
101
103
|
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
102
104
|
date_due: z.union([z.string(), z.null()]).optional(),
|
|
@@ -111,6 +111,8 @@ const updateCreditNoteSchemaDefinition = z
|
|
|
111
111
|
.min(1),
|
|
112
112
|
note: z.union([z.string(), z.null()]),
|
|
113
113
|
footer: z.union([z.string(), z.null()]),
|
|
114
|
+
signature: z.union([z.string(), z.null()]),
|
|
115
|
+
reference: z.union([z.string(), z.null()]),
|
|
114
116
|
payment_terms: z.union([z.string(), z.null()]),
|
|
115
117
|
currency_code: z.string(),
|
|
116
118
|
metadata: z.union([z.object({}).partial().passthrough(), z.null()]),
|
|
@@ -22,6 +22,7 @@ const createCustomerSchemaDefinition = z.object({
|
|
|
22
22
|
company_number: z.union([z.string(), z.null()]).optional(),
|
|
23
23
|
email: z.union([z.string(), z.null()]).optional(),
|
|
24
24
|
is_tax_subject: z.boolean().optional(),
|
|
25
|
+
is_end_consumer: z.union([z.boolean(), z.null()]).optional(),
|
|
25
26
|
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
26
27
|
});
|
|
27
28
|
|
|
@@ -44,6 +45,7 @@ const updateCustomerSchemaDefinition = z
|
|
|
44
45
|
company_number: z.union([z.string(), z.null()]),
|
|
45
46
|
email: z.union([z.string(), z.null()]),
|
|
46
47
|
is_tax_subject: z.boolean(),
|
|
48
|
+
is_end_consumer: z.union([z.boolean(), z.null()]),
|
|
47
49
|
metadata: z.union([z.record(z.string(), z.any()), z.null()]),
|
|
48
50
|
})
|
|
49
51
|
.partial();
|
|
@@ -85,6 +85,8 @@ const createDeliveryNoteSchemaDefinition = z.object({
|
|
|
85
85
|
payment_terms: z.union([z.string(), z.null()]).optional(),
|
|
86
86
|
tax_clause: z.union([z.string(), z.null()]).optional(),
|
|
87
87
|
footer: z.union([z.string(), z.null()]).optional(),
|
|
88
|
+
signature: z.union([z.string(), z.null()]).optional(),
|
|
89
|
+
reference: z.union([z.string(), z.null()]).optional(),
|
|
88
90
|
currency_code: z.string().max(3).optional(),
|
|
89
91
|
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
90
92
|
hide_prices: z.boolean().optional(),
|
|
@@ -70,6 +70,7 @@ const createEntitySchemaDefinition = z.object({
|
|
|
70
70
|
default_credit_note_note: z.union([z.string(), z.null()]),
|
|
71
71
|
default_credit_note_payment_terms: z.union([z.string(), z.null()]),
|
|
72
72
|
document_footer: z.union([z.string(), z.null()]),
|
|
73
|
+
default_document_signature: z.union([z.string(), z.null()]),
|
|
73
74
|
furs: z.union([
|
|
74
75
|
z
|
|
75
76
|
.object({
|
|
@@ -78,6 +79,7 @@ const createEntitySchemaDefinition = z.object({
|
|
|
78
79
|
operator_tax_number: z.string(),
|
|
79
80
|
operator_label: z.string(),
|
|
80
81
|
foreign_operator: z.boolean(),
|
|
82
|
+
environment: z.enum(["test", "production"]),
|
|
81
83
|
})
|
|
82
84
|
.partial()
|
|
83
85
|
.passthrough(),
|
|
@@ -91,6 +93,7 @@ const createEntitySchemaDefinition = z.object({
|
|
|
91
93
|
operator_label: z.string(),
|
|
92
94
|
u_sust_pdv: z.boolean().default(true),
|
|
93
95
|
numbering_sequence: z.enum(["N", "P"]).default("P"),
|
|
96
|
+
unified_numbering: z.union([z.boolean(), z.null()]),
|
|
94
97
|
certificate_expiry: z.string(),
|
|
95
98
|
})
|
|
96
99
|
.partial()
|
|
@@ -144,6 +147,8 @@ const createEntitySchemaDefinition = z.object({
|
|
|
144
147
|
domestic: z.union([z.string(), z.null()]),
|
|
145
148
|
intra_eu_b2b: z.union([z.string(), z.null()]),
|
|
146
149
|
intra_eu_b2c: z.union([z.string(), z.null()]),
|
|
150
|
+
"3w_b2b": z.union([z.string(), z.null()]),
|
|
151
|
+
"3w_b2c": z.union([z.string(), z.null()]),
|
|
147
152
|
export: z.union([z.string(), z.null()]),
|
|
148
153
|
})
|
|
149
154
|
.partial()
|
|
@@ -221,6 +226,7 @@ const patchEntitySchemaDefinition = z
|
|
|
221
226
|
default_credit_note_note: z.union([z.string(), z.null()]),
|
|
222
227
|
default_credit_note_payment_terms: z.union([z.string(), z.null()]),
|
|
223
228
|
document_footer: z.union([z.string(), z.null()]),
|
|
229
|
+
default_document_signature: z.union([z.string(), z.null()]),
|
|
224
230
|
furs: z.union([
|
|
225
231
|
z
|
|
226
232
|
.object({
|
|
@@ -229,6 +235,7 @@ const patchEntitySchemaDefinition = z
|
|
|
229
235
|
operator_tax_number: z.string(),
|
|
230
236
|
operator_label: z.string(),
|
|
231
237
|
foreign_operator: z.boolean(),
|
|
238
|
+
environment: z.enum(["test", "production"]),
|
|
232
239
|
})
|
|
233
240
|
.partial()
|
|
234
241
|
.passthrough(),
|
|
@@ -242,6 +249,7 @@ const patchEntitySchemaDefinition = z
|
|
|
242
249
|
operator_label: z.string(),
|
|
243
250
|
u_sust_pdv: z.boolean().default(true),
|
|
244
251
|
numbering_sequence: z.enum(["N", "P"]).default("P"),
|
|
252
|
+
unified_numbering: z.union([z.boolean(), z.null()]),
|
|
245
253
|
certificate_expiry: z.string(),
|
|
246
254
|
})
|
|
247
255
|
.partial()
|
|
@@ -295,6 +303,8 @@ const patchEntitySchemaDefinition = z
|
|
|
295
303
|
domestic: z.union([z.string(), z.null()]),
|
|
296
304
|
intra_eu_b2b: z.union([z.string(), z.null()]),
|
|
297
305
|
intra_eu_b2c: z.union([z.string(), z.null()]),
|
|
306
|
+
"3w_b2b": z.union([z.string(), z.null()]),
|
|
307
|
+
"3w_b2c": z.union([z.string(), z.null()]),
|
|
298
308
|
export: z.union([z.string(), z.null()]),
|
|
299
309
|
})
|
|
300
310
|
.partial()
|
|
@@ -87,6 +87,8 @@ const createEstimateSchemaDefinition = z.object({
|
|
|
87
87
|
payment_terms: z.union([z.string(), z.null()]).optional(),
|
|
88
88
|
tax_clause: z.union([z.string(), z.null()]).optional(),
|
|
89
89
|
footer: z.union([z.string(), z.null()]).optional(),
|
|
90
|
+
signature: z.union([z.string(), z.null()]).optional(),
|
|
91
|
+
reference: z.union([z.string(), z.null()]).optional(),
|
|
90
92
|
currency_code: z.string().max(3).optional(),
|
|
91
93
|
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
92
94
|
date_valid_till: z.union([z.string(), z.null()]).optional(),
|
|
@@ -11,11 +11,12 @@ import { z } from 'zod';
|
|
|
11
11
|
// Schema for update finasettings operation
|
|
12
12
|
const updateFinaSettingsSchemaDefinition = z
|
|
13
13
|
.object({
|
|
14
|
-
enabled: z.boolean(),
|
|
14
|
+
enabled: z.boolean().default(false),
|
|
15
15
|
operator_oib: z.string().min(11).max(11),
|
|
16
16
|
operator_label: z.string(),
|
|
17
|
-
u_sust_pdv: z.boolean(),
|
|
18
|
-
numbering_sequence: z.enum(["N", "P"]),
|
|
17
|
+
u_sust_pdv: z.boolean().default(true),
|
|
18
|
+
numbering_sequence: z.enum(["N", "P"]).default("P"),
|
|
19
|
+
unified_numbering: z.union([z.boolean(), z.null()]),
|
|
19
20
|
})
|
|
20
21
|
.partial();
|
|
21
22
|
|
|
@@ -98,6 +98,8 @@ const createInvoiceSchemaDefinition = z.object({
|
|
|
98
98
|
payment_terms: z.union([z.string(), z.null()]).optional(),
|
|
99
99
|
tax_clause: z.union([z.string(), z.null()]).optional(),
|
|
100
100
|
footer: z.union([z.string(), z.null()]).optional(),
|
|
101
|
+
signature: z.union([z.string(), z.null()]).optional(),
|
|
102
|
+
reference: z.union([z.string(), z.null()]).optional(),
|
|
101
103
|
currency_code: z.string().max(3).optional(),
|
|
102
104
|
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
103
105
|
date_due: z.union([z.string(), z.null()]).optional(),
|