@fiado/type-kit 3.259.0 → 3.261.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.
Files changed (115) hide show
  1. package/_test_/unit/platformRbac/PasswordPolicyInput.test.ts +58 -0
  2. package/_test_/unit/retailWizard/saleFinancier.test.ts +68 -0
  3. package/bin/helpdesk/dtos/ZendeskInternalAlertRequest.d.ts +18 -0
  4. package/bin/{walletFunding/dtos/CancelFundingReferenceRequest.js → helpdesk/dtos/ZendeskInternalAlertRequest.js} +18 -16
  5. package/bin/helpdesk/enums/InternalAlertChannelEnum.d.ts +7 -0
  6. package/bin/helpdesk/enums/InternalAlertChannelEnum.js +11 -0
  7. package/bin/helpdesk/enums/ZendeskAlertKindEnum.d.ts +7 -0
  8. package/bin/helpdesk/enums/ZendeskAlertKindEnum.js +11 -0
  9. package/bin/helpdesk/index.d.ts +4 -0
  10. package/bin/helpdesk/index.js +5 -0
  11. package/bin/helpdesk/interfaces/IZendeskAlertMessage.d.ts +6 -0
  12. package/bin/helpdesk/interfaces/IZendeskAlertMessage.js +2 -0
  13. package/bin/loanConfig/enums/ModifiableByRoleEnum.d.ts +11 -0
  14. package/bin/loanConfig/enums/ModifiableByRoleEnum.js +15 -0
  15. package/bin/platformRbac/application/Application.d.ts +17 -0
  16. package/bin/platformRbac/dtos/CreateTenantRequest.d.ts +12 -0
  17. package/bin/platformRbac/dtos/CreateTenantRequest.js +8 -0
  18. package/bin/platformRbac/dtos/PasswordPolicyInput.d.ts +30 -0
  19. package/bin/platformRbac/dtos/PasswordPolicyInput.js +72 -0
  20. package/bin/platformRbac/dtos/ResendOtpRequest.d.ts +22 -0
  21. package/bin/platformRbac/dtos/{CompleteMyProfileRequest.js → ResendOtpRequest.js} +14 -12
  22. package/bin/platformRbac/dtos/ResendSelfRegisterOtpRequest.d.ts +11 -0
  23. package/bin/platformRbac/dtos/ResendSelfRegisterOtpRequest.js +36 -0
  24. package/bin/platformRbac/index.d.ts +1 -0
  25. package/bin/platformRbac/index.js +3 -0
  26. package/bin/retailWizard/dtos/SaleException.d.ts +54 -0
  27. package/bin/retailWizard/dtos/SaleException.js +2 -0
  28. package/bin/retailWizard/dtos/SaleExceptionEvent.d.ts +18 -0
  29. package/bin/retailWizard/dtos/SaleExceptionEvent.js +2 -0
  30. package/bin/retailWizard/dtos/SaleExceptionPayload.d.ts +19 -0
  31. package/bin/retailWizard/dtos/SaleExceptionPayload.js +2 -0
  32. package/bin/retailWizard/dtos/SaleLogEntry.d.ts +29 -0
  33. package/bin/retailWizard/dtos/SaleLogEntry.js +2 -0
  34. package/bin/retailWizard/dtos/SalesReportDefinition.d.ts +20 -0
  35. package/bin/retailWizard/dtos/SalesReportDefinition.js +2 -0
  36. package/bin/retailWizard/dtos/SalesReportRun.d.ts +39 -0
  37. package/bin/retailWizard/dtos/SalesReportRun.js +2 -0
  38. package/bin/retailWizard/dtos/requests/BackofficeCancelSaleRequest.d.ts +26 -0
  39. package/bin/retailWizard/dtos/requests/BackofficeCancelSaleRequest.js +48 -0
  40. package/bin/retailWizard/dtos/requests/CreateSaleExceptionRequest.d.ts +28 -0
  41. package/bin/retailWizard/dtos/requests/CreateSaleExceptionRequest.js +96 -0
  42. package/bin/retailWizard/dtos/requests/ResolveSaleExceptionRequest.d.ts +15 -0
  43. package/bin/retailWizard/dtos/requests/ResolveSaleExceptionRequest.js +40 -0
  44. package/bin/retailWizard/dtos/requests/RunSalesReportRequest.d.ts +18 -0
  45. package/bin/retailWizard/dtos/requests/RunSalesReportRequest.js +45 -0
  46. package/bin/retailWizard/dtos/responses/BackofficeReportsResponse.d.ts +14 -0
  47. package/bin/retailWizard/dtos/responses/BackofficeReportsResponse.js +2 -0
  48. package/bin/retailWizard/dtos/responses/BackofficeSalesKpisResponse.d.ts +32 -0
  49. package/bin/retailWizard/dtos/responses/BackofficeSalesKpisResponse.js +2 -0
  50. package/bin/retailWizard/dtos/responses/BackofficeSalesStatsResponse.d.ts +26 -0
  51. package/bin/retailWizard/dtos/responses/BackofficeSalesStatsResponse.js +2 -0
  52. package/bin/retailWizard/enums/ReportFormatEnum.d.ts +5 -0
  53. package/bin/retailWizard/enums/ReportFormatEnum.js +9 -0
  54. package/bin/retailWizard/enums/ReportPeriodicityEnum.d.ts +9 -0
  55. package/bin/retailWizard/enums/ReportPeriodicityEnum.js +13 -0
  56. package/bin/retailWizard/enums/ReportRunStatusEnum.d.ts +18 -0
  57. package/bin/retailWizard/enums/ReportRunStatusEnum.js +22 -0
  58. package/bin/retailWizard/enums/SaleExceptionDecisionEnum.d.ts +10 -0
  59. package/bin/retailWizard/enums/SaleExceptionDecisionEnum.js +14 -0
  60. package/bin/retailWizard/enums/SaleExceptionEventKindEnum.d.ts +17 -0
  61. package/bin/retailWizard/enums/SaleExceptionEventKindEnum.js +21 -0
  62. package/bin/retailWizard/enums/SaleExceptionStatusEnum.d.ts +19 -0
  63. package/bin/retailWizard/enums/SaleExceptionStatusEnum.js +23 -0
  64. package/bin/retailWizard/enums/SaleExceptionTypeEnum.d.ts +19 -0
  65. package/bin/retailWizard/enums/SaleExceptionTypeEnum.js +23 -0
  66. package/bin/retailWizard/enums/SaleFinancierEnum.d.ts +18 -0
  67. package/bin/retailWizard/enums/SaleFinancierEnum.js +22 -0
  68. package/bin/retailWizard/enums/SalesReportIdEnum.d.ts +19 -0
  69. package/bin/retailWizard/enums/SalesReportIdEnum.js +23 -0
  70. package/bin/retailWizard/helpers/saleFinancier.d.ts +21 -0
  71. package/bin/retailWizard/helpers/saleFinancier.js +44 -0
  72. package/bin/retailWizard/index.d.ts +23 -0
  73. package/bin/retailWizard/index.js +29 -0
  74. package/package.json +1 -1
  75. package/src/helpdesk/dtos/ZendeskInternalAlertRequest.ts +31 -0
  76. package/src/helpdesk/enums/InternalAlertChannelEnum.ts +7 -0
  77. package/src/helpdesk/enums/ZendeskAlertKindEnum.ts +7 -0
  78. package/src/helpdesk/index.ts +7 -1
  79. package/src/helpdesk/interfaces/IZendeskAlertMessage.ts +7 -0
  80. package/src/platformRbac/application/Application.ts +17 -0
  81. package/src/platformRbac/dtos/CreateTenantRequest.ts +13 -0
  82. package/src/platformRbac/dtos/PasswordPolicyInput.ts +42 -0
  83. package/src/platformRbac/index.ts +3 -0
  84. package/src/retailWizard/dtos/SaleException.ts +55 -0
  85. package/src/retailWizard/dtos/SaleExceptionEvent.ts +19 -0
  86. package/src/retailWizard/dtos/SaleExceptionPayload.ts +19 -0
  87. package/src/retailWizard/dtos/SaleLogEntry.ts +30 -0
  88. package/src/retailWizard/dtos/SalesReportDefinition.ts +21 -0
  89. package/src/retailWizard/dtos/SalesReportRun.ts +40 -0
  90. package/src/retailWizard/dtos/requests/BackofficeCancelSaleRequest.ts +42 -0
  91. package/src/retailWizard/dtos/requests/CreateSaleExceptionRequest.ts +84 -0
  92. package/src/retailWizard/dtos/requests/ResolveSaleExceptionRequest.ts +25 -0
  93. package/src/retailWizard/dtos/requests/RunSalesReportRequest.ts +31 -0
  94. package/src/retailWizard/dtos/responses/BackofficeReportsResponse.ts +15 -0
  95. package/src/retailWizard/dtos/responses/BackofficeSalesKpisResponse.ts +34 -0
  96. package/src/retailWizard/dtos/responses/BackofficeSalesStatsResponse.ts +26 -0
  97. package/src/retailWizard/enums/ReportFormatEnum.ts +5 -0
  98. package/src/retailWizard/enums/ReportPeriodicityEnum.ts +9 -0
  99. package/src/retailWizard/enums/ReportRunStatusEnum.ts +18 -0
  100. package/src/retailWizard/enums/SaleExceptionDecisionEnum.ts +10 -0
  101. package/src/retailWizard/enums/SaleExceptionEventKindEnum.ts +17 -0
  102. package/src/retailWizard/enums/SaleExceptionStatusEnum.ts +19 -0
  103. package/src/retailWizard/enums/SaleExceptionTypeEnum.ts +19 -0
  104. package/src/retailWizard/enums/SaleFinancierEnum.ts +18 -0
  105. package/src/retailWizard/enums/SalesReportIdEnum.ts +19 -0
  106. package/src/retailWizard/helpers/saleFinancier.ts +48 -0
  107. package/src/retailWizard/index.ts +34 -0
  108. package/bin/platformRbac/dtos/CompleteMyProfileRequest.d.ts +0 -9
  109. package/bin/walletFunding/dtos/CancelFundingReferenceRequest.d.ts +0 -14
  110. package/bin/walletFunding/dtos/CancelFundingReferenceResponse.d.ts +0 -15
  111. package/bin/walletFunding/dtos/CancelFundingReferenceResponse.js +0 -13
  112. package/bin/walletFunding/dtos/CancelWalletFundingRequest.d.ts +0 -3
  113. package/bin/walletFunding/dtos/CancelWalletFundingRequest.js +0 -21
  114. package/bin/walletFunding/dtos/CancelWalletFundingResponse.d.ts +0 -7
  115. package/bin/walletFunding/dtos/CancelWalletFundingResponse.js +0 -6
@@ -0,0 +1,55 @@
1
+ import type { SaleExceptionTypeEnum } from '../enums/SaleExceptionTypeEnum';
2
+ import type { SaleExceptionStatusEnum } from '../enums/SaleExceptionStatusEnum';
3
+ import type { SaleExceptionEvent } from './SaleExceptionEvent';
4
+ import type { SaleExceptionPayload } from './SaleExceptionPayload';
5
+
6
+ /**
7
+ * Excepción de venta (`<T>RetailSaleException_GT`, pk `RETAILER#<retailerId>` ·
8
+ * sk `EXCEPTION#<createdAt>#<exceptionId>`, CERO GSIs). Bandeja del Admin VL, M6 §5.
9
+ * SureKeep Fase 3 — pista Retail.
10
+ *
11
+ * El `status` NO va en el sort key: el sk es inmutable en DynamoDB y el estado recorre
12
+ * `NEW → IN_REVIEW → APPROVED → APPLIED`. Meterlo ahí obligaría a borrar y reescribir el ítem en cada
13
+ * transición, perdiendo atomicidad y estabilidad del `exceptionId`. El filtro por estado se hace en
14
+ * memoria; son eventos raros.
15
+ *
16
+ * 🔴 **Hueco declarado (DEC-034):** `CAPTURE_FIX`, `BACKDATING` y `PRICE_OVERRIDE` mutan la venta
17
+ * después de cerrada, y el motor de comisiones solo escucha `SaleCompleted` / `SaleCancelled`. En el
18
+ * primer corte las excepciones **NO re-liquidan comisión**. Se ataca en F4 con un `SaleAmendedV1`.
19
+ */
20
+ export interface SaleException {
21
+ exceptionId: string;
22
+ retailerId: string;
23
+ storeId: string;
24
+ /** `null` en las excepciones generales, que no cuelgan de una venta puntual. */
25
+ saleId: string | null;
26
+ type: SaleExceptionTypeEnum;
27
+ status: SaleExceptionStatusEnum;
28
+ /** userId de quien la levantó (normalmente el vendedor). */
29
+ requestedBy: string;
30
+ /** Nombre para mostrar, congelado al alta. `null` si no se pudo resolver. */
31
+ requestedByName: string | null;
32
+ /** ISO-8601. */
33
+ requestedAt: string;
34
+ /** OBLIGATORIA. Es lo que hace auditable la excepción. */
35
+ justification: string;
36
+ /**
37
+ * Impacto económico en centavos, con signo: positivo si la excepción suma (devolución al cliente,
38
+ * reemplazo), negativo si resta (descuento autorizado). `null` cuando no aplica.
39
+ */
40
+ impactCents: number | null;
41
+ payload: SaleExceptionPayload;
42
+ /** userId del Admin VL que aprobó o rechazó. `null` mientras esté sin resolver. */
43
+ resolvedBy: string | null;
44
+ resolvedByName: string | null;
45
+ /** ISO-8601. `null` mientras esté sin resolver. */
46
+ resolvedAt: string | null;
47
+ /** Resolución OBLIGATORIA al aprobar o rechazar. `null` mientras esté sin resolver. */
48
+ resolution: string | null;
49
+ /** Hilo de solicitud, notas y resolución, en orden cronológico ascendente. */
50
+ timeline: SaleExceptionEvent[];
51
+ createdBy: string;
52
+ updatedBy: string;
53
+ createdAt: number;
54
+ updatedAt: number;
55
+ }
@@ -0,0 +1,19 @@
1
+ import type { SaleExceptionEventKindEnum } from '../enums/SaleExceptionEventKindEnum';
2
+
3
+ /**
4
+ * Una entrada del timeline de una excepción: el hilo de solicitud, notas y resolución que la pantalla
5
+ * de Excepciones dibuja bajo la ficha. SureKeep Fase 3 — pista Retail.
6
+ *
7
+ * Va EMBEBIDO en el ítem de `RetailSaleException_GT`, no en una tabla aparte: son pocos eventos por
8
+ * excepción, se leen siempre junto con la ficha y nunca se consultan por su cuenta.
9
+ */
10
+ export interface SaleExceptionEvent {
11
+ /** ISO-8601. */
12
+ at: string;
13
+ kind: SaleExceptionEventKindEnum;
14
+ /** userId del autor; `system` si lo escribió un proceso interno. */
15
+ authorId: string;
16
+ /** Nombre para mostrar, congelado al escribir el evento. `null` si no se pudo resolver. */
17
+ authorName: string | null;
18
+ body: string;
19
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Lo que la excepción PIDE cambiar en la venta. Todos los campos son opcionales porque cada tipo usa
3
+ * los suyos: `CAPTURE_FIX` manda `sku`, `PRICE_OVERRIDE` manda `amountCents`, `BACKDATING` manda
4
+ * `soldAt`, `EXTENDED_WARRANTY` manda `imei`. `LATE_CANCEL` no manda ninguno.
5
+ * SureKeep Fase 3 — pista Retail.
6
+ *
7
+ * Es un shape TIPADO y no un objeto libre a propósito: el payload cruza el borde HTTP y un
8
+ * `Record<string, unknown>` ahí deja al front sin contrato y al validador sin nada que validar.
9
+ */
10
+ export interface SaleExceptionPayload {
11
+ /** SKU correcto (CAPTURE_FIX). */
12
+ sku?: string;
13
+ /** Monto correcto en centavos (PRICE_OVERRIDE, CAPTURE_FIX). */
14
+ amountCents?: number;
15
+ /** Fecha real de la venta, ISO-8601 (BACKDATING). */
16
+ soldAt?: string;
17
+ /** IMEI del equipo de reemplazo (EXTENDED_WARRANTY). */
18
+ imei?: string;
19
+ }
@@ -0,0 +1,30 @@
1
+ /** Un campo que cambió en una acción auditada. `from` ausente en un alta. */
2
+ export interface SaleLogChange {
3
+ field: string;
4
+ from: string | null;
5
+ to: string | null;
6
+ }
7
+
8
+ /**
9
+ * Una entrada del tab «Bitácora» del drawer de una venta. Proyección de `SharedAuditTrail_GT`
10
+ * (`pk = ENTITY#SALE#<saleId>`, `sk = TS#<iso>#<ulid>`, orden cronológico nativo).
11
+ * SureKeep Fase 3 — pista Retail.
12
+ *
13
+ * El diff viaja APLANADO en `changes` y no como los objetos `old`/`new` crudos de la tabla: la
14
+ * pantalla dibuja una lista de «campo: antes → después», y un objeto libre en el contrato obligaría al
15
+ * front a adivinar la forma. Los valores se serializan a string por eso mismo.
16
+ *
17
+ * ⚠️ `changes` NUNCA lleva PII: la tabla de auditoría solo guarda identificadores y valores
18
+ * operacionales (status, storeId, type…). Ver `fiado-logging`.
19
+ */
20
+ export interface SaleLogEntry {
21
+ /** ISO-8601 del momento de la acción (sale del `sk`). */
22
+ at: string;
23
+ /** Acción auditada: `SALE_COMPLETE`, `SALE_CANCEL`, `PAYMENT_REGISTERED`… */
24
+ action: string;
25
+ /** Actor: userId del AuthContext, o `system` si la disparó un proceso interno. */
26
+ performedBy: string;
27
+ /** Motivo, cuando la acción lo exige (cancelación, excepción aplicada). */
28
+ reason: string | null;
29
+ changes: SaleLogChange[];
30
+ }
@@ -0,0 +1,21 @@
1
+ import type { SalesReportIdEnum } from '../enums/SalesReportIdEnum';
2
+ import type { ReportPeriodicityEnum } from '../enums/ReportPeriodicityEnum';
3
+ import type { ReportFormatEnum } from '../enums/ReportFormatEnum';
4
+
5
+ /**
6
+ * Un reporte del catálogo del Admin VL (M6 §5.1). Es CONFIGURACIÓN, no una fila de tabla: el catálogo
7
+ * de los 5 reportes vive en código y no se persiste — cambia con un deploy, no con un write.
8
+ * SureKeep Fase 3 — pista Retail.
9
+ */
10
+ export interface SalesReportDefinition {
11
+ reportId: SalesReportIdEnum;
12
+ /** Nombre para mostrar, en español mexicano. */
13
+ name: string;
14
+ description: string;
15
+ periodicity: ReportPeriodicityEnum;
16
+ formats: ReportFormatEnum[];
17
+ /** Referencia al documento de negocio que lo define (ej. `M6 §5.1`). */
18
+ reference: string;
19
+ /** ISO-8601 de la última corrida conocida; `null` si nunca corrió. */
20
+ lastRunAt: string | null;
21
+ }
@@ -0,0 +1,40 @@
1
+ import type { SalesReportIdEnum } from '../enums/SalesReportIdEnum';
2
+ import type { ReportFormatEnum } from '../enums/ReportFormatEnum';
3
+ import type { ReportRunStatusEnum } from '../enums/ReportRunStatusEnum';
4
+
5
+ /**
6
+ * Una corrida de reporte (`<T>RetailReportRun_GT`, pk `RETAILER#<retailerId>` ·
7
+ * sk `RUN#<requestedAt>#<runId>`, CERO GSIs). Es el log de la sección 3 de la pantalla de Reportes.
8
+ * SureKeep Fase 3 — pista Retail.
9
+ *
10
+ * La generación es ASÍNCRONA: el `POST .../run` persiste la corrida en `QUEUED` y devuelve el `runId`;
11
+ * el front poletea `GET /backoffice/reports`. API Gateway corta a los 29 s y un reporte de red
12
+ * completa no entra en una respuesta síncrona.
13
+ */
14
+ export interface SalesReportRun {
15
+ runId: string;
16
+ retailerId: string;
17
+ reportId: SalesReportIdEnum;
18
+ format: ReportFormatEnum;
19
+ status: ReportRunStatusEnum;
20
+ /** userId de quien la disparó; `system` si la lanzó el cron. */
21
+ requestedBy: string;
22
+ requestedByName: string | null;
23
+ /** ISO-8601. Es parte del sort key: ordena el log de corridas. */
24
+ requestedAt: string;
25
+ /** ISO-8601 del fin de la generación. `null` mientras no termine. */
26
+ completedAt: string | null;
27
+ /** Ventana de datos del reporte, ISO-8601. */
28
+ periodFrom: string | null;
29
+ periodTo: string | null;
30
+ /** Filas generadas. `null` mientras no termine. */
31
+ rowCount: number | null;
32
+ /** URL prefirmada de descarga. Solo con `status: READY`; `null` en cualquier otro estado. */
33
+ downloadUrl: string | null;
34
+ /** Por qué falló. Solo con `status: FAILED`. */
35
+ failureReason: string | null;
36
+ createdBy: string;
37
+ updatedBy: string;
38
+ createdAt: number;
39
+ updatedAt: number;
40
+ }
@@ -0,0 +1,42 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsBoolean, IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
3
+
4
+ /**
5
+ * POST /backoffice/sales/:saleId/cancel — cancelación del Admin VL. SureKeep Fase 3 — pista Retail.
6
+ *
7
+ * Es un endpoint SEPARADO de `POST /sales/:saleId/cancel` (el del vendedor), no una rama del mismo, y
8
+ * por eso lleva su propio DTO. Son dos reglas de negocio distintas: la del vendedor está acotada a su
9
+ * propia venta y a la ventana de 7 días; la del admin puede cancelar FUERA de ventana — que es justo
10
+ * lo que habilita la excepción `LATE_CANCEL`. Mezclarlas en un handler enredaría las dos.
11
+ *
12
+ * `outOfWindowExceptionId` es el puente entre las dos piezas: cancelar fuera de ventana exige una
13
+ * excepción `LATE_CANCEL` ya aprobada. Sin ella el manager rechaza, aunque el actor tenga el permiso.
14
+ */
15
+ export class BackofficeCancelSaleRequest {
16
+ @Expose()
17
+ @IsString()
18
+ @MinLength(10)
19
+ @MaxLength(200)
20
+ reason!: string;
21
+
22
+ /**
23
+ * Confirmación de que el equipo se verificó físicamente en tienda. Ausente y `false` se tratan
24
+ * igual (fail-closed): el manager responde `412 IMEI_NOT_RETURNABLE`, que es la respuesta
25
+ * semánticamente correcta y no un error de forma. `@IsBoolean()` estricto — el string `"false"` se
26
+ * rechaza en vez de colarse como truthy.
27
+ */
28
+ @Expose()
29
+ @IsOptional()
30
+ @IsBoolean()
31
+ equipmentVerified?: boolean;
32
+
33
+ /**
34
+ * Excepción `LATE_CANCEL` aprobada que autoriza cancelar fuera de la ventana. Solo se exige cuando
35
+ * la venta YA está fuera de ventana; dentro de ventana se ignora.
36
+ */
37
+ @Expose()
38
+ @IsOptional()
39
+ @IsString()
40
+ @MaxLength(64)
41
+ outOfWindowExceptionId?: string;
42
+ }
@@ -0,0 +1,84 @@
1
+ import { Expose, Type } from 'class-transformer';
2
+ import {
3
+ IsEnum,
4
+ IsInt,
5
+ IsISO8601,
6
+ IsOptional,
7
+ IsString,
8
+ MaxLength,
9
+ MinLength,
10
+ ValidateNested,
11
+ } from 'class-validator';
12
+ import { SaleExceptionTypeEnum } from '../../enums/SaleExceptionTypeEnum';
13
+
14
+ /** Lo que la excepción pide cambiar. Cada tipo usa los campos que le tocan; todos son opcionales. */
15
+ export class SaleExceptionPayloadInput {
16
+ @Expose()
17
+ @IsOptional()
18
+ @IsString()
19
+ @MaxLength(64)
20
+ sku?: string;
21
+
22
+ @Expose()
23
+ @IsOptional()
24
+ @IsInt()
25
+ amountCents?: number;
26
+
27
+ @Expose()
28
+ @IsOptional()
29
+ @IsISO8601()
30
+ soldAt?: string;
31
+
32
+ @Expose()
33
+ @IsOptional()
34
+ @IsString()
35
+ @MaxLength(20)
36
+ imei?: string;
37
+ }
38
+
39
+ /**
40
+ * POST /backoffice/exceptions — levanta una excepción sobre una venta. SureKeep Fase 3 — pista Retail.
41
+ *
42
+ * La `justification` es OBLIGATORIA y con mínimo real: es lo único que vuelve auditable la excepción,
43
+ * y un campo libre que acepta `"x"` no audita nada. Escribe `RetailSaleException_GT` +
44
+ * `SharedAuditTrail_GT` en la MISMA `TransactWriteItems`.
45
+ *
46
+ * El `retailerId` NO viaja en el request: sale del token (`scopeFromRetailer`).
47
+ */
48
+ export class CreateSaleExceptionRequest {
49
+ @Expose()
50
+ @IsEnum(SaleExceptionTypeEnum)
51
+ type!: SaleExceptionTypeEnum;
52
+
53
+ /** `null`/ausente en las excepciones generales, que no cuelgan de una venta puntual. */
54
+ @Expose()
55
+ @IsOptional()
56
+ @IsString()
57
+ @MaxLength(64)
58
+ saleId?: string;
59
+
60
+ /** Obligatorio cuando NO hay `saleId`; si hay venta, se toma el de la venta. */
61
+ @Expose()
62
+ @IsOptional()
63
+ @IsString()
64
+ @MaxLength(64)
65
+ storeId?: string;
66
+
67
+ @Expose()
68
+ @IsString()
69
+ @MinLength(20)
70
+ @MaxLength(1000)
71
+ justification!: string;
72
+
73
+ /** Impacto económico con signo, en centavos. Negativo si la excepción resta (descuento). */
74
+ @Expose()
75
+ @IsOptional()
76
+ @IsInt()
77
+ impactCents?: number;
78
+
79
+ @Expose()
80
+ @IsOptional()
81
+ @ValidateNested()
82
+ @Type(() => SaleExceptionPayloadInput)
83
+ payload?: SaleExceptionPayloadInput;
84
+ }
@@ -0,0 +1,25 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsEnum, IsString, MaxLength, MinLength } from 'class-validator';
3
+ import { SaleExceptionDecisionEnum } from '../../enums/SaleExceptionDecisionEnum';
4
+
5
+ /**
6
+ * POST /backoffice/exceptions/:exceptionId/resolve — aprueba o rechaza una excepción.
7
+ * SureKeep Fase 3 — pista Retail.
8
+ *
9
+ * La `resolution` es OBLIGATORIA en los dos veredictos, no solo al rechazar: un «aprobada» sin motivo
10
+ * escrito es exactamente el registro que después nadie puede defender ante una auditoría.
11
+ *
12
+ * **Idempotente** con el guard local (`IIdempotencyGuard` + `SureKeepSharedIdempotency_GT`): un doble
13
+ * click no puede resolver dos veces. ⚠️ El decorador `@Idempotent` NO existe en el gateway-adapter.
14
+ */
15
+ export class ResolveSaleExceptionRequest {
16
+ @Expose()
17
+ @IsEnum(SaleExceptionDecisionEnum)
18
+ decision!: SaleExceptionDecisionEnum;
19
+
20
+ @Expose()
21
+ @IsString()
22
+ @MinLength(10)
23
+ @MaxLength(1000)
24
+ resolution!: string;
25
+ }
@@ -0,0 +1,31 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsEnum, IsISO8601, IsOptional } from 'class-validator';
3
+ import { ReportFormatEnum } from '../../enums/ReportFormatEnum';
4
+
5
+ /**
6
+ * POST /backoffice/reports/:reportId/run — encola la generación de un reporte.
7
+ * SureKeep Fase 3 — pista Retail.
8
+ *
9
+ * Devuelve el `SalesReportRun` en `QUEUED`, NO el archivo: la generación es asíncrona por el límite de
10
+ * 29 s de API Gateway. El front poletea `GET /backoffice/reports` hasta ver `READY` y ahí usa el
11
+ * `downloadUrl`.
12
+ *
13
+ * El `reportId` viaja en el path, no acá. El `retailerId` sale del token.
14
+ */
15
+ export class RunSalesReportRequest {
16
+ @Expose()
17
+ @IsEnum(ReportFormatEnum)
18
+ format!: ReportFormatEnum;
19
+
20
+ /** Inicio de la ventana de datos, ISO-8601. Si falta, la resuelve la periodicidad del reporte. */
21
+ @Expose()
22
+ @IsOptional()
23
+ @IsISO8601()
24
+ periodFrom?: string;
25
+
26
+ /** Fin de la ventana de datos, ISO-8601. Si falta, la resuelve la periodicidad del reporte. */
27
+ @Expose()
28
+ @IsOptional()
29
+ @IsISO8601()
30
+ periodTo?: string;
31
+ }
@@ -0,0 +1,15 @@
1
+ import type { SalesReportDefinition } from '../SalesReportDefinition';
2
+ import type { SalesReportRun } from '../SalesReportRun';
3
+
4
+ /**
5
+ * GET /backoffice/reports — el catálogo de los 5 reportes de ventas MÁS el log de corridas recientes,
6
+ * que es exactamente lo que la pantalla dibuja en sus dos secciones. SureKeep Fase 3 — pista Retail.
7
+ *
8
+ * Van juntos en una sola respuesta y no en dos endpoints porque la pantalla siempre necesita los dos,
9
+ * y el catálogo son 5 ítems de configuración en código: partirlo costaría un round-trip para nada.
10
+ */
11
+ export interface BackofficeReportsResponse {
12
+ definitions: SalesReportDefinition[];
13
+ /** Últimas corridas del retailer, más recientes primero. */
14
+ runs: SalesReportRun[];
15
+ }
@@ -0,0 +1,34 @@
1
+ import type { SaleFinancierEnum } from '../../enums/SaleFinancierEnum';
2
+
3
+ /** Un tramo del mix por financiera del Home del Admin VL. */
4
+ export interface SalesFinancierMixEntry {
5
+ financier: SaleFinancierEnum;
6
+ count: number;
7
+ amountCents: number;
8
+ }
9
+
10
+ /**
11
+ * GET /backoffice/sales/kpis — los KPIs del Home del Admin VL. SureKeep Fase 3 — pista Retail.
12
+ *
13
+ * 🟡 **Incompleto por diseño:** el panel «Meta del día» necesita las metas por tienda, que viven en
14
+ * `retail-org-business` y **todavía no existen**. `goal` viaja `null` hasta entonces; todo lo demás
15
+ * son datos reales. El front debe tratar `null` como «sin meta configurada», no como cero.
16
+ *
17
+ * Las llaves van en inglés (el diseño no fija un payload literal acá, a diferencia de
18
+ * `BackofficeSalesStatsResponse`).
19
+ */
20
+ export interface BackofficeSalesKpisResponse {
21
+ /** Monto colocado hoy, en centavos. Mide precio del equipo, no lo cobrado en caja (DEC-032). */
22
+ soldTodayCents: number;
23
+ /** Piezas vendidas hoy. */
24
+ unitsToday: number;
25
+ /** Ticket promedio de hoy, en centavos. 0 si no hubo ventas. */
26
+ averageTicketCents: number;
27
+ /** Delta porcentual del monto de hoy contra ayer. `null` si ayer no hubo ventas (no se divide por cero). */
28
+ deltaVsYesterdayPct: number | null;
29
+ /** Monto del MISMO día de la semana pasada, en centavos — el baseline honesto contra la estacionalidad. */
30
+ baselineSameWeekdayCents: number;
31
+ mix: SalesFinancierMixEntry[];
32
+ /** Meta del día del retailer, en centavos. `null` mientras no exista la tabla de metas. */
33
+ goalCents: number | null;
34
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * GET /backoffice/sales/stats — los pills de conteo arriba de la tabla de Ventas del Admin VL.
3
+ * SureKeep Fase 3 — pista Retail.
4
+ *
5
+ * ⚠️ **Las llaves van en ESPAÑOL, a propósito y como única excepción del dominio.** Es el contrato
6
+ * literal que fija el diseño de F3 (`03_backoffice_ventasluga/lambdas/retail-wizard-business.md`) y
7
+ * contra el que el front ya mockeó la pantalla. Cambiarlas a inglés por consistencia rompería una
8
+ * pantalla ya construida sin ganar nada. Decisión de Andrés, 2026-08-03.
9
+ *
10
+ * `contado` + `credito` == `total`. `surekeep` + `externas` == `credito`: las dos particiones son
11
+ * completas y NO se solapan (ver `helpers/saleFinancier`).
12
+ */
13
+ export interface BackofficeSalesStatsResponse {
14
+ /** Ventas del retailer en la ventana consultada. */
15
+ total: number;
16
+ /** Subconjunto de `total` con `soldAt` en el día de hoy. */
17
+ hoy: number;
18
+ /** `type: CASH`. */
19
+ contado: number;
20
+ /** `type: CREDIT` o `EXTERNAL_REFERRAL` — todo lo que no es contado. */
21
+ credito: number;
22
+ /** Financiadas por la SOFOM (`type: CREDIT`). */
23
+ surekeep: number;
24
+ /** Financiadas por un tercero (`EXTERNAL_REFERRAL` con proveedor). */
25
+ externas: number;
26
+ }
@@ -0,0 +1,5 @@
1
+ /** Formato de salida de un reporte del Admin VL. SureKeep Fase 3 — pista Retail. */
2
+ export enum ReportFormatEnum {
3
+ CSV = 'CSV',
4
+ PDF = 'PDF',
5
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Periodicidad de un reporte del Admin VL. Los diarios y semanales corren por cron; los mensuales
3
+ * requieren corrida manual (M6 §5.1). SureKeep Fase 3 — pista Retail.
4
+ */
5
+ export enum ReportPeriodicityEnum {
6
+ DAILY = 'DAILY',
7
+ WEEKLY = 'WEEKLY',
8
+ MONTHLY = 'MONTHLY',
9
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Estado de una corrida de reporte. La generación es ASÍNCRONA: el `POST .../run` devuelve `QUEUED` y
3
+ * el front poletea el listado. SureKeep Fase 3 — pista Retail.
4
+ *
5
+ * Es async desde el día uno por el límite de 29 s de API Gateway: un reporte de red completa no entra
6
+ * en una respuesta síncrona. Mismo error que ya se corrigió en el cierre de corte de comisiones.
7
+ *
8
+ * QUEUED - Encolada, sin arrancar.
9
+ * RUNNING - Generándose.
10
+ * READY - Lista para descargar (`downloadUrl` presente).
11
+ * FAILED - Falló; `failureReason` explica por qué.
12
+ */
13
+ export enum ReportRunStatusEnum {
14
+ QUEUED = 'QUEUED',
15
+ RUNNING = 'RUNNING',
16
+ READY = 'READY',
17
+ FAILED = 'FAILED',
18
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Veredicto del Admin VL al resolver una excepción. Es un enum PROPIO y no un subconjunto de
3
+ * `SaleExceptionStatusEnum`: el request expresa una DECISIÓN («apruébala»), no un estado destino, y
4
+ * mezclarlos dejaría al caller mandar `APPLIED` o `NEW` en un endpoint que no los admite.
5
+ * SureKeep Fase 3 — pista Retail.
6
+ */
7
+ export enum SaleExceptionDecisionEnum {
8
+ APPROVE = 'APPROVE',
9
+ REJECT = 'REJECT',
10
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tipo de entrada del timeline de una excepción (la conversación que la pantalla de Excepciones
3
+ * dibuja bajo la ficha). SureKeep Fase 3 — pista Retail.
4
+ *
5
+ * REQUEST - El alta: quién la levantó y por qué.
6
+ * NOTE - Nota de seguimiento (pide evidencia, deja contexto).
7
+ * APPROVAL - Queda aprobada.
8
+ * REJECTION - Queda rechazada.
9
+ * APPLIED - El cambio ya se aplicó al sistema.
10
+ */
11
+ export enum SaleExceptionEventKindEnum {
12
+ REQUEST = 'REQUEST',
13
+ NOTE = 'NOTE',
14
+ APPROVAL = 'APPROVAL',
15
+ REJECTION = 'REJECTION',
16
+ APPLIED = 'APPLIED',
17
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Estado de una excepción de venta. SureKeep Fase 3 — pista Retail.
3
+ *
4
+ * NEW - Levantada por el vendedor, sin tomar.
5
+ * IN_REVIEW - El Admin VL la está revisando.
6
+ * APPROVED - Aprobada; falta aplicar el cambio a la venta.
7
+ * APPLIED - Aprobada Y aplicada.
8
+ * REJECTED - Rechazada, con resolución obligatoria.
9
+ *
10
+ * ⚠️ El estado NO va en el sort key de `RetailSaleException_GT`: el sk es inmutable en DynamoDB y esto
11
+ * cambia en todo el ciclo. El filtro por estado se hace en memoria (son eventos raros).
12
+ */
13
+ export enum SaleExceptionStatusEnum {
14
+ NEW = 'NEW',
15
+ IN_REVIEW = 'IN_REVIEW',
16
+ APPROVED = 'APPROVED',
17
+ APPLIED = 'APPLIED',
18
+ REJECTED = 'REJECTED',
19
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Tipo de excepción de venta (bandeja del Admin VL, M6 §5). SureKeep Fase 3 — pista Retail.
3
+ *
4
+ * CAPTURE_FIX - Corrección de captura: el POS registró un modelo/SKU distinto al vendido.
5
+ * LATE_CANCEL - Cancelación fuera de la ventana de 7 días.
6
+ * BACKDATING - Venta física ejecutada antes de su captura; se pide la fecha real.
7
+ * PRICE_OVERRIDE - Descuento o precio fuera de lista, autorizado por el Admin VL.
8
+ * EXTENDED_WARRANTY - Reemplazo de equipo bajo garantía extendida contratada en la venta.
9
+ *
10
+ * ⚠️ NO existe `AUTHORIZATION_CAP` (el `autorizacion_tope` del mockup): su dato es de conciliación,
11
+ * que es cobranza (D1), no retail. La fila del mockup no tiene backend en F3.
12
+ */
13
+ export enum SaleExceptionTypeEnum {
14
+ CAPTURE_FIX = 'CAPTURE_FIX',
15
+ LATE_CANCEL = 'LATE_CANCEL',
16
+ BACKDATING = 'BACKDATING',
17
+ PRICE_OVERRIDE = 'PRICE_OVERRIDE',
18
+ EXTENDED_WARRANTY = 'EXTENDED_WARRANTY',
19
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Financiera de la venta, DERIVADA — no es un campo de `RetailSale_GT`. Sale de `type` +
3
+ * `externalProvider` (ver `helpers/saleFinancier`). Existe porque las pantallas del Admin VL agrupan
4
+ * por financiera y el front, este lambda y el motor de comisiones tienen que derivarla IGUAL.
5
+ * SureKeep Fase 3 — pista Retail.
6
+ *
7
+ * NONE - Venta de contado: no hay financiera.
8
+ * SUREKEEP - Crédito propio de la SOFOM.
9
+ * PAYJOY / CREDIYA / LESPAGO / INNOVA - Derivada a un proveedor externo.
10
+ */
11
+ export enum SaleFinancierEnum {
12
+ NONE = 'NONE',
13
+ SUREKEEP = 'SUREKEEP',
14
+ PAYJOY = 'PAYJOY',
15
+ CREDIYA = 'CREDIYA',
16
+ LESPAGO = 'LESPAGO',
17
+ INNOVA = 'INNOVA',
18
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Catálogo de reportes de ventas del Admin VL (M6 §5.1). Los ids son los del mockup — el front los usa
3
+ * como llave de ruta y de traducción. SureKeep Fase 3 — pista Retail.
4
+ *
5
+ * RVL_01 - Ventas por período (diaria).
6
+ * RVL_02 - Ventas por negocio · tienda (semanal).
7
+ * RVL_03 - Ventas por vendedor (semanal).
8
+ * RVL_04 - Ventas por producto (mensual).
9
+ * RVL_05 - Ventas por financiera (mensual).
10
+ *
11
+ * ⚠️ FUERA de F3: `RVL-06` (cross-border, es M5) y `RVL-07` (conciliación, backend de cobranza).
12
+ */
13
+ export enum SalesReportIdEnum {
14
+ RVL_01 = 'RVL-01',
15
+ RVL_02 = 'RVL-02',
16
+ RVL_03 = 'RVL-03',
17
+ RVL_04 = 'RVL-04',
18
+ RVL_05 = 'RVL-05',
19
+ }
@@ -0,0 +1,48 @@
1
+ import { SaleTypeEnum } from '../enums/SaleTypeEnum';
2
+ import { ExternalProviderEnum } from '../enums/ExternalProviderEnum';
3
+ import { SaleFinancierEnum } from '../enums/SaleFinancierEnum';
4
+
5
+ /** Lo mínimo de una venta para derivar su financiera. */
6
+ export interface SaleFinancierSource {
7
+ type: SaleTypeEnum;
8
+ externalProvider: ExternalProviderEnum | null;
9
+ }
10
+
11
+ /**
12
+ * Mapa explícito proveedor → financiera. Es un `Record` COMPLETO a propósito: agregar un proveedor a
13
+ * `ExternalProviderEnum` sin agregarlo acá es error de compilación, que es exactamente el recordatorio
14
+ * que queremos. Un cast desde el enum de proveedor se vería más corto y perdería esa garantía.
15
+ */
16
+ const FINANCIER_BY_PROVIDER: Record<ExternalProviderEnum, SaleFinancierEnum> = {
17
+ [ExternalProviderEnum.PAYJOY]: SaleFinancierEnum.PAYJOY,
18
+ [ExternalProviderEnum.CREDIYA]: SaleFinancierEnum.CREDIYA,
19
+ [ExternalProviderEnum.LESPAGO]: SaleFinancierEnum.LESPAGO,
20
+ [ExternalProviderEnum.INNOVA]: SaleFinancierEnum.INNOVA,
21
+ };
22
+
23
+ /**
24
+ * Deriva la financiera de una venta. Vive acá —y no en el lambda— porque el Admin VL agrupa por
25
+ * financiera en tres pantallas distintas (Ventas, Reportes, Análisis) y el front tiene que llegar al
26
+ * MISMO número que el backend; dos implementaciones paralelas divergen al primer tipo nuevo.
27
+ *
28
+ * `EXTERNAL_REFERRAL` sin `externalProvider` cae en `NONE` en vez de reventar: hay ventas viejas sin
29
+ * el campo y un contador no es lugar para tirar una excepción.
30
+ */
31
+ export function financierOfSale(sale: SaleFinancierSource): SaleFinancierEnum {
32
+ if (sale.type === SaleTypeEnum.CREDIT) return SaleFinancierEnum.SUREKEEP;
33
+ if (sale.type === SaleTypeEnum.EXTERNAL_REFERRAL && sale.externalProvider) {
34
+ return FINANCIER_BY_PROVIDER[sale.externalProvider] ?? SaleFinancierEnum.NONE;
35
+ }
36
+ return SaleFinancierEnum.NONE;
37
+ }
38
+
39
+ /** ¿La financió SureKeep? (pill «SureKeep» de los contadores del Admin VL). */
40
+ export function isSureKeepFinanced(sale: SaleFinancierSource): boolean {
41
+ return financierOfSale(sale) === SaleFinancierEnum.SUREKEEP;
42
+ }
43
+
44
+ /** ¿La financió un tercero? (pill «Externas»). Contado NO cuenta: no tiene financiera. */
45
+ export function isExternallyFinanced(sale: SaleFinancierSource): boolean {
46
+ const financier = financierOfSale(sale);
47
+ return financier !== SaleFinancierEnum.SUREKEEP && financier !== SaleFinancierEnum.NONE;
48
+ }