@fiado/type-kit 3.460.0 → 3.462.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/loanCredit/customerPaymentDtos.test.ts +87 -0
- package/_test_/unit/loanCredit/dtos/createCustomerPaymentRequest.test.ts +78 -0
- package/_test_/unit/retailCatalog/catalogEnums.test.ts +34 -0
- package/_test_/unit/retailCatalog/stockRequests.test.ts +156 -0
- package/_test_/unit/retailLoan/paymentContract.test.ts +128 -0
- package/_test_/unit/retailWizard/multiItemRequests.test.ts +114 -0
- package/_test_/unit/retailWizard/multiItemShapes.test.ts +126 -0
- package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.d.ts +9 -0
- package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.js +35 -0
- package/bin/loanCredit/dtos/responses/LoanAdHocChargeRequested.d.ts +6 -0
- package/bin/loanCredit/dtos/responses/LoanCustomerPaymentOptions.d.ts +33 -0
- package/bin/loanCredit/dtos/responses/LoanCustomerPaymentOptions.js +2 -0
- package/bin/loanCredit/dtos/responses/LoanCustomerPaymentResult.d.ts +14 -0
- package/bin/loanCredit/dtos/responses/LoanCustomerPaymentResult.js +2 -0
- package/bin/loanCredit/enums/LoanCustomerPaymentTypeEnum.d.ts +5 -0
- package/bin/loanCredit/enums/LoanCustomerPaymentTypeEnum.js +9 -0
- package/bin/loanCredit/enums/LoanNextInstallmentModeEnum.d.ts +7 -0
- package/bin/loanCredit/enums/LoanNextInstallmentModeEnum.js +11 -0
- package/bin/loanCredit/enums/LoanPaymentOptionUnavailableReasonEnum.d.ts +13 -0
- package/bin/loanCredit/enums/LoanPaymentOptionUnavailableReasonEnum.js +17 -0
- package/bin/loanCredit/index.d.ts +6 -0
- package/bin/loanCredit/index.js +7 -0
- package/bin/retailCatalog/dtos/Product.d.ts +15 -1
- package/bin/retailCatalog/dtos/ProductComponent.d.ts +5 -0
- package/bin/retailCatalog/dtos/ProductComponent.js +29 -0
- package/bin/retailCatalog/dtos/requests/GetInventorySkuQuery.d.ts +10 -0
- package/bin/retailCatalog/dtos/requests/GetInventorySkuQuery.js +42 -0
- package/bin/retailCatalog/dtos/requests/InventoryLensQuery.d.ts +5 -0
- package/bin/retailCatalog/dtos/requests/InventoryLensQuery.js +11 -0
- package/bin/retailCatalog/dtos/requests/ReleaseStockRequest.d.ts +8 -0
- package/bin/retailCatalog/dtos/requests/ReleaseStockRequest.js +34 -0
- package/bin/retailCatalog/dtos/requests/RenewReservationsRequest.d.ts +6 -0
- package/bin/retailCatalog/dtos/requests/RenewReservationsRequest.js +30 -0
- package/bin/retailCatalog/dtos/requests/ReservationLineRef.d.ts +5 -0
- package/bin/retailCatalog/dtos/requests/ReservationLineRef.js +25 -0
- package/bin/retailCatalog/dtos/requests/ReserveStockRequest.d.ts +10 -0
- package/bin/retailCatalog/dtos/requests/ReserveStockRequest.js +42 -0
- package/bin/retailCatalog/dtos/requests/ReturnStockRequest.d.ts +9 -0
- package/bin/retailCatalog/dtos/requests/ReturnStockRequest.js +38 -0
- package/bin/retailCatalog/dtos/requests/SellStockRequest.d.ts +7 -0
- package/bin/retailCatalog/dtos/requests/SellStockRequest.js +34 -0
- package/bin/retailCatalog/dtos/requests/StockLine.d.ts +14 -0
- package/bin/retailCatalog/dtos/requests/StockLine.js +56 -0
- package/bin/retailCatalog/dtos/requests/SuggestPiecesQuery.d.ts +5 -0
- package/bin/retailCatalog/dtos/requests/SuggestPiecesQuery.js +26 -0
- package/bin/retailCatalog/dtos/requests/SyncInventoryRequest.d.ts +47 -0
- package/bin/retailCatalog/dtos/requests/SyncInventoryRequest.js +173 -0
- package/bin/retailCatalog/dtos/responses/GetInventorySkuResponse.d.ts +34 -0
- package/bin/retailCatalog/dtos/responses/GetInventorySkuResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/InventoryLensResponse.d.ts +43 -2
- package/bin/retailCatalog/dtos/responses/ListSkuPricesResponse.d.ts +6 -0
- package/bin/retailCatalog/dtos/responses/ListSkuPricesResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/ReleaseStockResponse.d.ts +10 -0
- package/bin/retailCatalog/dtos/responses/ReleaseStockResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/RenewReservationsResponse.d.ts +10 -0
- package/bin/retailCatalog/dtos/responses/RenewReservationsResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/ReserveStockResponse.d.ts +25 -0
- package/bin/retailCatalog/dtos/responses/ReserveStockResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/ReturnStockResponse.d.ts +4 -0
- package/bin/retailCatalog/dtos/responses/ReturnStockResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/SellStockResponse.d.ts +18 -0
- package/bin/retailCatalog/dtos/responses/SellStockResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/SkuPriceEntry.d.ts +9 -0
- package/bin/retailCatalog/dtos/responses/SkuPriceEntry.js +2 -0
- package/bin/retailCatalog/dtos/responses/StoreCatalogItem.d.ts +29 -0
- package/bin/retailCatalog/dtos/responses/StoreCatalogItem.js +2 -0
- package/bin/retailCatalog/dtos/responses/SuggestPiecesResponse.d.ts +6 -0
- package/bin/retailCatalog/dtos/responses/SuggestPiecesResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/SyncInventoryResponse.d.ts +13 -0
- package/bin/retailCatalog/dtos/responses/SyncInventoryResponse.js +2 -0
- package/bin/retailCatalog/dtos/validation/ValidateProductResponse.d.ts +6 -1
- package/bin/retailCatalog/enums/InventoryLensFilterEnum.d.ts +3 -1
- package/bin/retailCatalog/enums/InventoryLensFilterEnum.js +2 -0
- package/bin/retailCatalog/enums/InventoryLensPeriodEnum.d.ts +7 -0
- package/bin/retailCatalog/enums/InventoryLensPeriodEnum.js +11 -0
- package/bin/retailCatalog/enums/InventoryMovementTypeEnum.d.ts +9 -0
- package/bin/retailCatalog/enums/InventoryMovementTypeEnum.js +13 -0
- package/bin/retailCatalog/enums/InventorySyncRejectionCodeEnum.d.ts +9 -0
- package/bin/retailCatalog/enums/InventorySyncRejectionCodeEnum.js +13 -0
- package/bin/retailCatalog/enums/InventorySyncResultStatusEnum.d.ts +6 -0
- package/bin/retailCatalog/enums/InventorySyncResultStatusEnum.js +10 -0
- package/bin/retailCatalog/enums/PriceSourceEnum.d.ts +4 -0
- package/bin/retailCatalog/enums/PriceSourceEnum.js +8 -0
- package/bin/retailCatalog/enums/ProductCategoryEnum.d.ts +9 -0
- package/bin/retailCatalog/enums/ProductCategoryEnum.js +13 -0
- package/bin/retailCatalog/enums/TrackingModeEnum.d.ts +6 -0
- package/bin/retailCatalog/enums/TrackingModeEnum.js +10 -0
- package/bin/retailCatalog/index.d.ts +29 -0
- package/bin/retailCatalog/index.js +30 -0
- package/bin/retailLoan/dtos/CreateMyPaymentRequest.d.ts +7 -0
- package/bin/retailLoan/dtos/CreateMyPaymentRequest.js +30 -0
- package/bin/retailLoan/dtos/CreateMyPaymentResponse.d.ts +9 -0
- package/bin/retailLoan/dtos/CreateMyPaymentResponse.js +2 -0
- package/bin/retailLoan/dtos/MyPaymentOptions.d.ts +34 -0
- package/bin/retailLoan/dtos/MyPaymentOptions.js +2 -0
- package/bin/retailLoan/enums/MyCollectionOutcomeEnum.d.ts +11 -0
- package/bin/retailLoan/enums/MyCollectionOutcomeEnum.js +15 -0
- package/bin/retailLoan/enums/MyNextInstallmentModeEnum.d.ts +5 -0
- package/bin/retailLoan/enums/MyNextInstallmentModeEnum.js +9 -0
- package/bin/retailLoan/enums/MyPaymentTypeEnum.d.ts +6 -0
- package/bin/retailLoan/enums/MyPaymentTypeEnum.js +10 -0
- package/bin/retailLoan/enums/MyPaymentUnavailableReasonEnum.d.ts +8 -0
- package/bin/retailLoan/enums/MyPaymentUnavailableReasonEnum.js +12 -0
- package/bin/retailLoan/index.d.ts +7 -0
- package/bin/retailLoan/index.js +8 -0
- package/bin/retailWizard/dtos/Sale.d.ts +7 -0
- package/bin/retailWizard/dtos/SaleLine.d.ts +16 -0
- package/bin/retailWizard/dtos/SaleLine.js +2 -0
- package/bin/retailWizard/dtos/WizardLineIdentifiers.d.ts +5 -0
- package/bin/retailWizard/dtos/WizardLineIdentifiers.js +2 -0
- package/bin/retailWizard/dtos/WizardLineProduct.d.ts +15 -0
- package/bin/retailWizard/dtos/WizardLineProduct.js +2 -0
- package/bin/retailWizard/dtos/WizardProduct.d.ts +6 -0
- package/bin/retailWizard/dtos/WizardReservedLine.d.ts +35 -0
- package/bin/retailWizard/dtos/WizardReservedLine.js +2 -0
- package/bin/retailWizard/dtos/WizardSession.d.ts +5 -0
- package/bin/retailWizard/dtos/requests/AddProductRequest.d.ts +4 -8
- package/bin/retailWizard/dtos/requests/AddProductRequest.js +9 -8
- package/bin/retailWizard/dtos/requests/CompleteSaleRequest.d.ts +16 -0
- package/bin/retailWizard/dtos/requests/CompleteSaleRequest.js +59 -0
- package/bin/retailWizard/dtos/requests/DeleteProductRequest.d.ts +3 -1
- package/bin/retailWizard/dtos/requests/DeleteProductRequest.js +7 -1
- package/bin/retailWizard/dtos/requests/QuoteRequest.d.ts +2 -0
- package/bin/retailWizard/dtos/requests/QuoteRequest.js +7 -0
- package/bin/retailWizard/dtos/requests/SuggestIdentifiersQuery.d.ts +5 -0
- package/bin/retailWizard/dtos/requests/SuggestIdentifiersQuery.js +30 -0
- package/bin/retailWizard/enums/LinePaymentTypeEnum.d.ts +7 -0
- package/bin/retailWizard/enums/LinePaymentTypeEnum.js +11 -0
- package/bin/retailWizard/enums/WizardLineStatusEnum.d.ts +9 -0
- package/bin/retailWizard/enums/WizardLineStatusEnum.js +13 -0
- package/bin/retailWizard/index.d.ts +8 -0
- package/bin/retailWizard/index.js +8 -0
- package/package.json +1 -1
- package/src/loanCredit/dtos/requests/CreateCustomerPaymentRequest.ts +21 -0
- package/src/loanCredit/dtos/responses/LoanAdHocChargeRequested.ts +3 -0
- package/src/loanCredit/dtos/responses/LoanCustomerPaymentOptions.ts +32 -0
- package/src/loanCredit/dtos/responses/LoanCustomerPaymentResult.ts +12 -0
- package/src/loanCredit/enums/LoanCustomerPaymentTypeEnum.ts +5 -0
- package/src/loanCredit/enums/LoanNextInstallmentModeEnum.ts +7 -0
- package/src/loanCredit/enums/LoanPaymentOptionUnavailableReasonEnum.ts +13 -0
- package/src/loanCredit/index.ts +8 -0
- package/src/retailCatalog/dtos/Product.ts +17 -1
- package/src/retailCatalog/dtos/ProductComponent.ts +14 -0
- package/src/retailCatalog/dtos/requests/GetInventorySkuQuery.ts +27 -0
- package/src/retailCatalog/dtos/requests/InventoryLensQuery.ts +12 -1
- package/src/retailCatalog/dtos/requests/ReleaseStockRequest.ts +19 -0
- package/src/retailCatalog/dtos/requests/RenewReservationsRequest.ts +15 -0
- package/src/retailCatalog/dtos/requests/ReservationLineRef.ts +10 -0
- package/src/retailCatalog/dtos/requests/ReserveStockRequest.ts +24 -0
- package/src/retailCatalog/dtos/requests/ReturnStockRequest.ts +22 -0
- package/src/retailCatalog/dtos/requests/SellStockRequest.ts +18 -0
- package/src/retailCatalog/dtos/requests/StockLine.ts +36 -0
- package/src/retailCatalog/dtos/requests/SuggestPiecesQuery.ts +11 -0
- package/src/retailCatalog/dtos/requests/SyncInventoryRequest.ts +145 -0
- package/src/retailCatalog/dtos/responses/GetInventorySkuResponse.ts +38 -0
- package/src/retailCatalog/dtos/responses/InventoryLensResponse.ts +48 -2
- package/src/retailCatalog/dtos/responses/ListSkuPricesResponse.ts +7 -0
- package/src/retailCatalog/dtos/responses/ReleaseStockResponse.ts +11 -0
- package/src/retailCatalog/dtos/responses/RenewReservationsResponse.ts +11 -0
- package/src/retailCatalog/dtos/responses/ReserveStockResponse.ts +27 -0
- package/src/retailCatalog/dtos/responses/ReturnStockResponse.ts +4 -0
- package/src/retailCatalog/dtos/responses/SellStockResponse.ts +20 -0
- package/src/retailCatalog/dtos/responses/SkuPriceEntry.ts +10 -0
- package/src/retailCatalog/dtos/responses/StoreCatalogItem.ts +31 -0
- package/src/retailCatalog/dtos/responses/SuggestPiecesResponse.ts +7 -0
- package/src/retailCatalog/dtos/responses/SyncInventoryResponse.ts +15 -0
- package/src/retailCatalog/dtos/validation/ValidateProductResponse.ts +6 -1
- package/src/retailCatalog/enums/InventoryLensFilterEnum.ts +2 -0
- package/src/retailCatalog/enums/InventoryLensPeriodEnum.ts +7 -0
- package/src/retailCatalog/enums/InventoryMovementTypeEnum.ts +9 -0
- package/src/retailCatalog/enums/InventorySyncRejectionCodeEnum.ts +9 -0
- package/src/retailCatalog/enums/InventorySyncResultStatusEnum.ts +6 -0
- package/src/retailCatalog/enums/PriceSourceEnum.ts +4 -0
- package/src/retailCatalog/enums/ProductCategoryEnum.ts +9 -0
- package/src/retailCatalog/enums/TrackingModeEnum.ts +6 -0
- package/src/retailCatalog/index.ts +31 -0
- package/src/retailLoan/dtos/CreateMyPaymentRequest.ts +16 -0
- package/src/retailLoan/dtos/CreateMyPaymentResponse.ts +10 -0
- package/src/retailLoan/dtos/MyPaymentOptions.ts +28 -0
- package/src/retailLoan/enums/MyCollectionOutcomeEnum.ts +11 -0
- package/src/retailLoan/enums/MyNextInstallmentModeEnum.ts +5 -0
- package/src/retailLoan/enums/MyPaymentTypeEnum.ts +6 -0
- package/src/retailLoan/enums/MyPaymentUnavailableReasonEnum.ts +8 -0
- package/src/retailLoan/index.ts +9 -0
- package/src/retailWizard/dtos/Sale.ts +7 -0
- package/src/retailWizard/dtos/SaleLine.ts +17 -0
- package/src/retailWizard/dtos/WizardLineIdentifiers.ts +5 -0
- package/src/retailWizard/dtos/WizardLineProduct.ts +16 -0
- package/src/retailWizard/dtos/WizardProduct.ts +7 -0
- package/src/retailWizard/dtos/WizardReservedLine.ts +36 -0
- package/src/retailWizard/dtos/WizardSession.ts +5 -0
- package/src/retailWizard/dtos/requests/AddProductRequest.ts +9 -8
- package/src/retailWizard/dtos/requests/CompleteSaleRequest.ts +41 -0
- package/src/retailWizard/dtos/requests/DeleteProductRequest.ts +7 -1
- package/src/retailWizard/dtos/requests/QuoteRequest.ts +8 -1
- package/src/retailWizard/dtos/requests/SuggestIdentifiersQuery.ts +15 -0
- package/src/retailWizard/enums/LinePaymentTypeEnum.ts +7 -0
- package/src/retailWizard/enums/WizardLineStatusEnum.ts +9 -0
- package/src/retailWizard/index.ts +8 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import {
|
|
3
|
+
LoanAdHocChargeKindEnum,
|
|
4
|
+
LoanAdHocChargeRequested,
|
|
5
|
+
LoanCustomerPaymentOptions,
|
|
6
|
+
LoanCustomerPaymentResult,
|
|
7
|
+
LoanCustomerPaymentTypeEnum,
|
|
8
|
+
LoanNextInstallmentModeEnum,
|
|
9
|
+
LoanPaymentOptionUnavailableReasonEnum,
|
|
10
|
+
} from '../../../src/loanCredit/index';
|
|
11
|
+
import { CollectionResultStatus } from '../../../src/collection/enums/CollectionResultStatus';
|
|
12
|
+
|
|
13
|
+
describe('loanCredit — opciones y resultado del pago del cliente', () => {
|
|
14
|
+
it('la liquidación trae su cotización y el monto es el de la cotización', () => {
|
|
15
|
+
const options: LoanCustomerPaymentOptions = {
|
|
16
|
+
creditId: 'CR-1',
|
|
17
|
+
nextInstallment: {
|
|
18
|
+
available: true,
|
|
19
|
+
amountCents: 105000,
|
|
20
|
+
mode: LoanNextInstallmentModeEnum.ADVANCE,
|
|
21
|
+
installmentNumber: 4,
|
|
22
|
+
dueDate: '2026-10-01',
|
|
23
|
+
},
|
|
24
|
+
advance: { available: false, unavailableReason: LoanPaymentOptionUnavailableReasonEnum.NOT_AVAILABLE_YET },
|
|
25
|
+
settlement: {
|
|
26
|
+
available: true,
|
|
27
|
+
amountCents: 912345,
|
|
28
|
+
quote: {
|
|
29
|
+
creditId: 'CR-1',
|
|
30
|
+
overdueInstallmentsCents: 0,
|
|
31
|
+
outstandingCapitalCents: 890000,
|
|
32
|
+
currentPeriodInterestCents: 19263,
|
|
33
|
+
currentPeriodInterestIvaCents: 3082,
|
|
34
|
+
elapsedDays: 3,
|
|
35
|
+
periodDays: 7,
|
|
36
|
+
settlementAmountCents: 912345,
|
|
37
|
+
quotedAt: '2026-09-24T15:00:00.000Z',
|
|
38
|
+
},
|
|
39
|
+
savingsCents: 32655,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
expect(options.settlement.quote?.settlementAmountCents).toBe(options.settlement.amountCents);
|
|
44
|
+
expect(options.pendingCharge).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('un cargo en curso se informa con su tipo y su monto', () => {
|
|
48
|
+
const blocked = { available: false, unavailableReason: LoanPaymentOptionUnavailableReasonEnum.PAYMENT_IN_PROGRESS };
|
|
49
|
+
const options: LoanCustomerPaymentOptions = {
|
|
50
|
+
creditId: 'CR-1',
|
|
51
|
+
pendingCharge: { kind: LoanAdHocChargeKindEnum.ADVANCE, amountCents: 105000 },
|
|
52
|
+
nextInstallment: blocked,
|
|
53
|
+
advance: blocked,
|
|
54
|
+
settlement: blocked,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
expect(options.pendingCharge?.kind).toBe('ADVANCE');
|
|
58
|
+
expect(options.nextInstallment.amountCents).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('el resultado del pago puede venir sin la respuesta del motor', () => {
|
|
62
|
+
const result: LoanCustomerPaymentResult = {
|
|
63
|
+
creditId: 'CR-1',
|
|
64
|
+
type: LoanCustomerPaymentTypeEnum.NEXT_INSTALLMENT,
|
|
65
|
+
chargeId: 'CR-1#ADVANCE#1',
|
|
66
|
+
amountCents: 105000,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
expect(result.collection).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('el 202 del cargo ad hoc suma la respuesta del motor como campo opcional', () => {
|
|
73
|
+
const withoutCollection: LoanAdHocChargeRequested = {
|
|
74
|
+
creditId: 'CR-1',
|
|
75
|
+
chargeKind: LoanAdHocChargeKindEnum.ADVANCE,
|
|
76
|
+
chargeId: 'CR-1#ADVANCE#1',
|
|
77
|
+
amountCents: 105000,
|
|
78
|
+
};
|
|
79
|
+
const withCollection: LoanAdHocChargeRequested = {
|
|
80
|
+
...withoutCollection,
|
|
81
|
+
collection: { status: CollectionResultStatus.PARTIAL, amountCollectedCents: 50000 },
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
expect(withoutCollection.collection).toBeUndefined();
|
|
85
|
+
expect(withCollection.collection?.amountCollectedCents).toBe(50000);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { LoanCredit } from '../../../../src/index';
|
|
5
|
+
import {
|
|
6
|
+
CreateCustomerPaymentRequest,
|
|
7
|
+
LoanCustomerPaymentTypeEnum,
|
|
8
|
+
LoanNextInstallmentModeEnum,
|
|
9
|
+
LoanPaymentOptionUnavailableReasonEnum,
|
|
10
|
+
} from '../../../../src/loanCredit/index';
|
|
11
|
+
|
|
12
|
+
const sortedValues = (enumObject: Record<string, string>) => Object.values(enumObject).sort();
|
|
13
|
+
|
|
14
|
+
const errorProperties = async (body: object): Promise<string[]> => {
|
|
15
|
+
const dto = plainToInstance(CreateCustomerPaymentRequest, body);
|
|
16
|
+
const errors = await validate(dto, { whitelist: true, forbidNonWhitelisted: false });
|
|
17
|
+
return errors.map(error => error.property);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const VALID = {
|
|
21
|
+
type: LoanCustomerPaymentTypeEnum.SETTLEMENT,
|
|
22
|
+
expectedAmountCents: 912345,
|
|
23
|
+
idempotencyKey: '3f1c2a9e-8b7d-4c6e-9f10-2a3b4c5d6e7f',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe.each([
|
|
27
|
+
['LoanCustomerPaymentTypeEnum', LoanCustomerPaymentTypeEnum, ['NEXT_INSTALLMENT', 'SETTLEMENT']],
|
|
28
|
+
['LoanNextInstallmentModeEnum', LoanNextInstallmentModeEnum, ['ADVANCE', 'COLLECT_DUE']],
|
|
29
|
+
[
|
|
30
|
+
'LoanPaymentOptionUnavailableReasonEnum',
|
|
31
|
+
LoanPaymentOptionUnavailableReasonEnum,
|
|
32
|
+
['PAYMENT_IN_PROGRESS', 'CREDIT_NOT_CURRENT', 'USE_SETTLEMENT', 'NOT_AVAILABLE_YET', 'CREDIT_CLOSED'],
|
|
33
|
+
],
|
|
34
|
+
])('%s', (_name, enumObject, expected) => {
|
|
35
|
+
it('tiene exactamente los valores del contrato', () => {
|
|
36
|
+
expect(sortedValues(enumObject)).toEqual([...expected].sort());
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('cada clave es igual a su valor', () => {
|
|
40
|
+
for (const [key, value] of Object.entries(enumObject)) {
|
|
41
|
+
expect(key).toBe(value);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('CreateCustomerPaymentRequest', () => {
|
|
47
|
+
it.each([[LoanCustomerPaymentTypeEnum.NEXT_INSTALLMENT], [LoanCustomerPaymentTypeEnum.SETTLEMENT]])(
|
|
48
|
+
'acepta el tipo %s',
|
|
49
|
+
async type => {
|
|
50
|
+
expect(await errorProperties({ ...VALID, type })).toEqual([]);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
it('exige tipo, monto esperado y llave de idempotencia', async () => {
|
|
55
|
+
expect(await errorProperties({})).toEqual(
|
|
56
|
+
expect.arrayContaining(['type', 'expectedAmountCents', 'idempotencyKey']),
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it.each([['ADVANCE'], ['PARTIAL']])('rechaza el tipo %s', async type => {
|
|
61
|
+
expect(await errorProperties({ ...VALID, type })).toEqual(['type']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it.each([[0], [-100], [10.5], ['912345']])('rechaza el monto %p', async expectedAmountCents => {
|
|
65
|
+
expect(await errorProperties({ ...VALID, expectedAmountCents })).toEqual(['expectedAmountCents']);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it.each([['abc'], [''], [123]])('rechaza la llave %p', async idempotencyKey => {
|
|
69
|
+
expect(await errorProperties({ ...VALID, idempotencyKey })).toEqual(['idempotencyKey']);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('export por el index raíz', () => {
|
|
74
|
+
it('expone el request y sus enums en el namespace LoanCredit', () => {
|
|
75
|
+
expect(LoanCredit.CreateCustomerPaymentRequest).toBeDefined();
|
|
76
|
+
expect(LoanCredit.LoanCustomerPaymentTypeEnum.SETTLEMENT).toBe('SETTLEMENT');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import * as RetailCatalog from '../../../src/retailCatalog';
|
|
3
|
+
|
|
4
|
+
describe('retailCatalog — enums del inventario multi-item', () => {
|
|
5
|
+
it('expone las categorías de producto', () => {
|
|
6
|
+
expect(Object.values(RetailCatalog.ProductCategoryEnum)).toEqual(['PHONE', 'SIM', 'MODEM', 'ACCESSORY', 'BUNDLE']);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('expone los modos de rastreo', () => {
|
|
10
|
+
expect(Object.values(RetailCatalog.TrackingModeEnum)).toEqual(['IMEI', 'SERIAL', 'QUANTITY']);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('expone los tipos de movimiento', () => {
|
|
14
|
+
expect(Object.values(RetailCatalog.InventoryMovementTypeEnum)).toEqual([
|
|
15
|
+
'ENTRY', 'RESERVATION', 'RELEASE', 'SALE', 'RETURN', 'ADJUSTMENT',
|
|
16
|
+
]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('expone el resultado por ítem de la puerta de entrada y sus códigos de rechazo', () => {
|
|
20
|
+
expect(Object.values(RetailCatalog.InventorySyncResultStatusEnum)).toEqual(['APPLIED', 'NOOP', 'REJECTED']);
|
|
21
|
+
expect(Object.values(RetailCatalog.InventorySyncRejectionCodeEnum)).toEqual([
|
|
22
|
+
'DUPLICATE_IDENTIFIER', 'ADJUSTMENT_EXCEEDS_AVAILABLE', 'FUTURE_PRICE_NOT_SUPPORTED',
|
|
23
|
+
]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
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.InventoryLensPeriodEnum)).toEqual(['7d', '30d', '90d', 'all']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('agrega el modo lista al filtro del lente sin quitar los existentes', () => {
|
|
32
|
+
expect(Object.values(RetailCatalog.InventoryLensFilterEnum)).toEqual(['critical', 'stale', 'gap', 'transfers', 'list']);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance, type ClassConstructor } from 'class-transformer';
|
|
3
|
+
import { validateSync, type ValidationError } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
GetInventorySkuQuery,
|
|
6
|
+
InventoryLensFilterEnum,
|
|
7
|
+
InventoryLensQuery,
|
|
8
|
+
MdmLockModeEnum,
|
|
9
|
+
MdmProviderEnum,
|
|
10
|
+
ProductCategoryEnum,
|
|
11
|
+
ReleaseStockRequest,
|
|
12
|
+
RenewReservationsRequest,
|
|
13
|
+
ReserveStockRequest,
|
|
14
|
+
ReturnStockRequest,
|
|
15
|
+
SellStockRequest,
|
|
16
|
+
SuggestPiecesQuery,
|
|
17
|
+
SyncInventoryRequest,
|
|
18
|
+
TrackingModeEnum,
|
|
19
|
+
} from '../../../src/retailCatalog';
|
|
20
|
+
|
|
21
|
+
function paths(errors: ValidationError[], prefix = ''): string[] {
|
|
22
|
+
return errors.flatMap((e) => {
|
|
23
|
+
const p = prefix ? `${prefix}.${e.property}` : e.property;
|
|
24
|
+
return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
|
|
29
|
+
return paths(validateSync(plainToInstance(cls, payload) as object));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const saleLine = { lineId: 'L1', sku: 'KIT-1', qty: 1, components: [{ sku: 'SIM-1', qty: 1, identifiers: ['8952'] }] };
|
|
33
|
+
|
|
34
|
+
describe('ReserveStockRequest', () => {
|
|
35
|
+
const ok = { sessionId: 's', lineId: 'L1', reservationId: 'r', retailerId: 'ret', sku: 'SKU-1', qty: 2 };
|
|
36
|
+
it('acepta un request completo', () => expect(errorsOf(ReserveStockRequest, ok)).toEqual([]));
|
|
37
|
+
it('rechaza qty menor a 1', () => expect(errorsOf(ReserveStockRequest, { ...ok, qty: 0 })).toContain('qty'));
|
|
38
|
+
it('rechaza sin reservationId', () => {
|
|
39
|
+
const { reservationId: _omit, ...rest } = ok;
|
|
40
|
+
expect(errorsOf(ReserveStockRequest, rest)).toContain('reservationId');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('RenewReservationsRequest y ReleaseStockRequest', () => {
|
|
45
|
+
it('acepta líneas con lineId y reservationId', () => {
|
|
46
|
+
expect(errorsOf(RenewReservationsRequest, { sessionId: 's', lines: [{ lineId: 'L1', reservationId: 'r' }] })).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
it('valida cada línea anidada', () => {
|
|
49
|
+
expect(errorsOf(RenewReservationsRequest, { sessionId: 's', lines: [{ lineId: 'L1' }] })).toContain('lines.0.reservationId');
|
|
50
|
+
});
|
|
51
|
+
it('release exige onlyExpired booleano', () => {
|
|
52
|
+
const base = { sessionId: 's', lines: [{ lineId: 'L1', reservationId: 'r' }] };
|
|
53
|
+
expect(errorsOf(ReleaseStockRequest, { ...base, onlyExpired: false })).toEqual([]);
|
|
54
|
+
expect(errorsOf(ReleaseStockRequest, base)).toContain('onlyExpired');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('SuggestPiecesQuery', () => {
|
|
59
|
+
it('acepta un prefijo de 4 caracteres', () => expect(errorsOf(SuggestPiecesQuery, { sku: 'SKU-1', prefix: '3569' })).toEqual([]));
|
|
60
|
+
it('rechaza un prefijo corto', () => expect(errorsOf(SuggestPiecesQuery, { sku: 'SKU-1', prefix: '356' })).toContain('prefix'));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('SellStockRequest y ReturnStockRequest', () => {
|
|
64
|
+
it('acepta una venta con componentes e identificadores', () => {
|
|
65
|
+
expect(errorsOf(SellStockRequest, { sessionId: 's', retailerId: 'ret', lines: [saleLine] })).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
it('valida los identificadores del componente', () => {
|
|
68
|
+
const bad = { ...saleLine, components: [{ sku: 'SIM-1', qty: 1, identifiers: [123] }] };
|
|
69
|
+
expect(errorsOf(SellStockRequest, { sessionId: 's', retailerId: 'ret', lines: [bad] })).toContain('lines.0.components.0.identifiers');
|
|
70
|
+
});
|
|
71
|
+
it('devolución exige saleId y soldAt ISO', () => {
|
|
72
|
+
const ok = { saleId: 'sale', retailerId: 'ret', soldAt: '2026-09-23T10:00:00.000Z', lines: [saleLine] };
|
|
73
|
+
expect(errorsOf(ReturnStockRequest, ok)).toEqual([]);
|
|
74
|
+
expect(errorsOf(ReturnStockRequest, { ...ok, soldAt: 'ayer' })).toContain('soldAt');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('SyncInventoryRequest', () => {
|
|
79
|
+
const product = {
|
|
80
|
+
sku: 'KIT-1',
|
|
81
|
+
category: ProductCategoryEnum.BUNDLE,
|
|
82
|
+
trackingMode: TrackingModeEnum.QUANTITY,
|
|
83
|
+
sellable: true,
|
|
84
|
+
showInSalesFlow: true,
|
|
85
|
+
eligibleFinanciers: ['SUREKEEP'],
|
|
86
|
+
components: [{ sku: 'SIM-1', qty: 1 }],
|
|
87
|
+
tier: null,
|
|
88
|
+
price: { listPriceCents: 100000, basePriceCents: 90000, effectiveFrom: '2026-09-23T00:00:00.000Z' },
|
|
89
|
+
};
|
|
90
|
+
it('acepta productos y stock por cantidad o por identificadores', () => {
|
|
91
|
+
const stock = [{ storeId: 'st', sku: 'SIM-1', qty: 5 }, { storeId: 'st', sku: 'PH-1', identifiers: ['356938035643809'] }];
|
|
92
|
+
expect(errorsOf(SyncInventoryRequest, { products: [product], stock })).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
it('rechaza categoría desconocida y precio sin vigencia', () => {
|
|
95
|
+
const bad = { ...product, category: 'TABLET', price: { basePriceCents: 1 } };
|
|
96
|
+
const errors = errorsOf(SyncInventoryRequest, { products: [bad], stock: [] });
|
|
97
|
+
expect(errors).toEqual(expect.arrayContaining(['products.0.category', 'products.0.price.effectiveFrom']));
|
|
98
|
+
});
|
|
99
|
+
it('valida los componentes del kit', () => {
|
|
100
|
+
const bad = { ...product, components: [{ sku: 'SIM-1', qty: 0 }] };
|
|
101
|
+
expect(errorsOf(SyncInventoryRequest, { products: [bad], stock: [] })).toContain('products.0.components.0.qty');
|
|
102
|
+
});
|
|
103
|
+
it('acepta los datos descriptivos y MDM del producto nuevo', () => {
|
|
104
|
+
const full = {
|
|
105
|
+
...product,
|
|
106
|
+
brand: 'Samsung',
|
|
107
|
+
model: 'A15',
|
|
108
|
+
capacity: '128GB',
|
|
109
|
+
color: 'Negro',
|
|
110
|
+
description: 'Equipo de gama media',
|
|
111
|
+
imageUrl: 'https://cdn.example.com/a15.png',
|
|
112
|
+
mdmEnabled: true,
|
|
113
|
+
mdmProvider: MdmProviderEnum.TRUSTONIC,
|
|
114
|
+
mdmLockMode: MdmLockModeEnum.ONDEMAND,
|
|
115
|
+
};
|
|
116
|
+
expect(errorsOf(SyncInventoryRequest, { products: [full], stock: [] })).toEqual([]);
|
|
117
|
+
});
|
|
118
|
+
it('acepta mdmProvider y mdmLockMode en null', () => {
|
|
119
|
+
const noMdm = { ...product, mdmEnabled: false, mdmProvider: null, mdmLockMode: null };
|
|
120
|
+
expect(errorsOf(SyncInventoryRequest, { products: [noMdm], stock: [] })).toEqual([]);
|
|
121
|
+
});
|
|
122
|
+
it('rechaza tipos incorrectos en los datos descriptivos y MDM', () => {
|
|
123
|
+
const bad = {
|
|
124
|
+
...product,
|
|
125
|
+
brand: 1,
|
|
126
|
+
model: 2,
|
|
127
|
+
capacity: 3,
|
|
128
|
+
color: 4,
|
|
129
|
+
description: 5,
|
|
130
|
+
imageUrl: 6,
|
|
131
|
+
mdmEnabled: 'yes',
|
|
132
|
+
mdmProvider: 'ACME',
|
|
133
|
+
mdmLockMode: 'NEVER',
|
|
134
|
+
};
|
|
135
|
+
const errors = errorsOf(SyncInventoryRequest, { products: [bad], stock: [] });
|
|
136
|
+
const fields = ['brand', 'model', 'capacity', 'color', 'description', 'imageUrl', 'mdmEnabled', 'mdmProvider', 'mdmLockMode'];
|
|
137
|
+
expect(errors).toEqual(expect.arrayContaining(fields.map((f) => `products.0.${f}`)));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('InventoryLensQuery en modo lista', () => {
|
|
142
|
+
it('acepta filter=list con period y storeId', () => {
|
|
143
|
+
expect(errorsOf(InventoryLensQuery, { filter: InventoryLensFilterEnum.LIST, period: '30d', storeId: 'st' })).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
it('sigue aceptando filter solo', () => expect(errorsOf(InventoryLensQuery, { filter: 'critical' })).toEqual([]));
|
|
146
|
+
it('rechaza un periodo desconocido', () => expect(errorsOf(InventoryLensQuery, { filter: 'list', period: '15d' })).toContain('period'));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('GetInventorySkuQuery', () => {
|
|
150
|
+
it('convierte limit del query string a número', () => {
|
|
151
|
+
const dto = plainToInstance(GetInventorySkuQuery, { limit: '20', period: '7d' });
|
|
152
|
+
expect(dto.limit).toBe(20);
|
|
153
|
+
expect(validateSync(dto)).toEqual([]);
|
|
154
|
+
});
|
|
155
|
+
it('rechaza limit fuera de rango', () => expect(errorsOf(GetInventorySkuQuery, { limit: '0' })).toContain('limit'));
|
|
156
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { RetailLoan } from '../../../src/index';
|
|
5
|
+
import {
|
|
6
|
+
CreateMyPaymentRequest,
|
|
7
|
+
CreateMyPaymentResponse,
|
|
8
|
+
MyCollectionOutcomeEnum,
|
|
9
|
+
MyNextInstallmentModeEnum,
|
|
10
|
+
MyPaymentOptions,
|
|
11
|
+
MyPaymentTypeEnum,
|
|
12
|
+
MyPaymentUnavailableReasonEnum,
|
|
13
|
+
} from '../../../src/retailLoan/index';
|
|
14
|
+
import { LoanNextInstallmentModeEnum, LoanPaymentOptionUnavailableReasonEnum } from '../../../src/loanCredit/index';
|
|
15
|
+
|
|
16
|
+
const sortedValues = (enumObject: Record<string, string>) => Object.values(enumObject).sort();
|
|
17
|
+
|
|
18
|
+
const errorProperties = async (body: object): Promise<string[]> => {
|
|
19
|
+
const dto = plainToInstance(CreateMyPaymentRequest, body);
|
|
20
|
+
const errors = await validate(dto, { whitelist: true, forbidNonWhitelisted: false });
|
|
21
|
+
return errors.map(error => error.property);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe.each([
|
|
25
|
+
['MyPaymentTypeEnum', MyPaymentTypeEnum, ['NEXT_INSTALLMENT', 'ADVANCE', 'SETTLEMENT']],
|
|
26
|
+
['MyNextInstallmentModeEnum', MyNextInstallmentModeEnum, ['ADVANCE', 'COLLECT_DUE']],
|
|
27
|
+
[
|
|
28
|
+
'MyPaymentUnavailableReasonEnum',
|
|
29
|
+
MyPaymentUnavailableReasonEnum,
|
|
30
|
+
['PAYMENT_IN_PROGRESS', 'CREDIT_NOT_CURRENT', 'USE_SETTLEMENT', 'NOT_AVAILABLE_YET', 'CREDIT_CLOSED'],
|
|
31
|
+
],
|
|
32
|
+
['MyCollectionOutcomeEnum', MyCollectionOutcomeEnum, ['COLLECTED', 'PARTIAL', 'NOT_COLLECTED', 'UNKNOWN']],
|
|
33
|
+
])('%s', (_name, enumObject, expected) => {
|
|
34
|
+
it('tiene exactamente los valores del contrato', () => {
|
|
35
|
+
expect(sortedValues(enumObject)).toEqual([...expected].sort());
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('cada clave es igual a su valor', () => {
|
|
39
|
+
for (const [key, value] of Object.entries(enumObject)) {
|
|
40
|
+
expect(key).toBe(value);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('paridad con el contrato privado de loan-credit', () => {
|
|
46
|
+
it('los motivos de no disponible son los mismos', () => {
|
|
47
|
+
expect(sortedValues(MyPaymentUnavailableReasonEnum)).toEqual(sortedValues(LoanPaymentOptionUnavailableReasonEnum));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('los modos de la siguiente cuota son los mismos', () => {
|
|
51
|
+
expect(sortedValues(MyNextInstallmentModeEnum)).toEqual(sortedValues(LoanNextInstallmentModeEnum));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('CreateMyPaymentRequest', () => {
|
|
56
|
+
it.each([
|
|
57
|
+
[{ type: MyPaymentTypeEnum.NEXT_INSTALLMENT, expectedAmount: 1050 }],
|
|
58
|
+
[{ type: MyPaymentTypeEnum.SETTLEMENT, expectedAmount: 9123.45 }],
|
|
59
|
+
[{ type: MyPaymentTypeEnum.ADVANCE, expectedAmount: 2100.5 }],
|
|
60
|
+
])('acepta %j', async body => {
|
|
61
|
+
expect(await errorProperties(body)).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('exige tipo y monto esperado', async () => {
|
|
65
|
+
expect(await errorProperties({})).toEqual(expect.arrayContaining(['type', 'expectedAmount']));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('rechaza un tipo fuera del enum', async () => {
|
|
69
|
+
expect(await errorProperties({ type: 'PARTIAL', expectedAmount: 1050 })).toEqual(['type']);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it.each([[0], [-1050], [10.123], ['1050']])('rechaza el monto %p', async expectedAmount => {
|
|
73
|
+
expect(await errorProperties({ type: MyPaymentTypeEnum.SETTLEMENT, expectedAmount })).toEqual(['expectedAmount']);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('retailLoan — opciones y respuesta del pago', () => {
|
|
78
|
+
it('la liquidación trae su desglose y su ahorro en pesos', () => {
|
|
79
|
+
const options: MyPaymentOptions = {
|
|
80
|
+
nextInstallment: {
|
|
81
|
+
available: true,
|
|
82
|
+
amount: 1050,
|
|
83
|
+
mode: MyNextInstallmentModeEnum.ADVANCE,
|
|
84
|
+
installmentNumber: 4,
|
|
85
|
+
dueDate: '2026-10-01',
|
|
86
|
+
},
|
|
87
|
+
advance: { available: false, unavailableReason: MyPaymentUnavailableReasonEnum.NOT_AVAILABLE_YET },
|
|
88
|
+
settlement: {
|
|
89
|
+
available: true,
|
|
90
|
+
amount: 9123.45,
|
|
91
|
+
breakdown: { overdue: 0, capital: 8900, interest: 192.63, iva: 30.82 },
|
|
92
|
+
savings: 326.55,
|
|
93
|
+
quotedAt: '2026-09-24T15:00:00.000Z',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
expect(options.settlement.breakdown?.capital).toBe(8900);
|
|
98
|
+
expect(options.pendingPayment).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('un pago en curso se informa con su tipo y su monto', () => {
|
|
102
|
+
const blocked = { available: false, unavailableReason: MyPaymentUnavailableReasonEnum.PAYMENT_IN_PROGRESS };
|
|
103
|
+
const options: MyPaymentOptions = {
|
|
104
|
+
pendingPayment: { type: MyPaymentTypeEnum.NEXT_INSTALLMENT, amount: 1050 },
|
|
105
|
+
nextInstallment: blocked,
|
|
106
|
+
advance: blocked,
|
|
107
|
+
settlement: blocked,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
expect(options.pendingPayment?.type).toBe('NEXT_INSTALLMENT');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('la respuesta del pago siempre trae el desenlace del cobro', () => {
|
|
114
|
+
const response: CreateMyPaymentResponse = {
|
|
115
|
+
creditId: 'CR-1',
|
|
116
|
+
type: MyPaymentTypeEnum.SETTLEMENT,
|
|
117
|
+
amount: 9123.45,
|
|
118
|
+
collection: MyCollectionOutcomeEnum.UNKNOWN,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
expect(response.collection).toBe('UNKNOWN');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('expone el request en el namespace RetailLoan del index raíz', () => {
|
|
125
|
+
expect(RetailLoan.CreateMyPaymentRequest).toBeDefined();
|
|
126
|
+
expect(RetailLoan.MyCollectionOutcomeEnum.NOT_COLLECTED).toBe('NOT_COLLECTED');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance, type ClassConstructor } from 'class-transformer';
|
|
3
|
+
import { validateSync, type ValidationError } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
AddProductRequest,
|
|
6
|
+
CompleteSaleRequest,
|
|
7
|
+
DeleteProductRequest,
|
|
8
|
+
QuoteRequest,
|
|
9
|
+
SuggestIdentifiersQuery,
|
|
10
|
+
} from '../../../src/retailWizard';
|
|
11
|
+
|
|
12
|
+
function paths(errors: ValidationError[], prefix = ''): string[] {
|
|
13
|
+
return errors.flatMap((e) => {
|
|
14
|
+
const p = prefix ? `${prefix}.${e.property}` : e.property;
|
|
15
|
+
return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toDto<T extends object>(cls: ClassConstructor<T>, payload: object): T {
|
|
20
|
+
return plainToInstance(cls, payload, { excludeExtraneousValues: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
|
|
24
|
+
return paths(validateSync(toDto(cls, payload) as object));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('AddProductRequest — qty por línea', () => {
|
|
28
|
+
it('acepta solo el sku (qty ausente = 1 lo resuelve el silo)', () => {
|
|
29
|
+
expect(errorsOf(AddProductRequest, { sku: 'SKU-1' })).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
it('acepta qty explícita', () => {
|
|
32
|
+
expect(errorsOf(AddProductRequest, { sku: 'SKU-1', qty: 3 })).toEqual([]);
|
|
33
|
+
expect(toDto(AddProductRequest, { sku: 'SKU-1', qty: 3 }).qty).toBe(3);
|
|
34
|
+
});
|
|
35
|
+
it('rechaza qty menor a 1', () => {
|
|
36
|
+
expect(errorsOf(AddProductRequest, { sku: 'SKU-1', qty: 0 })).toContain('qty');
|
|
37
|
+
});
|
|
38
|
+
it('rechaza qty no entera', () => {
|
|
39
|
+
expect(errorsOf(AddProductRequest, { sku: 'SKU-1', qty: 1.5 })).toContain('qty');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('DeleteProductRequest — quitar por lineId', () => {
|
|
44
|
+
it('acepta lineId', () => {
|
|
45
|
+
expect(errorsOf(DeleteProductRequest, { lineId: 'L1' })).toEqual([]);
|
|
46
|
+
expect(toDto(DeleteProductRequest, { lineId: 'L1' }).lineId).toBe('L1');
|
|
47
|
+
});
|
|
48
|
+
it('sigue aceptando el request vacío', () => {
|
|
49
|
+
expect(errorsOf(DeleteProductRequest, {})).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
it('rechaza lineId que no es string', () => {
|
|
52
|
+
expect(errorsOf(DeleteProductRequest, { lineId: 7 })).toContain('lineId');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('QuoteRequest — financedLineId', () => {
|
|
57
|
+
const base = { downPaymentCents: 1000, termWeeks: 12 };
|
|
58
|
+
it('acepta sin financedLineId (compat)', () => {
|
|
59
|
+
expect(errorsOf(QuoteRequest, base)).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
it('acepta financedLineId', () => {
|
|
62
|
+
expect(errorsOf(QuoteRequest, { ...base, financedLineId: 'L2' })).toEqual([]);
|
|
63
|
+
expect(toDto(QuoteRequest, { ...base, financedLineId: 'L2' }).financedLineId).toBe('L2');
|
|
64
|
+
});
|
|
65
|
+
it('rechaza financedLineId vacío', () => {
|
|
66
|
+
expect(errorsOf(QuoteRequest, { ...base, financedLineId: '' })).toContain('financedLineId');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('CompleteSaleRequest — líneas con identificadores por componente', () => {
|
|
71
|
+
const ok = {
|
|
72
|
+
lines: [
|
|
73
|
+
{ lineId: 'L1', components: [{ sku: 'PHONE-1', identifiers: ['356938035643809'] }] },
|
|
74
|
+
{ lineId: 'L2', components: [{ sku: 'SIM-1', identifiers: ['8952140'] }, { sku: 'CASE-1', identifiers: [] }] },
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
it('acepta un request completo', () => {
|
|
78
|
+
expect(errorsOf(CompleteSaleRequest, ok)).toEqual([]);
|
|
79
|
+
const dto = toDto(CompleteSaleRequest, ok);
|
|
80
|
+
expect(dto.lines[1].components[0].identifiers).toEqual(['8952140']);
|
|
81
|
+
});
|
|
82
|
+
it('acepta lista de líneas vacía (venta solo por cantidad)', () => {
|
|
83
|
+
expect(errorsOf(CompleteSaleRequest, { lines: [] })).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
it('acepta sin lines (front sin body)', () => {
|
|
86
|
+
expect(errorsOf(CompleteSaleRequest, {})).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
it('rechaza lines que no es arreglo', () => {
|
|
89
|
+
expect(errorsOf(CompleteSaleRequest, { lines: 'L1' })).toContain('lines');
|
|
90
|
+
});
|
|
91
|
+
it('rechaza una línea sin lineId', () => {
|
|
92
|
+
expect(errorsOf(CompleteSaleRequest, { lines: [{ components: [] }] })).toContain('lines.0.lineId');
|
|
93
|
+
});
|
|
94
|
+
it('rechaza un identificador que no es string', () => {
|
|
95
|
+
const bad = { lines: [{ lineId: 'L1', components: [{ sku: 'SIM-1', identifiers: [123] }] }] };
|
|
96
|
+
expect(errorsOf(CompleteSaleRequest, bad)).toContain('lines.0.components.0.identifiers');
|
|
97
|
+
});
|
|
98
|
+
it('rechaza un componente sin sku', () => {
|
|
99
|
+
const bad = { lines: [{ lineId: 'L1', components: [{ identifiers: ['x'] }] }] };
|
|
100
|
+
expect(errorsOf(CompleteSaleRequest, bad)).toContain('lines.0.components.0.sku');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('SuggestIdentifiersQuery — prefijo mínimo 4', () => {
|
|
105
|
+
it('acepta sku y prefijo de 4', () => {
|
|
106
|
+
expect(errorsOf(SuggestIdentifiersQuery, { sku: 'SIM-1', prefix: '8952' })).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
it('rechaza prefijo corto', () => {
|
|
109
|
+
expect(errorsOf(SuggestIdentifiersQuery, { sku: 'SIM-1', prefix: '895' })).toContain('prefix');
|
|
110
|
+
});
|
|
111
|
+
it('rechaza sin sku', () => {
|
|
112
|
+
expect(errorsOf(SuggestIdentifiersQuery, { prefix: '8952' })).toContain('sku');
|
|
113
|
+
});
|
|
114
|
+
});
|