Clientes
This commit is contained in:
parent
c8e3191d05
commit
9683ee5102
@ -9,11 +9,11 @@ import {
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { CURRENCY_OPTIONS, LANGUAGE_OPTIONS } from "../../constants";
|
import { CURRENCY_OPTIONS, LANGUAGE_OPTIONS } from "../../constants";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CreateCustomerFormData } from "../../schemas";
|
import { CustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerAdditionalConfigFields = () => {
|
export const CustomerAdditionalConfigFields = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<CreateCustomerFormData>();
|
const { control } = useFormContext<CustomerFormData>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className='border-0 shadow-none'>
|
<Card className='border-0 shadow-none'>
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import {
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { COUNTRY_OPTIONS } from "../../constants";
|
import { COUNTRY_OPTIONS } from "../../constants";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CreateCustomerFormData } from "../../schemas";
|
import { CustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerAddressFields = () => {
|
export const CustomerAddressFields = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<CreateCustomerFormData>();
|
const { control } = useFormContext<CustomerFormData>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className='border-0 shadow-none'>
|
<Card className='border-0 shadow-none'>
|
||||||
|
|||||||
@ -15,11 +15,11 @@ import {
|
|||||||
} from "@repo/shadcn-ui/components";
|
} from "@repo/shadcn-ui/components";
|
||||||
import { useFormContext, useWatch } from "react-hook-form";
|
import { useFormContext, useWatch } from "react-hook-form";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CreateCustomerFormData } from "../../schemas";
|
import { CustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerBasicInfoFields = () => {
|
export const CustomerBasicInfoFields = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<CreateCustomerFormData>();
|
const { control } = useFormContext<CustomerFormData>();
|
||||||
|
|
||||||
const isCompany = useWatch({
|
const isCompany = useWatch({
|
||||||
control,
|
control,
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { FieldErrors, useFormContext } from "react-hook-form";
|
||||||
import { FieldErrors, FormProvider, useForm } from "react-hook-form";
|
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { CustomerFormData } from "../../schemas";
|
||||||
import { CreateCustomerFormData, CreateCustomerFormSchema } from "../../schemas";
|
|
||||||
import { FormDebug } from "../form-debug";
|
import { FormDebug } from "../form-debug";
|
||||||
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
||||||
import { CustomerAddressFields } from "./customer-address-fields";
|
import { CustomerAddressFields } from "./customer-address-fields";
|
||||||
@ -11,57 +9,26 @@ import { CustomerContactFields } from "./customer-contact-fields";
|
|||||||
|
|
||||||
interface CustomerFormProps {
|
interface CustomerFormProps {
|
||||||
formId: string;
|
formId: string;
|
||||||
initialValues: CreateCustomerFormData;
|
onSubmit: (data: CustomerFormData) => void;
|
||||||
onSubmit: (data: CreateCustomerFormData) => void;
|
onError: (errors: FieldErrors<CustomerFormData>) => void;
|
||||||
onError: (errors: FieldErrors<CreateCustomerFormData>) => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
onDirtyChange: (isDirty: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomerEditForm({
|
export const CustomerEditForm = ({ formId, onSubmit, onError }: CustomerFormProps) => {
|
||||||
formId,
|
const form = useFormContext<CustomerFormData>();
|
||||||
initialValues,
|
|
||||||
onSubmit,
|
|
||||||
onError,
|
|
||||||
disabled,
|
|
||||||
onDirtyChange,
|
|
||||||
}: CustomerFormProps) {
|
|
||||||
const form = useForm<CreateCustomerFormData>({
|
|
||||||
resolver: zodResolver(CreateCustomerFormSchema),
|
|
||||||
defaultValues: initialValues,
|
|
||||||
disabled,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
formState: { isDirty },
|
|
||||||
} = form;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (onDirtyChange) {
|
|
||||||
onDirtyChange(isDirty);
|
|
||||||
}
|
|
||||||
}, [isDirty, onDirtyChange]);
|
|
||||||
|
|
||||||
// Resetear el form si cambian los valores iniciales
|
|
||||||
useEffect(() => {
|
|
||||||
form.reset(initialValues);
|
|
||||||
}, [initialValues, form]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<form id={formId} onSubmit={form.handleSubmit(onSubmit, onError)}>
|
||||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit, onError)}>
|
<div className='xl:flex xl:flex-row-reverse xl:items-start'>
|
||||||
<div className='xl:flex xl:flex-row-reverse xl:items-start'>
|
<div className='w-full xl:w-6/12'>
|
||||||
<div className='w-full xl:w-6/12'>
|
<FormDebug />
|
||||||
<FormDebug />
|
|
||||||
</div>
|
|
||||||
<div className='w-full xl:grow'>
|
|
||||||
<CustomerBasicInfoFields />
|
|
||||||
<CustomerContactFields />
|
|
||||||
<CustomerAddressFields />
|
|
||||||
<CustomerAdditionalConfigFields />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div className='w-full xl:grow'>
|
||||||
</FormProvider>
|
<CustomerBasicInfoFields />
|
||||||
|
<CustomerContactFields />
|
||||||
|
<CustomerAddressFields />
|
||||||
|
<CustomerAdditionalConfigFields />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export * from "./use-create-customer-mutation";
|
export * from "./use-create-customer-mutation";
|
||||||
|
export * from "./use-customer-form";
|
||||||
export * from "./use-customer-query";
|
export * from "./use-customer-query";
|
||||||
export * from "./use-customers-context";
|
export * from "./use-customers-context";
|
||||||
export * from "./use-customers-query";
|
export * from "./use-customers-query";
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import { useDataSource } from "@erp/core/hooks";
|
|||||||
import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd";
|
import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { CreateCustomerRequestSchema, CustomerCreationResponseDTO } from "../../common";
|
import { CreateCustomerRequestSchema, CustomerCreationResponseDTO } from "../../common";
|
||||||
import { CreateCustomerFormData } from "../schemas";
|
import { CustomerFormData } from "../schemas";
|
||||||
import { CUSTOMERS_LIST_KEY } from "./use-update-customer-mutation";
|
import { CUSTOMERS_LIST_KEY } from "./use-update-customer-mutation";
|
||||||
|
|
||||||
type CreateCustomerPayload = {
|
type CreateCustomerPayload = {
|
||||||
data: CreateCustomerFormData;
|
data: CustomerFormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCreateCustomerMutation() {
|
export function useCreateCustomerMutation() {
|
||||||
|
|||||||
36
modules/customers/src/web/hooks/use-customer-form.ts
Normal file
36
modules/customers/src/web/hooks/use-customer-form.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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<CustomerFormData>({
|
||||||
|
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;
|
||||||
|
}
|
||||||
@ -2,14 +2,14 @@ import { useDataSource } from "@erp/core/hooks";
|
|||||||
import { ValidationErrorCollection } from "@repo/rdx-ddd";
|
import { ValidationErrorCollection } from "@repo/rdx-ddd";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { UpdateCustomerByIdRequestDTO, UpdateCustomerByIdRequestSchema } from "../../common";
|
import { UpdateCustomerByIdRequestDTO, UpdateCustomerByIdRequestSchema } from "../../common";
|
||||||
import { CreateCustomerFormData } from "../schemas";
|
import { CustomerFormData } from "../schemas";
|
||||||
import { CUSTOMER_QUERY_KEY } from "./use-customer-query";
|
import { CUSTOMER_QUERY_KEY } from "./use-customer-query";
|
||||||
|
|
||||||
export const CUSTOMERS_LIST_KEY = ["customers"] as const;
|
export const CUSTOMERS_LIST_KEY = ["customers"] as const;
|
||||||
|
|
||||||
type UpdateCustomerPayload = {
|
type UpdateCustomerPayload = {
|
||||||
id: string;
|
id: string;
|
||||||
data: CreateCustomerFormData;
|
data: CustomerFormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useUpdateCustomerMutation() {
|
export function useUpdateCustomerMutation() {
|
||||||
|
|||||||
@ -3,17 +3,15 @@ import { useNavigate } from "react-router-dom";
|
|||||||
|
|
||||||
import { FormCommitButtonGroup, UnsavedChangesProvider } from "@erp/core/hooks";
|
import { FormCommitButtonGroup, UnsavedChangesProvider } from "@erp/core/hooks";
|
||||||
import { showErrorToast, showSuccessToast } from "@repo/shadcn-ui/lib/utils";
|
import { showErrorToast, showSuccessToast } from "@repo/shadcn-ui/lib/utils";
|
||||||
import { useState } from "react";
|
import { FieldErrors, FormProvider } from "react-hook-form";
|
||||||
import { FieldErrors } from "react-hook-form";
|
|
||||||
import { CustomerEditForm, ErrorAlert } from "../../components";
|
import { CustomerEditForm, ErrorAlert } from "../../components";
|
||||||
import { useCreateCustomerMutation } from "../../hooks";
|
import { useCreateCustomerMutation, useCustomerForm } from "../../hooks";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CreateCustomerFormData, defaultCustomerFormData } from "../../schemas";
|
import { CustomerFormData, defaultCustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerCreate = () => {
|
export const CustomerCreate = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
|
||||||
|
|
||||||
// 1) Estado de creación (mutación)
|
// 1) Estado de creación (mutación)
|
||||||
const {
|
const {
|
||||||
@ -23,23 +21,26 @@ export const CustomerCreate = () => {
|
|||||||
error: createError,
|
error: createError,
|
||||||
} = useCreateCustomerMutation();
|
} = useCreateCustomerMutation();
|
||||||
|
|
||||||
// 2) Submit con navegación condicionada por éxito
|
// 2) Form hook
|
||||||
const handleSubmit = (formData: CreateCustomerFormData) => {
|
const form = useCustomerForm({
|
||||||
|
initialValues: defaultCustomerFormData,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3) Submit con navegación condicionada por éxito
|
||||||
|
const handleSubmit = (formData: CustomerFormData) => {
|
||||||
mutate(
|
mutate(
|
||||||
{ data: formData },
|
{ data: formData },
|
||||||
{
|
{
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
setIsDirty(false);
|
|
||||||
showSuccessToast(t("pages.create.successTitle"), t("pages.create.successMsg"));
|
showSuccessToast(t("pages.create.successTitle"), t("pages.create.successMsg"));
|
||||||
|
|
||||||
// El timeout es para que a React le dé tiempo a procesar
|
// 🔹 reset limpia el form e isDirty pasa a false
|
||||||
// el cambio de estado de isDirty / setIsDirty.
|
form.reset(defaultCustomerFormData);
|
||||||
setTimeout(() => {
|
|
||||||
navigate("/customers/list", {
|
navigate("/customers/list", {
|
||||||
state: { customerId: data.id, isNew: true },
|
state: { customerId: data.id, isNew: true },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
}, 0);
|
|
||||||
},
|
},
|
||||||
onError(error) {
|
onError(error) {
|
||||||
showErrorToast(t("pages.create.errorTitle"), error.message);
|
showErrorToast(t("pages.create.errorTitle"), error.message);
|
||||||
@ -48,7 +49,7 @@ export const CustomerCreate = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (errors: FieldErrors<CreateCustomerFormData>) => {
|
const handleError = (errors: FieldErrors<CustomerFormData>) => {
|
||||||
console.error("Errores en el formulario:", errors);
|
console.error("Errores en el formulario:", errors);
|
||||||
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
||||||
};
|
};
|
||||||
@ -57,7 +58,7 @@ export const CustomerCreate = () => {
|
|||||||
<>
|
<>
|
||||||
<AppBreadcrumb />
|
<AppBreadcrumb />
|
||||||
<AppContent>
|
<AppContent>
|
||||||
<UnsavedChangesProvider isDirty={isDirty}>
|
<UnsavedChangesProvider isDirty={form.formState.isDirty}>
|
||||||
<div className='flex items-center justify-between space-y-4 px-6'>
|
<div className='flex items-center justify-between space-y-4 px-6'>
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<h2 className='text-2xl font-bold tracking-tight text-balance scroll-m-2'>
|
<h2 className='text-2xl font-bold tracking-tight text-balance scroll-m-2'>
|
||||||
@ -90,13 +91,13 @@ export const CustomerCreate = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='flex flex-1 flex-col gap-4 p-4'>
|
<div className='flex flex-1 flex-col gap-4 p-4'>
|
||||||
<CustomerEditForm
|
<FormProvider {...form}>
|
||||||
formId={"customer-create-form"} // para que el botón del header pueda hacer submit
|
<CustomerEditForm
|
||||||
initialValues={defaultCustomerFormData}
|
formId='customer-create-form'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onError={handleError}
|
onError={handleError}
|
||||||
onDirtyChange={setIsDirty}
|
/>
|
||||||
/>
|
</FormProvider>
|
||||||
</div>
|
</div>
|
||||||
</UnsavedChangesProvider>
|
</UnsavedChangesProvider>
|
||||||
</AppContent>
|
</AppContent>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom";
|
|||||||
|
|
||||||
import { FormCommitButtonGroup, UnsavedChangesProvider, useUrlParamId } from "@erp/core/hooks";
|
import { FormCommitButtonGroup, UnsavedChangesProvider, useUrlParamId } from "@erp/core/hooks";
|
||||||
import { showErrorToast, showSuccessToast } from "@repo/shadcn-ui/lib/utils";
|
import { showErrorToast, showSuccessToast } from "@repo/shadcn-ui/lib/utils";
|
||||||
import { useState } from "react";
|
|
||||||
import { FieldErrors } from "react-hook-form";
|
import { FieldErrors } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
CustomerEditForm,
|
CustomerEditForm,
|
||||||
@ -11,15 +10,14 @@ import {
|
|||||||
ErrorAlert,
|
ErrorAlert,
|
||||||
NotFoundCard,
|
NotFoundCard,
|
||||||
} from "../../components";
|
} from "../../components";
|
||||||
import { useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
|
import { useCustomerForm, useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CreateCustomerFormData } from "../../schemas";
|
import { CustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerUpdate = () => {
|
export const CustomerUpdate = () => {
|
||||||
const customerId = useUrlParamId();
|
const customerId = useUrlParamId();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
|
||||||
|
|
||||||
// 1) Estado de carga del cliente (query)
|
// 1) Estado de carga del cliente (query)
|
||||||
const {
|
const {
|
||||||
@ -37,8 +35,13 @@ export const CustomerUpdate = () => {
|
|||||||
error: updateError,
|
error: updateError,
|
||||||
} = useUpdateCustomerMutation();
|
} = useUpdateCustomerMutation();
|
||||||
|
|
||||||
|
// 3) Form hook
|
||||||
|
const form = useCustomerForm({
|
||||||
|
initialValues: customerData,
|
||||||
|
});
|
||||||
|
|
||||||
// 3) Submit con navegación condicionada por éxito
|
// 3) Submit con navegación condicionada por éxito
|
||||||
const handleSubmit = (formData: CreateCustomerFormData) => {
|
const handleSubmit = (formData: CustomerFormData) => {
|
||||||
mutate(
|
mutate(
|
||||||
{ id: customerId!, data: formData },
|
{ id: customerId!, data: formData },
|
||||||
{
|
{
|
||||||
@ -62,7 +65,7 @@ export const CustomerUpdate = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (errors: FieldErrors<CreateCustomerFormData>) => {
|
const handleError = (errors: FieldErrors<CustomerFormData>) => {
|
||||||
console.error("Errores en el formulario:", errors);
|
console.error("Errores en el formulario:", errors);
|
||||||
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import * as z from "zod/v4";
|
import * as z from "zod/v4";
|
||||||
import { CreateCustomerFormSchema } from "./customer-create.form.schema";
|
import { CustomerFormSchema } from "./customer.form.schema";
|
||||||
|
|
||||||
export const UpdateCustomerFormSchema = CreateCustomerFormSchema.extend({
|
export const UpdateCustomerFormSchema = CustomerFormSchema.extend({
|
||||||
is_company: CreateCustomerFormSchema.shape.is_company.optional(),
|
is_company: CustomerFormSchema.shape.is_company.optional(),
|
||||||
name: CreateCustomerFormSchema.shape.name.optional(),
|
name: CustomerFormSchema.shape.name.optional(),
|
||||||
default_taxes: z.array(z.string()).optional(),
|
default_taxes: z.array(z.string()).optional(),
|
||||||
|
|
||||||
country: CreateCustomerFormSchema.shape.country.optional(),
|
country: CustomerFormSchema.shape.country.optional(),
|
||||||
|
|
||||||
language_code: CreateCustomerFormSchema.shape.language_code.optional(),
|
language_code: CustomerFormSchema.shape.language_code.optional(),
|
||||||
currency_code: CreateCustomerFormSchema.shape.currency_code.optional(),
|
currency_code: CustomerFormSchema.shape.currency_code.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UpdateCustomerFormData = z.infer<typeof UpdateCustomerFormSchema>;
|
export type UpdateCustomerFormData = z.infer<typeof UpdateCustomerFormSchema>;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as z from "zod/v4";
|
import * as z from "zod/v4";
|
||||||
|
|
||||||
export const CreateCustomerFormSchema = z.object({
|
export const CustomerFormSchema = z.object({
|
||||||
reference: z.string().optional(),
|
reference: z.string().optional(),
|
||||||
|
|
||||||
is_company: z.enum(["true", "false"]),
|
is_company: z.enum(["true", "false"]),
|
||||||
@ -55,9 +55,9 @@ export const CreateCustomerFormSchema = z.object({
|
|||||||
.default("EUR"),
|
.default("EUR"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CreateCustomerFormData = z.infer<typeof CreateCustomerFormSchema>;
|
export type CustomerFormData = z.infer<typeof CustomerFormSchema>;
|
||||||
|
|
||||||
export const defaultCustomerFormData: CreateCustomerFormData = {
|
export const defaultCustomerFormData: CustomerFormData = {
|
||||||
reference: "",
|
reference: "",
|
||||||
|
|
||||||
is_company: "true",
|
is_company: "true",
|
||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./customer-create.form.schema";
|
|
||||||
export * from "./customer.api.schema";
|
export * from "./customer.api.schema";
|
||||||
|
export * from "./customer.form.schema";
|
||||||
|
|||||||
16
modules/verifactu/package.json
Normal file
16
modules/verifactu/package.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@erp/verifactu",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"types": "src/index.ts",
|
||||||
|
"exports": {},
|
||||||
|
"peerDependencies": {
|
||||||
|
"sequelize": "^6.37.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"dependencies": {
|
||||||
|
"@erp/core": "workspace:*",
|
||||||
|
"@repo/rdx-ddd": "workspace:*",
|
||||||
|
"@repo/rdx-utils": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1 @@
|
|||||||
export * from "./send-invoice-verifactu.use-case";
|
export * from "./send";
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { VerifactuRecordService } from "../../../domain/services/verifactu-record.service";
|
import { Transaction } from "sequelize";
|
||||||
|
import { VerifactuRecordService } from "../../../domain";
|
||||||
|
|
||||||
type SendInvoiceVerifactuUseCaseInput = {
|
type SendInvoiceVerifactuUseCaseInput = {
|
||||||
invoice_id: string;
|
invoice_id: string;
|
||||||
@ -24,7 +25,7 @@ export class SendInvoiceVerifactuUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const invoiceId = idOrError.data;
|
const invoiceId = idOrError.data;
|
||||||
return this.transactionManager.complete(async (Transaction) => {
|
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||||
try {
|
try {
|
||||||
const invoiceOrError = await this.service.sendInvoiceToVerifactu(invoiceId, transaction);
|
const invoiceOrError = await this.service.sendInvoiceToVerifactu(invoiceId, transaction);
|
||||||
if (invoiceOrError.isFailure) {
|
if (invoiceOrError.isFailure) {
|
||||||
|
|||||||
3
modules/verifactu/src/api/domain/index.ts
Normal file
3
modules/verifactu/src/api/domain/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./aggregates";
|
||||||
|
export * from "./repositories";
|
||||||
|
export * from "./services";
|
||||||
1
modules/verifactu/src/api/domain/repositories/index.ts
Normal file
1
modules/verifactu/src/api/domain/repositories/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./verifactu-repository.interface";
|
||||||
1
modules/verifactu/src/api/domain/services/index.ts
Normal file
1
modules/verifactu/src/api/domain/services/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./verifactu-record.service";
|
||||||
161
modules/verifactu/src/api/infrastructure/dependencies.ts
Normal file
161
modules/verifactu/src/api/infrastructure/dependencies.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// modules/invoice/infrastructure/invoice-dependencies.factory.ts
|
||||||
|
|
||||||
|
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
|
||||||
|
|
||||||
|
import {
|
||||||
|
InMemoryMapperRegistry,
|
||||||
|
InMemoryPresenterRegistry,
|
||||||
|
SequelizeTransactionManager,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CreateCustomerInvoiceUseCase,
|
||||||
|
CustomerInvoiceFullPresenter,
|
||||||
|
CustomerInvoiceItemsFullPresenter,
|
||||||
|
CustomerInvoiceReportHTMLPresenter,
|
||||||
|
CustomerInvoiceReportPDFPresenter,
|
||||||
|
CustomerInvoiceReportPresenter,
|
||||||
|
GetCustomerInvoiceUseCase,
|
||||||
|
ListCustomerInvoicesPresenter,
|
||||||
|
ListCustomerInvoicesUseCase,
|
||||||
|
RecipientInvoiceFullPresenter,
|
||||||
|
ReportCustomerInvoiceUseCase,
|
||||||
|
} from "../application";
|
||||||
|
|
||||||
|
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core";
|
||||||
|
import { CustomerInvoiceItemsReportPersenter } from "../application/presenters/queries/customer-invoice-items.report.presenter";
|
||||||
|
import { CustomerInvoiceService } from "../domain";
|
||||||
|
import { CustomerInvoiceDomainMapper, CustomerInvoiceListMapper } from "./mappers";
|
||||||
|
import { CustomerInvoiceRepository } from "./sequelize";
|
||||||
|
|
||||||
|
export type CustomerInvoiceDeps = {
|
||||||
|
transactionManager: SequelizeTransactionManager;
|
||||||
|
mapperRegistry: IMapperRegistry;
|
||||||
|
presenterRegistry: IPresenterRegistry;
|
||||||
|
repo: CustomerInvoiceRepository;
|
||||||
|
service: CustomerInvoiceService;
|
||||||
|
catalogs: {
|
||||||
|
taxes: JsonTaxCatalogProvider;
|
||||||
|
};
|
||||||
|
build: {
|
||||||
|
list: () => ListCustomerInvoicesUseCase;
|
||||||
|
get: () => GetCustomerInvoiceUseCase;
|
||||||
|
create: () => CreateCustomerInvoiceUseCase;
|
||||||
|
//update: () => UpdateCustomerInvoiceUseCase;
|
||||||
|
//delete: () => DeleteCustomerInvoiceUseCase;
|
||||||
|
report: () => ReportCustomerInvoiceUseCase;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildCustomerInvoiceDependencies(params: ModuleParams): CustomerInvoiceDeps {
|
||||||
|
const { database } = params;
|
||||||
|
const transactionManager = new SequelizeTransactionManager(database);
|
||||||
|
const catalogs = { taxes: spainTaxCatalogProvider };
|
||||||
|
|
||||||
|
// Mapper Registry
|
||||||
|
const mapperRegistry = new InMemoryMapperRegistry();
|
||||||
|
mapperRegistry
|
||||||
|
.registerDomainMapper(
|
||||||
|
{ resource: "customer-invoice" },
|
||||||
|
new CustomerInvoiceDomainMapper({ taxCatalog: catalogs.taxes })
|
||||||
|
)
|
||||||
|
.registerQueryMappers([
|
||||||
|
{
|
||||||
|
key: { resource: "customer-invoice", query: "LIST" },
|
||||||
|
mapper: new CustomerInvoiceListMapper(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Repository & Services
|
||||||
|
const repo = new CustomerInvoiceRepository({ mapperRegistry, database });
|
||||||
|
const service = new CustomerInvoiceService(repo);
|
||||||
|
|
||||||
|
// Presenter Registry
|
||||||
|
const presenterRegistry = new InMemoryPresenterRegistry();
|
||||||
|
presenterRegistry.registerPresenters([
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
resource: "customer-invoice-items",
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
transactionManager,
|
||||||
|
repo,
|
||||||
|
mapperRegistry,
|
||||||
|
presenterRegistry,
|
||||||
|
service,
|
||||||
|
catalogs,
|
||||||
|
build: {
|
||||||
|
list: () => new ListCustomerInvoicesUseCase(service, transactionManager, presenterRegistry),
|
||||||
|
get: () => new GetCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||||
|
create: () =>
|
||||||
|
new CreateCustomerInvoiceUseCase(
|
||||||
|
service,
|
||||||
|
transactionManager,
|
||||||
|
presenterRegistry,
|
||||||
|
catalogs.taxes
|
||||||
|
),
|
||||||
|
// update: () => new UpdateCustomerInvoiceUseCase(service, transactionManager),
|
||||||
|
// delete: () => new DeleteCustomerInvoiceUseCase(service, transactionManager),
|
||||||
|
report: () =>
|
||||||
|
new ReportCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
3
modules/verifactu/src/api/infrastructure/index.ts
Normal file
3
modules/verifactu/src/api/infrastructure/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./mappers";
|
||||||
|
export * from "./sequelize";
|
||||||
|
export * from "./express";
|
||||||
33
modules/verifactu/tsconfig.json
Normal file
33
modules/verifactu/tsconfig.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@erp/customer-invoices/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@ -657,6 +657,21 @@ importers:
|
|||||||
specifier: ^3.2.4
|
specifier: ^3.2.4
|
||||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.3)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.0)(stylus@0.62.0)(terser@5.40.0)(tsx@4.19.4)
|
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.3)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.0)(stylus@0.62.0)(terser@5.40.0)(tsx@4.19.4)
|
||||||
|
|
||||||
|
modules/verifactu:
|
||||||
|
dependencies:
|
||||||
|
'@erp/core':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../core
|
||||||
|
'@repo/rdx-ddd':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/rdx-ddd
|
||||||
|
'@repo/rdx-utils':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/rdx-utils
|
||||||
|
sequelize:
|
||||||
|
specifier: ^6.37.5
|
||||||
|
version: 6.37.7(mysql2@3.14.1)
|
||||||
|
|
||||||
packages/rdx-criteria:
|
packages/rdx-criteria:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@codelytv/criteria':
|
'@codelytv/criteria':
|
||||||
@ -12806,7 +12821,7 @@ snapshots:
|
|||||||
|
|
||||||
wkx@0.5.0:
|
wkx@0.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 24.0.3
|
'@types/node': 22.15.32
|
||||||
|
|
||||||
wordwrap@1.0.0: {}
|
wordwrap@1.0.0: {}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user