@spaceinvoices/react-ui 0.4.0 → 0.4.2
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/registry.json +23 -27
- package/src/components/advance-invoices/advance-invoices.hooks.ts +32 -2
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +109 -4
- 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 +2 -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 +2 -0
- package/src/components/advance-invoices/create/locales/pt.ts +2 -0
- package/src/components/advance-invoices/create/locales/sl.ts +2 -0
- package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +17 -0
- package/src/components/advance-invoices/list/list-row-actions.tsx +3 -6
- package/src/components/advance-invoices/list/list-table.tsx +45 -2
- package/src/components/advance-invoices/list/locales/de.ts +3 -0
- package/src/components/advance-invoices/list/locales/en.ts +3 -0
- package/src/components/advance-invoices/list/locales/es.ts +3 -0
- package/src/components/advance-invoices/list/locales/fr.ts +3 -0
- package/src/components/advance-invoices/list/locales/hr.ts +3 -0
- package/src/components/advance-invoices/list/locales/it.ts +3 -0
- package/src/components/advance-invoices/list/locales/nl.ts +3 -0
- package/src/components/advance-invoices/list/locales/pl.ts +3 -0
- package/src/components/advance-invoices/list/locales/pt.ts +3 -0
- package/src/components/advance-invoices/list/locales/sl.ts +3 -0
- package/src/components/credit-notes/create/create-credit-note-form.tsx +177 -6
- package/src/components/credit-notes/create/locales/de.ts +8 -0
- package/src/components/credit-notes/create/locales/es.ts +8 -0
- package/src/components/credit-notes/create/locales/fr.ts +7 -0
- package/src/components/credit-notes/create/locales/hr.ts +7 -0
- package/src/components/credit-notes/create/locales/it.ts +9 -0
- package/src/components/credit-notes/create/locales/nl.ts +7 -0
- package/src/components/credit-notes/create/locales/pl.ts +7 -0
- package/src/components/credit-notes/create/locales/pt.ts +7 -0
- package/src/components/credit-notes/create/locales/sl.ts +7 -0
- package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
- package/src/components/credit-notes/list/list-row-actions.tsx +3 -6
- package/src/components/credit-notes/list/list-table.tsx +45 -2
- package/src/components/credit-notes/list/locales/de.ts +3 -0
- package/src/components/credit-notes/list/locales/en.ts +3 -0
- package/src/components/credit-notes/list/locales/es.ts +3 -0
- package/src/components/credit-notes/list/locales/fr.ts +3 -0
- package/src/components/credit-notes/list/locales/hr.ts +3 -0
- package/src/components/credit-notes/list/locales/it.ts +3 -0
- package/src/components/credit-notes/list/locales/nl.ts +3 -0
- package/src/components/credit-notes/list/locales/pl.ts +3 -0
- package/src/components/credit-notes/list/locales/pt.ts +3 -0
- package/src/components/credit-notes/list/locales/sl.ts +3 -0
- package/src/components/customers/create-customer-form/create-customer-form.tsx +0 -1
- package/src/components/documents/create/document-details-section.tsx +67 -1
- package/src/components/documents/create/mark-as-paid-section.tsx +11 -2
- package/src/components/documents/view/document-actions-bar.tsx +30 -0
- package/src/components/documents/view/locales/de.ts +5 -0
- package/src/components/documents/view/locales/es.ts +5 -0
- package/src/components/documents/view/locales/fr.ts +5 -0
- package/src/components/documents/view/locales/hr.ts +5 -0
- package/src/components/documents/view/locales/it.ts +5 -0
- package/src/components/documents/view/locales/nl.ts +5 -0
- package/src/components/documents/view/locales/pl.ts +5 -0
- package/src/components/documents/view/locales/pt.ts +5 -0
- package/src/components/documents/view/locales/sl.ts +5 -0
- package/src/components/entities/create-entity-form.tsx +1 -1
- package/src/components/entities/entity-settings-form/entity-settings-form.tsx +2 -3
- package/src/components/entities/entity-settings-form/locales/es.ts +2 -0
- package/src/components/entities/entity-settings-form/locales/fr.ts +2 -0
- package/src/components/entities/entity-settings-form/locales/hr.ts +2 -0
- package/src/components/entities/entity-settings-form/locales/it.ts +2 -0
- package/src/components/entities/entity-settings-form/locales/nl.ts +2 -0
- package/src/components/entities/entity-settings-form/locales/pl.ts +2 -0
- package/src/components/entities/entity-settings-form/locales/pt.ts +2 -0
- package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +109 -0
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +365 -35
- package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +101 -20
- package/src/components/entities/fina-settings-form/index.ts +1 -0
- package/src/components/entities/fina-settings-form/locales/de.ts +54 -34
- package/src/components/entities/fina-settings-form/locales/en.ts +51 -34
- package/src/components/entities/fina-settings-form/locales/es.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/fr.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/hr.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/it.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/nl.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/pl.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/pt.ts +50 -34
- package/src/components/entities/fina-settings-form/locales/sl.ts +50 -34
- package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +18 -0
- package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +64 -89
- package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +51 -323
- package/src/components/entities/furs-settings-form/furs-operator-required-dialog.tsx +106 -0
- package/src/components/entities/furs-settings-form/furs-settings-form.tsx +24 -10
- package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +5 -9
- package/src/components/entities/furs-settings-form/index.ts +1 -0
- package/src/components/entities/furs-settings-form/locales/de.ts +27 -3
- package/src/components/entities/furs-settings-form/locales/en.ts +17 -3
- package/src/components/entities/furs-settings-form/locales/es.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/fr.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/hr.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/it.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/nl.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/pl.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/pt.ts +26 -3
- package/src/components/entities/furs-settings-form/locales/sl.ts +16 -3
- package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +22 -0
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
- package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +1 -0
- package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +58 -34
- package/src/components/entities/settings/tax-rules-settings-form.tsx +4 -4
- package/src/components/estimates/list/list-row-actions.tsx +3 -7
- package/src/components/estimates/list/list-table.tsx +35 -2
- package/src/components/estimates/list/locales/de.ts +3 -0
- package/src/components/estimates/list/locales/en.ts +3 -0
- package/src/components/estimates/list/locales/es.ts +3 -0
- package/src/components/estimates/list/locales/fr.ts +3 -0
- package/src/components/estimates/list/locales/hr.ts +3 -0
- package/src/components/estimates/list/locales/it.ts +3 -0
- package/src/components/estimates/list/locales/nl.ts +3 -0
- package/src/components/estimates/list/locales/pl.ts +3 -0
- package/src/components/estimates/list/locales/pt.ts +3 -0
- package/src/components/estimates/list/locales/sl.ts +3 -0
- package/src/components/export/document-export-form.tsx +34 -34
- package/src/components/invoices/create/create-invoice-form.tsx +107 -5
- package/src/components/invoices/create/prepare-invoice-submission.ts +17 -0
- package/src/components/invoices/invoices.hooks.ts +32 -2
- package/src/components/invoices/list/list-row-actions.tsx +23 -8
- package/src/components/invoices/list/list-table.tsx +53 -2
- package/src/components/invoices/list/locales/de.ts +4 -0
- package/src/components/invoices/list/locales/en.ts +4 -0
- package/src/components/invoices/list/locales/es.ts +4 -0
- package/src/components/invoices/list/locales/fr.ts +4 -0
- package/src/components/invoices/list/locales/hr.ts +4 -0
- package/src/components/invoices/list/locales/it.ts +4 -0
- package/src/components/invoices/list/locales/nl.ts +4 -0
- package/src/components/invoices/list/locales/pl.ts +4 -0
- package/src/components/invoices/list/locales/pt.ts +4 -0
- package/src/components/invoices/list/locales/sl.ts +4 -0
- package/src/components/invoices/view/fiscalization-status-card.tsx +4 -1
- package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -7
- package/src/components/items/item-list-table/item-list-row.tsx +3 -2
- package/src/components/items/item-list-table/item-list-table.tsx +5 -1
- package/src/components/recurring-invoices/create-recurring-invoice-form/create-recurring-invoice-form.tsx +418 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/de.ts +45 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/es.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/fr.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/hr.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/it.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/nl.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pl.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pt.ts +44 -0
- package/src/components/recurring-invoices/create-recurring-invoice-form/locales/sl.ts +44 -0
- package/src/components/recurring-invoices/index.ts +3 -0
- package/src/components/recurring-invoices/list/index.ts +2 -0
- package/src/components/recurring-invoices/list/list-row-actions.tsx +139 -0
- package/src/components/recurring-invoices/list/list-table.tsx +179 -0
- package/src/components/recurring-invoices/list/locales/de.ts +27 -0
- package/src/components/recurring-invoices/list/locales/en.ts +5 -0
- package/src/components/recurring-invoices/list/locales/es.ts +27 -0
- package/src/components/recurring-invoices/list/locales/fr.ts +27 -0
- package/src/components/recurring-invoices/list/locales/hr.ts +27 -0
- package/src/components/recurring-invoices/list/locales/it.ts +27 -0
- package/src/components/recurring-invoices/list/locales/nl.ts +27 -0
- package/src/components/recurring-invoices/list/locales/pl.ts +27 -0
- package/src/components/recurring-invoices/list/locales/pt.ts +27 -0
- package/src/components/recurring-invoices/list/locales/sl.ts +27 -0
- package/src/components/recurring-invoices/recurring-invoices.hooks.ts +28 -0
- package/src/components/table/data-table.tsx +122 -5
- package/src/components/table/selection-toolbar.tsx +36 -0
- package/src/components/tax-reports/kir-export-form.tsx +75 -55
- package/src/components/taxes/tax-list-table/tax-list-row-actions.tsx +3 -6
- package/src/components/taxes/tax-list-table/tax-list-row.tsx +3 -2
- package/src/components/taxes/tax-list-table/tax-list-table.tsx +5 -1
- package/src/components/ui/checkbox.tsx +5 -5
- package/src/generate-schemas.ts +46 -7
- package/src/generated/schemas/advanceinvoice.ts +79 -187
- package/src/generated/schemas/authorizeshopify_body.ts +22 -0
- package/src/generated/schemas/creditnote.ts +60 -88
- package/src/generated/schemas/customadvanceinvoice.ts +70 -97
- package/src/generated/schemas/customcreditnote.ts +70 -97
- package/src/generated/schemas/customestimate.ts +68 -97
- package/src/generated/schemas/custominvoice.ts +70 -97
- package/src/generated/schemas/entity.ts +1 -1
- package/src/generated/schemas/estimate.ts +67 -172
- package/src/generated/schemas/index.ts +39 -28
- package/src/generated/schemas/invoice.ts +79 -187
- package/src/generated/schemas/order.ts +127 -0
- package/src/generated/schemas/orderintegration.ts +51 -0
- package/src/generated/schemas/payment.ts +2 -0
- package/src/generated/schemas/recurringinvoice.ts +61 -0
- package/src/generated/schemas/registerfursrealestatepremise_body.ts +11 -7
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +140 -269
- package/src/generated/schemas/rendercreditnotepreview_body.ts +141 -270
- package/src/generated/schemas/renderestimatepreview_body.ts +112 -212
- package/src/generated/schemas/renderinvoicepreview_body.ts +141 -270
- package/src/generated/schemas/webhook.ts +42 -0
- package/src/lib/furs-error-utils.ts +36 -0
- package/src/lib/schemas/advance-invoice.ts +3 -3
- package/src/lib/schemas/credit-note.ts +3 -3
- package/src/lib/schemas/estimate.ts +3 -3
- package/src/lib/schemas/invoice.ts +3 -3
- package/src/providers/white-label-provider.tsx +3 -0
|
@@ -38,4 +38,7 @@ export default {
|
|
|
38
38
|
"No results found": "Nenhuma nota de credito encontrada",
|
|
39
39
|
"Try adjusting your search criteria": "Tente ajustar os seus criterios de pesquisa",
|
|
40
40
|
"Clear search": "Limpar pesquisa",
|
|
41
|
+
selected: "selecionados",
|
|
42
|
+
"Export PDFs": "Exportar PDFs",
|
|
43
|
+
"Deselect all": "Desselecionar tudo",
|
|
41
44
|
} as const;
|
|
@@ -38,4 +38,7 @@ export default {
|
|
|
38
38
|
"No results found": "Ni najdenih dobropisov",
|
|
39
39
|
"Try adjusting your search criteria": "Poskusite prilagoditi iskalne kriterije",
|
|
40
40
|
"Clear search": "Počisti iskanje",
|
|
41
|
+
selected: "izbranih",
|
|
42
|
+
"Export PDFs": "Izvozi PDF-je",
|
|
43
|
+
"Deselect all": "Počisti izbiro",
|
|
41
44
|
} as const;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
2
2
|
import type { CompanyRegistryResult, CreateCustomerRequest, Customer } from "@spaceinvoices/js-sdk";
|
|
3
3
|
import { useForm } from "react-hook-form";
|
|
4
|
-
import type z from "zod";
|
|
5
4
|
import { CompanyRegistryAutocomplete } from "@/ui/components/company-registry";
|
|
6
5
|
import { FormInput } from "@/ui/components/form";
|
|
7
6
|
import { Form } from "@/ui/components/ui/form";
|
|
@@ -39,6 +39,25 @@ type FursInlineProps = {
|
|
|
39
39
|
isSkipped?: boolean;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
type FinaPremise = {
|
|
43
|
+
id: string;
|
|
44
|
+
premise_id: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type FinaDevice = {
|
|
48
|
+
id: string;
|
|
49
|
+
device_id: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type FinaInlineProps = {
|
|
53
|
+
premises: FinaPremise[];
|
|
54
|
+
devices: FinaDevice[];
|
|
55
|
+
selectedPremise?: string;
|
|
56
|
+
selectedDevice?: string;
|
|
57
|
+
onPremiseChange: (value: string | undefined) => void;
|
|
58
|
+
onDeviceChange: (value: string | undefined) => void;
|
|
59
|
+
};
|
|
60
|
+
|
|
42
61
|
type ServiceDateProps = {
|
|
43
62
|
dateType: "single" | "range";
|
|
44
63
|
onDateTypeChange: (type: "single" | "range") => void;
|
|
@@ -50,6 +69,7 @@ type DocumentDetailsSectionProps = {
|
|
|
50
69
|
t: (key: string) => string;
|
|
51
70
|
children?: React.ReactNode; // For document-specific additions (e.g., mark as paid for invoices)
|
|
52
71
|
fursInline?: FursInlineProps; // FURS premise/device inline with number
|
|
72
|
+
finaInline?: FinaInlineProps; // FINA premise/device inline with number
|
|
53
73
|
serviceDate?: ServiceDateProps; // Service date section (invoice only)
|
|
54
74
|
};
|
|
55
75
|
|
|
@@ -59,6 +79,7 @@ export function DocumentDetailsSection({
|
|
|
59
79
|
t,
|
|
60
80
|
children,
|
|
61
81
|
fursInline,
|
|
82
|
+
finaInline,
|
|
62
83
|
serviceDate,
|
|
63
84
|
}: DocumentDetailsSectionProps) {
|
|
64
85
|
// Determine the date field name based on document type
|
|
@@ -67,8 +88,9 @@ export function DocumentDetailsSection({
|
|
|
67
88
|
const dateFieldLabel =
|
|
68
89
|
documentType === "invoice" || documentType === "advance_invoice" ? t("Due Date") : t("Valid Until");
|
|
69
90
|
|
|
70
|
-
// Check if FURS inline should show premise/device selects
|
|
91
|
+
// Check if FURS/FINA inline should show premise/device selects
|
|
71
92
|
const showFursSelects = fursInline && !fursInline.isSkipped;
|
|
93
|
+
const showFinaSelects = !!finaInline;
|
|
72
94
|
|
|
73
95
|
return (
|
|
74
96
|
<div className="flex-1 space-y-4">
|
|
@@ -125,6 +147,50 @@ export function DocumentDetailsSection({
|
|
|
125
147
|
</TooltipContent>
|
|
126
148
|
</Tooltip>
|
|
127
149
|
</div>
|
|
150
|
+
) : showFinaSelects ? (
|
|
151
|
+
<div className="flex gap-2">
|
|
152
|
+
<Select
|
|
153
|
+
value={finaInline.selectedPremise || ""}
|
|
154
|
+
onValueChange={(v) => finaInline.onPremiseChange(v ?? undefined)}
|
|
155
|
+
>
|
|
156
|
+
<SelectTrigger className="w-24">
|
|
157
|
+
<SelectValue placeholder={t("Premise")} />
|
|
158
|
+
</SelectTrigger>
|
|
159
|
+
<SelectContent>
|
|
160
|
+
{finaInline.premises.map((premise) => (
|
|
161
|
+
<SelectItem key={premise.id} value={premise.premise_id}>
|
|
162
|
+
{premise.premise_id}
|
|
163
|
+
</SelectItem>
|
|
164
|
+
))}
|
|
165
|
+
</SelectContent>
|
|
166
|
+
</Select>
|
|
167
|
+
<Select
|
|
168
|
+
value={finaInline.selectedDevice || ""}
|
|
169
|
+
onValueChange={(v) => finaInline.onDeviceChange(v ?? undefined)}
|
|
170
|
+
disabled={!finaInline.selectedPremise || finaInline.devices.length === 0}
|
|
171
|
+
>
|
|
172
|
+
<SelectTrigger className="w-24">
|
|
173
|
+
<SelectValue placeholder={t("Device")} />
|
|
174
|
+
</SelectTrigger>
|
|
175
|
+
<SelectContent>
|
|
176
|
+
{finaInline.devices.map((device) => (
|
|
177
|
+
<SelectItem key={device.id} value={device.device_id}>
|
|
178
|
+
{device.device_id}
|
|
179
|
+
</SelectItem>
|
|
180
|
+
))}
|
|
181
|
+
</SelectContent>
|
|
182
|
+
</Select>
|
|
183
|
+
<Tooltip>
|
|
184
|
+
<TooltipTrigger asChild>
|
|
185
|
+
<FormControl>
|
|
186
|
+
<Input {...field} disabled className="flex-1" />
|
|
187
|
+
</FormControl>
|
|
188
|
+
</TooltipTrigger>
|
|
189
|
+
<TooltipContent>
|
|
190
|
+
<p>{t("Number format can be changed in settings")}</p>
|
|
191
|
+
</TooltipContent>
|
|
192
|
+
</Tooltip>
|
|
193
|
+
</div>
|
|
128
194
|
) : (
|
|
129
195
|
<Tooltip>
|
|
130
196
|
<TooltipTrigger asChild>
|
|
@@ -29,6 +29,8 @@ type MarkAsPaidSectionProps = {
|
|
|
29
29
|
onPaymentTypesChange: (values: string[]) => void;
|
|
30
30
|
/** Translation function */
|
|
31
31
|
t: (key: string) => string;
|
|
32
|
+
/** Always show payment type selector (e.g. for FINA fiscalization) */
|
|
33
|
+
alwaysShowPaymentType?: boolean;
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
export function MarkAsPaidSection({
|
|
@@ -37,9 +39,12 @@ export function MarkAsPaidSection({
|
|
|
37
39
|
paymentTypes,
|
|
38
40
|
onPaymentTypesChange,
|
|
39
41
|
t,
|
|
42
|
+
alwaysShowPaymentType,
|
|
40
43
|
}: MarkAsPaidSectionProps) {
|
|
44
|
+
const showPaymentTypes = checked || alwaysShowPaymentType;
|
|
45
|
+
|
|
41
46
|
return (
|
|
42
|
-
<div className={cn("flex flex-col gap-4 rounded-md border p-4",
|
|
47
|
+
<div className={cn("flex flex-col gap-4 rounded-md border p-4", showPaymentTypes && "gap-3")}>
|
|
43
48
|
<div className="flex flex-row items-center space-x-3 space-y-0">
|
|
44
49
|
<Checkbox checked={checked} onCheckedChange={(v) => onCheckedChange(v === true)} />
|
|
45
50
|
<div className="flex items-center gap-1 leading-none">
|
|
@@ -61,9 +66,13 @@ export function MarkAsPaidSection({
|
|
|
61
66
|
</div>
|
|
62
67
|
</div>
|
|
63
68
|
|
|
64
|
-
{
|
|
69
|
+
{showPaymentTypes && (
|
|
65
70
|
<div className="flex flex-col gap-2">
|
|
71
|
+
{alwaysShowPaymentType && !checked && (
|
|
72
|
+
<Label className="text-muted-foreground text-xs">{t("Payment Type")}</Label>
|
|
73
|
+
)}
|
|
66
74
|
{paymentTypes.map((type, index) => (
|
|
75
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: payment types list uses index key
|
|
67
76
|
<div key={index} className="flex items-center gap-2">
|
|
68
77
|
<Select
|
|
69
78
|
value={type}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AdvanceInvoice, CreditNote, Estimate, Invoice } from "@spaceinvoices/js-sdk";
|
|
2
2
|
import {
|
|
3
|
+
Ban,
|
|
3
4
|
Check,
|
|
4
5
|
CheckCircle,
|
|
5
6
|
ChevronDown,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
Mail,
|
|
12
13
|
Pencil,
|
|
13
14
|
Plus,
|
|
15
|
+
RefreshCw,
|
|
14
16
|
Share2,
|
|
15
17
|
Trash2,
|
|
16
18
|
} from "lucide-react";
|
|
@@ -88,6 +90,14 @@ interface DocumentActionsBarProps extends ComponentTranslationProps {
|
|
|
88
90
|
onDeleteDraft?: () => void;
|
|
89
91
|
/** Whether draft deletion is in progress */
|
|
90
92
|
isDeletingDraft?: boolean;
|
|
93
|
+
/** Called when user wants to create a recurring schedule from this document */
|
|
94
|
+
onCreateRecurring?: () => void;
|
|
95
|
+
/** Custom label for recurring button (e.g. "Edit Recurring" when one already exists) */
|
|
96
|
+
recurringLabel?: string;
|
|
97
|
+
/** Called when user wants to void the document */
|
|
98
|
+
onVoid?: () => void;
|
|
99
|
+
/** Whether voiding is in progress */
|
|
100
|
+
isVoiding?: boolean;
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
function getApiLocale(uiLanguage: string): string {
|
|
@@ -128,6 +138,10 @@ export function DocumentActionsBar({
|
|
|
128
138
|
isFinalizing,
|
|
129
139
|
onDeleteDraft,
|
|
130
140
|
isDeletingDraft,
|
|
141
|
+
onCreateRecurring,
|
|
142
|
+
recurringLabel,
|
|
143
|
+
onVoid,
|
|
144
|
+
isVoiding,
|
|
131
145
|
...i18nProps
|
|
132
146
|
}: DocumentActionsBarProps) {
|
|
133
147
|
const t = createTranslation({ translations, locale: currentLocale, ...i18nProps });
|
|
@@ -298,6 +312,14 @@ export function DocumentActionsBar({
|
|
|
298
312
|
</Button>
|
|
299
313
|
) : null}
|
|
300
314
|
|
|
315
|
+
{/* Recurring */}
|
|
316
|
+
{onCreateRecurring && (
|
|
317
|
+
<Button variant="outline" size="sm" onClick={onCreateRecurring} className="cursor-pointer">
|
|
318
|
+
<RefreshCw className="mr-2 h-4 w-4" />
|
|
319
|
+
{recurringLabel || t("Recurring")}
|
|
320
|
+
</Button>
|
|
321
|
+
)}
|
|
322
|
+
|
|
301
323
|
{/* Duplicate/Convert */}
|
|
302
324
|
{onDuplicate && (
|
|
303
325
|
<DropdownMenu>
|
|
@@ -318,6 +340,14 @@ export function DocumentActionsBar({
|
|
|
318
340
|
</DropdownMenu>
|
|
319
341
|
)}
|
|
320
342
|
|
|
343
|
+
{/* Void */}
|
|
344
|
+
{!isDraft && onVoid && (
|
|
345
|
+
<Button variant="destructive" size="sm" onClick={onVoid} disabled={isVoiding} className="cursor-pointer">
|
|
346
|
+
{isVoiding ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Ban className="mr-2 h-4 w-4" />}
|
|
347
|
+
{isVoiding ? t("Voiding...") : t("Void")}
|
|
348
|
+
</Button>
|
|
349
|
+
)}
|
|
350
|
+
|
|
321
351
|
{/* Draft Actions */}
|
|
322
352
|
{isDraft && onFinalize && (
|
|
323
353
|
<Button variant="default" size="sm" onClick={onFinalize} disabled={isFinalizing} className="cursor-pointer">
|
|
@@ -14,6 +14,7 @@ export default {
|
|
|
14
14
|
"Unshare failed": "Freigabe aufheben fehlgeschlagen",
|
|
15
15
|
"Download failed": "Download fehlgeschlagen",
|
|
16
16
|
"e-SLOG download failed": "e-SLOG Download fehlgeschlagen",
|
|
17
|
+
Recurring: "Wiederkehrend",
|
|
17
18
|
Duplicate: "Duplizieren",
|
|
18
19
|
"Duplicate invoice": "Rechnung duplizieren",
|
|
19
20
|
"Duplicate estimate": "Angebot duplizieren",
|
|
@@ -83,6 +84,10 @@ export default {
|
|
|
83
84
|
"Delete draft failed": "Entwurf löschen fehlgeschlagen",
|
|
84
85
|
Draft: "Entwurf",
|
|
85
86
|
|
|
87
|
+
// Void
|
|
88
|
+
Void: "Stornieren",
|
|
89
|
+
"Voiding...": "Stornierung...",
|
|
90
|
+
|
|
86
91
|
// Activity list
|
|
87
92
|
Activity: "Aktivität",
|
|
88
93
|
"No activity": "Keine Aktivität",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Error al dejar de compartir",
|
|
14
14
|
"Download failed": "Error en la descarga",
|
|
15
15
|
"e-SLOG download failed": "Error en la descarga e-SLOG",
|
|
16
|
+
Recurring: "Recurrente",
|
|
16
17
|
Duplicate: "Duplicar",
|
|
17
18
|
"Duplicate invoice": "Duplicar factura",
|
|
18
19
|
"Duplicate estimate": "Duplicar presupuesto",
|
|
@@ -81,6 +82,10 @@ export default {
|
|
|
81
82
|
"Delete draft failed": "Error al eliminar el borrador",
|
|
82
83
|
Draft: "Borrador",
|
|
83
84
|
|
|
85
|
+
// Void
|
|
86
|
+
Void: "Anular",
|
|
87
|
+
"Voiding...": "Anulando...",
|
|
88
|
+
|
|
84
89
|
// Activity list
|
|
85
90
|
Activity: "Actividad",
|
|
86
91
|
"No activity": "Sin actividad",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Échec de l'annulation du partage",
|
|
14
14
|
"Download failed": "Échec du téléchargement",
|
|
15
15
|
"e-SLOG download failed": "Échec du téléchargement e-SLOG",
|
|
16
|
+
Recurring: "Récurrent",
|
|
16
17
|
Duplicate: "Dupliquer",
|
|
17
18
|
"Duplicate invoice": "Dupliquer la facture",
|
|
18
19
|
"Duplicate estimate": "Dupliquer le devis",
|
|
@@ -81,6 +82,10 @@ export default {
|
|
|
81
82
|
"Delete draft failed": "Échec de la suppression du brouillon",
|
|
82
83
|
Draft: "Brouillon",
|
|
83
84
|
|
|
85
|
+
// Void
|
|
86
|
+
Void: "Annuler",
|
|
87
|
+
"Voiding...": "Annulation...",
|
|
88
|
+
|
|
84
89
|
// Activity list
|
|
85
90
|
Activity: "Activité",
|
|
86
91
|
"No activity": "Aucune activité",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Uklanjanje dijeljenja nije uspjelo",
|
|
14
14
|
"Download failed": "Preuzimanje nije uspjelo",
|
|
15
15
|
"e-SLOG download failed": "Preuzimanje e-SLOG nije uspjelo",
|
|
16
|
+
Recurring: "Ponavljajuće",
|
|
16
17
|
Duplicate: "Dupliciraj",
|
|
17
18
|
"Duplicate invoice": "Dupliciraj račun",
|
|
18
19
|
"Duplicate estimate": "Dupliciraj ponudu",
|
|
@@ -81,6 +82,10 @@ export default {
|
|
|
81
82
|
"Delete draft failed": "Brisanje nacrta nije uspjelo",
|
|
82
83
|
Draft: "Nacrt",
|
|
83
84
|
|
|
85
|
+
// Void
|
|
86
|
+
Void: "Storniraj",
|
|
87
|
+
"Voiding...": "Storniranje...",
|
|
88
|
+
|
|
84
89
|
// Activity list
|
|
85
90
|
Activity: "Aktivnost",
|
|
86
91
|
"No activity": "Nema aktivnosti",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Rimozione condivisione fallita",
|
|
14
14
|
"Download failed": "Download fallito",
|
|
15
15
|
"e-SLOG download failed": "Download e-SLOG fallito",
|
|
16
|
+
Recurring: "Ricorrente",
|
|
16
17
|
Duplicate: "Duplica",
|
|
17
18
|
"Duplicate invoice": "Duplica fattura",
|
|
18
19
|
"Duplicate estimate": "Duplica preventivo",
|
|
@@ -81,6 +82,10 @@ export default {
|
|
|
81
82
|
"Delete draft failed": "Eliminazione bozza fallita",
|
|
82
83
|
Draft: "Bozza",
|
|
83
84
|
|
|
85
|
+
// Void
|
|
86
|
+
Void: "Annulla",
|
|
87
|
+
"Voiding...": "Annullamento...",
|
|
88
|
+
|
|
84
89
|
// Activity list
|
|
85
90
|
Activity: "Attività",
|
|
86
91
|
"No activity": "Nessuna attività",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Opheffen van delen mislukt",
|
|
14
14
|
"Download failed": "Download mislukt",
|
|
15
15
|
"e-SLOG download failed": "e-SLOG download mislukt",
|
|
16
|
+
Recurring: "Terugkerend",
|
|
16
17
|
Duplicate: "Dupliceren",
|
|
17
18
|
"Duplicate invoice": "Factuur dupliceren",
|
|
18
19
|
"Duplicate estimate": "Offerte dupliceren",
|
|
@@ -82,6 +83,10 @@ export default {
|
|
|
82
83
|
"Delete draft failed": "Verwijderen van concept mislukt",
|
|
83
84
|
Draft: "Concept",
|
|
84
85
|
|
|
86
|
+
// Void
|
|
87
|
+
Void: "Nietig verklaren",
|
|
88
|
+
"Voiding...": "Nietig verklaren...",
|
|
89
|
+
|
|
85
90
|
// Activity list
|
|
86
91
|
Activity: "Activiteit",
|
|
87
92
|
"No activity": "Geen activiteit",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Cofnięcie udostępnienia nie powiodło się",
|
|
14
14
|
"Download failed": "Pobieranie nie powiodło się",
|
|
15
15
|
"e-SLOG download failed": "Pobieranie e-SLOG nie powiodło się",
|
|
16
|
+
Recurring: "Cykliczna",
|
|
16
17
|
Duplicate: "Duplikuj",
|
|
17
18
|
"Duplicate invoice": "Duplikuj fakturę",
|
|
18
19
|
"Duplicate estimate": "Duplikuj kosztorys",
|
|
@@ -81,6 +82,10 @@ export default {
|
|
|
81
82
|
"Delete draft failed": "Usunięcie szkicu nie powiodło się",
|
|
82
83
|
Draft: "Szkic",
|
|
83
84
|
|
|
85
|
+
// Void
|
|
86
|
+
Void: "Anuluj",
|
|
87
|
+
"Voiding...": "Anulowanie...",
|
|
88
|
+
|
|
84
89
|
// Activity list
|
|
85
90
|
Activity: "Aktywność",
|
|
86
91
|
"No activity": "Brak aktywności",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Falha ao remover partilha",
|
|
14
14
|
"Download failed": "Falha no download",
|
|
15
15
|
"e-SLOG download failed": "Falha no download e-SLOG",
|
|
16
|
+
Recurring: "Recorrente",
|
|
16
17
|
Duplicate: "Duplicar",
|
|
17
18
|
"Duplicate invoice": "Duplicar fatura",
|
|
18
19
|
"Duplicate estimate": "Duplicar orçamento",
|
|
@@ -81,6 +82,10 @@ export default {
|
|
|
81
82
|
"Delete draft failed": "Falha ao eliminar rascunho",
|
|
82
83
|
Draft: "Rascunho",
|
|
83
84
|
|
|
85
|
+
// Void
|
|
86
|
+
Void: "Anular",
|
|
87
|
+
"Voiding...": "Anulando...",
|
|
88
|
+
|
|
84
89
|
// Activity list
|
|
85
90
|
Activity: "Atividade",
|
|
86
91
|
"No activity": "Sem atividade",
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
"Unshare failed": "Odstranitev deljenja ni uspela",
|
|
14
14
|
"Download failed": "Prenos ni uspel",
|
|
15
15
|
"e-SLOG download failed": "Prenos e-SLOG ni uspel",
|
|
16
|
+
Recurring: "Ponavljajoče",
|
|
16
17
|
Duplicate: "Podvoji",
|
|
17
18
|
"Duplicate invoice": "Podvoji račun",
|
|
18
19
|
"Duplicate estimate": "Podvoji predračun",
|
|
@@ -82,6 +83,10 @@ export default {
|
|
|
82
83
|
"Delete draft failed": "Brisanje osnutka ni uspelo",
|
|
83
84
|
Draft: "Osnutek",
|
|
84
85
|
|
|
86
|
+
// Void
|
|
87
|
+
Void: "Storniraj",
|
|
88
|
+
"Voiding...": "Storniranje...",
|
|
89
|
+
|
|
85
90
|
// Activity list
|
|
86
91
|
Activity: "Aktivnost",
|
|
87
92
|
"No activity": "Ni aktivnosti",
|
|
@@ -47,7 +47,7 @@ export function CreateEntityForm({
|
|
|
47
47
|
country: "",
|
|
48
48
|
tax_number: "",
|
|
49
49
|
company_number: "",
|
|
50
|
-
environment,
|
|
50
|
+
environment: environment as "live" | "sandbox" | undefined,
|
|
51
51
|
...extraDefaults,
|
|
52
52
|
// defaultName takes priority over extraDefaults.name if provided
|
|
53
53
|
...(defaultName ? { name: defaultName } : {}),
|
|
@@ -74,7 +74,6 @@ const entitySettingsFormSchema = patchEntitySchema
|
|
|
74
74
|
.omit({
|
|
75
75
|
settings: true, // Remove nested settings - we'll flatten them
|
|
76
76
|
metadata: true, // Not used in this form
|
|
77
|
-
environment: true, // Not editable here
|
|
78
77
|
})
|
|
79
78
|
.extend({
|
|
80
79
|
// Flattened settings fields for easier form handling
|
|
@@ -85,8 +84,8 @@ const entitySettingsFormSchema = patchEntitySchema
|
|
|
85
84
|
message: "Must be a valid hex color (e.g., #5c6ac4)",
|
|
86
85
|
})
|
|
87
86
|
.optional(),
|
|
88
|
-
has_logo: z.
|
|
89
|
-
has_signature: z.
|
|
87
|
+
has_logo: z.boolean().nullable().optional(),
|
|
88
|
+
has_signature: z.boolean().nullable().optional(),
|
|
90
89
|
email: z
|
|
91
90
|
.union([z.string(), z.null()])
|
|
92
91
|
.refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Información de la empresa",
|
|
4
6
|
"Basic information about your company": "Información básica sobre su empresa",
|
|
5
7
|
// Branding settings
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Informations sur l'entreprise",
|
|
4
6
|
"Basic information about your company": "Informations de base sur votre entreprise",
|
|
5
7
|
// Branding settings
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Podaci o tvrtki",
|
|
4
6
|
"Basic information about your company": "Osnovni podaci o vašoj tvrtki",
|
|
5
7
|
// Branding settings
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Informazioni aziendali",
|
|
4
6
|
"Basic information about your company": "Informazioni di base sulla tua azienda",
|
|
5
7
|
// Branding settings
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Bedrijfsinformatie",
|
|
4
6
|
"Basic information about your company": "Basisinformatie over uw bedrijf",
|
|
5
7
|
// Branding settings
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Informacje o firmie",
|
|
4
6
|
"Basic information about your company": "Podstawowe informacje o Twojej firmie",
|
|
5
7
|
// Branding settings
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Company settings
|
|
3
|
+
"Tax ID 2": "Tax ID 2",
|
|
4
|
+
"Secondary tax identification number (optional)": "Secondary tax identification number (optional)",
|
|
3
5
|
"Company Information": "Informações da empresa",
|
|
4
6
|
"Basic information about your company": "Informações básicas sobre a sua empresa",
|
|
5
7
|
// Branding settings
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type FC, useState } from "react";
|
|
2
|
+
import { Button } from "@/ui/components/ui/button";
|
|
3
|
+
import {
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogDescription,
|
|
7
|
+
DialogFooter,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
} from "@/ui/components/ui/dialog";
|
|
11
|
+
import { Input } from "@/ui/components/ui/input";
|
|
12
|
+
import { useUpdateUserFinaSettings } from "./fina-settings.hooks";
|
|
13
|
+
|
|
14
|
+
interface FinaOperatorRequiredDialogProps {
|
|
15
|
+
open: boolean;
|
|
16
|
+
onOpenChange: (open: boolean) => void;
|
|
17
|
+
entityId: string;
|
|
18
|
+
onSaved: () => void;
|
|
19
|
+
t: (key: string) => string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const FinaOperatorRequiredDialog: FC<FinaOperatorRequiredDialogProps> = ({
|
|
23
|
+
open,
|
|
24
|
+
onOpenChange,
|
|
25
|
+
entityId,
|
|
26
|
+
onSaved,
|
|
27
|
+
t,
|
|
28
|
+
}) => {
|
|
29
|
+
const [operatorOib, setOperatorOib] = useState("");
|
|
30
|
+
const [operatorLabel, setOperatorLabel] = useState("");
|
|
31
|
+
|
|
32
|
+
const { mutate: updateUserSettings, isPending } = useUpdateUserFinaSettings({
|
|
33
|
+
onSuccess: () => {
|
|
34
|
+
setOperatorOib("");
|
|
35
|
+
setOperatorLabel("");
|
|
36
|
+
onSaved();
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
e.stopPropagation();
|
|
43
|
+
if (!operatorOib || !operatorLabel) return;
|
|
44
|
+
updateUserSettings({
|
|
45
|
+
entityId,
|
|
46
|
+
data: {
|
|
47
|
+
operator_oib: operatorOib,
|
|
48
|
+
operator_label: operatorLabel,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const oibError = operatorOib !== "" && !/^\d{11}$/.test(operatorOib);
|
|
54
|
+
const isValid = /^\d{11}$/.test(operatorOib) && operatorLabel.trim() !== "";
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
58
|
+
<DialogContent className="max-w-md">
|
|
59
|
+
<DialogHeader>
|
|
60
|
+
<DialogTitle>{t("FINA Operator Settings Required")}</DialogTitle>
|
|
61
|
+
<DialogDescription>
|
|
62
|
+
{t(
|
|
63
|
+
"Your FINA operator information is needed to fiscalize this document. Please enter your operator details.",
|
|
64
|
+
)}
|
|
65
|
+
</DialogDescription>
|
|
66
|
+
</DialogHeader>
|
|
67
|
+
|
|
68
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
69
|
+
<div>
|
|
70
|
+
<label htmlFor="fina-dialog-operator-oib" className="font-medium text-sm">
|
|
71
|
+
{t("Operator OIB")}
|
|
72
|
+
</label>
|
|
73
|
+
<Input
|
|
74
|
+
id="fina-dialog-operator-oib"
|
|
75
|
+
placeholder="12345678901"
|
|
76
|
+
value={operatorOib}
|
|
77
|
+
onChange={(e) => setOperatorOib(e.target.value.replace(/[^0-9]/g, ""))}
|
|
78
|
+
className={`mt-2 h-10${oibError ? "border-destructive" : ""}`}
|
|
79
|
+
maxLength={11}
|
|
80
|
+
/>
|
|
81
|
+
{oibError && <p className="mt-1 text-destructive text-xs">{t("OIB must be exactly 11 digits")}</p>}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div>
|
|
85
|
+
<label htmlFor="fina-dialog-operator-label" className="font-medium text-sm">
|
|
86
|
+
{t("Operator Label")}
|
|
87
|
+
</label>
|
|
88
|
+
<Input
|
|
89
|
+
id="fina-dialog-operator-label"
|
|
90
|
+
placeholder={t("e.g. Cashier 1")}
|
|
91
|
+
value={operatorLabel}
|
|
92
|
+
onChange={(e) => setOperatorLabel(e.target.value)}
|
|
93
|
+
className="mt-2 h-10"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<DialogFooter>
|
|
98
|
+
<Button type="button" variant="outline" onClick={() => onOpenChange(false)} disabled={isPending}>
|
|
99
|
+
{t("Cancel")}
|
|
100
|
+
</Button>
|
|
101
|
+
<Button type="submit" disabled={isPending || !isValid}>
|
|
102
|
+
{isPending ? t("Saving...") : t("Save & Retry")}
|
|
103
|
+
</Button>
|
|
104
|
+
</DialogFooter>
|
|
105
|
+
</form>
|
|
106
|
+
</DialogContent>
|
|
107
|
+
</Dialog>
|
|
108
|
+
);
|
|
109
|
+
};
|