@fiado/type-kit 3.473.0 → 3.474.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.
- package/_test_/unit/retailCatalog/accessorySubcategory.test.ts +65 -0
- package/_test_/unit/retailCatalog/ventaSaga.test.ts +4 -0
- package/_test_/unit/retailPromotions/constants.test.ts +9 -2
- package/_test_/unit/retailPromotions/discountCodeAndWizardRequests.test.ts +13 -0
- package/_test_/unit/retailPromotions/salePromotionRequests.test.ts +11 -3
- package/_test_/unit/retailWizard/promotionM2mRequests.test.ts +15 -7
- package/_test_/unit/retailWizard/ventaSagaMessages.test.ts +23 -0
- package/bin/retailCatalog/dtos/Product.d.ts +3 -0
- package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.d.ts +3 -0
- package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.js +6 -0
- package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.d.ts +3 -0
- package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.js +6 -0
- package/bin/retailCatalog/dtos/requests/HoldStockRequest.js +3 -0
- package/bin/retailCatalog/dtos/responses/StoreCatalogItem.d.ts +3 -0
- package/bin/retailCatalog/enums/AccessorySubcategoryEnum.d.ts +9 -0
- package/bin/retailCatalog/enums/AccessorySubcategoryEnum.js +13 -0
- package/bin/retailCatalog/index.d.ts +1 -0
- package/bin/retailCatalog/index.js +2 -1
- package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.d.ts +2 -2
- package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +5 -1
- package/bin/retailPromotions/dtos/SalePromotionLevers.d.ts +3 -2
- package/bin/retailPromotions/dtos/requests/GetSaleCouponQuery.d.ts +4 -0
- package/bin/retailPromotions/dtos/requests/GetSaleCouponQuery.js +25 -0
- package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.d.ts +3 -2
- package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.js +4 -4
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +7 -1
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +6 -0
- package/bin/retailPromotions/index.d.ts +1 -0
- package/bin/retailPromotions/index.js +1 -0
- package/bin/retailWizard/dtos/WizardGiftLine.d.ts +3 -1
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.d.ts +2 -3
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.js +2 -3
- package/bin/retailWizard/dtos/requests/QuoteRequest.d.ts +1 -1
- package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +2 -2
- package/bin/retailWizard/events/RetailPromotionInboundMessage.js +2 -0
- package/package.json +1 -1
- package/src/retailCatalog/dtos/Product.ts +3 -0
- package/src/retailCatalog/dtos/requests/BackofficeCreateProductRequest.ts +6 -0
- package/src/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.ts +6 -0
- package/src/retailCatalog/dtos/requests/HoldStockRequest.ts +4 -1
- package/src/retailCatalog/dtos/responses/StoreCatalogItem.ts +3 -0
- package/src/retailCatalog/enums/AccessorySubcategoryEnum.ts +9 -0
- package/src/retailCatalog/index.ts +2 -1
- package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +6 -2
- package/src/retailPromotions/dtos/SalePromotionLevers.ts +3 -2
- package/src/retailPromotions/dtos/requests/GetSaleCouponQuery.ts +11 -0
- package/src/retailPromotions/dtos/requests/SalePromotionLeversInput.ts +4 -5
- package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +6 -0
- package/src/retailPromotions/index.ts +1 -0
- package/src/retailWizard/dtos/WizardGiftLine.ts +3 -1
- package/src/retailWizard/dtos/requests/ApplyLinePromotionRequest.ts +2 -3
- package/src/retailWizard/dtos/requests/QuoteRequest.ts +1 -1
- 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(
|
|
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
|
+
});
|
|
@@ -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
|
|
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.
|
|
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',
|
|
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, ...
|
|
140
|
-
const { originalPriceCents: _o, ...
|
|
141
|
-
const { role: _r, ...
|
|
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 =
|
|
145
|
+
const a: WizardGiftLine = withoutAnchor;
|
|
144
146
|
// @ts-expect-error originalPriceCents es obligatorio en un GIFT
|
|
145
|
-
const b: WizardGiftLine =
|
|
147
|
+
const b: WizardGiftLine = withoutPrice;
|
|
146
148
|
// @ts-expect-error role GIFT es obligatorio
|
|
147
|
-
const c: WizardGiftLine =
|
|
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(
|
|
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', () => {
|
|
@@ -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
|
-
//
|
|
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 (`
|
|
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
|
-
|
|
16
|
+
accessorySubcategory: AccessorySubcategoryEnum | null;
|
|
16
17
|
maxPriceCents: number | null;
|
|
17
18
|
chooseCount: number;
|
|
18
19
|
}
|
|
@@ -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 `
|
|
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
|
-
|
|
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 `
|
|
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.
|
|
53
|
-
(0, class_validator_1.IsNotEmpty)(),
|
|
53
|
+
(0, class_validator_1.IsEnum)(AccessorySubcategoryEnum_1.AccessorySubcategoryEnum),
|
|
54
54
|
__metadata("design:type", String)
|
|
55
|
-
], GiftLeverInput.prototype, "
|
|
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),
|
|
@@ -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:
|
|
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
|
|
3
|
-
*
|
|
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
|
|
18
|
-
*
|
|
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.
|
|
11
|
+
/** Se ignora: el servidor financia la ancla. */
|
|
12
12
|
financedLineId?: string;
|
|
13
13
|
}
|
|
@@ -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
|
@@ -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
|
-
//
|
|
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 (`
|
|
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
|
-
|
|
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 `
|
|
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
|
-
@
|
|
44
|
-
|
|
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)
|
|
@@ -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:
|
|
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
|
|
9
|
-
*
|
|
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()
|
|
@@ -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)
|