@fiado/type-kit 3.473.0 → 3.475.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 (58) hide show
  1. package/_test_/unit/retailCatalog/accessorySubcategory.test.ts +65 -0
  2. package/_test_/unit/retailCatalog/ventaSaga.test.ts +4 -0
  3. package/_test_/unit/retailPromotions/constants.test.ts +9 -2
  4. package/_test_/unit/retailPromotions/discountCodeAndWizardRequests.test.ts +13 -0
  5. package/_test_/unit/retailPromotions/evaluatedSalePromotion.test.ts +38 -0
  6. package/_test_/unit/retailPromotions/salePromotionRequests.test.ts +11 -3
  7. package/_test_/unit/retailWizard/promotionM2mRequests.test.ts +41 -7
  8. package/_test_/unit/retailWizard/ventaSagaMessages.test.ts +23 -0
  9. package/bin/retailCatalog/dtos/Product.d.ts +3 -0
  10. package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.d.ts +3 -0
  11. package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.js +6 -0
  12. package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.d.ts +3 -0
  13. package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.js +6 -0
  14. package/bin/retailCatalog/dtos/requests/HoldStockRequest.js +3 -0
  15. package/bin/retailCatalog/dtos/responses/StoreCatalogItem.d.ts +3 -0
  16. package/bin/retailCatalog/enums/AccessorySubcategoryEnum.d.ts +9 -0
  17. package/bin/retailCatalog/enums/AccessorySubcategoryEnum.js +13 -0
  18. package/bin/retailCatalog/index.d.ts +1 -0
  19. package/bin/retailCatalog/index.js +2 -1
  20. package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.d.ts +2 -2
  21. package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +5 -1
  22. package/bin/retailPromotions/dtos/SalePromotionLevers.d.ts +3 -2
  23. package/bin/retailPromotions/dtos/requests/GetSaleCouponQuery.d.ts +4 -0
  24. package/bin/retailPromotions/dtos/requests/GetSaleCouponQuery.js +25 -0
  25. package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.d.ts +3 -2
  26. package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.js +4 -4
  27. package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.d.ts +2 -0
  28. package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +7 -1
  29. package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +6 -0
  30. package/bin/retailPromotions/index.d.ts +1 -0
  31. package/bin/retailPromotions/index.js +1 -0
  32. package/bin/retailWizard/dtos/WizardGiftLine.d.ts +3 -1
  33. package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.d.ts +2 -3
  34. package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.js +2 -3
  35. package/bin/retailWizard/dtos/requests/QuoteRequest.d.ts +1 -1
  36. package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.d.ts +2 -0
  37. package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +2 -2
  38. package/bin/retailWizard/events/RetailPromotionInboundMessage.js +2 -0
  39. package/package.json +1 -1
  40. package/src/retailCatalog/dtos/Product.ts +3 -0
  41. package/src/retailCatalog/dtos/requests/BackofficeCreateProductRequest.ts +6 -0
  42. package/src/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.ts +6 -0
  43. package/src/retailCatalog/dtos/requests/HoldStockRequest.ts +4 -1
  44. package/src/retailCatalog/dtos/responses/StoreCatalogItem.ts +3 -0
  45. package/src/retailCatalog/enums/AccessorySubcategoryEnum.ts +9 -0
  46. package/src/retailCatalog/index.ts +2 -1
  47. package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +6 -2
  48. package/src/retailPromotions/dtos/SalePromotionLevers.ts +3 -2
  49. package/src/retailPromotions/dtos/requests/GetSaleCouponQuery.ts +11 -0
  50. package/src/retailPromotions/dtos/requests/SalePromotionLeversInput.ts +4 -5
  51. package/src/retailPromotions/dtos/responses/WizardSalePromotionResponses.ts +2 -0
  52. package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +6 -0
  53. package/src/retailPromotions/index.ts +1 -0
  54. package/src/retailWizard/dtos/WizardGiftLine.ts +3 -1
  55. package/src/retailWizard/dtos/requests/ApplyLinePromotionRequest.ts +2 -3
  56. package/src/retailWizard/dtos/requests/QuoteRequest.ts +1 -1
  57. package/src/retailWizard/dtos/responses/PrivateWizardSessionResponse.ts +2 -0
  58. package/src/retailWizard/events/RetailPromotionInboundMessage.ts +4 -2
@@ -0,0 +1,65 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import {
5
+ AccessorySubcategoryEnum,
6
+ BackofficeCreateProductRequest,
7
+ BackofficeUpdateProductRequest,
8
+ Product,
9
+ ProductCategoryEnum,
10
+ type StoreCatalogItem,
11
+ TrackingModeEnum,
12
+ } from '../../../src/retailCatalog';
13
+
14
+ function paths(errors: ValidationError[], prefix = ''): string[] {
15
+ return errors.flatMap((e) => {
16
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
17
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
18
+ });
19
+ }
20
+
21
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
22
+ return paths(validateSync(plainToInstance(cls, payload) as object));
23
+ }
24
+
25
+ describe('AccessorySubcategoryEnum', () => {
26
+ it('expone las subcategorías de accesorio', () =>
27
+ expect(Object.values(AccessorySubcategoryEnum)).toEqual([
28
+ 'AUDIO', 'CHARGING', 'PROTECTION', 'CABLES', 'WEARABLES', 'OTHER',
29
+ ]));
30
+ });
31
+
32
+ describe('accessorySubcategory en alta y edición', () => {
33
+ const create = {
34
+ sku: 'ACC-1',
35
+ category: ProductCategoryEnum.ACCESSORY,
36
+ trackingMode: TrackingModeEnum.QUANTITY,
37
+ sellable: true,
38
+ showInSalesFlow: true,
39
+ };
40
+
41
+ it('es opcional en el alta', () => expect(errorsOf(BackofficeCreateProductRequest, create)).toEqual([]));
42
+ it('acepta una subcategoría válida en el alta', () =>
43
+ expect(errorsOf(BackofficeCreateProductRequest, { ...create, accessorySubcategory: 'AUDIO' })).toEqual([]));
44
+ it('rechaza una subcategoría inválida en el alta', () =>
45
+ expect(errorsOf(BackofficeCreateProductRequest, { ...create, accessorySubcategory: 'TOYS' })).toContain(
46
+ 'accessorySubcategory',
47
+ ));
48
+ it('acepta una subcategoría válida en la edición', () =>
49
+ expect(errorsOf(BackofficeUpdateProductRequest, { accessorySubcategory: 'CHARGING' })).toEqual([]));
50
+ it('rechaza una subcategoría inválida en la edición', () =>
51
+ expect(errorsOf(BackofficeUpdateProductRequest, { accessorySubcategory: 'toys' })).toContain('accessorySubcategory'));
52
+ });
53
+
54
+ describe('accessorySubcategory en lecturas', () => {
55
+ it('Product la lleva opcional', () => {
56
+ const p: Pick<Product, 'accessorySubcategory'> = { accessorySubcategory: AccessorySubcategoryEnum.PROTECTION };
57
+ const empty: Pick<Product, 'accessorySubcategory'> = {};
58
+ expect([p.accessorySubcategory, empty.accessorySubcategory]).toEqual(['PROTECTION', undefined]);
59
+ });
60
+
61
+ it('StoreCatalogItem la lleva opcional', () => {
62
+ const item: Pick<StoreCatalogItem, 'accessorySubcategory'> = { accessorySubcategory: AccessorySubcategoryEnum.CABLES };
63
+ expect(item.accessorySubcategory).toBe('CABLES');
64
+ });
65
+ });
@@ -54,6 +54,10 @@ describe('HoldStockRequest', () => {
54
54
  expect(errorsOf(HoldStockRequest, rest)).toEqual(expect.arrayContaining(['sessionId', 'retailerId']));
55
55
  });
56
56
  it('rechaza sin componentes', () => expect(errorsOf(HoldStockRequest, { ...ok, components: [] })).toContain('components'));
57
+ it.each(['sessionId', 'retailerId'])('rechaza %s vacío', (field) =>
58
+ expect(errorsOf(HoldStockRequest, { ...ok, [field]: '' })).toContain(field));
59
+ it('rechaza el sku vacío de un componente', () =>
60
+ expect(errorsOf(HoldStockRequest, { ...ok, components: [{ sku: '', identifiers: [] }] })).toContain('components.0.sku'));
57
61
  it('valida cada componente anidado', () => {
58
62
  expect(errorsOf(HoldStockRequest, { ...ok, components: [{ sku: 'PHONE-1', identifiers: [3] }] })).toContain(
59
63
  'components.0.identifiers',
@@ -38,12 +38,19 @@ describe('resolveSalePromotionTransition', () => {
38
38
  describe('catálogo de errores', () => {
39
39
  it('cada code tiene su status del catálogo cerrado', () => {
40
40
  for (const code of Object.values(RetailPromotionsErrorCodeEnum)) {
41
- expect([404, 409, 422]).toContain(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS[code]);
41
+ expect([403, 404, 409, 422]).toContain(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS[code]);
42
42
  }
43
43
  expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.CODE_NOT_FOUND).toBe(404);
44
44
  expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.DISCOUNT_AFTER_QUOTE).toBe(409);
45
45
  expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.CODE_NOT_OWNED).toBe(422);
46
- expect(Object.keys(RetailPromotionsErrorCodeEnum)).toHaveLength(16);
46
+ expect(Object.keys(RetailPromotionsErrorCodeEnum)).toHaveLength(20);
47
+ });
48
+
49
+ it('promo o cupón de venta inexistentes son 404; sesión sin retailer o tienda es 403', () => {
50
+ expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.SALE_PROMOTION_NOT_FOUND).toBe(404);
51
+ expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.SALE_COUPON_NOT_FOUND).toBe(404);
52
+ expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.MISSING_RETAILER_SCOPE).toBe(403);
53
+ expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.MISSING_STORE_SCOPE).toBe(403);
47
54
  });
48
55
 
49
56
  it('promo y código con rangos, signo o palancas inválidos son 422', () => {
@@ -5,6 +5,7 @@ import {
5
5
  ChangeDiscountCodeStatusRequest,
6
6
  CreateDiscountCodeRequest,
7
7
  EvaluateSalePromotionsRequest,
8
+ GetSaleCouponQuery,
8
9
  HoldSalePromotionRequest,
9
10
  ListDiscountCodesQuery,
10
11
  ListStoreSalePromotionsQuery,
@@ -82,3 +83,15 @@ describe('piezas del wizard', () => {
82
83
  expect(await errorPaths(HoldSalePromotionRequest, { promotionId: 'p1' })).toContain('promotionVersion');
83
84
  });
84
85
  });
86
+
87
+ describe('GetSaleCouponQuery', () => {
88
+ it('acepta sessionId o ninguno', async () => {
89
+ expect(await errorPaths(GetSaleCouponQuery, { sessionId: 'ses-1' })).toHaveLength(0);
90
+ expect(await errorPaths(GetSaleCouponQuery, {})).toHaveLength(0);
91
+ });
92
+
93
+ it('rechaza sessionId vacío o no texto', async () => {
94
+ expect(await errorPaths(GetSaleCouponQuery, { sessionId: '' })).toContain('sessionId');
95
+ expect(await errorPaths(GetSaleCouponQuery, { sessionId: 12 })).toContain('sessionId');
96
+ });
97
+ });
@@ -0,0 +1,38 @@
1
+ import 'reflect-metadata';
2
+ import {
3
+ EvaluatedSalePromotion,
4
+ RetailPromotionsErrorCodeEnum,
5
+ } from '../../../src/retailPromotions/index';
6
+
7
+ const basePromotion: EvaluatedSalePromotion = {
8
+ promotionId: 'promo-1',
9
+ version: 1,
10
+ name: 'Regalo',
11
+ customerText: 'Llévate un regalo',
12
+ levers: [],
13
+ lines: [],
14
+ totalDiscountCents: 0,
15
+ giftOptions: [],
16
+ giftChooseCount: null,
17
+ afterSale: [],
18
+ };
19
+
20
+ describe('EvaluatedSalePromotion.blockedBy', () => {
21
+ it('es opcional: una promo elegible no lo trae', () => {
22
+ expect(basePromotion.blockedBy).toBeUndefined();
23
+ });
24
+
25
+ it.each([
26
+ RetailPromotionsErrorCodeEnum.CUSTOMER_NOT_IDENTIFIED,
27
+ RetailPromotionsErrorCodeEnum.PROMOTION_NOT_APPLICABLE,
28
+ RetailPromotionsErrorCodeEnum.INSUFFICIENT_STOCK,
29
+ ])('acepta el motivo de bloqueo %s', (code) => {
30
+ const blocked: EvaluatedSalePromotion = { ...basePromotion, blockedBy: code };
31
+ expect(blocked.blockedBy).toBe(code);
32
+ });
33
+
34
+ it('acepta null como sin bloqueo', () => {
35
+ const open: EvaluatedSalePromotion = { ...basePromotion, blockedBy: null };
36
+ expect(open.blockedBy).toBeNull();
37
+ });
38
+ });
@@ -66,7 +66,7 @@ describe('CreateSalePromotionRequest', () => {
66
66
  expect(percent).toHaveLength(0);
67
67
  });
68
68
 
69
- it('regalo por lista exige SKUs; por gama exige categoría y precio máximo', async () => {
69
+ it('regalo por lista exige SKUs; por gama exige subcategoría de accesorio y precio máximo', async () => {
70
70
  const list = await errorPaths(CreateSalePromotionRequest, {
71
71
  ...base,
72
72
  levers: { gift: { mode: 'LIST', chooseCount: 1 } },
@@ -76,14 +76,22 @@ describe('CreateSalePromotionRequest', () => {
76
76
  ...base,
77
77
  levers: { gift: { mode: 'RANGE', chooseCount: 1 } },
78
78
  });
79
- expect(range).toEqual(expect.arrayContaining(['levers.gift.category', 'levers.gift.maxPriceCents']));
79
+ expect(range).toEqual(expect.arrayContaining(['levers.gift.accessorySubcategory', 'levers.gift.maxPriceCents']));
80
80
  const ok = await errorPaths(CreateSalePromotionRequest, {
81
81
  ...base,
82
- levers: { gift: { mode: 'RANGE', category: 'AUDIO', maxPriceCents: 30000, chooseCount: 1 } },
82
+ levers: { gift: { mode: 'RANGE', accessorySubcategory: 'AUDIO', maxPriceCents: 30000, chooseCount: 1 } },
83
83
  });
84
84
  expect(ok).toHaveLength(0);
85
85
  });
86
86
 
87
+ it('la gama del regalo solo acepta subcategorías de accesorio del catálogo', async () => {
88
+ const paths = await errorPaths(CreateSalePromotionRequest, {
89
+ ...base,
90
+ levers: { gift: { mode: 'RANGE', accessorySubcategory: 'Audífonos', maxPriceCents: 30000, chooseCount: 1 } },
91
+ });
92
+ expect(paths).toEqual(['levers.gift.accessorySubcategory']);
93
+ });
94
+
87
95
  it('valida las 7 palancas juntas', async () => {
88
96
  const paths = await errorPaths(CreateSalePromotionRequest, {
89
97
  ...base,
@@ -65,6 +65,8 @@ describe('SetAnchorGiftsRequest', () => {
65
65
  const ok = { promotionId: 'promo-1', promotionVersion: 1, items: [{ sku: 'CASE-1' }] };
66
66
 
67
67
  it('acepta un regalo con su promo', () => expect(errorsOf(SetAnchorGiftsRequest, ok)).toEqual([]));
68
+ it('acepta el mismo SKU repetido: son dos regalos iguales', () =>
69
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, items: [{ sku: 'CASE-1' }, { sku: 'CASE-1' }] })).toEqual([]));
68
70
  it('acepta dos regalos', () =>
69
71
  expect(errorsOf(SetAnchorGiftsRequest, { ...ok, items: [{ sku: 'CASE-1' }, { sku: 'MICA-1' }] })).toEqual([]));
70
72
  it('acepta items vacío con promo y versión en null (suelta todos)', () =>
@@ -136,17 +138,23 @@ describe('respuestas de los M2M de Promociones', () => {
136
138
  });
137
139
 
138
140
  it('una línea GIFT exige role GIFT, ancla y precio original', () => {
139
- const { linkedLineId: _l, ...sinAncla } = gift;
140
- const { originalPriceCents: _o, ...sinPrecio } = gift;
141
- const { role: _r, ...sinRole } = gift;
141
+ const { linkedLineId: _l, ...withoutAnchor } = gift;
142
+ const { originalPriceCents: _o, ...withoutPrice } = gift;
143
+ const { role: _r, ...withoutRole } = gift;
142
144
  // @ts-expect-error linkedLineId es obligatorio en un GIFT
143
- const a: WizardGiftLine = sinAncla;
145
+ const a: WizardGiftLine = withoutAnchor;
144
146
  // @ts-expect-error originalPriceCents es obligatorio en un GIFT
145
- const b: WizardGiftLine = sinPrecio;
147
+ const b: WizardGiftLine = withoutPrice;
146
148
  // @ts-expect-error role GIFT es obligatorio
147
- const c: WizardGiftLine = sinRole;
149
+ const c: WizardGiftLine = withoutRole;
150
+ const { promotionId: _p, ...withoutPromotion } = gift;
151
+ const { promotionVersion: _v, ...withoutVersion } = gift;
152
+ // @ts-expect-error promotionId es obligatorio en un GIFT
153
+ const d: WizardGiftLine = withoutPromotion;
154
+ // @ts-expect-error promotionVersion es obligatorio en un GIFT
155
+ const e: WizardGiftLine = withoutVersion;
148
156
  const line: WizardReservedLine = gift;
149
- expect([a, b, c, line]).toHaveLength(4);
157
+ expect([a, b, c, d, e, line]).toHaveLength(6);
150
158
  });
151
159
 
152
160
  it('la lectura privada trae líneas, ancla, crédito, paso y promo de venta', () => {
@@ -186,4 +194,30 @@ describe('respuestas de los M2M de Promociones', () => {
186
194
  };
187
195
  expect(view.payment?.paidAt).toBe('2026-09-28T20:00:00.000Z');
188
196
  });
197
+
198
+ it('la lectura privada acepta splitFinancingEnabled como booleano opcional', () => {
199
+ const base: PrivateWizardSessionResponse = {
200
+ sessionId: 'ses-1',
201
+ retailerId: 'ret',
202
+ storeId: 'st',
203
+ retailCustomerId: null,
204
+ state: WizardStateEnum.ACTIVE,
205
+ currentStep: WizardStepEnum.S06,
206
+ creditId: null,
207
+ anchorLineId: null,
208
+ salePromotionId: null,
209
+ salePromotionVersion: null,
210
+ lines: [],
211
+ };
212
+ const enabled: PrivateWizardSessionResponse = { ...base, splitFinancingEnabled: true };
213
+ const disabled: PrivateWizardSessionResponse = { ...base, splitFinancingEnabled: false };
214
+ // @ts-expect-error el flag es booleano, no string
215
+ const asString: PrivateWizardSessionResponse = { ...base, splitFinancingEnabled: 'true' };
216
+ expect([enabled.splitFinancingEnabled, disabled.splitFinancingEnabled, base.splitFinancingEnabled]).toEqual([
217
+ true,
218
+ false,
219
+ undefined,
220
+ ]);
221
+ expect(asString).toBeDefined();
222
+ });
189
223
  });
@@ -161,3 +161,26 @@ describe('RetailCommissionInboundMessage', () => {
161
161
  errorsOf(RetailCommissionInboundMessage, { ...ok, lines: [{ sku: 'X', qty: 0, netCents: -1, itemType: 'KIT' }] }),
162
162
  ).toEqual(expect.arrayContaining(['lines.0.qty', 'lines.0.netCents', 'lines.0.itemType'])));
163
163
  });
164
+
165
+ describe('RetailPromotionInboundMessage — tipos nullables y ids no vacíos', () => {
166
+ const base = {
167
+ type: RetailPromotionInboundMessageTypeEnum.SALE_CONFIRMED,
168
+ tenantId: 't',
169
+ retailerId: 'ret',
170
+ storeId: 'st',
171
+ saleId: 'sale-1',
172
+ sessionId: 'ses-1',
173
+ customerId: 'cus-1',
174
+ soldAt: '2026-09-28T20:00:00.000Z',
175
+ occurredAt: '2026-09-28T20:00:00.000Z',
176
+ lines: [{ lineId: 'L1', sku: 'PHONE-1', qty: 1, originalPriceCents: 100, identifiers: [] }],
177
+ };
178
+
179
+ it('tipa salePromotionId y salePromotionVersion como nullables', () => {
180
+ const msg: RetailPromotionInboundMessage = { ...base, salePromotionId: null, salePromotionVersion: null };
181
+ expect(errorsOf(RetailPromotionInboundMessage, msg)).toEqual([]);
182
+ });
183
+
184
+ it.each(['creditId', 'fiadoDirectoryId'])('rechaza %s vacío', (field) =>
185
+ expect(errorsOf(RetailPromotionInboundMessage, { ...base, [field]: '' })).toContain(field));
186
+ });
@@ -3,6 +3,7 @@ import { MdmProviderEnum } from '../enums/MdmProviderEnum';
3
3
  import { ProductStatusEnum } from '../enums/ProductStatusEnum';
4
4
  import { ProductTierEnum } from '../enums/ProductTierEnum';
5
5
  import { ProductCategoryEnum } from '../enums/ProductCategoryEnum';
6
+ import { AccessorySubcategoryEnum } from '../enums/AccessorySubcategoryEnum';
6
7
  import { TrackingModeEnum } from '../enums/TrackingModeEnum';
7
8
  import { ItemTypeEnum } from '../enums/ItemTypeEnum';
8
9
  import { ProductComponent } from './ProductComponent';
@@ -37,6 +38,8 @@ export declare class Product {
37
38
  createdAt: string;
38
39
  updatedAt: string;
39
40
  category?: ProductCategoryEnum;
41
+ /** Solo en ACCESSORY. */
42
+ accessorySubcategory?: AccessorySubcategoryEnum;
40
43
  trackingMode?: TrackingModeEnum;
41
44
  /** Se puede vender suelto (fuera de un kit). */
42
45
  sellable?: boolean;
@@ -1,4 +1,5 @@
1
1
  import { ProductCategoryEnum } from '../../enums/ProductCategoryEnum';
2
+ import { AccessorySubcategoryEnum } from '../../enums/AccessorySubcategoryEnum';
2
3
  import { ProductTierEnum } from '../../enums/ProductTierEnum';
3
4
  import { TrackingModeEnum } from '../../enums/TrackingModeEnum';
4
5
  import { MdmLockModeEnum } from '../../enums/MdmLockModeEnum';
@@ -8,6 +9,8 @@ export declare class BackofficeCreateProductRequest {
8
9
  sku: string;
9
10
  /** El kit (BUNDLE) se da de alta en backofficeCreateKit. */
10
11
  category: ProductCategoryEnum;
12
+ /** Solo en ACCESSORY. */
13
+ accessorySubcategory?: AccessorySubcategoryEnum;
11
14
  /** Inmutable tras el alta. */
12
15
  trackingMode: TrackingModeEnum;
13
16
  sellable: boolean;
@@ -14,6 +14,7 @@ const class_validator_1 = require("class-validator");
14
14
  const SkuPattern_1 = require("../../constants/SkuPattern");
15
15
  const NonBundleCategories_1 = require("../../constants/NonBundleCategories");
16
16
  const ProductCategoryEnum_1 = require("../../enums/ProductCategoryEnum");
17
+ const AccessorySubcategoryEnum_1 = require("../../enums/AccessorySubcategoryEnum");
17
18
  const ProductTierEnum_1 = require("../../enums/ProductTierEnum");
18
19
  const TrackingModeEnum_1 = require("../../enums/TrackingModeEnum");
19
20
  const MdmLockModeEnum_1 = require("../../enums/MdmLockModeEnum");
@@ -33,6 +34,11 @@ __decorate([
33
34
  (0, class_validator_1.IsIn)(NonBundleCategories_1.NON_BUNDLE_CATEGORIES),
34
35
  __metadata("design:type", String)
35
36
  ], BackofficeCreateProductRequest.prototype, "category", void 0);
37
+ __decorate([
38
+ (0, class_validator_1.IsOptional)(),
39
+ (0, class_validator_1.IsEnum)(AccessorySubcategoryEnum_1.AccessorySubcategoryEnum),
40
+ __metadata("design:type", String)
41
+ ], BackofficeCreateProductRequest.prototype, "accessorySubcategory", void 0);
36
42
  __decorate([
37
43
  (0, class_validator_1.IsEnum)(TrackingModeEnum_1.TrackingModeEnum),
38
44
  __metadata("design:type", String)
@@ -1,4 +1,5 @@
1
1
  import { ProductCategoryEnum } from '../../enums/ProductCategoryEnum';
2
+ import { AccessorySubcategoryEnum } from '../../enums/AccessorySubcategoryEnum';
2
3
  import { ProductTierEnum } from '../../enums/ProductTierEnum';
3
4
  import { ProductComponent } from '../ProductComponent';
4
5
  import { MdmLockModeEnum } from '../../enums/MdmLockModeEnum';
@@ -6,6 +7,8 @@ import { MdmProviderEnum } from '../../enums/MdmProviderEnum';
6
7
  /** Body de PATCH /backoffice/inventory/skus/{sku} (backofficeUpdateProduct); parcial, sin itemType ni trackingMode (inmutables) ni precio. */
7
8
  export declare class BackofficeUpdateProductRequest {
8
9
  category?: ProductCategoryEnum;
10
+ /** Solo en ACCESSORY. */
11
+ accessorySubcategory?: AccessorySubcategoryEnum;
9
12
  sellable?: boolean;
10
13
  showInSalesFlow?: boolean;
11
14
  /** Solo en kits; reemplaza la lista completa sin tocar los apartados vivos. */
@@ -14,6 +14,7 @@ const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const NonBundleCategories_1 = require("../../constants/NonBundleCategories");
16
16
  const ProductCategoryEnum_1 = require("../../enums/ProductCategoryEnum");
17
+ const AccessorySubcategoryEnum_1 = require("../../enums/AccessorySubcategoryEnum");
17
18
  const ProductTierEnum_1 = require("../../enums/ProductTierEnum");
18
19
  const ProductComponent_1 = require("../ProductComponent");
19
20
  const MdmLockModeEnum_1 = require("../../enums/MdmLockModeEnum");
@@ -28,6 +29,11 @@ __decorate([
28
29
  (0, class_validator_1.IsIn)(NonBundleCategories_1.NON_BUNDLE_CATEGORIES),
29
30
  __metadata("design:type", String)
30
31
  ], BackofficeUpdateProductRequest.prototype, "category", void 0);
32
+ __decorate([
33
+ (0, class_validator_1.IsOptional)(),
34
+ (0, class_validator_1.IsEnum)(AccessorySubcategoryEnum_1.AccessorySubcategoryEnum),
35
+ __metadata("design:type", String)
36
+ ], BackofficeUpdateProductRequest.prototype, "accessorySubcategory", void 0);
31
37
  __decorate([
32
38
  (0, class_validator_1.IsOptional)(),
33
39
  (0, class_validator_1.IsBoolean)(),
@@ -19,6 +19,7 @@ class HoldStockComponent {
19
19
  exports.HoldStockComponent = HoldStockComponent;
20
20
  __decorate([
21
21
  (0, class_validator_1.IsString)(),
22
+ (0, class_validator_1.IsNotEmpty)(),
22
23
  __metadata("design:type", String)
23
24
  ], HoldStockComponent.prototype, "sku", void 0);
24
25
  __decorate([
@@ -32,10 +33,12 @@ class HoldStockRequest extends ReservationLineRef_1.ReservationLineRef {
32
33
  exports.HoldStockRequest = HoldStockRequest;
33
34
  __decorate([
34
35
  (0, class_validator_1.IsString)(),
36
+ (0, class_validator_1.IsNotEmpty)(),
35
37
  __metadata("design:type", String)
36
38
  ], HoldStockRequest.prototype, "sessionId", void 0);
37
39
  __decorate([
38
40
  (0, class_validator_1.IsString)(),
41
+ (0, class_validator_1.IsNotEmpty)(),
39
42
  __metadata("design:type", String)
40
43
  ], HoldStockRequest.prototype, "retailerId", void 0);
41
44
  __decorate([
@@ -1,6 +1,7 @@
1
1
  import type { MdmLockModeEnum } from '../../enums/MdmLockModeEnum';
2
2
  import type { MdmProviderEnum } from '../../enums/MdmProviderEnum';
3
3
  import type { ProductCategoryEnum } from '../../enums/ProductCategoryEnum';
4
+ import type { AccessorySubcategoryEnum } from '../../enums/AccessorySubcategoryEnum';
4
5
  import type { ProductTierEnum } from '../../enums/ProductTierEnum';
5
6
  import type { TrackingModeEnum } from '../../enums/TrackingModeEnum';
6
7
  import type { ItemTypeEnum } from '../../enums/ItemTypeEnum';
@@ -16,6 +17,8 @@ export interface StoreCatalogItem {
16
17
  available: number;
17
18
  basePriceCents: number;
18
19
  category: ProductCategoryEnum;
20
+ /** Solo en ACCESSORY. */
21
+ accessorySubcategory?: AccessorySubcategoryEnum;
19
22
  trackingMode: TrackingModeEnum;
20
23
  brand: string;
21
24
  model: string;
@@ -0,0 +1,9 @@
1
+ /** Subcategoría de un producto ACCESSORY; sirve para regalar «por gama» en Promociones. */
2
+ export declare enum AccessorySubcategoryEnum {
3
+ AUDIO = "AUDIO",
4
+ CHARGING = "CHARGING",
5
+ PROTECTION = "PROTECTION",
6
+ CABLES = "CABLES",
7
+ WEARABLES = "WEARABLES",
8
+ OTHER = "OTHER"
9
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AccessorySubcategoryEnum = void 0;
4
+ /** Subcategoría de un producto ACCESSORY; sirve para regalar «por gama» en Promociones. */
5
+ var AccessorySubcategoryEnum;
6
+ (function (AccessorySubcategoryEnum) {
7
+ AccessorySubcategoryEnum["AUDIO"] = "AUDIO";
8
+ AccessorySubcategoryEnum["CHARGING"] = "CHARGING";
9
+ AccessorySubcategoryEnum["PROTECTION"] = "PROTECTION";
10
+ AccessorySubcategoryEnum["CABLES"] = "CABLES";
11
+ AccessorySubcategoryEnum["WEARABLES"] = "WEARABLES";
12
+ AccessorySubcategoryEnum["OTHER"] = "OTHER";
13
+ })(AccessorySubcategoryEnum || (exports.AccessorySubcategoryEnum = AccessorySubcategoryEnum = {}));
@@ -29,6 +29,7 @@ export * from './dtos/responses/InventoryLensResponse';
29
29
  export * from './dtos/validation/ValidateProductResponse';
30
30
  export * from './dtos/validation/ValidateInventoryResponse';
31
31
  export * from './enums/ProductCategoryEnum';
32
+ export * from './enums/AccessorySubcategoryEnum';
32
33
  export * from './enums/TrackingModeEnum';
33
34
  export * from './enums/InventoryMovementTypeEnum';
34
35
  export * from './enums/InventorySyncResultStatusEnum';
@@ -53,6 +53,7 @@ __exportStar(require("./dtos/validation/ValidateProductResponse"), exports);
53
53
  __exportStar(require("./dtos/validation/ValidateInventoryResponse"), exports);
54
54
  // Inventario multi-item (ciclo 1)
55
55
  __exportStar(require("./enums/ProductCategoryEnum"), exports);
56
+ __exportStar(require("./enums/AccessorySubcategoryEnum"), exports);
56
57
  __exportStar(require("./enums/TrackingModeEnum"), exports);
57
58
  __exportStar(require("./enums/InventoryMovementTypeEnum"), exports);
58
59
  __exportStar(require("./enums/InventorySyncResultStatusEnum"), exports);
@@ -99,7 +100,7 @@ __exportStar(require("./dtos/requests/BackofficeCreateTransferRequest"), exports
99
100
  __exportStar(require("./dtos/requests/BackofficeListTransfersQuery"), exports);
100
101
  __exportStar(require("./dtos/responses/TransferResponse"), exports);
101
102
  __exportStar(require("./dtos/responses/ListTransfersResponse"), exports);
102
- // Venta saga (ciclo 3)
103
+ // Saga de venta: hold y avisos de la cola
103
104
  __exportStar(require("./enums/ReservePurposeEnum"), exports);
104
105
  __exportStar(require("./enums/InventoryInboundMessageTypeEnum"), exports);
105
106
  __exportStar(require("./dtos/requests/HoldStockRequest"), exports);
@@ -1,3 +1,3 @@
1
1
  import { RetailPromotionsErrorCodeEnum as C } from '../enums/RetailPromotionsErrorCodeEnum';
2
- /** Status HTTP de cada code: 404 no existe · 409 conflicto de estado · 422 regla de negocio. */
3
- export declare const RETAIL_PROMOTIONS_ERROR_HTTP_STATUS: Readonly<Record<C, 404 | 409 | 422>>;
2
+ /** Status HTTP de cada code: 403 sesión sin alcance · 404 no existe · 409 conflicto de estado · 422 regla de negocio. */
3
+ export declare const RETAIL_PROMOTIONS_ERROR_HTTP_STATUS: Readonly<Record<C, 403 | 404 | 409 | 422>>;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RETAIL_PROMOTIONS_ERROR_HTTP_STATUS = void 0;
4
4
  const RetailPromotionsErrorCodeEnum_1 = require("../enums/RetailPromotionsErrorCodeEnum");
5
- /** Status HTTP de cada code: 404 no existe · 409 conflicto de estado · 422 regla de negocio. */
5
+ /** Status HTTP de cada code: 403 sesión sin alcance · 404 no existe · 409 conflicto de estado · 422 regla de negocio. */
6
6
  exports.RETAIL_PROMOTIONS_ERROR_HTTP_STATUS = {
7
7
  [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.INVALID_PROMOTION_TRANSITION]: 409,
8
8
  [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.CODE_TEXT_TAKEN]: 409,
@@ -20,4 +20,8 @@ exports.RETAIL_PROMOTIONS_ERROR_HTTP_STATUS = {
20
20
  [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.DISCOUNT_AFTER_QUOTE]: 409,
21
21
  [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.INVALID_SALE_PROMOTION]: 422,
22
22
  [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.INVALID_DISCOUNT_CODE]: 422,
23
+ [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.SALE_PROMOTION_NOT_FOUND]: 404,
24
+ [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.SALE_COUPON_NOT_FOUND]: 404,
25
+ [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.MISSING_RETAILER_SCOPE]: 403,
26
+ [RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.MISSING_STORE_SCOPE]: 403,
23
27
  };
@@ -1,3 +1,4 @@
1
+ import { AccessorySubcategoryEnum } from '../../retailCatalog/enums/AccessorySubcategoryEnum';
1
2
  import { ProductCategoryEnum } from '../../retailCatalog/enums/ProductCategoryEnum';
2
3
  import { ProductTierEnum } from '../../retailCatalog/enums/ProductTierEnum';
3
4
  import { CouponScopeEnum } from '../enums/CouponScopeEnum';
@@ -8,11 +9,11 @@ export interface AccessoryDiscountLever {
8
9
  percentBps: number;
9
10
  maxAmountCents: number | null;
10
11
  }
11
- /** Regalo por lista (`skus`) o por gama (`category` + `maxPriceCents`); el cliente elige `chooseCount`. */
12
+ /** Regalo por lista (`skus`) o por gama (`accessorySubcategory` + `maxPriceCents`); el cliente elige `chooseCount`. */
12
13
  export interface GiftLever {
13
14
  mode: GiftModeEnum;
14
15
  skus: string[] | null;
15
- category: string | null;
16
+ accessorySubcategory: AccessorySubcategoryEnum | null;
16
17
  maxPriceCents: number | null;
17
18
  chooseCount: number;
18
19
  }
@@ -0,0 +1,4 @@
1
+ /** Query de publicGetSaleCoupon (`GET /public/sales/{saleId}/coupon`). */
2
+ export declare class GetSaleCouponQuery {
3
+ sessionId?: string;
4
+ }
@@ -0,0 +1,25 @@
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.GetSaleCouponQuery = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /** Query de publicGetSaleCoupon (`GET /public/sales/{saleId}/coupon`). */
16
+ class GetSaleCouponQuery {
17
+ }
18
+ exports.GetSaleCouponQuery = GetSaleCouponQuery;
19
+ __decorate([
20
+ (0, class_transformer_1.Expose)(),
21
+ (0, class_validator_1.IsOptional)(),
22
+ (0, class_validator_1.IsString)(),
23
+ (0, class_validator_1.IsNotEmpty)(),
24
+ __metadata("design:type", String)
25
+ ], GetSaleCouponQuery.prototype, "sessionId", void 0);
@@ -1,3 +1,4 @@
1
+ import { AccessorySubcategoryEnum } from '../../../retailCatalog/enums/AccessorySubcategoryEnum';
1
2
  import { CouponScopeEnum } from '../../enums/CouponScopeEnum';
2
3
  import { GiftModeEnum } from '../../enums/GiftModeEnum';
3
4
  import { DiscountValueInput } from './DiscountValueInput';
@@ -5,11 +6,11 @@ export declare class AccessoryDiscountLeverInput {
5
6
  percentBps: number;
6
7
  maxAmountCents?: number | null;
7
8
  }
8
- /** Regalo por lista exige `skus`; por gama exige `category` y `maxPriceCents`. */
9
+ /** Regalo por lista exige `skus`; por gama exige `accessorySubcategory` y `maxPriceCents`. */
9
10
  export declare class GiftLeverInput {
10
11
  mode: GiftModeEnum;
11
12
  skus?: string[] | null;
12
- category?: string | null;
13
+ accessorySubcategory?: AccessorySubcategoryEnum | null;
13
14
  maxPriceCents?: number | null;
14
15
  chooseCount: number;
15
16
  }
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.SalePromotionLeversInput = exports.WalletBonusLeverInput = exports.ExtendedWarrantyLeverInput = exports.AirtimeLeverInput = exports.CouponLeverInput = exports.GiftLeverInput = exports.AccessoryDiscountLeverInput = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
+ const AccessorySubcategoryEnum_1 = require("../../../retailCatalog/enums/AccessorySubcategoryEnum");
15
16
  const CouponScopeEnum_1 = require("../../enums/CouponScopeEnum");
16
17
  const GiftModeEnum_1 = require("../../enums/GiftModeEnum");
17
18
  const DiscountValueInput_1 = require("./DiscountValueInput");
@@ -29,7 +30,7 @@ __decorate([
29
30
  (0, class_validator_1.IsInt)(),
30
31
  __metadata("design:type", Number)
31
32
  ], AccessoryDiscountLeverInput.prototype, "maxAmountCents", void 0);
32
- /** Regalo por lista exige `skus`; por gama exige `category` y `maxPriceCents`. */
33
+ /** Regalo por lista exige `skus`; por gama exige `accessorySubcategory` y `maxPriceCents`. */
33
34
  class GiftLeverInput {
34
35
  }
35
36
  exports.GiftLeverInput = GiftLeverInput;
@@ -49,10 +50,9 @@ __decorate([
49
50
  __decorate([
50
51
  (0, class_transformer_1.Expose)(),
51
52
  (0, class_validator_1.ValidateIf)((o) => o.mode === GiftModeEnum_1.GiftModeEnum.RANGE),
52
- (0, class_validator_1.IsString)(),
53
- (0, class_validator_1.IsNotEmpty)(),
53
+ (0, class_validator_1.IsEnum)(AccessorySubcategoryEnum_1.AccessorySubcategoryEnum),
54
54
  __metadata("design:type", String)
55
- ], GiftLeverInput.prototype, "category", void 0);
55
+ ], GiftLeverInput.prototype, "accessorySubcategory", void 0);
56
56
  __decorate([
57
57
  (0, class_transformer_1.Expose)(),
58
58
  (0, class_validator_1.ValidateIf)((o) => o.mode === GiftModeEnum_1.GiftModeEnum.RANGE),
@@ -46,6 +46,8 @@ export interface EvaluatedSalePromotion {
46
46
  giftOptions: GiftOption[];
47
47
  giftChooseCount: number | null;
48
48
  afterSale: AfterSaleBenefit[];
49
+ /** Si viene, la promo se muestra pero no se puede elegir; dice por qué (ej. CUSTOMER_NOT_IDENTIFIED). */
50
+ blockedBy?: RetailPromotionsErrorCodeEnum | null;
49
51
  }
50
52
  /** Resultado del código tecleado: si no aplica, `rejectionCode` dice por qué. */
51
53
  export interface EvaluatedDiscountCode {
@@ -19,5 +19,11 @@ export declare enum RetailPromotionsErrorCodeEnum {
19
19
  CODE_ALREADY_APPLIED = "CODE_ALREADY_APPLIED",
20
20
  DISCOUNT_AFTER_QUOTE = "DISCOUNT_AFTER_QUOTE",
21
21
  INVALID_SALE_PROMOTION = "INVALID_SALE_PROMOTION",
22
- INVALID_DISCOUNT_CODE = "INVALID_DISCOUNT_CODE"
22
+ INVALID_DISCOUNT_CODE = "INVALID_DISCOUNT_CODE",
23
+ SALE_PROMOTION_NOT_FOUND = "SALE_PROMOTION_NOT_FOUND",
24
+ SALE_COUPON_NOT_FOUND = "SALE_COUPON_NOT_FOUND",
25
+ /** La sesión no trae retailer: no se puede acotar la consulta. */
26
+ MISSING_RETAILER_SCOPE = "MISSING_RETAILER_SCOPE",
27
+ /** La sesión no trae tienda: no se puede acotar la consulta. */
28
+ MISSING_STORE_SCOPE = "MISSING_STORE_SCOPE"
23
29
  }
@@ -24,4 +24,10 @@ var RetailPromotionsErrorCodeEnum;
24
24
  RetailPromotionsErrorCodeEnum["DISCOUNT_AFTER_QUOTE"] = "DISCOUNT_AFTER_QUOTE";
25
25
  RetailPromotionsErrorCodeEnum["INVALID_SALE_PROMOTION"] = "INVALID_SALE_PROMOTION";
26
26
  RetailPromotionsErrorCodeEnum["INVALID_DISCOUNT_CODE"] = "INVALID_DISCOUNT_CODE";
27
+ RetailPromotionsErrorCodeEnum["SALE_PROMOTION_NOT_FOUND"] = "SALE_PROMOTION_NOT_FOUND";
28
+ RetailPromotionsErrorCodeEnum["SALE_COUPON_NOT_FOUND"] = "SALE_COUPON_NOT_FOUND";
29
+ /** La sesión no trae retailer: no se puede acotar la consulta. */
30
+ RetailPromotionsErrorCodeEnum["MISSING_RETAILER_SCOPE"] = "MISSING_RETAILER_SCOPE";
31
+ /** La sesión no trae tienda: no se puede acotar la consulta. */
32
+ RetailPromotionsErrorCodeEnum["MISSING_STORE_SCOPE"] = "MISSING_STORE_SCOPE";
27
33
  })(RetailPromotionsErrorCodeEnum || (exports.RetailPromotionsErrorCodeEnum = RetailPromotionsErrorCodeEnum = {}));
@@ -41,6 +41,7 @@ export * from './dtos/requests/ChangeDiscountCodeStatusRequest';
41
41
  export * from './dtos/requests/ListStoreSalePromotionsQuery';
42
42
  export * from './dtos/requests/EvaluateSalePromotionsRequest';
43
43
  export * from './dtos/requests/HoldSalePromotionRequest';
44
+ export * from './dtos/requests/GetSaleCouponQuery';
44
45
  export * from './dtos/responses/SalePromotionCrudResponses';
45
46
  export * from './dtos/responses/DiscountCodeResponses';
46
47
  export * from './dtos/responses/WizardSalePromotionResponses';
@@ -61,6 +61,7 @@ __exportStar(require("./dtos/requests/ChangeDiscountCodeStatusRequest"), exports
61
61
  __exportStar(require("./dtos/requests/ListStoreSalePromotionsQuery"), exports);
62
62
  __exportStar(require("./dtos/requests/EvaluateSalePromotionsRequest"), exports);
63
63
  __exportStar(require("./dtos/requests/HoldSalePromotionRequest"), exports);
64
+ __exportStar(require("./dtos/requests/GetSaleCouponQuery"), exports);
64
65
  // Response DTOs
65
66
  __exportStar(require("./dtos/responses/SalePromotionCrudResponses"), exports);
66
67
  __exportStar(require("./dtos/responses/DiscountCodeResponses"), exports);
@@ -1,8 +1,10 @@
1
1
  import type { WizardLineRoleEnum } from '../enums/WizardLineRoleEnum';
2
2
  import type { WizardReservedLine } from './WizardReservedLine';
3
- /** Línea GIFT: siempre ligada a un ancla y con el precio de lista que se regala. */
3
+ /** Línea GIFT: ligada a un ancla, con su promo y el precio de lista que se regala. */
4
4
  export type WizardGiftLine = WizardReservedLine & {
5
5
  role: WizardLineRoleEnum.GIFT;
6
6
  linkedLineId: string;
7
+ promotionId: string;
8
+ promotionVersion: number;
7
9
  originalPriceCents: number;
8
10
  };
@@ -1,7 +1,6 @@
1
1
  /**
2
- * PUT /private/wizard/sessions/{id}/lines/{lineId}/promotion (privateApplyLinePromotion, M2M de Promociones).
3
- * Reemplaza la promo de la línea por valor: un campo ausente queda borrado. `tenantId` va en el query.
4
- * Nunca toca role ni linkedLineId: los GIFT los gestiona el PUT …/gifts. Responde 204 sin body.
2
+ * PUT …/lines/{lineId}/promotion (privateApplyLinePromotion): reemplaza la promo de la línea por valor.
3
+ * Un campo ausente queda borrado; no toca role ni linkedLineId. `tenantId` va en el query.
5
4
  */
6
5
  export declare class ApplyLinePromotionRequest {
7
6
  promotionId?: string;
@@ -14,9 +14,8 @@ const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const hasPromotion = (o) => o.promotionId !== undefined || o.promotionVersion !== undefined;
16
16
  /**
17
- * PUT /private/wizard/sessions/{id}/lines/{lineId}/promotion (privateApplyLinePromotion, M2M de Promociones).
18
- * Reemplaza la promo de la línea por valor: un campo ausente queda borrado. `tenantId` va en el query.
19
- * Nunca toca role ni linkedLineId: los GIFT los gestiona el PUT …/gifts. Responde 204 sin body.
17
+ * PUT …/lines/{lineId}/promotion (privateApplyLinePromotion): reemplaza la promo de la línea por valor.
18
+ * Un campo ausente queda borrado; no toca role ni linkedLineId. `tenantId` va en el query.
20
19
  */
21
20
  class ApplyLinePromotionRequest {
22
21
  }
@@ -8,6 +8,6 @@
8
8
  export declare class QuoteRequest {
9
9
  downPaymentCents: number;
10
10
  termWeeks: number;
11
- /** Se ignora: el servidor financia la ancla. Se acepta un release más por el front viejo. */
11
+ /** Se ignora: el servidor financia la ancla. */
12
12
  financedLineId?: string;
13
13
  }
@@ -26,4 +26,6 @@ export interface PrivateWizardSessionResponse {
26
26
  paymentMethod?: PaymentMethodEnum | null;
27
27
  /** Cobro de contado; `null` mientras no se cobra. */
28
28
  payment?: WizardPayment | null;
29
+ /** Valor efectivo del flag SPLIT_FINANCING_ENABLED del wizard. */
30
+ splitFinancingEnabled?: boolean;
29
31
  }
@@ -34,7 +34,7 @@ export declare class RetailPromotionInboundMessage {
34
34
  occurredAt: string;
35
35
  creditId?: string;
36
36
  fiadoDirectoryId?: string;
37
- salePromotionId?: string;
38
- salePromotionVersion?: number;
37
+ salePromotionId?: string | null;
38
+ salePromotionVersion?: number | null;
39
39
  lines: RetailPromotionInboundLine[];
40
40
  }
@@ -132,11 +132,13 @@ __decorate([
132
132
  __decorate([
133
133
  (0, class_validator_1.IsOptional)(),
134
134
  (0, class_validator_1.IsString)(),
135
+ (0, class_validator_1.IsNotEmpty)(),
135
136
  __metadata("design:type", String)
136
137
  ], RetailPromotionInboundMessage.prototype, "creditId", void 0);
137
138
  __decorate([
138
139
  (0, class_validator_1.IsOptional)(),
139
140
  (0, class_validator_1.IsString)(),
141
+ (0, class_validator_1.IsNotEmpty)(),
140
142
  __metadata("design:type", String)
141
143
  ], RetailPromotionInboundMessage.prototype, "fiadoDirectoryId", void 0);
142
144
  __decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.473.0",
3
+ "version": "3.475.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -3,6 +3,7 @@ import { MdmProviderEnum } from '../enums/MdmProviderEnum';
3
3
  import { ProductStatusEnum } from '../enums/ProductStatusEnum';
4
4
  import { ProductTierEnum } from '../enums/ProductTierEnum';
5
5
  import { ProductCategoryEnum } from '../enums/ProductCategoryEnum';
6
+ import { AccessorySubcategoryEnum } from '../enums/AccessorySubcategoryEnum';
6
7
  import { TrackingModeEnum } from '../enums/TrackingModeEnum';
7
8
  import { ItemTypeEnum } from '../enums/ItemTypeEnum';
8
9
  import { ProductComponent } from './ProductComponent';
@@ -40,6 +41,8 @@ export class Product {
40
41
 
41
42
  // Campos del inventario multi-item; opcionales hasta que todo producto pase por la puerta de entrada.
42
43
  category?: ProductCategoryEnum;
44
+ /** Solo en ACCESSORY. */
45
+ accessorySubcategory?: AccessorySubcategoryEnum;
43
46
  trackingMode?: TrackingModeEnum;
44
47
  /** Se puede vender suelto (fuera de un kit). */
45
48
  sellable?: boolean;
@@ -2,6 +2,7 @@ import { IsArray, IsBoolean, IsEnum, IsIn, IsInt, IsISO8601, IsObject, IsOptiona
2
2
  import { SKU_PATTERN } from '../../constants/SkuPattern';
3
3
  import { NON_BUNDLE_CATEGORIES } from '../../constants/NonBundleCategories';
4
4
  import { ProductCategoryEnum } from '../../enums/ProductCategoryEnum';
5
+ import { AccessorySubcategoryEnum } from '../../enums/AccessorySubcategoryEnum';
5
6
  import { ProductTierEnum } from '../../enums/ProductTierEnum';
6
7
  import { TrackingModeEnum } from '../../enums/TrackingModeEnum';
7
8
  import { MdmLockModeEnum } from '../../enums/MdmLockModeEnum';
@@ -19,6 +20,11 @@ export class BackofficeCreateProductRequest {
19
20
  @IsIn(NON_BUNDLE_CATEGORIES)
20
21
  category!: ProductCategoryEnum;
21
22
 
23
+ /** Solo en ACCESSORY. */
24
+ @IsOptional()
25
+ @IsEnum(AccessorySubcategoryEnum)
26
+ accessorySubcategory?: AccessorySubcategoryEnum;
27
+
22
28
  /** Inmutable tras el alta. */
23
29
  @IsEnum(TrackingModeEnum)
24
30
  trackingMode!: TrackingModeEnum;
@@ -2,6 +2,7 @@ import { Type } from 'class-transformer';
2
2
  import { ArrayMinSize, IsArray, IsBoolean, IsEnum, IsIn, IsInt, IsISO8601, IsObject, IsOptional, IsString, Matches, Max, Min, Validate, ValidateNested } from 'class-validator';
3
3
  import { NON_BUNDLE_CATEGORIES } from '../../constants/NonBundleCategories';
4
4
  import { ProductCategoryEnum } from '../../enums/ProductCategoryEnum';
5
+ import { AccessorySubcategoryEnum } from '../../enums/AccessorySubcategoryEnum';
5
6
  import { ProductTierEnum } from '../../enums/ProductTierEnum';
6
7
  import { ProductComponent } from '../ProductComponent';
7
8
  import { MdmLockModeEnum } from '../../enums/MdmLockModeEnum';
@@ -14,6 +15,11 @@ export class BackofficeUpdateProductRequest {
14
15
  @IsIn(NON_BUNDLE_CATEGORIES)
15
16
  category?: ProductCategoryEnum;
16
17
 
18
+ /** Solo en ACCESSORY. */
19
+ @IsOptional()
20
+ @IsEnum(AccessorySubcategoryEnum)
21
+ accessorySubcategory?: AccessorySubcategoryEnum;
22
+
17
23
  @IsOptional()
18
24
  @IsBoolean()
19
25
  sellable?: boolean;
@@ -1,10 +1,11 @@
1
1
  import { Type } from 'class-transformer';
2
- import { ArrayMinSize, IsArray, IsString, ValidateNested } from 'class-validator';
2
+ import { ArrayMinSize, IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
3
3
  import { ReservationLineRef } from './ReservationLineRef';
4
4
 
5
5
  /** Componente de la línea con las piezas que quedan HELD de la sesión. */
6
6
  export class HoldStockComponent {
7
7
  @IsString()
8
+ @IsNotEmpty()
8
9
  sku!: string;
9
10
 
10
11
  @IsArray()
@@ -15,9 +16,11 @@ export class HoldStockComponent {
15
16
  /** Body de PUT /private/stores/{storeId}/holds (privateHoldStock); fija las piezas de una línea. */
16
17
  export class HoldStockRequest extends ReservationLineRef {
17
18
  @IsString()
19
+ @IsNotEmpty()
18
20
  sessionId!: string;
19
21
 
20
22
  @IsString()
23
+ @IsNotEmpty()
21
24
  retailerId!: string;
22
25
 
23
26
  @IsArray()
@@ -1,6 +1,7 @@
1
1
  import type { MdmLockModeEnum } from '../../enums/MdmLockModeEnum';
2
2
  import type { MdmProviderEnum } from '../../enums/MdmProviderEnum';
3
3
  import type { ProductCategoryEnum } from '../../enums/ProductCategoryEnum';
4
+ import type { AccessorySubcategoryEnum } from '../../enums/AccessorySubcategoryEnum';
4
5
  import type { ProductTierEnum } from '../../enums/ProductTierEnum';
5
6
  import type { TrackingModeEnum } from '../../enums/TrackingModeEnum';
6
7
  import type { ItemTypeEnum } from '../../enums/ItemTypeEnum';
@@ -18,6 +19,8 @@ export interface StoreCatalogItem {
18
19
  available: number;
19
20
  basePriceCents: number;
20
21
  category: ProductCategoryEnum;
22
+ /** Solo en ACCESSORY. */
23
+ accessorySubcategory?: AccessorySubcategoryEnum;
21
24
  trackingMode: TrackingModeEnum;
22
25
  brand: string;
23
26
  model: string;
@@ -0,0 +1,9 @@
1
+ /** Subcategoría de un producto ACCESSORY; sirve para regalar «por gama» en Promociones. */
2
+ export enum AccessorySubcategoryEnum {
3
+ AUDIO = 'AUDIO',
4
+ CHARGING = 'CHARGING',
5
+ PROTECTION = 'PROTECTION',
6
+ CABLES = 'CABLES',
7
+ WEARABLES = 'WEARABLES',
8
+ OTHER = 'OTHER',
9
+ }
@@ -45,6 +45,7 @@ export * from './dtos/validation/ValidateInventoryResponse';
45
45
 
46
46
  // Inventario multi-item (ciclo 1)
47
47
  export * from './enums/ProductCategoryEnum';
48
+ export * from './enums/AccessorySubcategoryEnum';
48
49
  export * from './enums/TrackingModeEnum';
49
50
  export * from './enums/InventoryMovementTypeEnum';
50
51
  export * from './enums/InventorySyncResultStatusEnum';
@@ -93,7 +94,7 @@ export * from './dtos/requests/BackofficeListTransfersQuery';
93
94
  export * from './dtos/responses/TransferResponse';
94
95
  export * from './dtos/responses/ListTransfersResponse';
95
96
 
96
- // Venta saga (ciclo 3)
97
+ // Saga de venta: hold y avisos de la cola
97
98
  export * from './enums/ReservePurposeEnum';
98
99
  export * from './enums/InventoryInboundMessageTypeEnum';
99
100
  export * from './dtos/requests/HoldStockRequest';
@@ -1,7 +1,7 @@
1
1
  import { RetailPromotionsErrorCodeEnum as C } from '../enums/RetailPromotionsErrorCodeEnum';
2
2
 
3
- /** Status HTTP de cada code: 404 no existe · 409 conflicto de estado · 422 regla de negocio. */
4
- export const RETAIL_PROMOTIONS_ERROR_HTTP_STATUS: Readonly<Record<C, 404 | 409 | 422>> = {
3
+ /** Status HTTP de cada code: 403 sesión sin alcance · 404 no existe · 409 conflicto de estado · 422 regla de negocio. */
4
+ export const RETAIL_PROMOTIONS_ERROR_HTTP_STATUS: Readonly<Record<C, 403 | 404 | 409 | 422>> = {
5
5
  [C.INVALID_PROMOTION_TRANSITION]: 409,
6
6
  [C.CODE_TEXT_TAKEN]: 409,
7
7
  [C.PROMOTION_NOT_ACTIVE]: 409,
@@ -18,4 +18,8 @@ export const RETAIL_PROMOTIONS_ERROR_HTTP_STATUS: Readonly<Record<C, 404 | 409 |
18
18
  [C.DISCOUNT_AFTER_QUOTE]: 409,
19
19
  [C.INVALID_SALE_PROMOTION]: 422,
20
20
  [C.INVALID_DISCOUNT_CODE]: 422,
21
+ [C.SALE_PROMOTION_NOT_FOUND]: 404,
22
+ [C.SALE_COUPON_NOT_FOUND]: 404,
23
+ [C.MISSING_RETAILER_SCOPE]: 403,
24
+ [C.MISSING_STORE_SCOPE]: 403,
21
25
  };
@@ -1,3 +1,4 @@
1
+ import { AccessorySubcategoryEnum } from '../../retailCatalog/enums/AccessorySubcategoryEnum';
1
2
  import { ProductCategoryEnum } from '../../retailCatalog/enums/ProductCategoryEnum';
2
3
  import { ProductTierEnum } from '../../retailCatalog/enums/ProductTierEnum';
3
4
  import { CouponScopeEnum } from '../enums/CouponScopeEnum';
@@ -10,11 +11,11 @@ export interface AccessoryDiscountLever {
10
11
  maxAmountCents: number | null;
11
12
  }
12
13
 
13
- /** Regalo por lista (`skus`) o por gama (`category` + `maxPriceCents`); el cliente elige `chooseCount`. */
14
+ /** Regalo por lista (`skus`) o por gama (`accessorySubcategory` + `maxPriceCents`); el cliente elige `chooseCount`. */
14
15
  export interface GiftLever {
15
16
  mode: GiftModeEnum;
16
17
  skus: string[] | null;
17
- category: string | null;
18
+ accessorySubcategory: AccessorySubcategoryEnum | null;
18
19
  maxPriceCents: number | null;
19
20
  chooseCount: number;
20
21
  }
@@ -0,0 +1,11 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
3
+
4
+ /** Query de publicGetSaleCoupon (`GET /public/sales/{saleId}/coupon`). */
5
+ export class GetSaleCouponQuery {
6
+ @Expose()
7
+ @IsOptional()
8
+ @IsString()
9
+ @IsNotEmpty()
10
+ sessionId?: string;
11
+ }
@@ -4,12 +4,12 @@ import {
4
4
  IsArray,
5
5
  IsEnum,
6
6
  IsInt,
7
- IsNotEmpty,
8
7
  IsOptional,
9
8
  IsString,
10
9
  ValidateIf,
11
10
  ValidateNested,
12
11
  } from 'class-validator';
12
+ import { AccessorySubcategoryEnum } from '../../../retailCatalog/enums/AccessorySubcategoryEnum';
13
13
  import { CouponScopeEnum } from '../../enums/CouponScopeEnum';
14
14
  import { GiftModeEnum } from '../../enums/GiftModeEnum';
15
15
  import { DiscountValueInput } from './DiscountValueInput';
@@ -25,7 +25,7 @@ export class AccessoryDiscountLeverInput {
25
25
  maxAmountCents?: number | null;
26
26
  }
27
27
 
28
- /** Regalo por lista exige `skus`; por gama exige `category` y `maxPriceCents`. */
28
+ /** Regalo por lista exige `skus`; por gama exige `accessorySubcategory` y `maxPriceCents`. */
29
29
  export class GiftLeverInput {
30
30
  @Expose()
31
31
  @IsEnum(GiftModeEnum)
@@ -40,9 +40,8 @@ export class GiftLeverInput {
40
40
 
41
41
  @Expose()
42
42
  @ValidateIf((o: GiftLeverInput) => o.mode === GiftModeEnum.RANGE)
43
- @IsString()
44
- @IsNotEmpty()
45
- category?: string | null;
43
+ @IsEnum(AccessorySubcategoryEnum)
44
+ accessorySubcategory?: AccessorySubcategoryEnum | null;
46
45
 
47
46
  @Expose()
48
47
  @ValidateIf((o: GiftLeverInput) => o.mode === GiftModeEnum.RANGE)
@@ -51,6 +51,8 @@ export interface EvaluatedSalePromotion {
51
51
  giftOptions: GiftOption[];
52
52
  giftChooseCount: number | null;
53
53
  afterSale: AfterSaleBenefit[];
54
+ /** Si viene, la promo se muestra pero no se puede elegir; dice por qué (ej. CUSTOMER_NOT_IDENTIFIED). */
55
+ blockedBy?: RetailPromotionsErrorCodeEnum | null;
54
56
  }
55
57
 
56
58
  /** Resultado del código tecleado: si no aplica, `rejectionCode` dice por qué. */
@@ -20,4 +20,10 @@ export enum RetailPromotionsErrorCodeEnum {
20
20
  DISCOUNT_AFTER_QUOTE = 'DISCOUNT_AFTER_QUOTE',
21
21
  INVALID_SALE_PROMOTION = 'INVALID_SALE_PROMOTION',
22
22
  INVALID_DISCOUNT_CODE = 'INVALID_DISCOUNT_CODE',
23
+ SALE_PROMOTION_NOT_FOUND = 'SALE_PROMOTION_NOT_FOUND',
24
+ SALE_COUPON_NOT_FOUND = 'SALE_COUPON_NOT_FOUND',
25
+ /** La sesión no trae retailer: no se puede acotar la consulta. */
26
+ MISSING_RETAILER_SCOPE = 'MISSING_RETAILER_SCOPE',
27
+ /** La sesión no trae tienda: no se puede acotar la consulta. */
28
+ MISSING_STORE_SCOPE = 'MISSING_STORE_SCOPE',
23
29
  }
@@ -48,6 +48,7 @@ export * from './dtos/requests/ChangeDiscountCodeStatusRequest';
48
48
  export * from './dtos/requests/ListStoreSalePromotionsQuery';
49
49
  export * from './dtos/requests/EvaluateSalePromotionsRequest';
50
50
  export * from './dtos/requests/HoldSalePromotionRequest';
51
+ export * from './dtos/requests/GetSaleCouponQuery';
51
52
 
52
53
  // Response DTOs
53
54
  export * from './dtos/responses/SalePromotionCrudResponses';
@@ -1,9 +1,11 @@
1
1
  import type { WizardLineRoleEnum } from '../enums/WizardLineRoleEnum';
2
2
  import type { WizardReservedLine } from './WizardReservedLine';
3
3
 
4
- /** Línea GIFT: siempre ligada a un ancla y con el precio de lista que se regala. */
4
+ /** Línea GIFT: ligada a un ancla, con su promo y el precio de lista que se regala. */
5
5
  export type WizardGiftLine = WizardReservedLine & {
6
6
  role: WizardLineRoleEnum.GIFT;
7
7
  linkedLineId: string;
8
+ promotionId: string;
9
+ promotionVersion: number;
8
10
  originalPriceCents: number;
9
11
  };
@@ -5,9 +5,8 @@ const hasPromotion = (o: ApplyLinePromotionRequest): boolean =>
5
5
  o.promotionId !== undefined || o.promotionVersion !== undefined;
6
6
 
7
7
  /**
8
- * PUT /private/wizard/sessions/{id}/lines/{lineId}/promotion (privateApplyLinePromotion, M2M de Promociones).
9
- * Reemplaza la promo de la línea por valor: un campo ausente queda borrado. `tenantId` va en el query.
10
- * Nunca toca role ni linkedLineId: los GIFT los gestiona el PUT …/gifts. Responde 204 sin body.
8
+ * PUT …/lines/{lineId}/promotion (privateApplyLinePromotion): reemplaza la promo de la línea por valor.
9
+ * Un campo ausente queda borrado; no toca role ni linkedLineId. `tenantId` va en el query.
11
10
  */
12
11
  export class ApplyLinePromotionRequest {
13
12
  @Expose()
@@ -19,7 +19,7 @@ export class QuoteRequest {
19
19
  @Min(1)
20
20
  termWeeks!: number;
21
21
 
22
- /** Se ignora: el servidor financia la ancla. Se acepta un release más por el front viejo. */
22
+ /** Se ignora: el servidor financia la ancla. */
23
23
  @Expose()
24
24
  @IsOptional()
25
25
  @IsString()
@@ -27,4 +27,6 @@ export interface PrivateWizardSessionResponse {
27
27
  paymentMethod?: PaymentMethodEnum | null;
28
28
  /** Cobro de contado; `null` mientras no se cobra. */
29
29
  payment?: WizardPayment | null;
30
+ /** Valor efectivo del flag SPLIT_FINANCING_ENABLED del wizard. */
31
+ splitFinancingEnabled?: boolean;
30
32
  }
@@ -114,21 +114,23 @@ export class RetailPromotionInboundMessage {
114
114
 
115
115
  @IsOptional()
116
116
  @IsString()
117
+ @IsNotEmpty()
117
118
  creditId?: string;
118
119
 
119
120
  @IsOptional()
120
121
  @IsString()
122
+ @IsNotEmpty()
121
123
  fiadoDirectoryId?: string;
122
124
 
123
125
  @IsOptional()
124
126
  @IsString()
125
127
  @IsNotEmpty()
126
- salePromotionId?: string;
128
+ salePromotionId?: string | null;
127
129
 
128
130
  @IsOptional()
129
131
  @IsInt()
130
132
  @Min(1)
131
- salePromotionVersion?: number;
133
+ salePromotionVersion?: number | null;
132
134
 
133
135
  @IsArray()
134
136
  @ArrayMinSize(1)