@fiado/type-kit 3.464.0 → 3.466.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 (118) hide show
  1. package/_test_/unit/retailCatalog/backofficeRequests.test.ts +283 -0
  2. package/_test_/unit/retailCatalog/catalogEnums.test.ts +28 -1
  3. package/_test_/unit/retailCatalog/responseFields.test.ts +53 -0
  4. package/_test_/unit/retailCatalog/returnConditions.test.ts +64 -0
  5. package/_test_/unit/retailWizard/cancelSaleConditions.test.ts +41 -0
  6. package/bin/remittance/enums/RemittanceCorridor.d.ts +6 -5
  7. package/bin/remittance/enums/RemittanceCorridor.js +6 -4
  8. package/bin/retailCatalog/constants/AdjustmentReasons.d.ts +3 -0
  9. package/bin/retailCatalog/constants/AdjustmentReasons.js +10 -0
  10. package/bin/retailCatalog/constants/NonBundleCategories.d.ts +3 -0
  11. package/bin/retailCatalog/constants/NonBundleCategories.js +6 -0
  12. package/bin/retailCatalog/dtos/Product.d.ts +3 -0
  13. package/bin/retailCatalog/dtos/requests/BackofficeCreateKitRequest.d.ts +14 -0
  14. package/bin/retailCatalog/dtos/requests/BackofficeCreateKitRequest.js +66 -0
  15. package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.d.ts +30 -0
  16. package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.js +117 -0
  17. package/bin/retailCatalog/dtos/requests/BackofficeCreateServiceRequest.d.ts +9 -0
  18. package/bin/retailCatalog/dtos/requests/BackofficeCreateServiceRequest.js +43 -0
  19. package/bin/retailCatalog/dtos/requests/BackofficeCreateSkuPriceRequest.d.ts +7 -0
  20. package/bin/retailCatalog/dtos/requests/BackofficeCreateSkuPriceRequest.js +34 -0
  21. package/bin/retailCatalog/dtos/requests/BackofficeCreateTransferRequest.d.ts +9 -0
  22. package/bin/retailCatalog/dtos/requests/BackofficeCreateTransferRequest.js +48 -0
  23. package/bin/retailCatalog/dtos/requests/BackofficeListTransfersQuery.d.ts +5 -0
  24. package/bin/retailCatalog/dtos/requests/BackofficeListTransfersQuery.js +23 -0
  25. package/bin/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.d.ts +12 -0
  26. package/bin/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.js +49 -0
  27. package/bin/retailCatalog/dtos/requests/BackofficeRegisterEntryRequest.d.ts +9 -0
  28. package/bin/retailCatalog/dtos/requests/BackofficeRegisterEntryRequest.js +43 -0
  29. package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.d.ts +27 -0
  30. package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.js +118 -0
  31. package/bin/retailCatalog/dtos/requests/ChangeInventoryStatusRequest.d.ts +1 -0
  32. package/bin/retailCatalog/dtos/requests/ChangeInventoryStatusRequest.js +1 -0
  33. package/bin/retailCatalog/dtos/requests/CreateProductRequest.d.ts +4 -1
  34. package/bin/retailCatalog/dtos/requests/CreateProductRequest.js +4 -1
  35. package/bin/retailCatalog/dtos/requests/RegisterInventoryRequest.d.ts +4 -1
  36. package/bin/retailCatalog/dtos/requests/RegisterInventoryRequest.js +4 -1
  37. package/bin/retailCatalog/dtos/requests/ReturnPieceCondition.d.ts +11 -0
  38. package/bin/retailCatalog/dtos/requests/ReturnPieceCondition.js +57 -0
  39. package/bin/retailCatalog/dtos/requests/ReturnStockLine.d.ts +6 -0
  40. package/bin/retailCatalog/dtos/requests/ReturnStockLine.js +27 -0
  41. package/bin/retailCatalog/dtos/requests/ReturnStockRequest.d.ts +2 -2
  42. package/bin/retailCatalog/dtos/requests/ReturnStockRequest.js +2 -2
  43. package/bin/retailCatalog/dtos/requests/TransferInventoryRequest.d.ts +4 -1
  44. package/bin/retailCatalog/dtos/requests/TransferInventoryRequest.js +4 -1
  45. package/bin/retailCatalog/dtos/requests/UpdateProductRequest.d.ts +4 -1
  46. package/bin/retailCatalog/dtos/requests/UpdateProductRequest.js +4 -1
  47. package/bin/retailCatalog/dtos/requests/UpdateProductStatusRequest.d.ts +4 -1
  48. package/bin/retailCatalog/dtos/requests/UpdateProductStatusRequest.js +4 -1
  49. package/bin/retailCatalog/dtos/responses/InventoryLensResponse.d.ts +3 -0
  50. package/bin/retailCatalog/dtos/responses/ListTransfersResponse.d.ts +5 -0
  51. package/bin/retailCatalog/dtos/responses/ListTransfersResponse.js +2 -0
  52. package/bin/retailCatalog/dtos/responses/ReserveStockResponse.d.ts +2 -0
  53. package/bin/retailCatalog/dtos/responses/SkuPriceEntry.d.ts +2 -0
  54. package/bin/retailCatalog/dtos/responses/StoreCatalogItem.d.ts +3 -0
  55. package/bin/retailCatalog/dtos/responses/TransferResponse.d.ts +16 -0
  56. package/bin/retailCatalog/dtos/responses/TransferResponse.js +2 -0
  57. package/bin/retailCatalog/enums/InventoryItemStatusEnum.d.ts +3 -1
  58. package/bin/retailCatalog/enums/InventoryItemStatusEnum.js +2 -0
  59. package/bin/retailCatalog/enums/InventoryMovementReasonEnum.d.ts +7 -0
  60. package/bin/retailCatalog/enums/InventoryMovementReasonEnum.js +11 -0
  61. package/bin/retailCatalog/enums/InventoryMovementTypeEnum.d.ts +4 -1
  62. package/bin/retailCatalog/enums/InventoryMovementTypeEnum.js +3 -0
  63. package/bin/retailCatalog/enums/ItemTypeEnum.d.ts +5 -0
  64. package/bin/retailCatalog/enums/ItemTypeEnum.js +9 -0
  65. package/bin/retailCatalog/enums/PriceSourceEnum.d.ts +3 -2
  66. package/bin/retailCatalog/enums/PriceSourceEnum.js +2 -1
  67. package/bin/retailCatalog/enums/ReturnConditionEnum.d.ts +5 -0
  68. package/bin/retailCatalog/enums/ReturnConditionEnum.js +9 -0
  69. package/bin/retailCatalog/enums/TransferStateEnum.d.ts +6 -0
  70. package/bin/retailCatalog/enums/TransferStateEnum.js +10 -0
  71. package/bin/retailCatalog/index.d.ts +20 -0
  72. package/bin/retailCatalog/index.js +21 -0
  73. package/bin/retailCatalog/validators/IsPieceRefConstraint.d.ts +6 -0
  74. package/bin/retailCatalog/validators/IsPieceRefConstraint.js +26 -0
  75. package/bin/retailWizard/dtos/requests/BackofficeCancelSaleRequest.d.ts +3 -0
  76. package/bin/retailWizard/dtos/requests/BackofficeCancelSaleRequest.js +9 -0
  77. package/bin/retailWizard/dtos/requests/CancelSaleRequest.d.ts +3 -0
  78. package/bin/retailWizard/dtos/requests/CancelSaleRequest.js +9 -0
  79. package/package.json +1 -1
  80. package/src/remittance/enums/RemittanceCorridor.ts +6 -4
  81. package/src/retailCatalog/constants/AdjustmentReasons.ts +8 -0
  82. package/src/retailCatalog/constants/NonBundleCategories.ts +4 -0
  83. package/src/retailCatalog/dtos/Product.ts +3 -0
  84. package/src/retailCatalog/dtos/requests/BackofficeCreateKitRequest.ts +47 -0
  85. package/src/retailCatalog/dtos/requests/BackofficeCreateProductRequest.ts +92 -0
  86. package/src/retailCatalog/dtos/requests/BackofficeCreateServiceRequest.ts +26 -0
  87. package/src/retailCatalog/dtos/requests/BackofficeCreateSkuPriceRequest.ts +19 -0
  88. package/src/retailCatalog/dtos/requests/BackofficeCreateTransferRequest.ts +31 -0
  89. package/src/retailCatalog/dtos/requests/BackofficeListTransfersQuery.ts +9 -0
  90. package/src/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.ts +34 -0
  91. package/src/retailCatalog/dtos/requests/BackofficeRegisterEntryRequest.ts +28 -0
  92. package/src/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.ts +93 -0
  93. package/src/retailCatalog/dtos/requests/ChangeInventoryStatusRequest.ts +1 -0
  94. package/src/retailCatalog/dtos/requests/CreateProductRequest.ts +4 -1
  95. package/src/retailCatalog/dtos/requests/RegisterInventoryRequest.ts +4 -1
  96. package/src/retailCatalog/dtos/requests/ReturnPieceCondition.ts +48 -0
  97. package/src/retailCatalog/dtos/requests/ReturnStockLine.ts +13 -0
  98. package/src/retailCatalog/dtos/requests/ReturnStockRequest.ts +3 -3
  99. package/src/retailCatalog/dtos/requests/TransferInventoryRequest.ts +4 -1
  100. package/src/retailCatalog/dtos/requests/UpdateProductRequest.ts +4 -1
  101. package/src/retailCatalog/dtos/requests/UpdateProductStatusRequest.ts +4 -1
  102. package/src/retailCatalog/dtos/responses/InventoryLensResponse.ts +3 -0
  103. package/src/retailCatalog/dtos/responses/ListTransfersResponse.ts +6 -0
  104. package/src/retailCatalog/dtos/responses/ReserveStockResponse.ts +2 -0
  105. package/src/retailCatalog/dtos/responses/SkuPriceEntry.ts +2 -0
  106. package/src/retailCatalog/dtos/responses/StoreCatalogItem.ts +3 -0
  107. package/src/retailCatalog/dtos/responses/TransferResponse.ts +17 -0
  108. package/src/retailCatalog/enums/InventoryItemStatusEnum.ts +2 -0
  109. package/src/retailCatalog/enums/InventoryMovementReasonEnum.ts +7 -0
  110. package/src/retailCatalog/enums/InventoryMovementTypeEnum.ts +3 -0
  111. package/src/retailCatalog/enums/ItemTypeEnum.ts +5 -0
  112. package/src/retailCatalog/enums/PriceSourceEnum.ts +2 -1
  113. package/src/retailCatalog/enums/ReturnConditionEnum.ts +5 -0
  114. package/src/retailCatalog/enums/TransferStateEnum.ts +6 -0
  115. package/src/retailCatalog/index.ts +22 -0
  116. package/src/retailCatalog/validators/IsPieceRefConstraint.ts +19 -0
  117. package/src/retailWizard/dtos/requests/BackofficeCancelSaleRequest.ts +11 -2
  118. package/src/retailWizard/dtos/requests/CancelSaleRequest.ts +11 -2
@@ -0,0 +1,283 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import {
5
+ BackofficeCreateKitRequest,
6
+ BackofficeCreateProductRequest,
7
+ BackofficeCreateServiceRequest,
8
+ BackofficeCreateSkuPriceRequest,
9
+ BackofficeCreateTransferRequest,
10
+ BackofficeListTransfersQuery,
11
+ BackofficeRegisterAdjustmentRequest,
12
+ BackofficeRegisterEntryRequest,
13
+ BackofficeUpdateProductRequest,
14
+ ADJUSTMENT_REASONS,
15
+ InventoryMovementReasonEnum,
16
+ IsPieceRefConstraint,
17
+ NON_BUNDLE_CATEGORIES,
18
+ ProductCategoryEnum,
19
+ TrackingModeEnum,
20
+ TransferStateEnum,
21
+ type ListTransfersResponse,
22
+ type TransferResponse,
23
+ } from '../../../src/retailCatalog';
24
+
25
+ function paths(errors: ValidationError[], prefix = ''): string[] {
26
+ return errors.flatMap((e) => {
27
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
28
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
29
+ });
30
+ }
31
+
32
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
33
+ return paths(validateSync(plainToInstance(cls, payload) as object, { whitelist: true, forbidNonWhitelisted: true }));
34
+ }
35
+
36
+ function without<T extends object>(obj: T, key: keyof T): object {
37
+ return Object.fromEntries(Object.entries(obj).filter(([k]) => k !== key));
38
+ }
39
+
40
+ describe('BackofficeCreateProductRequest (backofficeCreateProduct)', () => {
41
+ const ok = {
42
+ sku: 'PH-A15',
43
+ category: ProductCategoryEnum.PHONE,
44
+ trackingMode: TrackingModeEnum.IMEI,
45
+ sellable: true,
46
+ showInSalesFlow: true,
47
+ };
48
+ it('acepta el mínimo', () => expect(errorsOf(BackofficeCreateProductRequest, ok)).toEqual([]));
49
+ it('acepta los datos descriptivos opcionales', () => {
50
+ const full = { ...ok, brand: 'Samsung', model: 'A15', capacity: '128GB', color: 'Negro', description: 'Gama media', imageUrl: 'https://x/y.png', tier: 'MID' };
51
+ expect(errorsOf(BackofficeCreateProductRequest, full)).toEqual([]);
52
+ });
53
+ it.each(['sku', 'category', 'trackingMode', 'sellable', 'showInSalesFlow'] as const)('rechaza sin %s', (key) => {
54
+ expect(errorsOf(BackofficeCreateProductRequest, without(ok, key))).toContain(key);
55
+ });
56
+ it('rechaza BUNDLE: el kit tiene su propio endpoint', () => {
57
+ expect(errorsOf(BackofficeCreateProductRequest, { ...ok, category: ProductCategoryEnum.BUNDLE })).toContain('category');
58
+ });
59
+ it('rechaza enums inválidos y booleanos como string', () => {
60
+ const errors = errorsOf(BackofficeCreateProductRequest, { ...ok, category: 'TABLET', trackingMode: 'RFID', sellable: 'true', tier: 'ULTRA' });
61
+ expect(errors).toEqual(expect.arrayContaining(['category', 'trackingMode', 'sellable', 'tier']));
62
+ });
63
+ it('rechaza sku con espacios y descripción en blanco', () => {
64
+ const errors = errorsOf(BackofficeCreateProductRequest, { ...ok, sku: ' ', description: ' ' });
65
+ expect(errors).toEqual(expect.arrayContaining(['sku', 'description']));
66
+ });
67
+ });
68
+
69
+ describe('BackofficeCreateKitRequest (backofficeCreateKit)', () => {
70
+ const ok = { sku: 'KIT-A15', sellable: true, showInSalesFlow: true, components: [{ sku: 'PH-A15', qty: 1 }, { sku: 'SIM-1', qty: 1 }] };
71
+ it('acepta un kit con componentes', () => expect(errorsOf(BackofficeCreateKitRequest, ok)).toEqual([]));
72
+ it.each(['sku', 'sellable', 'showInSalesFlow', 'components'] as const)('rechaza sin %s', (key) => {
73
+ expect(errorsOf(BackofficeCreateKitRequest, without(ok, key))).toContain(key);
74
+ });
75
+ it('rechaza un kit sin componentes', () => expect(errorsOf(BackofficeCreateKitRequest, { ...ok, components: [] })).toContain('components'));
76
+ it('valida cada componente', () => {
77
+ const errors = errorsOf(BackofficeCreateKitRequest, { ...ok, components: [{ sku: 'SIM 1', qty: 0 }] });
78
+ expect(errors).toEqual(expect.arrayContaining(['components.0.sku', 'components.0.qty']));
79
+ });
80
+ it('rechaza descripción en blanco', () => expect(errorsOf(BackofficeCreateKitRequest, { ...ok, description: ' ' })).toContain('description'));
81
+ });
82
+
83
+ describe('BackofficeCreateServiceRequest (backofficeCreateService)', () => {
84
+ const ok = { sku: 'TA-100', description: 'Tiempo aire 100', sellable: true, showInSalesFlow: true };
85
+ it('acepta un servicio', () => expect(errorsOf(BackofficeCreateServiceRequest, ok)).toEqual([]));
86
+ it.each(['sku', 'description', 'sellable', 'showInSalesFlow'] as const)('rechaza sin %s', (key) => {
87
+ expect(errorsOf(BackofficeCreateServiceRequest, without(ok, key))).toContain(key);
88
+ });
89
+ it('rechaza descripción en blanco y sellable no booleano', () => {
90
+ const errors = errorsOf(BackofficeCreateServiceRequest, { ...ok, description: ' ', sellable: 1 });
91
+ expect(errors).toEqual(expect.arrayContaining(['description', 'sellable']));
92
+ });
93
+ });
94
+
95
+ describe('BackofficeUpdateProductRequest (backofficeUpdateProduct)', () => {
96
+ it('acepta un cuerpo vacío (parcial)', () => expect(errorsOf(BackofficeUpdateProductRequest, {})).toEqual([]));
97
+ it('acepta flags, categoría, datos y componentes', () => {
98
+ const body = { sellable: false, showInSalesFlow: true, category: 'ACCESSORY', description: 'Funda', components: [{ sku: 'SIM-1', qty: 2 }] };
99
+ expect(errorsOf(BackofficeUpdateProductRequest, body)).toEqual([]);
100
+ });
101
+ it('rechaza pasar a BUNDLE y componentes inválidos', () => {
102
+ const errors = errorsOf(BackofficeUpdateProductRequest, { category: 'BUNDLE', components: [{ sku: 'SIM-1', qty: 0 }] });
103
+ expect(errors).toEqual(expect.arrayContaining(['category', 'components.0.qty']));
104
+ });
105
+ it('rechaza tipos incorrectos y texto en blanco', () => {
106
+ const errors = errorsOf(BackofficeUpdateProductRequest, { sellable: 'no', brand: ' ', tier: 'ULTRA' });
107
+ expect(errors).toEqual(expect.arrayContaining(['sellable', 'brand', 'tier']));
108
+ });
109
+ it('no expone itemType ni trackingMode (inmutables)', () => {
110
+ const dto = new BackofficeUpdateProductRequest();
111
+ expect('itemType' in dto || 'trackingMode' in dto).toBe(false);
112
+ });
113
+ });
114
+
115
+ describe('BackofficeCreateSkuPriceRequest (backofficeCreateSkuPrice)', () => {
116
+ const ok = { basePriceCents: 90000 };
117
+ it('acepta solo el precio base', () => expect(errorsOf(BackofficeCreateSkuPriceRequest, ok)).toEqual([]));
118
+ it('acepta lista y combo', () => {
119
+ expect(errorsOf(BackofficeCreateSkuPriceRequest, { ...ok, listPriceCents: 100000, comboPriceCents: 85000 })).toEqual([]);
120
+ expect(errorsOf(BackofficeCreateSkuPriceRequest, { ...ok, listPriceCents: null })).toEqual([]);
121
+ });
122
+ it('rechaza sin precio base', () => expect(errorsOf(BackofficeCreateSkuPriceRequest, {})).toContain('basePriceCents'));
123
+ it('rechaza negativos, decimales y strings', () => {
124
+ const errors = errorsOf(BackofficeCreateSkuPriceRequest, { basePriceCents: -1, listPriceCents: 10.5, comboPriceCents: '100' });
125
+ expect(errors).toEqual(expect.arrayContaining(['basePriceCents', 'listPriceCents', 'comboPriceCents']));
126
+ });
127
+ });
128
+
129
+ describe('BackofficeRegisterEntryRequest (backofficeRegisterEntry)', () => {
130
+ const ok = { storeId: 'st-1', sku: 'SIM-1', qty: 5 };
131
+ it('acepta una entrada por cantidad', () => expect(errorsOf(BackofficeRegisterEntryRequest, ok)).toEqual([]));
132
+ it('acepta una entrada con identificadores', () => {
133
+ expect(errorsOf(BackofficeRegisterEntryRequest, { ...ok, qty: 2, identifiers: ['356938035643809', '356938035643810'] })).toEqual([]);
134
+ });
135
+ it.each(['storeId', 'sku', 'qty'] as const)('rechaza sin %s', (key) => {
136
+ expect(errorsOf(BackofficeRegisterEntryRequest, without(ok, key))).toContain(key);
137
+ });
138
+ it('rechaza qty 0, storeId en blanco e identificadores vacíos, en blanco o repetidos', () => {
139
+ expect(errorsOf(BackofficeRegisterEntryRequest, { ...ok, qty: 0, storeId: ' ' })).toEqual(expect.arrayContaining(['qty', 'storeId']));
140
+ expect(errorsOf(BackofficeRegisterEntryRequest, { ...ok, identifiers: [] })).toContain('identifiers');
141
+ expect(errorsOf(BackofficeRegisterEntryRequest, { ...ok, identifiers: [' '] })).toContain('identifiers');
142
+ expect(errorsOf(BackofficeRegisterEntryRequest, { ...ok, identifiers: ['A1', 'A1'] })).toContain('identifiers');
143
+ });
144
+ });
145
+
146
+ describe('BackofficeRegisterAdjustmentRequest (backofficeRegisterAdjustment)', () => {
147
+ const ok = { storeId: 'st-1', sku: 'SIM-1', qty: 1, reason: InventoryMovementReasonEnum.SHRINKAGE };
148
+ it('acepta una merma', () => expect(errorsOf(BackofficeRegisterAdjustmentRequest, ok)).toEqual([]));
149
+ it('acepta ajuste y garantía con identificador', () => {
150
+ expect(errorsOf(BackofficeRegisterAdjustmentRequest, { ...ok, reason: 'ADJUSTMENT' })).toEqual([]);
151
+ expect(errorsOf(BackofficeRegisterAdjustmentRequest, { ...ok, reason: 'WARRANTY', identifiers: ['356938035643809'] })).toEqual([]);
152
+ });
153
+ it.each(['storeId', 'sku', 'qty', 'reason'] as const)('rechaza sin %s', (key) => {
154
+ expect(errorsOf(BackofficeRegisterAdjustmentRequest, without(ok, key))).toContain(key);
155
+ });
156
+ it('rechaza DAMAGED_RETURN (es de devoluciones) y motivos desconocidos', () => {
157
+ expect(errorsOf(BackofficeRegisterAdjustmentRequest, { ...ok, reason: 'DAMAGED_RETURN' })).toContain('reason');
158
+ expect(errorsOf(BackofficeRegisterAdjustmentRequest, { ...ok, reason: 'LOST' })).toContain('reason');
159
+ });
160
+ it('rechaza qty negativa y sku en blanco', () => {
161
+ expect(errorsOf(BackofficeRegisterAdjustmentRequest, { ...ok, qty: -2, sku: ' ' })).toEqual(expect.arrayContaining(['qty', 'sku']));
162
+ });
163
+ });
164
+
165
+ describe('BackofficeCreateTransferRequest (backofficeCreateTransfer)', () => {
166
+ const ok = { fromStoreId: 'st-1', toStoreId: 'st-2', sku: 'SIM-1', qty: 3 };
167
+ it('acepta un traspaso por cantidad', () => expect(errorsOf(BackofficeCreateTransferRequest, ok)).toEqual([]));
168
+ it('acepta un traspaso con identificadores', () => {
169
+ expect(errorsOf(BackofficeCreateTransferRequest, { ...ok, qty: 1, identifiers: ['356938035643809'] })).toEqual([]);
170
+ });
171
+ it('deja pasar misma tienda: la rechaza el dominio con SAME_STORE_TRANSFER', () => {
172
+ expect(errorsOf(BackofficeCreateTransferRequest, { ...ok, toStoreId: 'st-1' })).toEqual([]);
173
+ });
174
+ it.each(['fromStoreId', 'toStoreId', 'sku', 'qty'] as const)('rechaza sin %s', (key) => {
175
+ expect(errorsOf(BackofficeCreateTransferRequest, without(ok, key))).toContain(key);
176
+ });
177
+ it('rechaza tiendas en blanco, qty 0 e identificadores no string', () => {
178
+ const errors = errorsOf(BackofficeCreateTransferRequest, { ...ok, fromStoreId: ' ', qty: 0, identifiers: [123] });
179
+ expect(errors).toEqual(expect.arrayContaining(['fromStoreId', 'qty', 'identifiers']));
180
+ });
181
+ });
182
+
183
+ describe('BackofficeListTransfersQuery (backofficeListTransfers)', () => {
184
+ it('acepta state=OPEN y sin state', () => {
185
+ expect(errorsOf(BackofficeListTransfersQuery, { state: 'OPEN' })).toEqual([]);
186
+ expect(errorsOf(BackofficeListTransfersQuery, {})).toEqual([]);
187
+ });
188
+ it('rechaza un estado desconocido', () => expect(errorsOf(BackofficeListTransfersQuery, { state: 'PENDING' })).toContain('state'));
189
+ });
190
+
191
+ describe('TransferResponse y ListTransfersResponse', () => {
192
+ it('describen un traspaso abierto', () => {
193
+ const transfer: TransferResponse = {
194
+ transferId: 'trf-1',
195
+ fromStoreId: 'st-1',
196
+ toStoreId: 'st-2',
197
+ sku: 'PH-A15',
198
+ qty: 1,
199
+ identifiers: ['356938035643809'],
200
+ state: TransferStateEnum.OPEN,
201
+ createdBy: 'user-1',
202
+ updatedBy: 'user-1',
203
+ createdAt: '2026-09-24T10:00:00.000Z',
204
+ updatedAt: '2026-09-24T10:00:00.000Z',
205
+ };
206
+ const list: ListTransfersResponse = { items: [transfer] };
207
+ expect(list.items[0].state).toBe('OPEN');
208
+ });
209
+ });
210
+
211
+ describe('financiamiento, MDM y externalId en las altas y edición backoffice', () => {
212
+ const product = { sku: 'PH-A15', category: ProductCategoryEnum.PHONE, trackingMode: TrackingModeEnum.IMEI, sellable: true, showInSalesFlow: true };
213
+ const kit = { sku: 'KIT-A15', sellable: true, showInSalesFlow: true, components: [{ sku: 'PH-A15', qty: 1 }] };
214
+ const financed = { eligibleFinanciers: ['SUREKEEP'], mdmEnabled: true, mdmProvider: 'TRUSTONIC', mdmLockMode: 'ONDEMAND', externalId: 'ZC-1' };
215
+ const cases: [string, ClassConstructor<object>, object][] = [
216
+ ['BackofficeCreateProductRequest', BackofficeCreateProductRequest, product],
217
+ ['BackofficeUpdateProductRequest', BackofficeUpdateProductRequest, {}],
218
+ ];
219
+
220
+ describe.each(cases)('%s', (_name, cls, ok) => {
221
+ it('acepta sin los campos (opcionales)', () => expect(errorsOf(cls, ok)).toEqual([]));
222
+ it('acepta financieras, MDM coherente y externalId', () => {
223
+ expect(errorsOf(cls, { ...ok, ...financed })).toEqual([]);
224
+ expect(errorsOf(cls, { ...ok, eligibleFinanciers: [], mdmEnabled: false })).toEqual([]);
225
+ });
226
+ it('rechaza tipos incorrectos', () => {
227
+ const errors = errorsOf(cls, { ...ok, eligibleFinanciers: 'SUREKEEP', mdmEnabled: 'true', externalId: 5 });
228
+ expect(errors).toEqual(expect.arrayContaining(['eligibleFinanciers', 'mdmEnabled', 'externalId']));
229
+ expect(errorsOf(cls, { ...ok, eligibleFinanciers: [' '], externalId: ' ' })).toEqual(expect.arrayContaining(['eligibleFinanciers', 'externalId']));
230
+ });
231
+ it('rechaza enums MDM inválidos', () => {
232
+ const errors = errorsOf(cls, { ...ok, ...financed, mdmProvider: 'KNOX', mdmLockMode: 'NEVER' });
233
+ expect(errors).toEqual(expect.arrayContaining(['mdmProvider', 'mdmLockMode']));
234
+ });
235
+ it('rechaza MDM incoherente', () => {
236
+ expect(errorsOf(cls, { ...ok, mdmEnabled: true })).toContain('mdmEnabled');
237
+ expect(errorsOf(cls, { ...ok, mdmEnabled: false, mdmProvider: 'TRUSTONIC', mdmLockMode: 'ONDEMAND' })).toContain('mdmEnabled');
238
+ });
239
+ });
240
+
241
+ describe('BackofficeCreateProductRequest con MDM a medias', () => {
242
+ it('rechaza mdmProvider o mdmLockMode sin mdmEnabled', () => {
243
+ expect(errorsOf(BackofficeCreateProductRequest, { ...product, mdmProvider: 'TRUSTONIC', mdmLockMode: 'ONDEMAND' })).toContain('mdmEnabled');
244
+ expect(errorsOf(BackofficeCreateProductRequest, { ...product, mdmProvider: 'TRUSTONIC' })).toContain('mdmEnabled');
245
+ expect(errorsOf(BackofficeCreateProductRequest, { ...product, mdmLockMode: 'ONDEMAND' })).toContain('mdmEnabled');
246
+ });
247
+ it('rechaza mdmEnabled false con proveedor', () => {
248
+ expect(errorsOf(BackofficeCreateProductRequest, { ...product, mdmEnabled: false, mdmProvider: 'TRUSTONIC' })).toContain('mdmEnabled');
249
+ });
250
+ it('acepta la config coherente y la ausencia total de MDM', () => {
251
+ expect(errorsOf(BackofficeCreateProductRequest, { ...product, mdmEnabled: true, mdmProvider: 'TRUSTONIC', mdmLockMode: 'ONDEMAND' })).toEqual([]);
252
+ expect(errorsOf(BackofficeCreateProductRequest, { ...product, mdmEnabled: false })).toEqual([]);
253
+ expect(errorsOf(BackofficeCreateProductRequest, product)).toEqual([]);
254
+ });
255
+ });
256
+
257
+ it('el kit lleva financieras y externalId, pero no MDM (sale del teléfono componente)', () => {
258
+ expect(errorsOf(BackofficeCreateKitRequest, { ...kit, eligibleFinanciers: ['SUREKEEP'], externalId: 'ZC-2' })).toEqual([]);
259
+ expect(errorsOf(BackofficeCreateKitRequest, { ...kit, eligibleFinanciers: 'SUREKEEP', externalId: ' ' })).toEqual(expect.arrayContaining(['eligibleFinanciers', 'externalId']));
260
+ expect(errorsOf(BackofficeCreateKitRequest, { ...kit, mdmEnabled: true })).toContain('mdmEnabled');
261
+ const dto = new BackofficeCreateKitRequest();
262
+ expect(['mdmEnabled', 'mdmProvider', 'mdmLockMode'].some((k) => k in dto)).toBe(false);
263
+ });
264
+
265
+ it('el servicio solo lleva externalId', () => {
266
+ const service = { sku: 'TA-100', description: 'Tiempo aire 100', sellable: true, showInSalesFlow: true };
267
+ expect(errorsOf(BackofficeCreateServiceRequest, { ...service, externalId: 'ZC-9' })).toEqual([]);
268
+ expect(errorsOf(BackofficeCreateServiceRequest, { ...service, externalId: ' ' })).toContain('externalId');
269
+ const dto = new BackofficeCreateServiceRequest();
270
+ expect(['eligibleFinanciers', 'mdmEnabled', 'mdmProvider', 'mdmLockMode'].some((k) => k in dto)).toBe(false);
271
+ });
272
+ });
273
+
274
+ describe('constantes y validadores compartidos del catálogo', () => {
275
+ it('NON_BUNDLE_CATEGORIES excluye BUNDLE', () => {
276
+ expect(NON_BUNDLE_CATEGORIES).not.toContain(ProductCategoryEnum.BUNDLE);
277
+ expect(NON_BUNDLE_CATEGORIES).toHaveLength(Object.values(ProductCategoryEnum).length - 1);
278
+ });
279
+ it('ADJUSTMENT_REASONS no incluye DAMAGED_RETURN', () => {
280
+ expect(ADJUSTMENT_REASONS).toEqual([InventoryMovementReasonEnum.SHRINKAGE, InventoryMovementReasonEnum.ADJUSTMENT, InventoryMovementReasonEnum.WARRANTY]);
281
+ });
282
+ it('IsPieceRefConstraint se exporta desde validators', () => expect(typeof IsPieceRefConstraint).toBe('function'));
283
+ });
@@ -13,6 +13,7 @@ describe('retailCatalog — enums del inventario multi-item', () => {
13
13
  it('expone los tipos de movimiento', () => {
14
14
  expect(Object.values(RetailCatalog.InventoryMovementTypeEnum)).toEqual([
15
15
  'ENTRY', 'RESERVATION', 'RELEASE', 'SALE', 'RETURN', 'ADJUSTMENT',
16
+ 'TRANSFER_OUT', 'TRANSFER_IN', 'TRANSFER_CANCEL',
16
17
  ]);
17
18
  });
18
19
 
@@ -24,7 +25,7 @@ describe('retailCatalog — enums del inventario multi-item', () => {
24
25
  });
25
26
 
26
27
  it('expone el origen del precio y los periodos del lente', () => {
27
- expect(Object.values(RetailCatalog.PriceSourceEnum)).toEqual(['ZENTRAL_SYNC']);
28
+ expect(Object.values(RetailCatalog.PriceSourceEnum)).toEqual(['ZENTRAL_SYNC', 'BACKOFFICE']);
28
29
  expect(Object.values(RetailCatalog.InventoryLensPeriodEnum)).toEqual(['7d', '30d', '90d', 'all']);
29
30
  });
30
31
 
@@ -32,3 +33,29 @@ describe('retailCatalog — enums del inventario multi-item', () => {
32
33
  expect(Object.values(RetailCatalog.InventoryLensFilterEnum)).toEqual(['critical', 'stale', 'gap', 'transfers', 'list']);
33
34
  });
34
35
  });
36
+
37
+ describe('retailCatalog — enums de la operación independiente', () => {
38
+ it('expone el tipo de ítem', () => {
39
+ expect(Object.values(RetailCatalog.ItemTypeEnum)).toEqual(['PRODUCT', 'SERVICE']);
40
+ });
41
+
42
+ it('agrega IN_TRANSIT al estado de pieza sin quitar los existentes', () => {
43
+ expect(Object.values(RetailCatalog.InventoryItemStatusEnum)).toEqual([
44
+ 'AVAILABLE', 'RESERVED', 'SOLD', 'RETURNED', 'DAMAGED', 'IN_TRANSIT',
45
+ ]);
46
+ });
47
+
48
+ it('expone la condición de una devolución', () => {
49
+ expect(Object.values(RetailCatalog.ReturnConditionEnum)).toEqual(['GOOD', 'DAMAGED']);
50
+ });
51
+
52
+ it('expone el estado de un traspaso', () => {
53
+ expect(Object.values(RetailCatalog.TransferStateEnum)).toEqual(['OPEN', 'RECEIVED', 'CANCELLED']);
54
+ });
55
+
56
+ it('expone el motivo de un movimiento', () => {
57
+ expect(Object.values(RetailCatalog.InventoryMovementReasonEnum)).toEqual([
58
+ 'SHRINKAGE', 'ADJUSTMENT', 'WARRANTY', 'DAMAGED_RETURN',
59
+ ]);
60
+ });
61
+ });
@@ -0,0 +1,53 @@
1
+ import 'reflect-metadata';
2
+ import * as RetailCatalog from '../../../src/retailCatalog';
3
+
4
+ // ts-jest compila con type-check: si un campo nuevo no existe en el tipo, la suite no compila.
5
+ describe('retailCatalog — campos nuevos opcionales en respuestas', () => {
6
+ it('Product y ProductResponse aceptan itemType y siguen válidos sin él', () => {
7
+ const product = new RetailCatalog.Product();
8
+ product.itemType = RetailCatalog.ItemTypeEnum.SERVICE;
9
+ const response: RetailCatalog.ProductResponse = product;
10
+ expect(response.itemType).toBe('SERVICE');
11
+ expect(new RetailCatalog.Product().itemType).toBeUndefined();
12
+ });
13
+
14
+ it('StoreCatalogItem acepta itemType', () => {
15
+ const item: Pick<RetailCatalog.StoreCatalogItem, 'sku' | 'itemType'> = {
16
+ sku: 'SKU-1',
17
+ itemType: RetailCatalog.ItemTypeEnum.PRODUCT,
18
+ };
19
+ expect(item.itemType).toBe('PRODUCT');
20
+ });
21
+
22
+ it('la fila del lente en modo lista acepta itemType', () => {
23
+ const row: Pick<RetailCatalog.InventoryLensListItem, 'sku' | 'itemType'> = {
24
+ sku: 'SKU-1',
25
+ itemType: RetailCatalog.ItemTypeEnum.SERVICE,
26
+ };
27
+ expect(row.itemType).toBe('SERVICE');
28
+ });
29
+
30
+ it('ReservedComponent acepta priceCents y sigue válido sin él', () => {
31
+ const base: RetailCatalog.ReservedComponent = {
32
+ sku: 'SKU-1',
33
+ qty: 1,
34
+ category: RetailCatalog.ProductCategoryEnum.PHONE,
35
+ trackingMode: RetailCatalog.TrackingModeEnum.IMEI,
36
+ };
37
+ const priced: RetailCatalog.ReservedComponent = { ...base, priceCents: 150000 };
38
+ expect(base.priceCents).toBeUndefined();
39
+ expect(priced.priceCents).toBe(150000);
40
+ });
41
+
42
+ it('SkuPriceEntry acepta comboPriceCents y el origen BACKOFFICE', () => {
43
+ const entry: RetailCatalog.SkuPriceEntry = {
44
+ effectiveFrom: '2026-09-24T00:00:00.000Z',
45
+ listPriceCents: null,
46
+ basePriceCents: 200000,
47
+ comboPriceCents: 180000,
48
+ source: RetailCatalog.PriceSourceEnum.BACKOFFICE,
49
+ createdBy: 'system',
50
+ };
51
+ expect(entry.comboPriceCents).toBe(180000);
52
+ });
53
+ });
@@ -0,0 +1,64 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import { ReturnConditionEnum, ReturnPieceCondition, ReturnStockLine, ReturnStockRequest } from '../../../src/retailCatalog';
5
+
6
+ function paths(errors: ValidationError[], prefix = ''): string[] {
7
+ return errors.flatMap((e) => {
8
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
9
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
10
+ });
11
+ }
12
+
13
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
14
+ return paths(validateSync(plainToInstance(cls, payload) as object, { whitelist: true, forbidNonWhitelisted: true }));
15
+ }
16
+
17
+ describe('ReturnPieceCondition', () => {
18
+ it('una pieza con identificador y sin condición queda GOOD', () => {
19
+ const dto = plainToInstance(ReturnPieceCondition, { sku: 'PH-A15', identifier: '356938035643809' });
20
+ expect(dto.condition).toBe(ReturnConditionEnum.GOOD);
21
+ expect(validateSync(dto)).toEqual([]);
22
+ });
23
+ it('acepta un accesorio por cantidad dañado con motivo', () => {
24
+ expect(errorsOf(ReturnPieceCondition, { sku: 'FUNDA-1', qty: 2, condition: 'DAMAGED', reason: 'Rota' })).toEqual([]);
25
+ });
26
+ it('DAMAGED exige motivo; GOOD no', () => {
27
+ expect(errorsOf(ReturnPieceCondition, { sku: 'FUNDA-1', qty: 1, condition: 'DAMAGED' })).toContain('reason');
28
+ expect(errorsOf(ReturnPieceCondition, { sku: 'FUNDA-1', qty: 1, condition: 'DAMAGED', reason: ' ' })).toContain('reason');
29
+ expect(errorsOf(ReturnPieceCondition, { sku: 'PH-A15', identifier: '356938035643809', condition: 'DAMAGED', reason: 'Pantalla rota' })).toEqual([]);
30
+ expect(errorsOf(ReturnPieceCondition, { sku: 'PH-A15', identifier: '356938035643809', condition: 'GOOD' })).toEqual([]);
31
+ });
32
+ it('rechaza sin sku, sin identifier ni qty, y con ambos', () => {
33
+ expect(errorsOf(ReturnPieceCondition, { identifier: 'A1' })).toContain('sku');
34
+ expect(errorsOf(ReturnPieceCondition, { sku: 'FUNDA-1' })).toContain('identifier');
35
+ expect(errorsOf(ReturnPieceCondition, { sku: 'FUNDA-1', identifier: 'A1', qty: 1 })).toContain('identifier');
36
+ });
37
+ it('rechaza condición desconocida, qty 0, identificador y motivo en blanco', () => {
38
+ const errors = errorsOf(ReturnPieceCondition, { sku: 'FUNDA-1', qty: 0, condition: 'BROKEN', reason: ' ' });
39
+ expect(errors).toEqual(expect.arrayContaining(['qty', 'condition', 'reason']));
40
+ expect(errorsOf(ReturnPieceCondition, { sku: 'PH-1', identifier: ' ' })).toContain('identifier');
41
+ });
42
+ });
43
+
44
+ describe('ReturnStockRequest con condición por pieza', () => {
45
+ const line = { lineId: 'L1', sku: 'KIT-1', qty: 1, components: [{ sku: 'PH-A15', qty: 1, identifiers: ['356938035643809'] }, { sku: 'FUNDA-1', qty: 2, identifiers: [] }] };
46
+ const base = { saleId: 'sale', retailerId: 'ret', soldAt: '2026-09-23T10:00:00.000Z' };
47
+ it('un consumidor viejo sin condiciones sigue validando', () => {
48
+ expect(errorsOf(ReturnStockRequest, { ...base, lines: [line] })).toEqual([]);
49
+ });
50
+ it('acepta condiciones por pieza y por cantidad', () => {
51
+ const conditions = [
52
+ { sku: 'PH-A15', identifier: '356938035643809', condition: 'DAMAGED', reason: 'Pantalla rota' },
53
+ { sku: 'FUNDA-1', qty: 1, condition: 'DAMAGED', reason: 'Descosida' },
54
+ ];
55
+ const dto = plainToInstance(ReturnStockRequest, { ...base, lines: [{ ...line, conditions }] });
56
+ expect(dto.lines[0]).toBeInstanceOf(ReturnStockLine);
57
+ expect(dto.lines[0].conditions?.[0]).toBeInstanceOf(ReturnPieceCondition);
58
+ expect(validateSync(dto)).toEqual([]);
59
+ });
60
+ it('valida cada condición anidada', () => {
61
+ const conditions = [{ sku: 'PH-A15', identifier: '356938035643809', condition: 'LOST' }];
62
+ expect(errorsOf(ReturnStockRequest, { ...base, lines: [{ ...line, conditions }] })).toContain('lines.0.conditions.0.condition');
63
+ });
64
+ });
@@ -0,0 +1,41 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import { ReturnPieceCondition } from '../../../src/retailCatalog';
5
+ import { BackofficeCancelSaleRequest, CancelSaleRequest } from '../../../src/retailWizard';
6
+
7
+ function paths(errors: ValidationError[], prefix = ''): string[] {
8
+ return errors.flatMap((e) => {
9
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
10
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
11
+ });
12
+ }
13
+
14
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
15
+ return paths(validateSync(plainToInstance(cls, payload, { excludeExtraneousValues: true }) as object));
16
+ }
17
+
18
+ const conditions = [
19
+ { sku: 'PH-A15', identifier: '356938035643809', condition: 'DAMAGED', reason: 'Pantalla rota' },
20
+ { sku: 'FUNDA-1', qty: 1 },
21
+ ];
22
+
23
+ describe.each([
24
+ ['CancelSaleRequest', CancelSaleRequest, { reason: 'Cliente se arrepintió' }],
25
+ ['BackofficeCancelSaleRequest', BackofficeCancelSaleRequest, { reason: 'Cliente se arrepintió' }],
26
+ ] as const)('%s con condición por pieza', (_name, cls, base) => {
27
+ it('sin returnConditions sigue validando', () => expect(errorsOf(cls, base)).toEqual([]));
28
+ it('acepta condiciones y las hidrata aun con excludeExtraneousValues', () => {
29
+ const dto = plainToInstance(cls, { ...base, returnConditions: conditions }, { excludeExtraneousValues: true });
30
+ expect(validateSync(dto)).toEqual([]);
31
+ expect(dto.returnConditions?.[0]).toBeInstanceOf(ReturnPieceCondition);
32
+ expect(dto.returnConditions?.[0].reason).toBe('Pantalla rota');
33
+ expect(dto.returnConditions?.[1].condition).toBe('GOOD');
34
+ });
35
+ it('valida cada condición y rechaza un no-arreglo', () => {
36
+ expect(errorsOf(cls, { ...base, returnConditions: [{ sku: 'FUNDA-1', condition: 'LOST' }] })).toEqual(
37
+ expect.arrayContaining(['returnConditions.0.condition', 'returnConditions.0.identifier']),
38
+ );
39
+ expect(errorsOf(cls, { ...base, returnConditions: 'GOOD' })).toContain('returnConditions');
40
+ });
41
+ });
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * Corredores de remesa soportados (Dashboard Ops · Variables Operativas).
3
3
  *
4
- * D-PB-3: nomenclatura por MONEDA ISO-4217 (`USD-<destino>`). `USD_SVC` es un
5
- * símbolo para El Salvador (su payout real es USD; usar la moneda colisionaría
6
- * como "USD-USD"). Por eso el destino real vive en CORRIDOR_INFO y el filtro de
7
- * transacciones usa countryISO (no ambiguo).
4
+ * D-PB-3: nomenclatura por MONEDA ISO-4217 (`USD-<destino>`). `USD_SVC` (El Salvador)
5
+ * y `USD_NIC` (Nicaragua) son símbolos: su payout real es USD y usar la moneda
6
+ * colisionaría como "USD-USD". Por eso el destino real vive en CORRIDOR_INFO y el
7
+ * filtro de transacciones usa countryISO (no ambiguo).
8
8
  */
9
9
  export declare enum RemittanceCorridor {
10
10
  USD_GTQ = "USD-GTQ",
11
11
  USD_MXN = "USD-MXN",
12
12
  USD_HNL = "USD-HNL",
13
- USD_SVC = "USD-SVC"
13
+ USD_SVC = "USD-SVC",
14
+ USD_NIC = "USD-NIC"
14
15
  }
15
16
  /** Destino real del corredor. El filtro de tx usa countryISO; currencyISO es el payout. */
16
17
  export declare const CORRIDOR_INFO: Record<RemittanceCorridor, {
@@ -4,10 +4,10 @@ exports.CORRIDOR_INFO = exports.RemittanceCorridor = void 0;
4
4
  /**
5
5
  * Corredores de remesa soportados (Dashboard Ops · Variables Operativas).
6
6
  *
7
- * D-PB-3: nomenclatura por MONEDA ISO-4217 (`USD-<destino>`). `USD_SVC` es un
8
- * símbolo para El Salvador (su payout real es USD; usar la moneda colisionaría
9
- * como "USD-USD"). Por eso el destino real vive en CORRIDOR_INFO y el filtro de
10
- * transacciones usa countryISO (no ambiguo).
7
+ * D-PB-3: nomenclatura por MONEDA ISO-4217 (`USD-<destino>`). `USD_SVC` (El Salvador)
8
+ * y `USD_NIC` (Nicaragua) son símbolos: su payout real es USD y usar la moneda
9
+ * colisionaría como "USD-USD". Por eso el destino real vive en CORRIDOR_INFO y el
10
+ * filtro de transacciones usa countryISO (no ambiguo).
11
11
  */
12
12
  var RemittanceCorridor;
13
13
  (function (RemittanceCorridor) {
@@ -15,6 +15,7 @@ var RemittanceCorridor;
15
15
  RemittanceCorridor["USD_MXN"] = "USD-MXN";
16
16
  RemittanceCorridor["USD_HNL"] = "USD-HNL";
17
17
  RemittanceCorridor["USD_SVC"] = "USD-SVC";
18
+ RemittanceCorridor["USD_NIC"] = "USD-NIC";
18
19
  })(RemittanceCorridor || (exports.RemittanceCorridor = RemittanceCorridor = {}));
19
20
  /** Destino real del corredor. El filtro de tx usa countryISO; currencyISO es el payout. */
20
21
  exports.CORRIDOR_INFO = {
@@ -22,4 +23,5 @@ exports.CORRIDOR_INFO = {
22
23
  [RemittanceCorridor.USD_MXN]: { countryISO: "MX", currencyISO: "MXN" },
23
24
  [RemittanceCorridor.USD_HNL]: { countryISO: "HN", currencyISO: "HNL" },
24
25
  [RemittanceCorridor.USD_SVC]: { countryISO: "SV", currencyISO: "USD" },
26
+ [RemittanceCorridor.USD_NIC]: { countryISO: "NI", currencyISO: "USD" },
25
27
  };
@@ -0,0 +1,3 @@
1
+ import { InventoryMovementReasonEnum } from '../enums/InventoryMovementReasonEnum';
2
+ /** Motivos que el admin puede registrar en un ajuste; DAMAGED_RETURN lo genera la devolución. */
3
+ export declare const ADJUSTMENT_REASONS: InventoryMovementReasonEnum[];
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ADJUSTMENT_REASONS = void 0;
4
+ const InventoryMovementReasonEnum_1 = require("../enums/InventoryMovementReasonEnum");
5
+ /** Motivos que el admin puede registrar en un ajuste; DAMAGED_RETURN lo genera la devolución. */
6
+ exports.ADJUSTMENT_REASONS = [
7
+ InventoryMovementReasonEnum_1.InventoryMovementReasonEnum.SHRINKAGE,
8
+ InventoryMovementReasonEnum_1.InventoryMovementReasonEnum.ADJUSTMENT,
9
+ InventoryMovementReasonEnum_1.InventoryMovementReasonEnum.WARRANTY,
10
+ ];
@@ -0,0 +1,3 @@
1
+ import { ProductCategoryEnum } from '../enums/ProductCategoryEnum';
2
+ /** Categorías que admite el alta y la edición de producto; el kit (BUNDLE) tiene su propio endpoint. */
3
+ export declare const NON_BUNDLE_CATEGORIES: (ProductCategoryEnum.PHONE | ProductCategoryEnum.SIM | ProductCategoryEnum.MODEM | ProductCategoryEnum.ACCESSORY)[];
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NON_BUNDLE_CATEGORIES = void 0;
4
+ const ProductCategoryEnum_1 = require("../enums/ProductCategoryEnum");
5
+ /** Categorías que admite el alta y la edición de producto; el kit (BUNDLE) tiene su propio endpoint. */
6
+ exports.NON_BUNDLE_CATEGORIES = Object.values(ProductCategoryEnum_1.ProductCategoryEnum).filter((c) => c !== ProductCategoryEnum_1.ProductCategoryEnum.BUNDLE);
@@ -4,6 +4,7 @@ import { ProductStatusEnum } from '../enums/ProductStatusEnum';
4
4
  import { ProductTierEnum } from '../enums/ProductTierEnum';
5
5
  import { ProductCategoryEnum } from '../enums/ProductCategoryEnum';
6
6
  import { TrackingModeEnum } from '../enums/TrackingModeEnum';
7
+ import { ItemTypeEnum } from '../enums/ItemTypeEnum';
7
8
  import { ProductComponent } from './ProductComponent';
8
9
  /**
9
10
  * Product — aggregate root del catálogo del dominio `retailCatalog`
@@ -45,4 +46,6 @@ export declare class Product {
45
46
  components?: ProductComponent[] | null;
46
47
  /** Llave del registro PRICE# del que salen basePriceCents y listPriceCents. */
47
48
  currentPriceSk?: string | null;
49
+ /** Producto físico o servicio; inmutable tras el alta. Ausente = PRODUCT. */
50
+ itemType?: ItemTypeEnum;
48
51
  }
@@ -0,0 +1,14 @@
1
+ import { ProductComponent } from '../ProductComponent';
2
+ /** Body de POST /backoffice/catalog/kits (backofficeCreateKit); category BUNDLE y origen BACKOFFICE los fija el servidor. */
3
+ export declare class BackofficeCreateKitRequest {
4
+ sku: string;
5
+ sellable: boolean;
6
+ showInSalesFlow: boolean;
7
+ components: ProductComponent[];
8
+ description?: string;
9
+ imageUrl?: string;
10
+ /** Id del SKU en el sistema del retailer. */
11
+ externalId?: string;
12
+ /** Financieras que pueden financiar este SKU; vacío = no financiable. */
13
+ eligibleFinanciers?: string[];
14
+ }