Repaso de clientes

This commit is contained in:
David Arranz 2026-04-04 19:38:09 +02:00
parent 6f11d0cebd
commit 04184ebf1d
13 changed files with 98 additions and 99 deletions

View File

@ -4,4 +4,4 @@ import {
} from "./get-customer-by-id.response.dto";
export const CreateCustomerResponseSchema = GetCustomerByIdResponseSchema;
export type CustomerCreationResponseDTO = GetCustomerByIdResponseDTO;
export type CreateCustomerResponseDTO = GetCustomerByIdResponseDTO;

View File

@ -48,11 +48,11 @@ export const useCustomerCreateController = (options?: UseCustomerCreateControlle
const submitHandler = form.handleSubmit(
async (formData) => {
const inputPayload: CreateCustomerParams = buildCreateCustomerParams(formData);
const params: CreateCustomerParams = buildCreateCustomerParams(formData);
try {
// Enviamos cambios al servidor
const created = await mutateAsync(inputPayload); // payload es CustomerCreatePayload
const created = await mutateAsync(params); // payload es CustomerCreatePayload
if (options?.successToasts !== false) {
showSuccessToast(
@ -69,7 +69,7 @@ export const useCustomerCreateController = (options?: UseCustomerCreateControlle
showErrorToast(t("pages.create.error.title"), normalizedError.message);
}
options?.onError?.(normalizedError, inputPayload);
options?.onError?.(normalizedError, params);
}
},
(errors: FieldErrors<CustomerCreateForm>) => {

View File

@ -21,38 +21,42 @@ import type { CustomerCreateForm } from "../entities";
*/
export const buildCreateCustomerParams = (formData: CustomerCreateForm): CreateCustomerParams => {
// La API de creación de cliente requiere un ID único para el nuevo cliente
const id = UniqueID.generateNewID().toString();
return {
// Generamos un ID único para el nuevo cliente.
// La API de creación de cliente requiere un ID,
id: UniqueID.generateNewID().toString(),
id,
reference: formData.reference,
is_company: formData.isCompany ? "1" : "0",
name: formData.name,
trade_name: formData.tradeName,
tin: formData.tin,
default_taxes: formData.defaultTaxes,
data: {
id,
reference: formData.reference,
is_company: formData.isCompany ? "1" : "0",
name: formData.name,
trade_name: formData.tradeName,
tin: formData.tin,
default_taxes: formData.defaultTaxes,
street: formData.street,
street2: formData.street2,
city: formData.city,
province: formData.province,
postal_code: formData.postalCode,
country: formData.country,
street: formData.street,
street2: formData.street2,
city: formData.city,
province: formData.province,
postal_code: formData.postalCode,
country: formData.country,
email_primary: formData.primaryEmail,
email_secondary: formData.secondaryEmail,
phone_primary: formData.primaryPhone,
phone_secondary: formData.secondaryPhone,
mobile_primary: formData.primaryMobile,
mobile_secondary: formData.secondaryMobile,
email_primary: formData.primaryEmail,
email_secondary: formData.secondaryEmail,
phone_primary: formData.primaryPhone,
phone_secondary: formData.secondaryPhone,
mobile_primary: formData.primaryMobile,
mobile_secondary: formData.secondaryMobile,
fax: formData.fax,
website: formData.website,
fax: formData.fax,
website: formData.website,
legal_record: formData.legalRecord,
legal_record: formData.legalRecord,
language_code: formData.languageCode,
currency_code: formData.currencyCode,
language_code: formData.languageCode,
currency_code: formData.currencyCode,
},
};
};

View File

@ -1,4 +1,4 @@
import type { CustomerCreationResponseDTO } from "@erp/customers/common";
import type { CreateCustomerResponseDTO } from "@erp/customers/common";
import type { GetCustomerByIdResult, UpdateCustomerByIdResult } from "../api";
import type { Customer } from "../entities";
@ -18,7 +18,7 @@ import type { Customer } from "../entities";
export const GetCustomerByIdAdapter = {
fromDTO(
dto: GetCustomerByIdResult | CustomerCreationResponseDTO | UpdateCustomerByIdResult,
dto: GetCustomerByIdResult | CreateCustomerResponseDTO | UpdateCustomerByIdResult,
context?: unknown
): Customer {
const taxesAdapter = (taxes: string) =>

View File

@ -1,6 +1,6 @@
import type { IDataSource } from "@erp/core/client";
import type { CreateCustomerRequestDTO, CustomerCreationResponseDTO } from "../../../common";
import type { CreateCustomerRequestDTO, CreateCustomerResponseDTO } from "../../../common";
/**
* Crea un nuevo cliente en el sistema utilizando la fuente de datos proporcionada.
@ -11,12 +11,22 @@ import type { CreateCustomerRequestDTO, CustomerCreationResponseDTO } from "../.
* @throws Error si el ID del cliente no es proporcionado o si la creación falla.
*/
export type CreateCustomerParams = CreateCustomerRequestDTO;
export interface CreateCustomerParams {
id: string;
data: CreateCustomerRequestDTO;
}
export type CreateCustomerResult = CustomerCreationResponseDTO;
export type CreateCustomerResult = CreateCustomerResponseDTO;
export function createCustomer(dataSource: IDataSource, params: CreateCustomerParams) {
const { id } = params;
const { id, data } = params;
if (!id) throw new Error("customerId is required");
return dataSource.createOne<CreateCustomerRequestDTO, CreateCustomerResult>("customers", params);
if (id !== data.id) throw new Error("customerId in params must match id in data");
return dataSource.createOne<CreateCustomerRequestDTO, CreateCustomerResponseDTO>(
"customers",
data
);
}

View File

@ -5,23 +5,22 @@ import type { IDataSource } from "@erp/core/client";
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para eliminar el cliente.
* @param signal - Un AbortSignal para cancelar la solicitud si es necesario.
* @returns Una promesa que se resuelve cuando el cliente ha sido eliminado exitosamente.
* @throws Error si el ID del cliente no es proporcionado o si la eliminación falla.
*/
export type DeleteCustomerByIdParams = {
export interface DeleteCustomerByIdParams {
id: string;
};
signal?: AbortSignal;
}
export type DeleteCustomerByIdResult = void;
export function deleteCustomerById(
dataSource: IDataSource,
params: DeleteCustomerByIdParams,
signal?: AbortSignal
params: DeleteCustomerByIdParams
): Promise<DeleteCustomerByIdResult> {
const { id } = params;
const { id, signal } = params;
if (!id) throw new Error("customerId is required");
return dataSource.deleteOne<DeleteCustomerByIdResult>("customers", id, {
signal,

View File

@ -7,23 +7,19 @@ import type { GetCustomerByIdResponseDTO } from "../../../common";
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para obtener el cliente, incluyendo su ID.
* @param signal - Un AbortSignal para cancelar la solicitud si es necesario.
* @returns Una promesa que resuelve con los detalles del cliente solicitado.
* @throws Error si el ID del cliente no es proporcionado o si la recuperación falla.
*/
export type GetCustomerByIdParams = {
export interface GetCustomerByIdParams {
id: string;
};
signal?: AbortSignal;
}
export type GetCustomerByIdResult = GetCustomerByIdResponseDTO;
export function getCustomerById(
dataSource: IDataSource,
params: GetCustomerByIdParams,
signal?: AbortSignal
) {
const { id } = params;
export function getCustomerById(dataSource: IDataSource, params: GetCustomerByIdParams) {
const { id, signal } = params;
if (!id) throw new Error("customerId is required");
return dataSource.getOne<GetCustomerByIdResult>("customers", id, { signal });
return dataSource.getOne<GetCustomerByIdResponseDTO>("customers", id, { signal });
}

View File

@ -1,5 +1,5 @@
export * from "./create-customer.api";
export * from "./delete-customer-by-id.api";
export * from "./get-customer-by-id.api";
export * from "./get-list-customers-by-criteria.api";
export * from "./list-customers-by-criteria.api";
export * from "./update-customer-by-id.api";

View File

@ -9,24 +9,23 @@ import type { ListCustomersResponseDTO } from "../../../common";
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para listar los clientes, incluyendo los criterios de búsqueda.
* @param signal - Un AbortSignal para cancelar la solicitud si es necesario.
* @returns Una promesa que resuelve con una lista de clientes que cumplen con los criterios especificados.
* @throws Error si la recuperación de la lista de clientes falla.
*/
export type ListCustomersByCriteriaParams = {
criteria: CriteriaDTO;
criteria?: CriteriaDTO;
signal?: AbortSignal;
};
export type ListCustomersResult = ListCustomersResponseDTO;
export function getListCustomersByCriteria(
dataSource: IDataSource,
params: ListCustomersByCriteriaParams,
signal?: AbortSignal
params: ListCustomersByCriteriaParams
): Promise<ListCustomersResult> {
const { criteria } = params;
return dataSource.getList<ListCustomersResult>("customers", {
const { criteria, signal } = params || { criteria: undefined, signal: undefined };
return dataSource.getList<ListCustomersResponseDTO>("customers", {
signal,
...criteria,
});

View File

@ -25,7 +25,7 @@ export function updateCustomerById(
const { id, data } = params;
if (!id) throw new Error("customerId is required");
return dataSource.updateOne<UpdateCustomerByIdRequestDTO, UpdateCustomerByIdResult>(
return dataSource.updateOne<UpdateCustomerByIdRequestDTO, UpdateCustomerByIdResponseDTO>(
"customers",
id,
data

View File

@ -1,5 +1,5 @@
import { useDataSource } from "@erp/core/hooks";
import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd";
import { ValidationErrorCollection } from "@repo/rdx-ddd";
import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query";
import { CreateCustomerRequestSchema } from "../../../common";
@ -25,15 +25,12 @@ export const useCustomerCreateMutation = () => {
mutationKey: CUSTOMER_CREATE_KEY,
mutationFn: async (payload) => {
const id = UniqueID.generateNewID().toString();
const payloadWithId: CreateCustomerParams = { ...payload, id };
const result = schema.safeParse(payloadWithId);
const result = schema.safeParse(payload);
if (!result.success) {
throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error));
}
const dto: CreateCustomerResult = await createCustomer(dataSource, payloadWithId);
const dto: CreateCustomerResult = await createCustomer(dataSource, payload);
return GetCustomerByIdAdapter.fromDTO(dto);
},

View File

@ -1,11 +1,7 @@
import { useDataSource } from "@erp/core/hooks";
import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query";
import {
type DeleteCustomerByIdParams,
type DeleteCustomerByIdResult,
deleteCustomerById,
} from "../api";
import { type DeleteCustomerByIdParams, deleteCustomerById } from "../api";
import {
type DeleteCustomerCacheContext,
@ -22,33 +18,31 @@ export const useCustomerDeleteMutation = () => {
const queryClient = useQueryClient();
const dataSource = useDataSource();
return useMutation<
DeleteCustomerByIdResult,
DefaultError,
DeleteCustomerByIdParams,
DeleteCustomerContext
>({
mutationKey: CUSTOMER_DELETE_KEY,
mutationFn: async ({ id }) => {
if (!id) {
throw new Error("customerId is required");
}
return useMutation<{ id: string }, DefaultError, DeleteCustomerByIdParams, DeleteCustomerContext>(
{
mutationKey: CUSTOMER_DELETE_KEY,
mutationFn: async ({ id, signal }) => {
if (!id) {
throw new Error("customerId is required");
}
await deleteCustomerById(dataSource, { id }, new AbortController().signal);
},
onMutate: async ({ id }) => {
return prepareDeleteCustomerOptimisticUpdate(queryClient, id);
},
await deleteCustomerById(dataSource, { id, signal });
return { id };
},
onMutate: async ({ id }) => {
return prepareDeleteCustomerOptimisticUpdate(queryClient, id);
},
onError: (_error, _variables, context) => {
rollbackDeleteCustomerOptimisticUpdate(queryClient, context);
},
onError: (_error, _variables, context) => {
rollbackDeleteCustomerOptimisticUpdate(queryClient, context);
},
onSuccess: (_, variables) => {
finalizeDeletedCustomerCaches(queryClient, variables.id);
},
onSettled: async () => {
await invalidateCustomerListQueries(queryClient);
},
});
onSuccess: ({ id }) => {
finalizeDeletedCustomerCaches(queryClient, id);
},
onSettled: async () => {
await invalidateCustomerListQueries(queryClient);
},
}
);
};

View File

@ -98,8 +98,8 @@ export const useCustomerUpdateController = (
}
const previousData = customerData;
const patchData = buildCustomerUpdatePatch(formData, dirtyFields);
const patchData = buildCustomerUpdatePatch(formData, dirtyFields);
const params: UpdateCustomerByIdParams = buildUpdateCustomerByIdParams(customerId, patchData);
try {