@fiado/type-kit 3.222.0 → 3.223.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,4 +8,13 @@ export declare class KycPersonData {
8
8
  maternalLastName?: string;
9
9
  curp?: string;
10
10
  dateOfBirth?: string;
11
+ /**
12
+ * Vigencia del documento de identidad (el `expirationDate` que el OCR lee del INE). Aditivo y
13
+ * opcional: un mensaje viejo sin el campo sigue validando, y un INE cuyo OCR no la resolvió viaja
14
+ * sin ella.
15
+ *
16
+ * Se usa para mostrarle al vendedor con qué documento se verificó al cliente ("INE vigente, vence
17
+ * 2033"). NO es un gate: quién decide si la identidad es válida es el proveedor, no esta fecha.
18
+ */
19
+ documentExpiresAt?: string;
11
20
  }
@@ -49,3 +49,9 @@ __decorate([
49
49
  (0, class_validator_1.IsString)(),
50
50
  __metadata("design:type", String)
51
51
  ], KycPersonData.prototype, "dateOfBirth", void 0);
52
+ __decorate([
53
+ (0, class_transformer_1.Expose)(),
54
+ (0, class_validator_1.IsOptional)(),
55
+ (0, class_validator_1.IsString)(),
56
+ __metadata("design:type", String)
57
+ ], KycPersonData.prototype, "documentExpiresAt", void 0);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Domicilio del cliente Retail tal como lo entrega el OCR del INE (paso 02e de SureKeep F2), ya
3
+ * normalizado contra `places-business`. **TODOS los campos son opcionales**: el OCR es best-effort y
4
+ * una credencial puede no traer número exterior, colonia ni CP.
5
+ *
6
+ * Cierra TD-014 / TD-ADDRESSBASE-OCR. El campo `address` del upsert-from-kyc apuntaba al `AddressBase`
7
+ * canónico de Fiado, que exige ~12 campos (`tenantId`, `provider`, `validFrom`, `addressStatus`,
8
+ * `countryId`, `typeOfAddressId`…) que el OCR NO tiene y que nadie puede inventar en ese boundary.
9
+ * Consecuencia real: `retail-customer-business` tuvo que redefinir el DTO LOCALMENTE (antipatrón
10
+ * `DTO_LOCAL`) para no rechazar el payload con 400 VALIDATION_ERROR, y `retail-wizard-business` se
11
+ * quedó SIN mandar el domicilio — se extraía del INE y se descartaba en el último salto.
12
+ *
13
+ * Este DTO es el shape que los dos repos ya usaban de hecho: espeja 1:1 el `addressSchema` de
14
+ * Dynamoose que realmente se persiste en `RetailCustomer_GT`.
15
+ *
16
+ * ⚠️ **NO es el `AddressBase` canónico ni lo reemplaza.** `AddressBase` sigue siendo el domicilio
17
+ * gobernado de Fiado (`Address_GT`, con vigencias, proveedor y estatus). Este es el espejo ligero que
18
+ * vive embebido en el ítem del cliente Retail.
19
+ *
20
+ * ⚠️ PII: es el domicilio de una persona física. Nunca se loguea.
21
+ */
22
+ export declare class RetailAddress {
23
+ /** Etiqueta completa legible, tal como la devuelve el geocoder (equivalente a `Place.Label`). */
24
+ fullAddress?: string;
25
+ street?: string;
26
+ /** Número exterior. */
27
+ externalNumber?: string;
28
+ internalNumber?: string;
29
+ /** Colonia. */
30
+ neighborhood?: string;
31
+ /** Ciudad o municipio (el OCR del INE usa uno u otro indistintamente). */
32
+ city?: string;
33
+ /** Estado de la República. */
34
+ state?: string;
35
+ /** Código postal. */
36
+ zipCode?: string;
37
+ country?: string;
38
+ /** Referencias de ubicación capturadas a mano (el OCR nunca las trae). */
39
+ reference?: string;
40
+ lat?: number;
41
+ lng?: number;
42
+ }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RetailAddress = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /**
16
+ * Domicilio del cliente Retail tal como lo entrega el OCR del INE (paso 02e de SureKeep F2), ya
17
+ * normalizado contra `places-business`. **TODOS los campos son opcionales**: el OCR es best-effort y
18
+ * una credencial puede no traer número exterior, colonia ni CP.
19
+ *
20
+ * Cierra TD-014 / TD-ADDRESSBASE-OCR. El campo `address` del upsert-from-kyc apuntaba al `AddressBase`
21
+ * canónico de Fiado, que exige ~12 campos (`tenantId`, `provider`, `validFrom`, `addressStatus`,
22
+ * `countryId`, `typeOfAddressId`…) que el OCR NO tiene y que nadie puede inventar en ese boundary.
23
+ * Consecuencia real: `retail-customer-business` tuvo que redefinir el DTO LOCALMENTE (antipatrón
24
+ * `DTO_LOCAL`) para no rechazar el payload con 400 VALIDATION_ERROR, y `retail-wizard-business` se
25
+ * quedó SIN mandar el domicilio — se extraía del INE y se descartaba en el último salto.
26
+ *
27
+ * Este DTO es el shape que los dos repos ya usaban de hecho: espeja 1:1 el `addressSchema` de
28
+ * Dynamoose que realmente se persiste en `RetailCustomer_GT`.
29
+ *
30
+ * ⚠️ **NO es el `AddressBase` canónico ni lo reemplaza.** `AddressBase` sigue siendo el domicilio
31
+ * gobernado de Fiado (`Address_GT`, con vigencias, proveedor y estatus). Este es el espejo ligero que
32
+ * vive embebido en el ítem del cliente Retail.
33
+ *
34
+ * ⚠️ PII: es el domicilio de una persona física. Nunca se loguea.
35
+ */
36
+ class RetailAddress {
37
+ }
38
+ exports.RetailAddress = RetailAddress;
39
+ __decorate([
40
+ (0, class_transformer_1.Expose)(),
41
+ (0, class_validator_1.IsOptional)(),
42
+ (0, class_validator_1.IsString)(),
43
+ __metadata("design:type", String)
44
+ ], RetailAddress.prototype, "fullAddress", void 0);
45
+ __decorate([
46
+ (0, class_transformer_1.Expose)(),
47
+ (0, class_validator_1.IsOptional)(),
48
+ (0, class_validator_1.IsString)(),
49
+ __metadata("design:type", String)
50
+ ], RetailAddress.prototype, "street", void 0);
51
+ __decorate([
52
+ (0, class_transformer_1.Expose)(),
53
+ (0, class_validator_1.IsOptional)(),
54
+ (0, class_validator_1.IsString)(),
55
+ __metadata("design:type", String)
56
+ ], RetailAddress.prototype, "externalNumber", void 0);
57
+ __decorate([
58
+ (0, class_transformer_1.Expose)(),
59
+ (0, class_validator_1.IsOptional)(),
60
+ (0, class_validator_1.IsString)(),
61
+ __metadata("design:type", String)
62
+ ], RetailAddress.prototype, "internalNumber", void 0);
63
+ __decorate([
64
+ (0, class_transformer_1.Expose)(),
65
+ (0, class_validator_1.IsOptional)(),
66
+ (0, class_validator_1.IsString)(),
67
+ __metadata("design:type", String)
68
+ ], RetailAddress.prototype, "neighborhood", void 0);
69
+ __decorate([
70
+ (0, class_transformer_1.Expose)(),
71
+ (0, class_validator_1.IsOptional)(),
72
+ (0, class_validator_1.IsString)(),
73
+ __metadata("design:type", String)
74
+ ], RetailAddress.prototype, "city", void 0);
75
+ __decorate([
76
+ (0, class_transformer_1.Expose)(),
77
+ (0, class_validator_1.IsOptional)(),
78
+ (0, class_validator_1.IsString)(),
79
+ __metadata("design:type", String)
80
+ ], RetailAddress.prototype, "state", void 0);
81
+ __decorate([
82
+ (0, class_transformer_1.Expose)(),
83
+ (0, class_validator_1.IsOptional)(),
84
+ (0, class_validator_1.IsString)(),
85
+ __metadata("design:type", String)
86
+ ], RetailAddress.prototype, "zipCode", void 0);
87
+ __decorate([
88
+ (0, class_transformer_1.Expose)(),
89
+ (0, class_validator_1.IsOptional)(),
90
+ (0, class_validator_1.IsString)(),
91
+ __metadata("design:type", String)
92
+ ], RetailAddress.prototype, "country", void 0);
93
+ __decorate([
94
+ (0, class_transformer_1.Expose)(),
95
+ (0, class_validator_1.IsOptional)(),
96
+ (0, class_validator_1.IsString)(),
97
+ __metadata("design:type", String)
98
+ ], RetailAddress.prototype, "reference", void 0);
99
+ __decorate([
100
+ (0, class_transformer_1.Expose)(),
101
+ (0, class_validator_1.IsOptional)(),
102
+ (0, class_validator_1.IsNumber)(),
103
+ __metadata("design:type", Number)
104
+ ], RetailAddress.prototype, "lat", void 0);
105
+ __decorate([
106
+ (0, class_transformer_1.Expose)(),
107
+ (0, class_validator_1.IsOptional)(),
108
+ (0, class_validator_1.IsNumber)(),
109
+ __metadata("design:type", Number)
110
+ ], RetailAddress.prototype, "lng", void 0);
@@ -1,4 +1,4 @@
1
- import { AddressBase } from '../../address/dtos/AddressBase';
1
+ import { RetailAddress } from './RetailAddress';
2
2
  import { AccountStatusEnum } from '../enums/AccountStatusEnum';
3
3
  import { KycModalityEnum } from '../enums/KycModalityEnum';
4
4
  import { RetailCustomerStatusEnum } from '../enums/RetailCustomerStatusEnum';
@@ -29,6 +29,8 @@ export declare class RetailCustomer {
29
29
  email: string | null;
30
30
  /** Fecha de nacimiento (ISO 8601, admite fecha sola); se completa con el OCR del KYC. */
31
31
  birthDate?: string;
32
+ /** Vigencia del documento con el que se verificó la identidad (OCR del INE). Informativo. */
33
+ documentExpiresAt?: string;
32
34
  fiadoDirectoryId: string | null;
33
35
  fiadoPeopleId: string | null;
34
36
  pcfAccountId: string | null;
@@ -37,7 +39,7 @@ export declare class RetailCustomer {
37
39
  accountStatus: AccountStatusEnum | null;
38
40
  failReason: string | null;
39
41
  welcomeAccredited: boolean;
40
- address: AddressBase | null;
42
+ address: RetailAddress | null;
41
43
  addressConfirmed: boolean;
42
44
  kycStatus: RetailKycStatusEnum;
43
45
  kycModality: KycModalityEnum | null;
@@ -13,7 +13,7 @@ exports.RetailCustomer = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const regex_1 = require("../../helpers/constans/regex");
16
- const AddressBase_1 = require("../../address/dtos/AddressBase");
16
+ const RetailAddress_1 = require("./RetailAddress");
17
17
  const AccountStatusEnum_1 = require("../enums/AccountStatusEnum");
18
18
  const KycModalityEnum_1 = require("../enums/KycModalityEnum");
19
19
  const RetailCustomerStatusEnum_1 = require("../enums/RetailCustomerStatusEnum");
@@ -94,6 +94,13 @@ __decorate([
94
94
  (0, class_validator_1.Matches)(regex_1.regexIso8601Date, { message: 'birthDate debe ser una fecha ISO 8601' }),
95
95
  __metadata("design:type", String)
96
96
  ], RetailCustomer.prototype, "birthDate", void 0);
97
+ __decorate([
98
+ (0, class_transformer_1.Expose)(),
99
+ (0, class_validator_1.IsOptional)(),
100
+ (0, class_validator_1.IsString)(),
101
+ (0, class_validator_1.Matches)(regex_1.regexIso8601Date, { message: 'documentExpiresAt debe ser una fecha ISO 8601' }),
102
+ __metadata("design:type", String)
103
+ ], RetailCustomer.prototype, "documentExpiresAt", void 0);
97
104
  __decorate([
98
105
  (0, class_transformer_1.Expose)(),
99
106
  (0, class_validator_1.IsOptional)(),
@@ -145,8 +152,8 @@ __decorate([
145
152
  (0, class_transformer_1.Expose)(),
146
153
  (0, class_validator_1.IsOptional)(),
147
154
  (0, class_validator_1.ValidateNested)(),
148
- (0, class_transformer_1.Type)(() => AddressBase_1.AddressBase),
149
- __metadata("design:type", AddressBase_1.AddressBase)
155
+ (0, class_transformer_1.Type)(() => RetailAddress_1.RetailAddress),
156
+ __metadata("design:type", RetailAddress_1.RetailAddress)
150
157
  ], RetailCustomer.prototype, "address", void 0);
151
158
  __decorate([
152
159
  (0, class_transformer_1.Expose)(),
@@ -1,4 +1,4 @@
1
- import { AddressBase } from '../../../address/dtos/AddressBase';
1
+ import { RetailAddress } from '../RetailAddress';
2
2
  import { KycModalityEnum } from '../../enums/KycModalityEnum';
3
3
  import { RetailKycStatusEnum } from '../../enums/RetailKycStatusEnum';
4
4
  /**
@@ -17,6 +17,12 @@ export declare class UpsertCustomerFromKycRequest {
17
17
  phone: string;
18
18
  curp: string;
19
19
  birthDate?: string;
20
+ /**
21
+ * Vigencia del documento con el que se verificó la identidad (el `expirationDate` que el OCR lee
22
+ * del INE). Se guarda para poder decirle al vendedor CON QUÉ documento quedó registrado el cliente.
23
+ * NO es un gate de nada: quién valida la identidad es el proveedor de KYC.
24
+ */
25
+ documentExpiresAt?: string;
20
26
  rfc?: string;
21
27
  email?: string;
22
28
  /** Score de confianza que devuelve Metamap junto con el resultado del KYC (F1 §3.5). */
@@ -36,5 +42,13 @@ export declare class UpsertCustomerFromKycRequest {
36
42
  hasActiveMxAccount?: boolean;
37
43
  kycStatus: RetailKycStatusEnum;
38
44
  kycModality: KycModalityEnum;
39
- address?: AddressBase;
45
+ /**
46
+ * Domicilio del INE (paso 02e), ya normalizado contra `places-business`. Se persiste embebido en la
47
+ * MISMA TX del upsert.
48
+ *
49
+ * Cierra TD-ADDRESSBASE-OCR: apuntaba al `AddressBase` canónico, que exige ~12 campos que el OCR no
50
+ * trae — el `@ValidateNested` rechazaba el payload real con 400 VALIDATION_ERROR. `RetailAddress` es
51
+ * el shape ligero que ambos extremos ya usaban de hecho. Ver el DTO para el detalle.
52
+ */
53
+ address?: RetailAddress;
40
54
  }
@@ -13,7 +13,7 @@ exports.UpsertCustomerFromKycRequest = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const regex_1 = require("../../../helpers/constans/regex");
16
- const AddressBase_1 = require("../../../address/dtos/AddressBase");
16
+ const RetailAddress_1 = require("../RetailAddress");
17
17
  const KycModalityEnum_1 = require("../../enums/KycModalityEnum");
18
18
  const RetailKycStatusEnum_1 = require("../../enums/RetailKycStatusEnum");
19
19
  /**
@@ -66,6 +66,13 @@ __decorate([
66
66
  (0, class_validator_1.Matches)(regex_1.regexIso8601Date, { message: 'birthDate debe ser una fecha ISO 8601' }),
67
67
  __metadata("design:type", String)
68
68
  ], UpsertCustomerFromKycRequest.prototype, "birthDate", void 0);
69
+ __decorate([
70
+ (0, class_transformer_1.Expose)(),
71
+ (0, class_validator_1.IsOptional)(),
72
+ (0, class_validator_1.IsString)(),
73
+ (0, class_validator_1.Matches)(regex_1.regexIso8601Date, { message: 'documentExpiresAt debe ser una fecha ISO 8601' }),
74
+ __metadata("design:type", String)
75
+ ], UpsertCustomerFromKycRequest.prototype, "documentExpiresAt", void 0);
69
76
  __decorate([
70
77
  (0, class_transformer_1.Expose)(),
71
78
  (0, class_validator_1.IsOptional)(),
@@ -138,6 +145,6 @@ __decorate([
138
145
  (0, class_transformer_1.Expose)(),
139
146
  (0, class_validator_1.IsOptional)(),
140
147
  (0, class_validator_1.ValidateNested)(),
141
- (0, class_transformer_1.Type)(() => AddressBase_1.AddressBase),
142
- __metadata("design:type", AddressBase_1.AddressBase)
148
+ (0, class_transformer_1.Type)(() => RetailAddress_1.RetailAddress),
149
+ __metadata("design:type", RetailAddress_1.RetailAddress)
143
150
  ], UpsertCustomerFromKycRequest.prototype, "address", void 0);
@@ -1,11 +1,15 @@
1
- import { AddressBase } from '../../../address/dtos/AddressBase';
1
+ import { RetailAddress } from '../RetailAddress';
2
2
  import { AccountStatusEnum } from '../../enums/AccountStatusEnum';
3
3
  import { KycModalityEnum } from '../../enums/KycModalityEnum';
4
4
  import { RetailCustomerStatusEnum } from '../../enums/RetailCustomerStatusEnum';
5
5
  import { RetailKycStatusEnum } from '../../enums/RetailKycStatusEnum';
6
6
  /**
7
7
  * Vista de un cliente retail hacia el consumidor (BO / api-invoker). SureKeep Fase 2 — pista Retail.
8
- * `address` reusa el `AddressBase` canónico de Fiado (ver TD en RetailCustomer.address).
8
+ *
9
+ * `address` es el `RetailAddress` ligero — el shape que el lambda REALMENTE persiste. Antes decía
10
+ * `AddressBase`, y esa mentira de tipos ya había producido un bug silencioso: los consumidores leían
11
+ * `address.municipality` / `address.region` / `address.postalCode` (campos de `AddressBase`) y en
12
+ * runtime obtenían `undefined`, porque lo persistido se llama `city` / `state` / `zipCode`.
9
13
  */
10
14
  export interface CustomerResponse {
11
15
  retailCustomerId: string;
@@ -20,6 +24,8 @@ export interface CustomerResponse {
20
24
  rfc: string | null;
21
25
  email: string | null;
22
26
  birthDate: string | null;
27
+ /** Vigencia del documento de identidad con el que se verificó al cliente (OCR del INE). */
28
+ documentExpiresAt: string | null;
23
29
  fiadoDirectoryId: string | null;
24
30
  fiadoPeopleId: string | null;
25
31
  pcfAccountId: string | null;
@@ -28,7 +34,7 @@ export interface CustomerResponse {
28
34
  accountStatus: AccountStatusEnum | null;
29
35
  failReason: string | null;
30
36
  welcomeAccredited: boolean;
31
- address: AddressBase | null;
37
+ address: RetailAddress | null;
32
38
  addressConfirmed: boolean;
33
39
  kycStatus: RetailKycStatusEnum;
34
40
  kycModality: KycModalityEnum | null;
@@ -4,6 +4,7 @@ export * from './enums/KycModalityEnum';
4
4
  export * from './enums/KycReviewResultEnum';
5
5
  export * from './enums/RetailCustomerStatusEnum';
6
6
  export * from './dtos/KycReview';
7
+ export * from './dtos/RetailAddress';
7
8
  export * from './dtos/RetailCustomer';
8
9
  export * from './dtos/requests/CreateCustomerRequest';
9
10
  export * from './dtos/requests/UpsertCustomerFromKycRequest';
@@ -22,6 +22,7 @@ __exportStar(require("./enums/KycReviewResultEnum"), exports);
22
22
  __exportStar(require("./enums/RetailCustomerStatusEnum"), exports);
23
23
  // Entidades (planas, con class-validator + @Expose)
24
24
  __exportStar(require("./dtos/KycReview"), exports);
25
+ __exportStar(require("./dtos/RetailAddress"), exports);
25
26
  __exportStar(require("./dtos/RetailCustomer"), exports);
26
27
  // Request DTOs (input validado por endpoint)
27
28
  __exportStar(require("./dtos/requests/CreateCustomerRequest"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.222.0",
3
+ "version": "3.223.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -20,4 +20,15 @@ export class KycPersonData {
20
20
 
21
21
  @Expose() @IsOptional() @IsString()
22
22
  dateOfBirth?: string;
23
+
24
+ /**
25
+ * Vigencia del documento de identidad (el `expirationDate` que el OCR lee del INE). Aditivo y
26
+ * opcional: un mensaje viejo sin el campo sigue validando, y un INE cuyo OCR no la resolvió viaja
27
+ * sin ella.
28
+ *
29
+ * Se usa para mostrarle al vendedor con qué documento se verificó al cliente ("INE vigente, vence
30
+ * 2033"). NO es un gate: quién decide si la identidad es válida es el proveedor, no esta fecha.
31
+ */
32
+ @Expose() @IsOptional() @IsString()
33
+ documentExpiresAt?: string;
23
34
  }
@@ -0,0 +1,92 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsNumber, IsOptional, IsString } from 'class-validator';
3
+
4
+ /**
5
+ * Domicilio del cliente Retail tal como lo entrega el OCR del INE (paso 02e de SureKeep F2), ya
6
+ * normalizado contra `places-business`. **TODOS los campos son opcionales**: el OCR es best-effort y
7
+ * una credencial puede no traer número exterior, colonia ni CP.
8
+ *
9
+ * Cierra TD-014 / TD-ADDRESSBASE-OCR. El campo `address` del upsert-from-kyc apuntaba al `AddressBase`
10
+ * canónico de Fiado, que exige ~12 campos (`tenantId`, `provider`, `validFrom`, `addressStatus`,
11
+ * `countryId`, `typeOfAddressId`…) que el OCR NO tiene y que nadie puede inventar en ese boundary.
12
+ * Consecuencia real: `retail-customer-business` tuvo que redefinir el DTO LOCALMENTE (antipatrón
13
+ * `DTO_LOCAL`) para no rechazar el payload con 400 VALIDATION_ERROR, y `retail-wizard-business` se
14
+ * quedó SIN mandar el domicilio — se extraía del INE y se descartaba en el último salto.
15
+ *
16
+ * Este DTO es el shape que los dos repos ya usaban de hecho: espeja 1:1 el `addressSchema` de
17
+ * Dynamoose que realmente se persiste en `RetailCustomer_GT`.
18
+ *
19
+ * ⚠️ **NO es el `AddressBase` canónico ni lo reemplaza.** `AddressBase` sigue siendo el domicilio
20
+ * gobernado de Fiado (`Address_GT`, con vigencias, proveedor y estatus). Este es el espejo ligero que
21
+ * vive embebido en el ítem del cliente Retail.
22
+ *
23
+ * ⚠️ PII: es el domicilio de una persona física. Nunca se loguea.
24
+ */
25
+ export class RetailAddress {
26
+ /** Etiqueta completa legible, tal como la devuelve el geocoder (equivalente a `Place.Label`). */
27
+ @Expose()
28
+ @IsOptional()
29
+ @IsString()
30
+ fullAddress?: string;
31
+
32
+ @Expose()
33
+ @IsOptional()
34
+ @IsString()
35
+ street?: string;
36
+
37
+ /** Número exterior. */
38
+ @Expose()
39
+ @IsOptional()
40
+ @IsString()
41
+ externalNumber?: string;
42
+
43
+ @Expose()
44
+ @IsOptional()
45
+ @IsString()
46
+ internalNumber?: string;
47
+
48
+ /** Colonia. */
49
+ @Expose()
50
+ @IsOptional()
51
+ @IsString()
52
+ neighborhood?: string;
53
+
54
+ /** Ciudad o municipio (el OCR del INE usa uno u otro indistintamente). */
55
+ @Expose()
56
+ @IsOptional()
57
+ @IsString()
58
+ city?: string;
59
+
60
+ /** Estado de la República. */
61
+ @Expose()
62
+ @IsOptional()
63
+ @IsString()
64
+ state?: string;
65
+
66
+ /** Código postal. */
67
+ @Expose()
68
+ @IsOptional()
69
+ @IsString()
70
+ zipCode?: string;
71
+
72
+ @Expose()
73
+ @IsOptional()
74
+ @IsString()
75
+ country?: string;
76
+
77
+ /** Referencias de ubicación capturadas a mano (el OCR nunca las trae). */
78
+ @Expose()
79
+ @IsOptional()
80
+ @IsString()
81
+ reference?: string;
82
+
83
+ @Expose()
84
+ @IsOptional()
85
+ @IsNumber()
86
+ lat?: number;
87
+
88
+ @Expose()
89
+ @IsOptional()
90
+ @IsNumber()
91
+ lng?: number;
92
+ }
@@ -11,7 +11,7 @@ import {
11
11
  ValidateNested,
12
12
  } from 'class-validator';
13
13
  import { regexIso8601, regexIso8601Date } from '../../helpers/constans/regex';
14
- import { AddressBase } from '../../address/dtos/AddressBase';
14
+ import { RetailAddress } from './RetailAddress';
15
15
  import { AccountStatusEnum } from '../enums/AccountStatusEnum';
16
16
  import { KycModalityEnum } from '../enums/KycModalityEnum';
17
17
  import { RetailCustomerStatusEnum } from '../enums/RetailCustomerStatusEnum';
@@ -85,6 +85,13 @@ export class RetailCustomer {
85
85
  @Matches(regexIso8601Date, { message: 'birthDate debe ser una fecha ISO 8601' })
86
86
  birthDate?: string;
87
87
 
88
+ /** Vigencia del documento con el que se verificó la identidad (OCR del INE). Informativo. */
89
+ @Expose()
90
+ @IsOptional()
91
+ @IsString()
92
+ @Matches(regexIso8601Date, { message: 'documentExpiresAt debe ser una fecha ISO 8601' })
93
+ documentExpiresAt?: string;
94
+
88
95
  @Expose()
89
96
  @IsOptional()
90
97
  @IsString()
@@ -124,18 +131,22 @@ export class RetailCustomer {
124
131
  @IsBoolean()
125
132
  welcomeAccredited!: boolean;
126
133
 
127
- // TD-ADDRESSBASE-OCR: por decisión de Andrés (2026-07) el domicilio reusa el `AddressBase` canónico
128
- // de Fiado (`address/dtos/AddressBase`, espejo de Address_GT) tal cual manda el spec (componente 01 +
129
- // modelo-datos F2). RIESGO detectado: `AddressBase` tiene ~12 campos requeridos (directoryId, tenantId,
130
- // createdBy, typeOfAddressId, typeOfDirectoryId, provider, addressStatus, validFrom, isPrincipal,
131
- // helpNeeded, countryId, country) que un domicilio de copia OCR-parcial puede NO llenar con
132
- // @ValidateNested esto puede RECHAZAR en runtime los payloads OCR de kyc/confirm y address/confirm.
133
- // Se acepta ahora para apegarse al doc; ajustar después (posible tipo de dirección ligero para captura).
134
+ // TD-ADDRESSBASE-OCR CERRADO (2026-07-26) con el "tipo de dirección ligero para captura" que este
135
+ // mismo comentario dejaba pendiente. El riesgo anotado se materializó: `AddressBase` exige ~12 campos
136
+ // (directoryId, tenantId, createdBy, typeOfAddressId, typeOfDirectoryId, provider, addressStatus,
137
+ // validFrom, isPrincipal, helpNeeded, countryId, country) que un domicilio OCR-parcial NO llena, y con
138
+ // @ValidateNested el payload real del INE se rechazaba con 400 VALIDATION_ERROR. Consecuencias que ya
139
+ // estaban en producción: `retail-customer-business` redefinió el DTO local (antipatrón DTO_LOCAL) para
140
+ // poder recibirlo, y `retail-wizard-business` directamente dejó de mandar el domicilio.
141
+ //
142
+ // `RetailAddress` es el espejo ligero (todo opcional) del `addressSchema` que se persiste de hecho.
143
+ // El `AddressBase` canónico sigue siendo el domicilio gobernado de Fiado (Address_GT) — este NO lo
144
+ // reemplaza, solo deja de mentir sobre lo que vive embebido en el cliente Retail.
134
145
  @Expose()
135
146
  @IsOptional()
136
147
  @ValidateNested()
137
- @Type(() => AddressBase)
138
- address!: AddressBase | null;
148
+ @Type(() => RetailAddress)
149
+ address!: RetailAddress | null;
139
150
 
140
151
  @Expose()
141
152
  @IsBoolean()
@@ -11,7 +11,7 @@ import {
11
11
  ValidateNested,
12
12
  } from 'class-validator';
13
13
  import { regexCurp, regexIso8601Date, regexPhoneMx } from '../../../helpers/constans/regex';
14
- import { AddressBase } from '../../../address/dtos/AddressBase';
14
+ import { RetailAddress } from '../RetailAddress';
15
15
  import { KycModalityEnum } from '../../enums/KycModalityEnum';
16
16
  import { RetailKycStatusEnum } from '../../enums/RetailKycStatusEnum';
17
17
 
@@ -63,6 +63,17 @@ export class UpsertCustomerFromKycRequest {
63
63
  @Matches(regexIso8601Date, { message: 'birthDate debe ser una fecha ISO 8601' })
64
64
  birthDate?: string;
65
65
 
66
+ /**
67
+ * Vigencia del documento con el que se verificó la identidad (el `expirationDate` que el OCR lee
68
+ * del INE). Se guarda para poder decirle al vendedor CON QUÉ documento quedó registrado el cliente.
69
+ * NO es un gate de nada: quién valida la identidad es el proveedor de KYC.
70
+ */
71
+ @Expose()
72
+ @IsOptional()
73
+ @IsString()
74
+ @Matches(regexIso8601Date, { message: 'documentExpiresAt debe ser una fecha ISO 8601' })
75
+ documentExpiresAt?: string;
76
+
66
77
  @Expose()
67
78
  @IsOptional()
68
79
  @IsString()
@@ -126,12 +137,17 @@ export class UpsertCustomerFromKycRequest {
126
137
  @IsEnum(KycModalityEnum)
127
138
  kycModality!: KycModalityEnum;
128
139
 
129
- // TD-ADDRESSBASE-OCR: reusa el `AddressBase` canónico por decisión de Andrés (apego al spec). El OCR
130
- // (paso 02e) puede no traer los ~12 campos requeridos de AddressBase → posible rechazo en runtime con
131
- // @ValidateNested. Se persiste en la MISMA TX del upsert. Ajustar después (ver RetailCustomer.address).
140
+ /**
141
+ * Domicilio del INE (paso 02e), ya normalizado contra `places-business`. Se persiste embebido en la
142
+ * MISMA TX del upsert.
143
+ *
144
+ * Cierra TD-ADDRESSBASE-OCR: apuntaba al `AddressBase` canónico, que exige ~12 campos que el OCR no
145
+ * trae — el `@ValidateNested` rechazaba el payload real con 400 VALIDATION_ERROR. `RetailAddress` es
146
+ * el shape ligero que ambos extremos ya usaban de hecho. Ver el DTO para el detalle.
147
+ */
132
148
  @Expose()
133
149
  @IsOptional()
134
150
  @ValidateNested()
135
- @Type(() => AddressBase)
136
- address?: AddressBase;
151
+ @Type(() => RetailAddress)
152
+ address?: RetailAddress;
137
153
  }
@@ -1,4 +1,4 @@
1
- import { AddressBase } from '../../../address/dtos/AddressBase';
1
+ import { RetailAddress } from '../RetailAddress';
2
2
  import { AccountStatusEnum } from '../../enums/AccountStatusEnum';
3
3
  import { KycModalityEnum } from '../../enums/KycModalityEnum';
4
4
  import { RetailCustomerStatusEnum } from '../../enums/RetailCustomerStatusEnum';
@@ -6,7 +6,11 @@ import { RetailKycStatusEnum } from '../../enums/RetailKycStatusEnum';
6
6
 
7
7
  /**
8
8
  * Vista de un cliente retail hacia el consumidor (BO / api-invoker). SureKeep Fase 2 — pista Retail.
9
- * `address` reusa el `AddressBase` canónico de Fiado (ver TD en RetailCustomer.address).
9
+ *
10
+ * `address` es el `RetailAddress` ligero — el shape que el lambda REALMENTE persiste. Antes decía
11
+ * `AddressBase`, y esa mentira de tipos ya había producido un bug silencioso: los consumidores leían
12
+ * `address.municipality` / `address.region` / `address.postalCode` (campos de `AddressBase`) y en
13
+ * runtime obtenían `undefined`, porque lo persistido se llama `city` / `state` / `zipCode`.
10
14
  */
11
15
  export interface CustomerResponse {
12
16
  retailCustomerId: string;
@@ -21,6 +25,8 @@ export interface CustomerResponse {
21
25
  rfc: string | null;
22
26
  email: string | null;
23
27
  birthDate: string | null;
28
+ /** Vigencia del documento de identidad con el que se verificó al cliente (OCR del INE). */
29
+ documentExpiresAt: string | null;
24
30
  fiadoDirectoryId: string | null;
25
31
  fiadoPeopleId: string | null;
26
32
  pcfAccountId: string | null;
@@ -29,7 +35,7 @@ export interface CustomerResponse {
29
35
  accountStatus: AccountStatusEnum | null;
30
36
  failReason: string | null;
31
37
  welcomeAccredited: boolean;
32
- address: AddressBase | null;
38
+ address: RetailAddress | null;
33
39
  addressConfirmed: boolean;
34
40
  kycStatus: RetailKycStatusEnum;
35
41
  kycModality: KycModalityEnum | null;
@@ -7,6 +7,7 @@ export * from './enums/RetailCustomerStatusEnum';
7
7
 
8
8
  // Entidades (planas, con class-validator + @Expose)
9
9
  export * from './dtos/KycReview';
10
+ export * from './dtos/RetailAddress';
10
11
  export * from './dtos/RetailCustomer';
11
12
 
12
13
  // Request DTOs (input validado por endpoint)