@fiado/type-kit 3.314.0 → 3.315.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.
@@ -30,6 +30,29 @@ describe('RegisterCardBatchRequest — el lote se recibe por cantidad, no por n
30
30
  ).toContain('supplierBatchRef');
31
31
  });
32
32
 
33
+ it('rechaza un folio en blanco: degradaría la clave del marcador a una constante por tienda', () => {
34
+ for (const supplierBatchRef of ['', ' ']) {
35
+ expect(
36
+ errorsOf(RegisterCardBatchRequest, {
37
+ storeId: 'STORE#1',
38
+ quantity: 50,
39
+ supplierBatchRef,
40
+ }),
41
+ ).toContain('supplierBatchRef');
42
+ }
43
+ });
44
+
45
+ // El folio es la clave de idempotencia: sin recortar, reteclearlo con un espacio de más registra
46
+ // el mismo lote dos veces y suma el stock doble.
47
+ it('recorta los espacios del folio antes de que llegue al dominio', () => {
48
+ const dto = plainToInstance(RegisterCardBatchRequest, {
49
+ storeId: 'STORE#1',
50
+ quantity: 50,
51
+ supplierBatchRef: ' L-4471 ',
52
+ });
53
+ expect(dto.supplierBatchRef).toBe('L-4471');
54
+ });
55
+
33
56
  it('rechaza una cantidad de 0, negativa o fraccionaria', () => {
34
57
  for (const quantity of [0, -1, 1.5]) {
35
58
  expect(
@@ -103,18 +126,44 @@ describe('RegisterCardDeliveryRequest — entrega en un solo acto, idempotente p
103
126
  });
104
127
 
105
128
  describe('WriteOffCardStockRequest — baja de inventario por cantidad', () => {
106
- it('acepta cantidad + motivo del catálogo', () => {
129
+ const OPERATION_ID = '3f1a9c52-8d0e-4b7a-9c31-6ad2f0e15b74';
130
+
131
+ it('acepta cantidad + motivo del catálogo + clave de operación', () => {
107
132
  expect(
108
133
  errorsOf(WriteOffCardStockRequest, {
109
134
  quantity: 3,
110
135
  reason: CardWriteOffReasonEnum.PLASTIC_DAMAGED,
136
+ operationId: OPERATION_ID,
111
137
  }),
112
138
  ).toEqual([]);
113
139
  });
114
140
 
141
+ it('la clave de operación es obligatoria: sin ella un doble clic descuenta dos veces', () => {
142
+ expect(
143
+ errorsOf(WriteOffCardStockRequest, {
144
+ quantity: 3,
145
+ reason: CardWriteOffReasonEnum.LOST,
146
+ }),
147
+ ).toContain('operationId');
148
+ });
149
+
150
+ it('rechaza una clave de operación que no sea un UUID', () => {
151
+ expect(
152
+ errorsOf(WriteOffCardStockRequest, {
153
+ quantity: 3,
154
+ reason: CardWriteOffReasonEnum.LOST,
155
+ operationId: 'baja-del-martes',
156
+ }),
157
+ ).toContain('operationId');
158
+ });
159
+
115
160
  it('rechaza un motivo fuera del catálogo', () => {
116
161
  expect(
117
- errorsOf(WriteOffCardStockRequest, { quantity: 3, reason: 'THEFT_REPORTED' }),
162
+ errorsOf(WriteOffCardStockRequest, {
163
+ quantity: 3,
164
+ reason: 'THEFT_REPORTED',
165
+ operationId: OPERATION_ID,
166
+ }),
118
167
  ).toContain('reason');
119
168
  });
120
169
 
@@ -124,6 +173,7 @@ describe('WriteOffCardStockRequest — baja de inventario por cantidad', () => {
124
173
  errorsOf(WriteOffCardStockRequest, {
125
174
  quantity,
126
175
  reason: CardWriteOffReasonEnum.LOST,
176
+ operationId: OPERATION_ID,
127
177
  }),
128
178
  ).toContain('quantity');
129
179
  }
@@ -5,7 +5,10 @@
5
5
  export declare class RegisterCardBatchRequest {
6
6
  storeId: string;
7
7
  quantity: number;
8
- /** Folio de la remesa del proveedor: lo único que distingue dos entradas iguales a la misma tienda. */
8
+ /**
9
+ * Folio de la remesa del proveedor: lo único que distingue dos entradas iguales a la misma tienda.
10
+ * Se recorta y no admite blanco, porque es la clave de idempotencia de la recepción.
11
+ */
9
12
  supplierBatchRef: string;
10
13
  notes?: string | null;
11
14
  }
@@ -10,6 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.RegisterCardBatchRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
13
14
  const class_validator_1 = require("class-validator");
14
15
  /**
15
16
  * Body del registro (recepción) de un lote de tarjetas para una tienda. Los sobres llegan sellados:
@@ -30,6 +31,8 @@ __decorate([
30
31
  ], RegisterCardBatchRequest.prototype, "quantity", void 0);
31
32
  __decorate([
32
33
  (0, class_validator_1.IsString)(),
34
+ (0, class_transformer_1.Transform)(({ value }) => (typeof value === 'string' ? value.trim() : value)),
35
+ (0, class_validator_1.IsNotEmpty)(),
33
36
  __metadata("design:type", String)
34
37
  ], RegisterCardBatchRequest.prototype, "supplierBatchRef", void 0);
35
38
  __decorate([
@@ -3,5 +3,10 @@ import { CardWriteOffReasonEnum } from '../../enums/CardWriteOffReasonEnum';
3
3
  export declare class WriteOffCardStockRequest {
4
4
  quantity: number;
5
5
  reason: CardWriteOffReasonEnum;
6
+ /**
7
+ * Clave de idempotencia de la baja, generada por el caller una vez por intento. Un reintento
8
+ * devuelve el asiento original: sin ella un doble clic descontaría el doble.
9
+ */
10
+ operationId: string;
6
11
  notes?: string | null;
7
12
  }
@@ -25,6 +25,10 @@ __decorate([
25
25
  (0, class_validator_1.IsEnum)(CardWriteOffReasonEnum_1.CardWriteOffReasonEnum),
26
26
  __metadata("design:type", String)
27
27
  ], WriteOffCardStockRequest.prototype, "reason", void 0);
28
+ __decorate([
29
+ (0, class_validator_1.IsUUID)(),
30
+ __metadata("design:type", String)
31
+ ], WriteOffCardStockRequest.prototype, "operationId", void 0);
28
32
  __decorate([
29
33
  (0, class_validator_1.IsOptional)(),
30
34
  (0, class_validator_1.IsString)(),
@@ -5,7 +5,10 @@
5
5
  export interface CardDeliveryResponse {
6
6
  deliveryId: string;
7
7
  movementSk: string;
8
- /** Saldo disponible que quedó en la tienda después de descontar la entrega. */
9
- availableAfter: number;
8
+ /**
9
+ * Saldo disponible de la tienda al momento de responder, leído después de la transacción. No es
10
+ * el saldo del instante del asiento: si otra entrega cae en medio, refleja las dos.
11
+ */
12
+ availableNow: number;
10
13
  occurredAt: string;
11
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.314.0",
3
+ "version": "3.315.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -1,4 +1,5 @@
1
- import { IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
1
+ import { Transform } from 'class-transformer';
2
+ import { IsInt, IsNotEmpty, IsOptional, IsString, Max, Min } from 'class-validator';
2
3
 
3
4
  /**
4
5
  * Body del registro (recepción) de un lote de tarjetas para una tienda. Los sobres llegan sellados:
@@ -13,8 +14,13 @@ export class RegisterCardBatchRequest {
13
14
  @Max(1000)
14
15
  quantity!: number;
15
16
 
16
- /** Folio de la remesa del proveedor: lo único que distingue dos entradas iguales a la misma tienda. */
17
+ /**
18
+ * Folio de la remesa del proveedor: lo único que distingue dos entradas iguales a la misma tienda.
19
+ * Se recorta y no admite blanco, porque es la clave de idempotencia de la recepción.
20
+ */
17
21
  @IsString()
22
+ @Transform(({ value }) => (typeof value === 'string' ? value.trim() : value))
23
+ @IsNotEmpty()
18
24
  supplierBatchRef!: string;
19
25
 
20
26
  @IsOptional()
@@ -1,4 +1,4 @@
1
- import { IsEnum, IsInt, IsOptional, IsString, Min } from 'class-validator';
1
+ import { IsEnum, IsInt, IsOptional, IsString, IsUUID, Min } from 'class-validator';
2
2
  import { CardWriteOffReasonEnum } from '../../enums/CardWriteOffReasonEnum';
3
3
 
4
4
  /** Body de la baja de inventario de tarjetas de una tienda, por cantidad. */
@@ -10,6 +10,13 @@ export class WriteOffCardStockRequest {
10
10
  @IsEnum(CardWriteOffReasonEnum)
11
11
  reason!: CardWriteOffReasonEnum;
12
12
 
13
+ /**
14
+ * Clave de idempotencia de la baja, generada por el caller una vez por intento. Un reintento
15
+ * devuelve el asiento original: sin ella un doble clic descontaría el doble.
16
+ */
17
+ @IsUUID()
18
+ operationId!: string;
19
+
13
20
  @IsOptional()
14
21
  @IsString()
15
22
  notes?: string | null;
@@ -5,7 +5,10 @@
5
5
  export interface CardDeliveryResponse {
6
6
  deliveryId: string;
7
7
  movementSk: string;
8
- /** Saldo disponible que quedó en la tienda después de descontar la entrega. */
9
- availableAfter: number;
8
+ /**
9
+ * Saldo disponible de la tienda al momento de responder, leído después de la transacción. No es
10
+ * el saldo del instante del asiento: si otra entrega cae en medio, refleja las dos.
11
+ */
12
+ availableNow: number;
10
13
  occurredAt: string;
11
14
  }