Clientes y facturas de cliente
This commit is contained in:
parent
327756413d
commit
3846199cbe
@ -75,7 +75,7 @@
|
||||
"uuid": "^11.0.5",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
"zod": "^3.25.67"
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DateTime } from "luxon";
|
||||
import http from "node:http";
|
||||
import os from "node:os";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { createApp } from "./app";
|
||||
import { ENV } from "./config";
|
||||
import { tryConnectToDatabase } from "./config/database";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { IModuleServer, ModuleParams } from "@erp/core/api";
|
||||
import { logger } from "../logger";
|
||||
import { initModels, registerModels } from "./model-loader";
|
||||
import { registerService } from "./service-registry";
|
||||
import { getService, listServices, registerService } from "./service-registry";
|
||||
|
||||
const registeredModules: Map<string, IModuleServer> = new Map();
|
||||
const initializedModules = new Set<string>();
|
||||
@ -81,7 +81,13 @@ async function loadModule(name: string, params: ModuleParams, stack: string[]) {
|
||||
|
||||
// 3) Registrar dependencias que expone (permite async)
|
||||
const pkgApi = await withPhase(name, "registerDependencies", async () => {
|
||||
return await Promise.resolve(pkg.registerDependencies?.(params));
|
||||
return await Promise.resolve(
|
||||
pkg.registerDependencies?.({
|
||||
...params,
|
||||
listServices,
|
||||
getService,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 4) Registrar modelos de Sequelize, si existen
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import customerInvoicesAPIModule from "@erp/customer-invoices/api";
|
||||
import customersAPIModule from "@erp/customers/api";
|
||||
//import verifactuAPIModule from "@erp/verifactu/api";
|
||||
import verifactuAPIModule from "@erp/verifactu/api";
|
||||
|
||||
import { registerModule } from "./lib";
|
||||
|
||||
@ -8,5 +8,5 @@ export const registerModules = () => {
|
||||
//registerModule(authAPIModule);
|
||||
registerModule(customersAPIModule);
|
||||
registerModule(customerInvoicesAPIModule);
|
||||
//registerModule(verifactuAPIModule);
|
||||
registerModule(verifactuAPIModule);
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"@repo/rdx-utils": "workspace:*",
|
||||
"@repo/rdx-ui": "workspace:*",
|
||||
"@repo/shadcn-ui": "workspace:*",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@tanstack/react-query": "^5.75.4",
|
||||
"axios": "^1.9.0",
|
||||
"express": "^4.18.2",
|
||||
@ -35,6 +36,6 @@
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-router-dom": "^6.26.0",
|
||||
"sequelize": "^6.37.5",
|
||||
"zod": "^3.25.67"
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { TaxCatalogProvider } from "@erp/core";
|
||||
import { Percentage, ValueObject } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const DEFAULT_SCALE = 2;
|
||||
const DEFAULT_MIN_VALUE = 0;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { ZodSchema } from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { InternalApiError, ValidationApiError } from "../errors";
|
||||
import { ExpressController } from "../express-controller";
|
||||
|
||||
@ -35,7 +35,7 @@ export type ValidateRequestWithSchemaOptions = {
|
||||
};
|
||||
|
||||
export const validateRequest = <T extends "body" | "query" | "params">(
|
||||
schema: ZodSchema,
|
||||
schema: z.ZodObject<any>,
|
||||
source: T = "body" as T,
|
||||
options: ValidateRequestWithSchemaOptions = { sanitize: true }
|
||||
): RequestHandler => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { AmountBaseSchema } from "./base.schemas";
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Cadena con valor numérico:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
Esquema del DTO para Criteria.fromPrimitives(...)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { MetadataSchema } from "./metadata.dto";
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const MetadataSchema = z
|
||||
.object({
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { NumericStringSchema } from "./base.schemas";
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { NumericStringSchema } from "./base.schemas";
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export * from "./use-datasource";
|
||||
export * from "./use-hook-form";
|
||||
export * from "./use-pagination";
|
||||
export * from "./use-query-key";
|
||||
export * from "./use-toggle";
|
||||
|
||||
1
modules/core/src/web/hooks/use-hook-form/index.ts
Normal file
1
modules/core/src/web/hooks/use-hook-form/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./use-hook-form";
|
||||
44
modules/core/src/web/hooks/use-hook-form/use-hook-form.ts
Normal file
44
modules/core/src/web/hooks/use-hook-form/use-hook-form.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useEffect } from "react";
|
||||
import { FieldValues, UseFormProps, UseFormReturn, useForm } from "react-hook-form";
|
||||
import * as z4 from "zod/v4/core";
|
||||
|
||||
type UseHookFormProps<T extends FieldValues = FieldValues> = UseFormProps<T> & {
|
||||
resolverSchema: z4.$ZodType<T, any>;
|
||||
initialValues: UseFormProps<T>["defaultValues"];
|
||||
onDirtyChange?: (isDirty: boolean) => void;
|
||||
};
|
||||
|
||||
export function useHookForm<T extends FieldValues = FieldValues>({
|
||||
resolverSchema,
|
||||
initialValues,
|
||||
disabled,
|
||||
onDirtyChange,
|
||||
...rest
|
||||
}: UseHookFormProps<T>): UseFormReturn<T> {
|
||||
const form = useForm<T>({
|
||||
...rest,
|
||||
resolver: zodResolver(resolverSchema),
|
||||
defaultValues: initialValues,
|
||||
disabled,
|
||||
});
|
||||
|
||||
const {
|
||||
formState: { isDirty },
|
||||
} = form;
|
||||
|
||||
useEffect(() => {
|
||||
onDirtyChange?.(isDirty);
|
||||
}, [isDirty, onDirtyChange]);
|
||||
|
||||
useEffect(() => {
|
||||
const applyReset = async () => {
|
||||
const values = typeof initialValues === "function" ? await initialValues() : initialValues;
|
||||
|
||||
form.reset(values);
|
||||
};
|
||||
applyReset();
|
||||
}, [initialValues, form]);
|
||||
|
||||
return form;
|
||||
}
|
||||
@ -20,7 +20,7 @@
|
||||
"react-hook-form": "^7.58.1",
|
||||
"react-i18next": "^15.5.1",
|
||||
"sequelize": "^6.37.5",
|
||||
"zod": "^3.25.67"
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hookform/devtools": "^4.4.0",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DomainValidationError } from "@erp/core/api";
|
||||
import { ValueObject } from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
interface CustomerInvoiceItemDescriptionProps {
|
||||
value: string;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DomainValidationError } from "@erp/core/api";
|
||||
import { ValueObject } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
interface ICustomerInvoiceNumberProps {
|
||||
value: string;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DomainValidationError } from "@erp/core/api";
|
||||
import { ValueObject } from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
interface ICustomerInvoiceSerieProps {
|
||||
value: string;
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { IModuleServer, ModuleParams } from "@erp/core/api";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Transaction } from "sequelize";
|
||||
import { customerInvoicesRouter, models } from "./infrastructure";
|
||||
import { buildCustomerInvoiceDependencies } from "./infrastructure/dependencies";
|
||||
|
||||
export const customerInvoicesAPIModule: IModuleServer = {
|
||||
name: "customer-invoices",
|
||||
@ -12,16 +15,33 @@ export const customerInvoicesAPIModule: IModuleServer = {
|
||||
customerInvoicesRouter(params);
|
||||
logger.info("🚀 CustomerInvoices module initialized", { label: this.name });
|
||||
},
|
||||
|
||||
async registerDependencies(params) {
|
||||
const { database, logger } = params;
|
||||
const { logger, listServices } = params; /* = ModuleParams & {
|
||||
getService: (name: string) => any;
|
||||
};*/
|
||||
|
||||
logger.info("🚀 CustomerInvoices module dependencies registered", {
|
||||
label: this.name,
|
||||
});
|
||||
|
||||
logger.info(listServices());
|
||||
//getService()
|
||||
|
||||
const deps = buildCustomerInvoiceDependencies(params);
|
||||
|
||||
return {
|
||||
models,
|
||||
services: {
|
||||
getCustomerInvoice: () => {},
|
||||
/*...*/
|
||||
getInvoiceByIdInCompany: (
|
||||
companyId: UniqueID,
|
||||
invoiceId: UniqueID,
|
||||
transaction?: Transaction
|
||||
) => {
|
||||
const { service } = deps;
|
||||
|
||||
return service.getInvoiceByIdInCompany(companyId, invoiceId, transaction);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@ -45,10 +45,12 @@ export type CustomerInvoiceDeps = {
|
||||
//delete: () => DeleteCustomerInvoiceUseCase;
|
||||
report: () => ReportCustomerInvoiceUseCase;
|
||||
};
|
||||
getService: (name: string) => any;
|
||||
listServices: () => string[];
|
||||
};
|
||||
|
||||
export function buildCustomerInvoiceDependencies(params: ModuleParams): CustomerInvoiceDeps {
|
||||
const { database } = params;
|
||||
const { database, listServices, getService } = params;
|
||||
const transactionManager = new SequelizeTransactionManager(database);
|
||||
const catalogs = { taxes: spainTaxCatalogProvider };
|
||||
|
||||
@ -157,5 +159,7 @@ export function buildCustomerInvoiceDependencies(params: ModuleParams): Customer
|
||||
report: () =>
|
||||
new ReportCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||
},
|
||||
listServices,
|
||||
getService,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { NumericStringSchema, PercentageSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CreateCustomerInvoiceItemRequestSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CriteriaSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CustomerInvoiceListRequestSchema = CriteriaSchema;
|
||||
export type CustomerInvoiceListRequestDTO = z.infer<typeof CustomerInvoiceListRequestSchema>;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Este DTO es utilizado por el endpoint:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const GetCustomerInvoiceByIdRequestSchema = z.object({
|
||||
invoice_id: z.string(),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ReportCustomerInvoiceByIdRequestSchema = z.object({
|
||||
invoice_id: z.string(),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const UpdateCustomerInvoiceByIdParamsRequestSchema = z.object({
|
||||
customer_id: z.string(),
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
PercentageSchema,
|
||||
QuantitySchema,
|
||||
} from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CreateCustomerInvoiceResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { MetadataSchema, MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const GetCustomerInvoiceByIdResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { MetadataSchema, MoneySchema, createListViewResponseSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ListCustomerInvoicesResponseSchema = createListViewResponseSchema(
|
||||
z.object({
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { AmountSchema, MetadataSchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const UpdateCustomerInvoiceByIdResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export * from "./use-create-customer-invoice-mutation";
|
||||
export * from "./use-customer-invoice-query";
|
||||
export * from "./use-customer-invoices-context";
|
||||
export * from "./use-customer-invoices-query";
|
||||
export * from "./use-detail-columns";
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import { DefaultError, type QueryKey, useQuery } from "@tanstack/react-query";
|
||||
import { CustomerInvoiceData } from "../schemas";
|
||||
|
||||
export const CUSTOMER_INVOICE_QUERY_KEY = (id: string): QueryKey =>
|
||||
["customer_invoice", id] as const;
|
||||
|
||||
type CustomerInvoiceQueryOptions = {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export function useCustomerInvoiceQuery(invoiceId?: string, options?: CustomerInvoiceQueryOptions) {
|
||||
const dataSource = useDataSource();
|
||||
const enabled = (options?.enabled ?? true) && !!invoiceId;
|
||||
|
||||
return useQuery<CustomerInvoiceData, DefaultError>({
|
||||
queryKey: CUSTOMER_INVOICE_QUERY_KEY(invoiceId ?? "unknown"),
|
||||
queryFn: async (context) => {
|
||||
const { signal } = context;
|
||||
if (!invoiceId) {
|
||||
if (!invoiceId) throw new Error("invoiceId is required");
|
||||
}
|
||||
return await dataSource.getOne<CustomerInvoiceData>("customer-invoices", invoiceId, {
|
||||
signal,
|
||||
});
|
||||
},
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
export function useQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = unknown,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey
|
||||
>
|
||||
|
||||
TQueryFnData: the type returned from the queryFn.
|
||||
TError: the type of Errors to expect from the queryFn.
|
||||
TData: the type our data property will eventually have.
|
||||
Only relevant if you use the select option,
|
||||
because then the data property can be different
|
||||
from what the queryFn returns.
|
||||
Otherwise, it will default to whatever the queryFn returns.
|
||||
TQueryKey: the type of our queryKey, only relevant
|
||||
if you use the queryKey that is passed to your queryFn.
|
||||
|
||||
*/
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { CreateCustomerInvoiceRequestSchema } from "../../../common/dto";
|
||||
|
||||
export const CustomerInvoiceItemDataFormSchema = CreateCustomerInvoiceRequestSchema.extend({
|
||||
|
||||
@ -0,0 +1,152 @@
|
||||
import { useHookForm, useUrlParamId } from "@erp/core/hooks";
|
||||
import { AppBreadcrumb, AppContent } from "@repo/rdx-ui/components";
|
||||
import { Button } from "@repo/shadcn-ui/components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useCustomerInvoiceQuery } from "../../hooks";
|
||||
import { useTranslation } from "../../i18n";
|
||||
|
||||
export const CustomerInvoiceUpdate = () => {
|
||||
const invoiceId = useUrlParamId();
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 1) Estado de carga del cliente (query)
|
||||
const {
|
||||
data: invoiceData,
|
||||
isLoading: isLoadingInvoice,
|
||||
isError: isLoadError,
|
||||
error: loadError,
|
||||
} = useCustomerInvoiceQuery(invoiceId, { enabled: !!invoiceId });
|
||||
|
||||
// 2) Estado de actualización (mutación)
|
||||
const {
|
||||
mutate,
|
||||
isPending: isUpdating,
|
||||
isError: isUpdateError,
|
||||
error: updateError,
|
||||
} = useUpdateCustomerInvoice();
|
||||
|
||||
// 3) Form hook
|
||||
const form = useHookForm<CustomerInvoiceFormData>({
|
||||
resolverSchema: CustomerInvoiceFormSchema,
|
||||
initialValues: customerInvoiceData ?? defaultCustomerInvoiceFormData,
|
||||
disabled: isUpdating,
|
||||
});
|
||||
|
||||
const handleSubmit = (data: any) => {
|
||||
// Handle form submission logic here
|
||||
console.log("Form submitted with data:", data);
|
||||
mutate(data);
|
||||
|
||||
// Navigate to the list page after submission
|
||||
navigate("/customer-invoices/list");
|
||||
};
|
||||
|
||||
if (isError) {
|
||||
console.error("Error creating customer invoice:", error);
|
||||
// Optionally, you can show an error message to the user
|
||||
}
|
||||
|
||||
// Render the component
|
||||
// You can also handle loading state if needed
|
||||
// For example, you can disable the submit button while the mutation is in progress
|
||||
// const isLoading = useCreateCustomerInvoiceMutation().isLoading;
|
||||
|
||||
// Return the JSX for the component
|
||||
// You can customize the form and its fields as needed
|
||||
// For example, you can use a form library like react-hook-form or Formik to handle form state and validation
|
||||
// Here, we are using a simple form with a submit button
|
||||
|
||||
// Note: Make sure to replace the form fields with your actual invoice fields
|
||||
// and handle validation as needed.
|
||||
// This is just a basic example to demonstrate the structure of the component.
|
||||
|
||||
// If you are using a form library, you can pass the handleSubmit function to the form's onSubmit prop
|
||||
// and use the form library's methods to handle form state and validation.
|
||||
|
||||
// Example of a simple form submission handler
|
||||
// You can replace this with your actual form handling logic
|
||||
// const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
// event.preventDefault();
|
||||
// const formData = new FormData(event.currentTarget);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBreadcrumb />
|
||||
<AppContent>
|
||||
<div className='flex items-center justify-between space-y-2'>
|
||||
<div>
|
||||
<h2 className='text-2xl font-bold tracking-tight'>{t("pages.create.title")}</h2>
|
||||
<p className='text-muted-foreground'>{t("pages.create.description")}</p>
|
||||
</div>
|
||||
<div className='flex items-center justify-end mb-4'>
|
||||
<Button className='cursor-pointer' onClick={() => navigate("/customer-invoices/list")}>
|
||||
{t("pages.create.back_to_list")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-1 flex-col gap-4 p-4'>
|
||||
<CustomerInvoiceEditForm onSubmit={handleSubmit} isPending={isPending} />
|
||||
</div>
|
||||
</AppContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
return (
|
||||
<>
|
||||
<div className='flex items-center justify-between space-y-2'>
|
||||
<div>
|
||||
<h2 className='text-2xl font-bold tracking-tight'>
|
||||
{t('customerInvoices.list.title' />
|
||||
</h2>
|
||||
<p className='text-muted-foreground'>
|
||||
{t('CustomerInvoices.list.subtitle' />
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<Button onClick={() => navigate("/CustomerInvoices/add")}>
|
||||
<PlusIcon className='w-4 h-4 mr-2' />
|
||||
{t("customerInvoices.create.title")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs value={status} onValueChange={setStatus}>
|
||||
<div className='flex flex-col items-start justify-between mb-4 sm:flex-row sm:items-center'>
|
||||
<div className='w-full mb-4 sm:w-auto sm:mb-0'>
|
||||
<TabsList className='hidden sm:flex'>
|
||||
{CustomerInvoiceStatuses.map((s) => (
|
||||
<TabsTrigger key={s.value} value={s.value}>
|
||||
{s.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
<div className='flex items-center w-full space-x-2 sm:hidden'>
|
||||
<Label>{t("customerInvoices.list.tabs_title")}</Label>
|
||||
<Select value={status} onValueChange={setStatus}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder='Seleccionar estado' />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{CustomerInvoiceStatuses.map((s) => (
|
||||
<SelectItem key={s.value} value={s.value}>
|
||||
{s.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{CustomerInvoiceStatuses.map((s) => (
|
||||
<TabsContent key={s.value} value={s.value}>
|
||||
<CustomerInvoicesGrid />
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
};
|
||||
*/
|
||||
@ -1,13 +1,16 @@
|
||||
//import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { ListCustomerInvoicesResponseDTO } from "@erp/customer-invoices/common";
|
||||
import {
|
||||
GetCustomerInvoiceByIdResponseSchema,
|
||||
ListCustomerInvoicesResponseDTO,
|
||||
} from "@erp/customer-invoices/common";
|
||||
|
||||
/*export const CustomerCreateSchema = CreateCustomerRequestSchema;
|
||||
export const CustomerUpdateSchema = UpdateCustomerByIdRequestSchema;
|
||||
export const CustomerSchema = GetCustomerByIdResponseSchema.omit({
|
||||
export const CustomerUpdateSchema = UpdateCustomerByIdRequestSchema;*/
|
||||
export const CustomerSchema = GetCustomerInvoiceByIdResponseSchema.omit({
|
||||
metadata: true,
|
||||
});
|
||||
|
||||
export type CustomerData = z.infer<typeof CustomerSchema>;*/
|
||||
export type CustomerInvoiceData = z.infer<typeof CustomerSchema>;
|
||||
|
||||
export type CustomerInvoicesListData = ListCustomerInvoicesResponseDTO;
|
||||
|
||||
@ -1 +1,9 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CustomerInvoiceFormSchema = z.object({
|
||||
invoice_number: z.string().optional(),
|
||||
status: z.string().optional(),
|
||||
series: z.string().optional(),
|
||||
});
|
||||
|
||||
export const CustomerInvoiceFormData = z.infer<typeof CustomerInvoiceFormSchema>;
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
"react-hook-form": "^7.58.1",
|
||||
"react-i18next": "^15.5.1",
|
||||
"sequelize": "^6.37.5",
|
||||
"zod": "^3.25.67"
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DomainValidationError, ValueObject } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
interface ICustomerNumberProps {
|
||||
value: string;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DomainValidationError, ValueObject } from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
interface ICustomerSerieProps {
|
||||
value: string;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth/api";
|
||||
import { ILogger, ModuleParams, validateRequest } from "@erp/core/api";
|
||||
import { ModuleParams, validateRequest } from "@erp/core/api";
|
||||
import { ILogger } from "@repo/rdx-logger";
|
||||
import { Application, NextFunction, Request, Response, Router } from "express";
|
||||
import { Sequelize } from "sequelize";
|
||||
import {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CreateCustomerRequestSchema = z.object({
|
||||
id: z.string().nonempty(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CriteriaSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CustomerListRequestSchema = CriteriaSchema;
|
||||
export type CustomerListRequestDTO = z.infer<typeof CustomerListRequestSchema>;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Este DTO es utilizado por el endpoint:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Este DTO es utilizado por el endpoint:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const UpdateCustomerByIdParamsRequestSchema = z.object({
|
||||
customer_id: z.string(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { MetadataSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CreateCustomerResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { MetadataSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const GetCustomerByIdResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { MetadataSchema, createListViewResponseSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ListCustomersResponseSchema = createListViewResponseSchema(
|
||||
z.object({
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { MetadataSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const UpdateCustomerByIdResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export * from "./use-create-customer-mutation";
|
||||
export * from "./use-customer-form";
|
||||
export * from "./use-customer-query";
|
||||
export * from "./use-customers-context";
|
||||
export * from "./use-customers-query";
|
||||
|
||||
@ -9,7 +9,7 @@ type CreateCustomerPayload = {
|
||||
data: CustomerFormData;
|
||||
};
|
||||
|
||||
export function useCreateCustomerMutation() {
|
||||
export function useCreateCustomer() {
|
||||
const queryClient = useQueryClient();
|
||||
const dataSource = useDataSource();
|
||||
const schema = CreateCustomerRequestSchema;
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { CustomerFormData, CustomerFormSchema } from "../schemas";
|
||||
|
||||
type UseCustomerFormProps = {
|
||||
initialValues: CustomerFormData;
|
||||
disabled?: boolean;
|
||||
onDirtyChange?: (isDirty: boolean) => void;
|
||||
};
|
||||
|
||||
export function useCustomerForm({ initialValues, disabled, onDirtyChange }: UseCustomerFormProps) {
|
||||
const form = useForm({
|
||||
resolver: zodResolver(CustomerFormSchema),
|
||||
defaultValues: initialValues,
|
||||
disabled,
|
||||
});
|
||||
|
||||
const {
|
||||
formState: { isDirty },
|
||||
} = form;
|
||||
|
||||
// Avisar cuando cambia el dirty state
|
||||
useEffect(() => {
|
||||
if (onDirtyChange) {
|
||||
onDirtyChange(isDirty);
|
||||
}
|
||||
}, [isDirty, onDirtyChange]);
|
||||
|
||||
// Resetear el form si cambian los valores iniciales
|
||||
useEffect(() => {
|
||||
form.reset(initialValues);
|
||||
}, [initialValues, form]);
|
||||
|
||||
return form;
|
||||
}
|
||||
@ -12,22 +12,19 @@ export function useCustomerQuery(customerId?: string, options?: CustomerQueryOpt
|
||||
const dataSource = useDataSource();
|
||||
const enabled = (options?.enabled ?? true) && !!customerId;
|
||||
|
||||
const queryResult = useQuery<CustomerData, DefaultError>({
|
||||
return useQuery<CustomerData, DefaultError>({
|
||||
queryKey: CUSTOMER_QUERY_KEY(customerId ?? "unknown"),
|
||||
queryFn: async (context) => {
|
||||
const { signal } = context;
|
||||
if (!customerId) {
|
||||
if (!customerId) throw new Error("customerId is required");
|
||||
}
|
||||
const customer = await dataSource.getOne<CustomerData>("customers", customerId, {
|
||||
return await dataSource.getOne<CustomerData>("customers", customerId, {
|
||||
signal,
|
||||
});
|
||||
return customer;
|
||||
},
|
||||
enabled,
|
||||
});
|
||||
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -14,7 +14,7 @@ type UpdateCustomerPayload = {
|
||||
data: Partial<CustomerFormData>;
|
||||
};
|
||||
|
||||
export function useUpdateCustomerMutation() {
|
||||
export function useUpdateCustomer() {
|
||||
const queryClient = useQueryClient();
|
||||
const dataSource = useDataSource();
|
||||
const schema = UpdateCustomerByIdRequestSchema;
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { AppBreadcrumb, AppContent } from "@repo/rdx-ui/components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { FormCommitButtonGroup, UnsavedChangesProvider } from "@erp/core/hooks";
|
||||
import { FormCommitButtonGroup, UnsavedChangesProvider, useHookForm } from "@erp/core/hooks";
|
||||
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||
import { FieldErrors, FormProvider } from "react-hook-form";
|
||||
import { CustomerEditForm, ErrorAlert } from "../../components";
|
||||
import { useCreateCustomerMutation, useCustomerForm } from "../../hooks";
|
||||
import { useCreateCustomer } from "../../hooks";
|
||||
import { useTranslation } from "../../i18n";
|
||||
import { CustomerFormData, defaultCustomerFormData } from "../../schemas";
|
||||
import { CustomerFormData, CustomerFormSchema, defaultCustomerFormData } from "../../schemas";
|
||||
|
||||
export const CustomerCreate = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -19,11 +19,13 @@ export const CustomerCreate = () => {
|
||||
isPending: isCreating,
|
||||
isError: isCreateError,
|
||||
error: createError,
|
||||
} = useCreateCustomerMutation();
|
||||
} = useCreateCustomer();
|
||||
|
||||
// 2) Form hook
|
||||
const form = useCustomerForm({
|
||||
const form = useHookForm<CustomerFormData>({
|
||||
resolverSchema: CustomerFormSchema,
|
||||
initialValues: defaultCustomerFormData,
|
||||
disabled: isCreating,
|
||||
});
|
||||
|
||||
// 3) Submit con navegación condicionada por éxito
|
||||
|
||||
@ -2,7 +2,12 @@ import { AppBreadcrumb, AppContent, BackHistoryButton } from "@repo/rdx-ui/compo
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { formHasAnyDirty, pickFormDirtyValues } from "@erp/core/client";
|
||||
import { FormCommitButtonGroup, UnsavedChangesProvider, useUrlParamId } from "@erp/core/hooks";
|
||||
import {
|
||||
FormCommitButtonGroup,
|
||||
UnsavedChangesProvider,
|
||||
useHookForm,
|
||||
useUrlParamId,
|
||||
} from "@erp/core/hooks";
|
||||
import { showErrorToast, showSuccessToast, showWarningToast } from "@repo/rdx-ui/helpers";
|
||||
import { FieldErrors, FormProvider } from "react-hook-form";
|
||||
import {
|
||||
@ -11,9 +16,9 @@ import {
|
||||
ErrorAlert,
|
||||
NotFoundCard,
|
||||
} from "../../components";
|
||||
import { useCustomerForm, useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
|
||||
import { useCustomerQuery, useUpdateCustomer } from "../../hooks";
|
||||
import { useTranslation } from "../../i18n";
|
||||
import { CustomerFormData, defaultCustomerFormData } from "../../schemas";
|
||||
import { CustomerFormData, CustomerFormSchema, defaultCustomerFormData } from "../../schemas";
|
||||
|
||||
export const CustomerUpdate = () => {
|
||||
const customerId = useUrlParamId();
|
||||
@ -34,11 +39,13 @@ export const CustomerUpdate = () => {
|
||||
isPending: isUpdating,
|
||||
isError: isUpdateError,
|
||||
error: updateError,
|
||||
} = useUpdateCustomerMutation();
|
||||
} = useUpdateCustomer();
|
||||
|
||||
// 3) Form hook
|
||||
const form = useCustomerForm({
|
||||
const form = useHookForm<CustomerFormData>({
|
||||
resolverSchema: CustomerFormSchema,
|
||||
initialValues: customerData ?? defaultCustomerFormData,
|
||||
disabled: isUpdating,
|
||||
});
|
||||
|
||||
// 4) Submit con navegación condicionada por éxito
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import {
|
||||
CreateCustomerRequestSchema,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CustomerFormSchema = z.object({
|
||||
reference: z.string().optional(),
|
||||
|
||||
@ -3,17 +3,23 @@
|
||||
"version": "0.0.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"exports": { "./api": "./src/api/index.ts" },
|
||||
"exports": {
|
||||
"./api": "./src/api/index.ts",
|
||||
"./common": "./src/common/index.ts"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"sequelize": "^6.37.5",
|
||||
"express": "^4.18.2"
|
||||
"express": "^4.18.2",
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": { "@types/express": "^4.17.21" },
|
||||
"dependencies": {
|
||||
"@erp/auth": "workspace:*",
|
||||
"@erp/core": "workspace:*",
|
||||
"@repo/rdx-ddd": "workspace:*",
|
||||
"@repo/rdx-criteria": "workspace:*",
|
||||
"@repo/rdx-utils": "workspace:*",
|
||||
"@repo/rdx-logger": "workspace:*"
|
||||
"@repo/rdx-logger": "workspace:*",
|
||||
"@erp/customer-invoices": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./verifactu-record.full.presenter";
|
||||
//export * from "./verifactu-record.full.representer";
|
||||
@ -0,0 +1,24 @@
|
||||
import { Presenter } from "@erp/core/api";
|
||||
import { GetVerifactuRecordByIdResponseDTO } from "@erp/verifactu-records/common";
|
||||
import { VerifactuRecord } from "../../../domain";
|
||||
|
||||
export class VerifactuRecordFullPresenter extends Presenter<
|
||||
VerifactuRecord,
|
||||
GetVerifactuRecordByIdResponseDTO
|
||||
> {
|
||||
toOutput(record: VerifactuRecord): GetVerifactuRecordByIdResponseDTO {
|
||||
return {
|
||||
id: record.id.toString(),
|
||||
invoice_id: record.invoiceId.toString(),
|
||||
|
||||
estado: record.estado.toPrimitive(),
|
||||
url: record.url.toPrimitive(),
|
||||
// qr1: toEmptyString(record.qr1, (value) => value.toString()),
|
||||
|
||||
metadata: {
|
||||
entity: "verifactu-records",
|
||||
link: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,7 @@ export class SendInvoiceUseCase {
|
||||
|
||||
public async execute(params: SendInvoiceUseCaseInput) {
|
||||
const { invoice_id } = params;
|
||||
|
||||
console.log("CASO DE USO -----ESTO ES UNA PRUEBA>>>>>>", invoice_id);
|
||||
const idOrError = UniqueID.create(invoice_id);
|
||||
|
||||
if (idOrError.isFailure) {
|
||||
@ -25,14 +25,41 @@ export class SendInvoiceUseCase {
|
||||
}
|
||||
|
||||
const invoiceId = idOrError.data;
|
||||
console.log("CASO DE USO -----VALIDADO>>>>>>", invoiceId);
|
||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||
try {
|
||||
const invoiceOrError = await this.service.sendInvoiceToVerifactu(invoiceId, transaction);
|
||||
console.log(
|
||||
"CASO DE USO -----LLamar servicio de invoices para recuperar el invoice>>>>>>",
|
||||
invoiceId
|
||||
);
|
||||
const invoice = {
|
||||
serie: "A",
|
||||
numero: "1",
|
||||
fecha_expedicion: "12-09-2025",
|
||||
tipo_factura: "F1",
|
||||
descripcion: "Venta de bienes",
|
||||
nif: "A15022510",
|
||||
nombre: "Empresa de prueba SL",
|
||||
lineas: [
|
||||
{
|
||||
base_imponible: "200",
|
||||
tipo_impositivo: "21",
|
||||
cuota_repercutida: "42",
|
||||
},
|
||||
{
|
||||
base_imponible: "100",
|
||||
tipo_impositivo: "10",
|
||||
cuota_repercutida: "10",
|
||||
},
|
||||
],
|
||||
importe_total: "352.00",
|
||||
};
|
||||
const invoiceOrError = await this.service.sendInvoiceToVerifactu(invoice, transaction);
|
||||
if (invoiceOrError.isFailure) {
|
||||
return Result.fail(invoiceOrError.error);
|
||||
}
|
||||
|
||||
const invoice = invoiceOrError.data;
|
||||
const invoice2 = invoiceOrError.data;
|
||||
return Result.ok({});
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DomainValidationError } from "@erp/core/api";
|
||||
import { ValueObject } from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
interface VerifactuRecordUrlProps {
|
||||
value: string;
|
||||
|
||||
@ -42,29 +42,6 @@ export class VerifactuRecordService {
|
||||
// return Result.fail(invoiceResult.error);
|
||||
// }
|
||||
|
||||
const invoice = {
|
||||
serie: "A",
|
||||
numero: "1",
|
||||
fecha_expedicion: "12-09-2025",
|
||||
tipo_factura: "F1",
|
||||
descripcion: "Venta de bienes",
|
||||
nif: "A15022510",
|
||||
nombre: "Empresa de prueba SL",
|
||||
lineas: [
|
||||
{
|
||||
base_imponible: "200",
|
||||
tipo_impositivo: "21",
|
||||
cuota_repercutida: "42",
|
||||
},
|
||||
{
|
||||
base_imponible: "100",
|
||||
tipo_impositivo: "10",
|
||||
cuota_repercutida: "10",
|
||||
},
|
||||
],
|
||||
importe_total: "352.00",
|
||||
};
|
||||
|
||||
console.log("ESTO ES UNA PRUEBA>>>>>>");
|
||||
|
||||
return Result.ok(invoice);
|
||||
|
||||
@ -4,11 +4,9 @@ import { models, verifactuRouter } from "./infrastructure";
|
||||
export const verifactuAPIModule: IModuleServer = {
|
||||
name: "verifactu",
|
||||
version: "1.0.0",
|
||||
dependencies: ["customers-invoices"],
|
||||
dependencies: ["customer-invoices"],
|
||||
|
||||
async init(params: ModuleParams) {
|
||||
// const contacts = getService<ContactsService>("contacts");
|
||||
console.log("111111111111111111111111111A>>>>>>>>>>>>>>>>>>>");
|
||||
const { logger } = params;
|
||||
verifactuRouter(params);
|
||||
logger.info("🚀 Verifactu module initialized", { label: this.name });
|
||||
|
||||
@ -2,116 +2,62 @@
|
||||
|
||||
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
|
||||
|
||||
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||
import {
|
||||
InMemoryMapperRegistry,
|
||||
InMemoryPresenterRegistry,
|
||||
SequelizeTransactionManager,
|
||||
} from "@erp/core/api";
|
||||
import { SendCustomerInvoiceUseCase } from "../application";
|
||||
import { CustomerInvoiceItemsReportPersenter } from "../application/presenters/queries/customer-invoice-items.report.presenter";
|
||||
import { CustomerInvoiceService } from "../domain";
|
||||
import { CustomerInvoiceDomainMapper, CustomerInvoiceListMapper } from "./mappers";
|
||||
import { CustomerInvoiceRepository } from "./sequelize";
|
||||
import { SendInvoiceUseCase } from "../application";
|
||||
import { VerifactuRecordFullPresenter } from "../application/presenters/domain/verifactu-record.full.presenter";
|
||||
import { VerifactuRecordService } from "../domain";
|
||||
//import { VerifactuRecordItemsReportPersenter } from "../application/presenters/queries/verifactu-record-items.report.presenter";
|
||||
import { VerifactuRecordDomainMapper } from "./mappers";
|
||||
import { VerifactuRecordRepository } from "./sequelize";
|
||||
|
||||
export type CustomerInvoiceDeps = {
|
||||
export type VerifactuRecordDeps = {
|
||||
transactionManager: SequelizeTransactionManager;
|
||||
mapperRegistry: IMapperRegistry;
|
||||
presenterRegistry: IPresenterRegistry;
|
||||
repo: CustomerInvoiceRepository;
|
||||
service: CustomerInvoiceService;
|
||||
catalogs: {
|
||||
taxes: JsonTaxCatalogProvider;
|
||||
};
|
||||
repo: VerifactuRecordRepository;
|
||||
service: VerifactuRecordService;
|
||||
build: {
|
||||
send: () => SendCustomerInvoiceUseCase;
|
||||
send: () => SendInvoiceUseCase;
|
||||
};
|
||||
getService: (name: string) => any;
|
||||
listServices: () => string[];
|
||||
};
|
||||
|
||||
export function buildVerifactuDependencies(params: ModuleParams): CustomerInvoiceDeps {
|
||||
const { database } = params;
|
||||
export function buildVerifactuDependencies(params: ModuleParams): VerifactuRecordDeps {
|
||||
const { database, listServices, getService } = params;
|
||||
const transactionManager = new SequelizeTransactionManager(database);
|
||||
|
||||
// Mapper Registry
|
||||
const mapperRegistry = new InMemoryMapperRegistry();
|
||||
mapperRegistry
|
||||
.registerDomainMapper(
|
||||
{ resource: "customer-invoice" },
|
||||
new CustomerInvoiceDomainMapper({ taxCatalog: catalogs.taxes })
|
||||
)
|
||||
.registerQueryMappers([
|
||||
mapperRegistry.registerDomainMapper(
|
||||
{ resource: "verifactu-record" },
|
||||
new VerifactuRecordDomainMapper({})
|
||||
);
|
||||
/* .registerQueryMappers([
|
||||
{
|
||||
key: { resource: "customer-invoice", query: "LIST" },
|
||||
mapper: new CustomerInvoiceListMapper(),
|
||||
key: { resource: "verifactu-record", query: "LIST" },
|
||||
mapper: new VerifactuRecordListMapper(),
|
||||
},
|
||||
]);
|
||||
*/
|
||||
|
||||
// Repository & Services
|
||||
const repo = new CustomerInvoiceRepository({ mapperRegistry, database });
|
||||
const service = new CustomerInvoiceService(repo);
|
||||
const repo = new VerifactuRecordRepository({ mapperRegistry, database });
|
||||
const service = new VerifactuRecordService(repo);
|
||||
|
||||
// Presenter Registry
|
||||
const presenterRegistry = new InMemoryPresenterRegistry();
|
||||
presenterRegistry.registerPresenters([
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice-items",
|
||||
resource: "verifactu-record",
|
||||
projection: "FULL",
|
||||
},
|
||||
presenter: new CustomerInvoiceItemsFullPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "recipient-invoice",
|
||||
projection: "FULL",
|
||||
},
|
||||
presenter: new RecipientInvoiceFullPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice",
|
||||
projection: "FULL",
|
||||
},
|
||||
presenter: new CustomerInvoiceFullPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice",
|
||||
projection: "LIST",
|
||||
},
|
||||
presenter: new ListCustomerInvoicesPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice",
|
||||
projection: "REPORT",
|
||||
format: "JSON",
|
||||
},
|
||||
presenter: new CustomerInvoiceReportPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice-items",
|
||||
projection: "REPORT",
|
||||
format: "JSON",
|
||||
},
|
||||
presenter: new CustomerInvoiceItemsReportPersenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice",
|
||||
projection: "REPORT",
|
||||
format: "HTML",
|
||||
},
|
||||
presenter: new CustomerInvoiceReportHTMLPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice",
|
||||
projection: "REPORT",
|
||||
format: "PDF",
|
||||
},
|
||||
presenter: new CustomerInvoiceReportPDFPresenter(presenterRegistry),
|
||||
presenter: new VerifactuRecordFullPresenter(presenterRegistry),
|
||||
},
|
||||
]);
|
||||
|
||||
@ -121,21 +67,20 @@ export function buildVerifactuDependencies(params: ModuleParams): CustomerInvoic
|
||||
mapperRegistry,
|
||||
presenterRegistry,
|
||||
service,
|
||||
catalogs,
|
||||
build: {
|
||||
list: () => new ListCustomerInvoicesUseCase(service, transactionManager, presenterRegistry),
|
||||
get: () => new GetCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||
create: () =>
|
||||
new CreateCustomerInvoiceUseCase(
|
||||
send: () => new SendInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||
// get: () => new GetVerifactuRecordUseCase(service, transactionManager, presenterRegistry),
|
||||
/* create: () =>
|
||||
new CreateVerifactuRecordUseCase(
|
||||
service,
|
||||
transactionManager,
|
||||
presenterRegistry,
|
||||
catalogs.taxes
|
||||
),
|
||||
// update: () => new UpdateCustomerInvoiceUseCase(service, transactionManager),
|
||||
// delete: () => new DeleteCustomerInvoiceUseCase(service, transactionManager),
|
||||
report: () =>
|
||||
new ReportCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||
*/
|
||||
// update: () => new UpdateVerifactuRecordUseCase(service, transactionManager),
|
||||
// delete: () => new DeleteVerifactuRecordUseCase(service, transactionManager),
|
||||
},
|
||||
listServices,
|
||||
getService,
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,8 +15,6 @@ export class SendInvoiceVerifactuController extends ExpressController {
|
||||
}
|
||||
const { invoice_id } = this.req.params;
|
||||
|
||||
console.log("CONTROLLER -----ESTO ES UNA PRUEBA>>>>>>");
|
||||
|
||||
const result = await this.useCase.execute({ invoice_id });
|
||||
|
||||
return result.match(
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth/api";
|
||||
import { ILogger, ModuleParams, validateRequest } from "@erp/core/api";
|
||||
import { ModuleParams, validateRequest } from "@erp/core/api";
|
||||
import { SendInvoiceByIdRequestSchema } from "@erp/verifactu/common";
|
||||
import { ILogger } from "@repo/rdx-logger";
|
||||
import { Application, NextFunction, Request, Response, Router } from "express";
|
||||
import { Sequelize } from "sequelize";
|
||||
import { SendCustomerInvoiceByIdRequestSchema } from "../../../common/dto";
|
||||
import { buildVerifactuDependencies } from "../dependencies";
|
||||
import { SendInvoiceVerifactuController } from "./controllers";
|
||||
|
||||
@ -14,7 +15,6 @@ export const verifactuRouter = (params: ModuleParams) => {
|
||||
logger: ILogger;
|
||||
};
|
||||
|
||||
console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>>>>>>>>>>>>>>>>>>");
|
||||
const deps = buildVerifactuDependencies(params);
|
||||
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
@ -40,11 +40,17 @@ export const verifactuRouter = (params: ModuleParams) => {
|
||||
router.get(
|
||||
"/:invoice_id/sendVerifactu",
|
||||
//checkTabContext,
|
||||
validateRequest(SendCustomerInvoiceByIdRequestSchema, "params"),
|
||||
validateRequest(SendInvoiceByIdRequestSchema, "params"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.build.report();
|
||||
const { listServices } = deps;
|
||||
const useCase = deps.build.send();
|
||||
|
||||
logger.info(listServices());
|
||||
|
||||
const controller = new SendInvoiceVerifactuController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
app.use(`${baseRoutePath}/verifactu`, router);
|
||||
};
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from "./verifactu-record.mapper";
|
||||
@ -1,5 +1,4 @@
|
||||
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||
import { VerifactuRecordEstado } from "@erp/customer-invoices/api/domain/aggregates/value-objects";
|
||||
import {
|
||||
UniqueID,
|
||||
ValidationErrorCollection,
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./domain";
|
||||
//export * from "./queries";
|
||||
@ -0,0 +1,74 @@
|
||||
import { MetadataSchema, MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const GetVerifactuRecordByIdResponseSchema = z.object({
|
||||
id: z.uuid(),
|
||||
company_id: z.uuid(),
|
||||
|
||||
invoice_number: z.string(),
|
||||
status: z.string(),
|
||||
series: z.string(),
|
||||
|
||||
invoice_date: z.string(),
|
||||
operation_date: z.string(),
|
||||
|
||||
notes: z.string(),
|
||||
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
|
||||
customer_id: z.string(),
|
||||
recipient: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
tin: z.string(),
|
||||
street: z.string(),
|
||||
street2: z.string(),
|
||||
city: z.string(),
|
||||
province: z.string(),
|
||||
postal_code: z.string(),
|
||||
country: z.string(),
|
||||
}),
|
||||
|
||||
taxes: z.string(),
|
||||
|
||||
payment_method: z
|
||||
.object({
|
||||
payment_id: z.string(),
|
||||
payment_description: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
subtotal_amount: MoneySchema,
|
||||
discount_percentage: PercentageSchema,
|
||||
discount_amount: MoneySchema,
|
||||
taxable_amount: MoneySchema,
|
||||
taxes_amount: MoneySchema,
|
||||
total_amount: MoneySchema,
|
||||
|
||||
items: z.array(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
isNonValued: z.string(),
|
||||
position: z.string(),
|
||||
description: z.string(),
|
||||
quantity: QuantitySchema,
|
||||
unit_amount: MoneySchema,
|
||||
|
||||
taxes: z.string(),
|
||||
|
||||
subtotal_amount: MoneySchema,
|
||||
discount_percentage: PercentageSchema,
|
||||
discount_amount: MoneySchema,
|
||||
taxable_amount: MoneySchema,
|
||||
taxes_amount: MoneySchema,
|
||||
total_amount: MoneySchema,
|
||||
})
|
||||
),
|
||||
|
||||
metadata: MetadataSchema.optional(),
|
||||
});
|
||||
|
||||
export type GetVerifactuRecordByIdResponseDTO = z.infer<
|
||||
typeof GetVerifactuRecordByIdResponseSchema
|
||||
>;
|
||||
@ -1 +1,2 @@
|
||||
export * from "./send-customer-invoice-by-id.request.dto";
|
||||
export * from "./get-verifactu-record-by-id.response.dto";
|
||||
export * from "./send-invoice-by-id.request.dto";
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import * as z from "zod/v4";
|
||||
|
||||
export const SendCustomerInvoiceByIdRequestSchema = z.object({
|
||||
invoice_id: z.string(),
|
||||
});
|
||||
|
||||
export type SendCustomerInvoiceByIdRequestDTO = z.infer<
|
||||
typeof SendCustomerInvoiceByIdRequestSchema
|
||||
>;
|
||||
@ -0,0 +1,7 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const SendInvoiceByIdRequestSchema = z.object({
|
||||
invoice_id: z.string(),
|
||||
});
|
||||
|
||||
export type SendInvoiceByIdRequestDTO = z.infer<typeof SendInvoiceByIdRequestSchema>;
|
||||
@ -20,6 +20,6 @@
|
||||
"dinero.js": "^1.9.1",
|
||||
"libphonenumber-js": "^1.11.20",
|
||||
"shallow-equal-object": "^1.1.1",
|
||||
"zod": "^3.24.4"
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { isPossiblePhoneNumber, parsePhoneNumberWithError } from "libphonenumber-js";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result, generateUUIDv4 } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
"i18next": "^25.1.1",
|
||||
"react-hook-form": "^7.58.1",
|
||||
"typescript": "^5.8.3",
|
||||
"zod": "^3.25.67"
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
@ -59,6 +59,6 @@
|
||||
"react-router-dom": "^6.26.0",
|
||||
"recharts": "^2.15.3",
|
||||
"sonner": "^2.0.3",
|
||||
"zod": "^3.24.4"
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ import {
|
||||
import * as React from "react";
|
||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
|
||||
import { toast } from "sonner";
|
||||
import * as z from "zod/v4";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { Badge } from "@repo/shadcn-ui/components/badge";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user