feat: add verifactu status management and enhance issued invoices UI components
This commit is contained in:
parent
19452c3187
commit
451a1013d0
@ -182,6 +182,7 @@ export class SequelizeIssuedInvoiceRepositoryV2
|
|||||||
reference: { type: "root", column: "reference" },
|
reference: { type: "root", column: "reference" },
|
||||||
description: { type: "root", column: "description" },
|
description: { type: "root", column: "description" },
|
||||||
recipient_name: { type: "root", column: "customer_name" },
|
recipient_name: { type: "root", column: "customer_name" },
|
||||||
|
verifactu_status: { type: "association", association: "verifactu", column: "estado" },
|
||||||
},
|
},
|
||||||
sortableFields: ["recipient_name", "invoice_number", "invoice_date", "id", "created_at", "serie"],
|
sortableFields: ["recipient_name", "invoice_number", "invoice_date", "id", "created_at", "serie"],
|
||||||
fullTextTableAlias: "IssuedInvoiceModel",
|
fullTextTableAlias: "IssuedInvoiceModel",
|
||||||
|
|||||||
@ -292,6 +292,11 @@ export class IssuedInvoiceRepository
|
|||||||
type: "root",
|
type: "root",
|
||||||
column: "recipient_name",
|
column: "recipient_name",
|
||||||
},
|
},
|
||||||
|
verifactu_status: {
|
||||||
|
type: "association",
|
||||||
|
association: "verifactu",
|
||||||
|
column: "estado",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
sortableFields: [
|
sortableFields: [
|
||||||
"recipient_name",
|
"recipient_name",
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
import { getUserErrorMessage } from "@erp/core/client";
|
import { getUserErrorMessage } from "@erp/core/client";
|
||||||
import { useDebounce } from "@erp/core/hooks";
|
import { useDataSource, useDebounce } from "@erp/core/hooks";
|
||||||
|
import { useQueries } from "@tanstack/react-query";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
IssuedInvoiceList,
|
IssuedInvoiceList,
|
||||||
|
IssuedInvoiceStatusCounts,
|
||||||
ListIssuedInvoicesByCriteriaParams,
|
ListIssuedInvoicesByCriteriaParams,
|
||||||
VerifactuRecordStatus,
|
VerifactuRecordStatus,
|
||||||
} from "../../shared";
|
} from "../../shared";
|
||||||
|
import {
|
||||||
|
EMPTY_ISSUED_INVOICE_STATUS_COUNTS,
|
||||||
|
getListIssuedInvoicesByCriteria,
|
||||||
|
} from "../../shared";
|
||||||
import { useIssuedInvoiceListQuery } from "../../shared/";
|
import { useIssuedInvoiceListQuery } from "../../shared/";
|
||||||
|
|
||||||
type VerifactuRecordListStatusFilter = "all" | VerifactuRecordStatus;
|
type VerifactuRecordListStatusFilter = "all" | VerifactuRecordStatus;
|
||||||
@ -17,9 +23,26 @@ const EMPTY_ISSUED_INVOICES_LIST: IssuedInvoiceList = {
|
|||||||
perPage: 5,
|
perPage: 5,
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
totalItems: 0,
|
totalItems: 0,
|
||||||
|
statusCounts: EMPTY_ISSUED_INVOICE_STATUS_COUNTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STATUS_COUNT_OPTIONS: ReadonlyArray<{
|
||||||
|
value: VerifactuRecordStatus;
|
||||||
|
countKey: keyof Omit<IssuedInvoiceStatusCounts, "all">;
|
||||||
|
}> = [
|
||||||
|
{ value: "Pendiente", countKey: "pendiente" },
|
||||||
|
{ value: "Correcto", countKey: "correcto" },
|
||||||
|
{ value: "Aceptado con errores", countKey: "aceptadoConErrores" },
|
||||||
|
{ value: "Incorrecto", countKey: "incorrecto" },
|
||||||
|
{ value: "Duplicado", countKey: "duplicado" },
|
||||||
|
{ value: "Anulado", countKey: "anulado" },
|
||||||
|
{ value: "Factura inexistente", countKey: "facturaInexistente" },
|
||||||
|
{ value: "No registrado", countKey: "rechazado" },
|
||||||
|
{ value: "Error servidor AEAT", countKey: "errorServidorAeat" },
|
||||||
|
];
|
||||||
|
|
||||||
export const useIssuedInvoiceListController = () => {
|
export const useIssuedInvoiceListController = () => {
|
||||||
|
const dataSource = useDataSource();
|
||||||
const [pageIndex, setPageIndex] = useState(0);
|
const [pageIndex, setPageIndex] = useState(0);
|
||||||
const [pageSize, setPageSize] = useState(5);
|
const [pageSize, setPageSize] = useState(5);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
@ -38,14 +61,50 @@ export const useIssuedInvoiceListController = () => {
|
|||||||
filters:
|
filters:
|
||||||
verifactuStatusFilter === "all"
|
verifactuStatusFilter === "all"
|
||||||
? []
|
? []
|
||||||
: [{ field: "verifactu.status", operator: "EQUALS", value: verifactuStatusFilter }],
|
: [{ field: "verifactu_status", operator: "EQUALS", value: verifactuStatusFilter }],
|
||||||
}),
|
}),
|
||||||
[debouncedSearch, pageIndex, pageSize, verifactuStatusFilter]
|
[debouncedSearch, pageIndex, pageSize, verifactuStatusFilter]
|
||||||
);
|
);
|
||||||
|
|
||||||
const query = useIssuedInvoiceListQuery({ criteria });
|
const query = useIssuedInvoiceListQuery({ criteria });
|
||||||
|
const statusCountQueries = useQueries({
|
||||||
|
queries: STATUS_COUNT_OPTIONS.map((statusOption) => ({
|
||||||
|
queryKey: ["issued_invoices", "status_count", debouncedSearch || "", statusOption.value],
|
||||||
|
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||||
|
const response = await getListIssuedInvoicesByCriteria(dataSource, {
|
||||||
|
signal,
|
||||||
|
criteria: {
|
||||||
|
q: debouncedSearch || "",
|
||||||
|
pageNumber: 0,
|
||||||
|
pageSize: 1,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: "verifactu_status",
|
||||||
|
operator: "EQUALS",
|
||||||
|
value: statusOption.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.total_items;
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
const errorMessage = query.error ? getUserErrorMessage(query.error) : null;
|
const errorMessage = query.error ? getUserErrorMessage(query.error) : null;
|
||||||
|
const statusCounts = useMemo<IssuedInvoiceStatusCounts>(() => {
|
||||||
|
const counts = {
|
||||||
|
...EMPTY_ISSUED_INVOICE_STATUS_COUNTS,
|
||||||
|
all: query.data?.totalItems ?? 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
STATUS_COUNT_OPTIONS.forEach((statusOption, index) => {
|
||||||
|
counts[statusOption.countKey] = statusCountQueries[index]?.data ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return counts;
|
||||||
|
}, [query.data?.totalItems, statusCountQueries]);
|
||||||
|
|
||||||
const setStatusFilterValue = (value: string) => {
|
const setStatusFilterValue = (value: string) => {
|
||||||
const nextValue = (value || "all") as VerifactuRecordListStatusFilter;
|
const nextValue = (value || "all") as VerifactuRecordListStatusFilter;
|
||||||
@ -85,7 +144,10 @@ export const useIssuedInvoiceListController = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: query.data ?? EMPTY_ISSUED_INVOICES_LIST,
|
data: {
|
||||||
|
...(query.data ?? EMPTY_ISSUED_INVOICES_LIST),
|
||||||
|
statusCounts,
|
||||||
|
},
|
||||||
isLoading: query.isLoading,
|
isLoading: query.isLoading,
|
||||||
isFetching: query.isFetching,
|
isFetching: query.isFetching,
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
export * from "./issued-invoices-grid-empty";
|
export * from "./issued-invoices-grid-empty";
|
||||||
|
export * from "./issued-invoices-status-segmented-filter";
|
||||||
export * from "./verifactu-status-badge";
|
export * from "./verifactu-status-badge";
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
import { Card, CardContent } from "@repo/shadcn-ui/components";
|
|
||||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||||
import { ChartNoAxesColumnIncreasingIcon } from "lucide-react";
|
import { ChartNoAxesColumnIncreasingIcon } from "lucide-react";
|
||||||
|
|
||||||
export const IssuedInvoicesEmptyGrid = ({ className }: React.ComponentProps<"div">) => {
|
export const IssuedInvoicesEmptyGrid = ({ className }: React.ComponentProps<"div">) => {
|
||||||
return (
|
return (
|
||||||
<Card className={cn("w-full max-w-lg", className)}>
|
<div
|
||||||
<CardContent>
|
className={cn(
|
||||||
<div className="rounded-md border border-dashed p-6 text-center">
|
"rounded border border-dashed border-border bg-muted/30 px-4 py-10 text-center",
|
||||||
<ChartNoAxesColumnIncreasingIcon className="text-muted-foreground mx-auto size-12" />
|
className
|
||||||
<p className="mt-2 text-sm font-medium">No data to show</p>
|
)}
|
||||||
<p className="text-muted-foreground mt-1 text-sm">May take 15 minutes for data to load</p>
|
>
|
||||||
|
<ChartNoAxesColumnIncreasingIcon className="mx-auto size-12 text-muted-foreground" />
|
||||||
|
<p className="mt-3 text-sm font-medium">Todavia no hay facturas emitidas para mostrar</p>
|
||||||
|
<p className="mt-1 text-sm text-muted-foreground">
|
||||||
|
Cuando emitas proformas aprobadas, apareceran aqui con su estado en Verifactu.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,183 @@
|
|||||||
|
import { Badge, ToggleGroup, ToggleGroupItem } from "@repo/shadcn-ui/components";
|
||||||
|
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||||
|
import {
|
||||||
|
AlertTriangleIcon,
|
||||||
|
BanIcon,
|
||||||
|
BugIcon,
|
||||||
|
CheckCheckIcon,
|
||||||
|
CheckIcon,
|
||||||
|
Clock3Icon,
|
||||||
|
CopyIcon,
|
||||||
|
FileQuestionIcon,
|
||||||
|
XCircleIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import type { ElementType } from "react";
|
||||||
|
|
||||||
|
import { useTranslation } from "../../../../i18n";
|
||||||
|
import type { IssuedInvoiceStatusCounts, VerifactuRecordStatus } from "../../../shared";
|
||||||
|
|
||||||
|
export type IssuedInvoiceStatusFilter = "all" | VerifactuRecordStatus;
|
||||||
|
|
||||||
|
export type IssuedInvoicesStatusSegmentedFilterProps = {
|
||||||
|
value: IssuedInvoiceStatusFilter;
|
||||||
|
onValueChange: (value: IssuedInvoiceStatusFilter) => void;
|
||||||
|
counts: IssuedInvoiceStatusCounts;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ISSUED_INVOICE_STATUS_FILTER_OPTIONS = [
|
||||||
|
{
|
||||||
|
value: "all",
|
||||||
|
label: "Todas",
|
||||||
|
icon: null,
|
||||||
|
countKey: "all",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-primary data-[state=on]:bg-primary data-[state=on]:text-primary-foreground",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Pendiente",
|
||||||
|
label: "Pendiente",
|
||||||
|
icon: Clock3Icon,
|
||||||
|
countKey: "pendiente",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-slate-300 data-[state=on]:bg-slate-100 data-[state=on]:text-slate-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Correcto",
|
||||||
|
label: "Correcto",
|
||||||
|
icon: CheckCheckIcon,
|
||||||
|
countKey: "correcto",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-blue-300 data-[state=on]:bg-blue-50 data-[state=on]:text-blue-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Aceptado con errores",
|
||||||
|
label: "Aceptado con errores",
|
||||||
|
icon: CheckIcon,
|
||||||
|
countKey: "aceptadoConErrores",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-amber-300 data-[state=on]:bg-amber-50 data-[state=on]:text-amber-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Incorrecto",
|
||||||
|
label: "Incorrecto",
|
||||||
|
icon: XCircleIcon,
|
||||||
|
countKey: "incorrecto",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-red-300 data-[state=on]:bg-red-50 data-[state=on]:text-red-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Duplicado",
|
||||||
|
label: "Duplicado",
|
||||||
|
icon: CopyIcon,
|
||||||
|
countKey: "duplicado",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-yellow-300 data-[state=on]:bg-yellow-50 data-[state=on]:text-yellow-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Anulado",
|
||||||
|
label: "Anulado",
|
||||||
|
icon: BanIcon,
|
||||||
|
countKey: "anulado",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-emerald-300 data-[state=on]:bg-emerald-50 data-[state=on]:text-emerald-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Factura inexistente",
|
||||||
|
label: "Factura inexistente",
|
||||||
|
icon: FileQuestionIcon,
|
||||||
|
countKey: "facturaInexistente",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-rose-300 data-[state=on]:bg-rose-50 data-[state=on]:text-rose-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "No registrado",
|
||||||
|
label: "No registrado",
|
||||||
|
icon: AlertTriangleIcon,
|
||||||
|
countKey: "rechazado",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-orange-300 data-[state=on]:bg-orange-50 data-[state=on]:text-orange-700",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Error servidor AEAT",
|
||||||
|
label: "Error servidor AEAT",
|
||||||
|
icon: BugIcon,
|
||||||
|
countKey: "errorServidorAeat",
|
||||||
|
activeClassName:
|
||||||
|
"data-[state=on]:border-red-300 data-[state=on]:bg-red-50 data-[state=on]:text-red-700",
|
||||||
|
},
|
||||||
|
] as const satisfies readonly {
|
||||||
|
value: IssuedInvoiceStatusFilter;
|
||||||
|
label: string;
|
||||||
|
icon: ElementType | null;
|
||||||
|
countKey: keyof IssuedInvoiceStatusCounts;
|
||||||
|
activeClassName: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
const isIssuedInvoiceStatusFilter = (value: string): value is IssuedInvoiceStatusFilter => {
|
||||||
|
return ISSUED_INVOICE_STATUS_FILTER_OPTIONS.some((option) => option.value === value);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IssuedInvoicesStatusSegmentedFilter = ({
|
||||||
|
value,
|
||||||
|
onValueChange,
|
||||||
|
counts,
|
||||||
|
disabled = false,
|
||||||
|
}: IssuedInvoicesStatusSegmentedFilterProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleValueChange = (nextValue: string) => {
|
||||||
|
if (!(nextValue && isIssuedInvoiceStatusFilter(nextValue)) || nextValue === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChange(nextValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToggleGroup
|
||||||
|
aria-label={t("pages.issued_invoices.list.filters.status.label", "Filtrar por estado")}
|
||||||
|
className="flex w-full flex-wrap justify-start gap-1"
|
||||||
|
disabled={disabled}
|
||||||
|
onValueChange={handleValueChange}
|
||||||
|
size="sm"
|
||||||
|
spacing={1}
|
||||||
|
type="single"
|
||||||
|
value={value}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{ISSUED_INVOICE_STATUS_FILTER_OPTIONS.map((option) => (
|
||||||
|
<ToggleGroupItem
|
||||||
|
className={cn(
|
||||||
|
"group h-auto min-w-fit rounded-full border-transparent px-3 py-2 text-xs shadow-none transition-colors sm:text-sm",
|
||||||
|
"text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground",
|
||||||
|
"data-[state=on]:shadow-none",
|
||||||
|
option.activeClassName
|
||||||
|
)}
|
||||||
|
key={option.value}
|
||||||
|
onPointerDown={(event) => {
|
||||||
|
if (option.value === value) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={option.value}
|
||||||
|
>
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
{option.icon ? <option.icon className="size-3.5" /> : null}
|
||||||
|
<span>{t(`catalog.issued_invoices.status.${option.value.toLowerCase()}.label`, option.label)}</span>
|
||||||
|
<Badge
|
||||||
|
className={cn(
|
||||||
|
"min-w-5 rounded-full px-1.5 text-[11px] font-semibold tabular-nums",
|
||||||
|
"bg-muted text-muted-foreground",
|
||||||
|
"group-data-[state=on]:bg-white/20 group-data-[state=on]:text-current"
|
||||||
|
)}
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
{counts[option.countKey]}
|
||||||
|
</Badge>
|
||||||
|
</span>
|
||||||
|
</ToggleGroupItem>
|
||||||
|
))}
|
||||||
|
</ToggleGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,24 +1,17 @@
|
|||||||
import { ErrorState, PageHeader, SimpleSearchInput } from "@erp/core/components";
|
import { ErrorState, PageHeader, SimpleSearchInput } from "@erp/core/components";
|
||||||
import { LogoVerifactu } from "@repo/rdx-ui/components";
|
import { LogoVerifactu } from "@repo/rdx-ui/components";
|
||||||
import {
|
import { Alert, AlertDescription, AlertTitle, Button } from "@repo/shadcn-ui/components";
|
||||||
Alert,
|
import { PlusIcon } from "lucide-react";
|
||||||
AlertDescription,
|
|
||||||
AlertTitle,
|
|
||||||
Button,
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@repo/shadcn-ui/components";
|
|
||||||
import { FilterIcon, PlusIcon } from "lucide-react";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { useTranslation } from "../../../../i18n";
|
import { useTranslation } from "../../../../i18n";
|
||||||
import { useIssuedInvoiceListPageController } from "../../controllers";
|
import { useIssuedInvoiceListPageController } from "../../controllers";
|
||||||
import { IssuedInvoicesGrid, useIssuedInvoicesGridColumns } from "../blocks";
|
import { IssuedInvoicesGrid, useIssuedInvoicesGridColumns } from "../blocks";
|
||||||
import { IssuedInvoicesEmptyGrid } from "../components";
|
import {
|
||||||
|
IssuedInvoicesEmptyGrid,
|
||||||
|
IssuedInvoicesStatusSegmentedFilter,
|
||||||
|
} from "../components";
|
||||||
|
|
||||||
export const ListIssuedInvoicesPage = () => {
|
export const ListIssuedInvoicesPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -96,11 +89,6 @@ export const ListIssuedInvoicesPage = () => {
|
|||||||
<LogoVerifactu className="text-green-800 w-50" color="" />
|
<LogoVerifactu className="text-green-800 w-50" color="" />
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
{/* Search and filters */}
|
|
||||||
{listCtrl.data.totalItems === 0 && <IssuedInvoicesEmptyGrid className="mx-auto" />}
|
|
||||||
|
|
||||||
{listCtrl.data.totalItems !== 0 && (
|
|
||||||
<>
|
|
||||||
<div className="min-h-0 flex-1 overflow-auto">
|
<div className="min-h-0 flex-1 overflow-auto">
|
||||||
<IssuedInvoicesGrid
|
<IssuedInvoicesGrid
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@ -115,58 +103,31 @@ export const ListIssuedInvoicesPage = () => {
|
|||||||
pageSize={listCtrl.pageSize}
|
pageSize={listCtrl.pageSize}
|
||||||
searching={isSearchPending}
|
searching={isSearchPending}
|
||||||
topContent={
|
topContent={
|
||||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div className="space-y-5">
|
||||||
|
<div className="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
|
||||||
|
<div className="w-full">
|
||||||
<SimpleSearchInput
|
<SimpleSearchInput
|
||||||
loading={listCtrl.isLoading}
|
loading={listCtrl.isLoading}
|
||||||
onPendingChange={setIsSearchPending}
|
onPendingChange={setIsSearchPending}
|
||||||
onSearchChange={listCtrl.setSearchValue}
|
onSearchChange={listCtrl.setSearchValue}
|
||||||
|
placeholder="Buscar por numero, cliente o CIF..."
|
||||||
value={listCtrl.search}
|
value={listCtrl.search}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Select
|
<IssuedInvoicesStatusSegmentedFilter
|
||||||
onValueChange={(value) => listCtrl.setStatusFilter(value ?? "all")}
|
counts={listCtrl.data.statusCounts}
|
||||||
|
disabled={listCtrl.isLoading}
|
||||||
|
onValueChange={listCtrl.setStatusFilter}
|
||||||
value={listCtrl.statusFilter}
|
value={listCtrl.statusFilter}
|
||||||
>
|
/>
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
|
||||||
<FilterIcon aria-hidden className="mr-2 size-4" />
|
|
||||||
<SelectValue placeholder={t("filters.status")} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="all">
|
|
||||||
{t("catalog.issued_invoices.status.all.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Correcto">
|
|
||||||
{t("catalog.issued_invoices.status.correcto.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="Pendiente">
|
|
||||||
{t("catalog.issued_invoices.status.pendiente.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="aceptado_con_error">
|
|
||||||
{t("catalog.issued_invoices.status.aceptado_con_error.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="incorrecto">
|
|
||||||
{t("catalog.issued_invoices.status.incorrecto.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="duplicado">
|
|
||||||
{t("catalog.issued_invoices.status.duplicado.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="anulado">
|
|
||||||
{t("catalog.issued_invoices.status.anulado.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="factura_inexistente">
|
|
||||||
{t("catalog.issued_invoices.status.duplicado.label")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="rechazado">
|
|
||||||
{t("catalog.issued_invoices.status.rechazado.label")}
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)}
|
{listCtrl.data.totalItems === 0 ? <IssuedInvoicesEmptyGrid /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { MoneyDTOHelper } from "@erp/core";
|
|||||||
import { MoneyHelper } from "@repo/rdx-utils";
|
import { MoneyHelper } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { ListIssuedInvoicesResponseDTO } from "../../../../common";
|
import type { ListIssuedInvoicesResponseDTO } from "../../../../common";
|
||||||
|
import { EMPTY_ISSUED_INVOICE_STATUS_COUNTS } from "../entities";
|
||||||
import type { IssuedInvoiceList, IssuedInvoiceListRow, IssuedInvoiceStatus } from "../entities";
|
import type { IssuedInvoiceList, IssuedInvoiceListRow, IssuedInvoiceStatus } from "../entities";
|
||||||
import type { VerifactuRecordStatus } from "../entities/verifactu-record-status.entity";
|
import type { VerifactuRecordStatus } from "../entities/verifactu-record-status.entity";
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ export const ListIssuedInvoicesAdapter = {
|
|||||||
perPage: dto.per_page,
|
perPage: dto.per_page,
|
||||||
totalPages: dto.total_pages,
|
totalPages: dto.total_pages,
|
||||||
totalItems: dto.total_items,
|
totalItems: dto.total_items,
|
||||||
|
statusCounts: EMPTY_ISSUED_INVOICE_STATUS_COUNTS,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export * from "./issued-invoice-list.entity";
|
export * from "./issued-invoice-list.entity";
|
||||||
export * from "./issued-invoice-list-row.entity";
|
export * from "./issued-invoice-list-row.entity";
|
||||||
export * from "./issued-invoice-status.entity";
|
export * from "./issued-invoice-status.entity";
|
||||||
|
export * from "./issued-invoice-status-counts.entity";
|
||||||
export * from "./verifactu-record-status.entity";
|
export * from "./verifactu-record-status.entity";
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { IssuedInvoiceListRow } from "./issued-invoice-list-row.entity";
|
import type { IssuedInvoiceListRow } from "./issued-invoice-list-row.entity";
|
||||||
|
import type { IssuedInvoiceStatusCounts } from "./issued-invoice-status-counts.entity";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface que representa la respuesta paginada de una lista de proformas,
|
* Interface que representa la respuesta paginada de una lista de proformas,
|
||||||
@ -11,4 +12,5 @@ export interface IssuedInvoiceList {
|
|||||||
totalItems: number;
|
totalItems: number;
|
||||||
page: number;
|
page: number;
|
||||||
perPage: number;
|
perPage: number;
|
||||||
|
statusCounts: IssuedInvoiceStatusCounts;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
export interface IssuedInvoiceStatusCounts {
|
||||||
|
all: number;
|
||||||
|
pendiente: number;
|
||||||
|
correcto: number;
|
||||||
|
aceptadoConErrores: number;
|
||||||
|
incorrecto: number;
|
||||||
|
duplicado: number;
|
||||||
|
anulado: number;
|
||||||
|
facturaInexistente: number;
|
||||||
|
rechazado: number;
|
||||||
|
errorServidorAeat: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EMPTY_ISSUED_INVOICE_STATUS_COUNTS: IssuedInvoiceStatusCounts = {
|
||||||
|
all: 0,
|
||||||
|
pendiente: 0,
|
||||||
|
correcto: 0,
|
||||||
|
aceptadoConErrores: 0,
|
||||||
|
incorrecto: 0,
|
||||||
|
duplicado: 0,
|
||||||
|
anulado: 0,
|
||||||
|
facturaInexistente: 0,
|
||||||
|
rechazado: 0,
|
||||||
|
errorServidorAeat: 0,
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user