diff --git a/modules/customer-invoices/src/web/proformas/create/utils/build-proforma-create-default.ts b/modules/customer-invoices/src/web/proformas/create/utils/build-proforma-create-default.ts index 980fd914..d305afec 100644 --- a/modules/customer-invoices/src/web/proformas/create/utils/build-proforma-create-default.ts +++ b/modules/customer-invoices/src/web/proformas/create/utils/build-proforma-create-default.ts @@ -6,7 +6,7 @@ export const buildProformaCreateDefault = (): ProformaCreateForm => { return { customerId: "", invoiceDate: DateHelper.buildTodayIsoDate(), - series: "", + series: "F26", languageCode: "es", currencyCode: "EUR", }; diff --git a/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts b/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts index 31bf0245..f58d7579 100644 --- a/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts +++ b/modules/customers/src/common/features/customer-selection/entities/customer-selection-option.entity.ts @@ -2,6 +2,9 @@ * Representa un cliente seleccionable desde cualquier módulo. * Debe ser lo suficientemente rico para mostrar en UI, * pero sin arrastrar todo el shape de Customer. + * + * No usar objetos de dominio, todo tipos primitivos y strings, + * para desacoplar de la capa de dominio. */ export interface CustomerSelectionOption { id: string; diff --git a/modules/customers/src/common/features/get-customer/controllers/index.ts b/modules/customers/src/common/features/get-customer/controllers/index.ts new file mode 100644 index 00000000..d60e0f32 --- /dev/null +++ b/modules/customers/src/common/features/get-customer/controllers/index.ts @@ -0,0 +1 @@ +export * from "./use-get-customer-controller"; diff --git a/modules/customers/src/common/features/get-customer/controllers/use-get-customer-controller.ts b/modules/customers/src/common/features/get-customer/controllers/use-get-customer-controller.ts new file mode 100644 index 00000000..7cfc8b23 --- /dev/null +++ b/modules/customers/src/common/features/get-customer/controllers/use-get-customer-controller.ts @@ -0,0 +1,26 @@ +import { useCustomerGetQuery } from "../../../../web/shared"; +import { buildCustomerData } from "../utils"; + +export const useGetCustomerController = (customerId: string, options?: { enabled?: boolean }) => { + const { + data, + isLoading, + isError: isLoadError, + error: loadError, + refetch, + } = useCustomerGetQuery({ + id: customerId, + enabled: options?.enabled ?? true, + }); + + const customerData = data ? buildCustomerData(data) : undefined; + + return { + customerData, + isLoading, + isLoadError, + loadError, + + refetch, + }; +}; diff --git a/modules/customers/src/common/features/get-customer/entities/customer-data.entity.ts b/modules/customers/src/common/features/get-customer/entities/customer-data.entity.ts new file mode 100644 index 00000000..d5ac4bca --- /dev/null +++ b/modules/customers/src/common/features/get-customer/entities/customer-data.entity.ts @@ -0,0 +1,64 @@ +/** + * Representa un cliente con todo el shape de Customer. + * + * No usar objetos de dominio, todo tipos primitivos y strings, + * para desacoplar de la capa de dominio. + */ + +export interface CustomerData { + id: string; + status: string; + reference: string | null; + + isCompany: boolean; + name: string; + tradeName: string | null; + tin: string | null; + + address: { + street: string | null; + street2: string | null; + city: string | null; + province: string | null; + postalCode: string | null; + country: string | null; + }; + + contact: { + emailPrimary: string | null; + emailSecondary: string | null; + phonePrimary: string | null; + phoneSecondary: string | null; + mobilePrimary: string | null; + mobileSecondary: string | null; + + fax: string | null; + website: string | null; + }; + + legalRecord: string | null; + + paymentMethod: { + id: string; + name: string; + description: string; + } | null; + + paymentTerm: { + id: string; + description: string; + } | null; + + taxRegime: { + code: string; + description: string; + } | null; + + fiscalDefaults: { + usesEquivalenceSurcharge: boolean; + usesRetention: boolean; + }; + + languageCode: string; + currencyCode: string; +} diff --git a/modules/customers/src/common/features/get-customer/entities/index.ts b/modules/customers/src/common/features/get-customer/entities/index.ts new file mode 100644 index 00000000..3d233006 --- /dev/null +++ b/modules/customers/src/common/features/get-customer/entities/index.ts @@ -0,0 +1 @@ +export * from './customer-data.entity'; diff --git a/modules/customers/src/common/features/get-customer/index.ts b/modules/customers/src/common/features/get-customer/index.ts new file mode 100644 index 00000000..6b67c80e --- /dev/null +++ b/modules/customers/src/common/features/get-customer/index.ts @@ -0,0 +1 @@ +export * from "./controllers"; diff --git a/modules/customers/src/common/features/get-customer/utils/build-customer-data.ts b/modules/customers/src/common/features/get-customer/utils/build-customer-data.ts new file mode 100644 index 00000000..b48dedc8 --- /dev/null +++ b/modules/customers/src/common/features/get-customer/utils/build-customer-data.ts @@ -0,0 +1,71 @@ +import type { Customer } from "../../../../web/shared"; +import type { CustomerData } from "../entities"; + +/** + * Mapper explícito para desacoplar UI del shape de customers/shared. + */ +export const buildCustomerData = (customer: Customer): CustomerData => { + return { + id: customer.id, + status: customer.status, + reference: customer.reference, + + isCompany: customer.isCompany, + name: customer.name, + tradeName: customer.tradeName, + tin: customer.tin, + + address: { + street: customer.address.street, + street2: null, + city: customer.address.city, + province: customer.address.province, + postalCode: customer.address.postalCode, + country: customer.address.country, + }, + + contact: { + emailPrimary: customer.contact.primaryEmail, + emailSecondary: customer.contact.secondaryEmail, + phonePrimary: customer.contact.primaryPhone, + phoneSecondary: customer.contact.secondaryPhone, + mobilePrimary: customer.contact.primaryMobile, + mobileSecondary: customer.contact.secondaryMobile, + + fax: customer.contact.fax, + website: customer.contact.website, + }, + + legalRecord: customer.legalRecord, + + paymentMethod: customer.paymentMethod + ? { + id: customer.paymentMethod.id, + name: customer.paymentMethod.name, + description: customer.paymentMethod.description, + } + : null, + + paymentTerm: customer.paymentTerm + ? { + id: customer.paymentTerm.id, + description: customer.paymentTerm.description, + } + : null, + + taxRegime: customer.taxRegime + ? { + code: customer.taxRegime.code, + description: customer.taxRegime.description, + } + : null, + + fiscalDefaults: { + usesEquivalenceSurcharge: customer.fiscalDefaults.usesEquivalenceSurcharge, + usesRetention: customer.fiscalDefaults.usesRetention, + }, + + languageCode: customer.languageCode, + currencyCode: customer.currencyCode, + }; +}; diff --git a/modules/customers/src/common/features/get-customer/utils/index.ts b/modules/customers/src/common/features/get-customer/utils/index.ts new file mode 100644 index 00000000..42234997 --- /dev/null +++ b/modules/customers/src/common/features/get-customer/utils/index.ts @@ -0,0 +1 @@ +export * from './build-customer-selection-option'; diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-refresh-token-domain.mapper.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-refresh-token-domain.mapper.ts index 6ea2e1bc..6edb71cc 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-refresh-token-domain.mapper.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-refresh-token-domain.mapper.ts @@ -1,7 +1,7 @@ import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api"; import { UniqueID, - UtcDate, + UtcDateTime, ValidationErrorCollection, type ValidationErrorDetail, extractOrPushError, @@ -10,11 +10,7 @@ import { } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { - RefreshToken, - RefreshTokenHash, - type RefreshTokenInternalProps, -} from "../../../../domain"; +import { RefreshToken, RefreshTokenHash, type RefreshTokenInternalProps } from "../../../../domain"; import type { RefreshTokenCreationAttributes, RefreshTokenModel } from "../models"; function toUtcIsoString(value: Date | string): string { @@ -45,13 +41,13 @@ export class SequelizeRefreshTokenDomainMapper extends SequelizeDomainMapper< errors ); const expiresAt = extractOrPushError( - UtcDate.createFromISO(toUtcIsoString(source.expires_at)), + UtcDateTime.createFromISO(toUtcIsoString(source.expires_at)), "expires_at", errors ); const revokedAt = extractOrPushError( maybeFromNullableResult(source.revoked_at, (value) => - UtcDate.createFromISO(toUtcIsoString(value)) + UtcDateTime.createFromISO(toUtcIsoString(value)) ), "revoked_at", errors @@ -62,7 +58,7 @@ export class SequelizeRefreshTokenDomainMapper extends SequelizeDomainMapper< errors ); const createdAt = extractOrPushError( - UtcDate.createFromISO(toUtcIsoString(source.created_at)), + UtcDateTime.createFromISO(toUtcIsoString(source.created_at)), "created_at", errors ); @@ -98,7 +94,9 @@ export class SequelizeRefreshTokenDomainMapper extends SequelizeDomainMapper< token_hash: source.tokenHash.toPrimitive(), expires_at: source.expiresAt.toISOString(), revoked_at: maybeToNullable(source.revokedAt, (revokedAt) => revokedAt.toISOString()), - replaced_by_token_id: maybeToNullable(source.replacedByTokenId, (value) => value.toPrimitive()), + replaced_by_token_id: maybeToNullable(source.replacedByTokenId, (value) => + value.toPrimitive() + ), created_at: source.createdAt.toISOString(), }); }