UNPKG

channelape-sdk

Version:
226 lines 11.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InventoryBatchAdjustmentsService = void 0; const Q = require("q"); const channelape_logger_1 = require("channelape-logger"); const UnknownStatusError_1 = require("../model/UnknownStatusError"); const InventoryStatus_1 = require("../enum/InventoryStatus"); class InventoryBatchAdjustmentsService { constructor(inventoryItemQuantitiesService, inventoriesService, locationsService) { this.inventoryItemQuantitiesService = inventoryItemQuantitiesService; this.inventoriesService = inventoriesService; this.locationsService = locationsService; this.logger = new channelape_logger_1.Logger('InventorybatchAdjustmentsService', channelape_logger_1.LogLevel.VERBOSE); } /** * Adjusts multiple inventory adjustments for multiple SKU's. If an inventory item does not yet exist * for a given SKU, then a new inventory item is created for it. * * The `deduplicationKey` on each AdjustmentBySku should indicate a unique identifier which will prevent * another adjustment for the same deduplication key, inventory item, location, and status. For example, * if you only wanted to allow one adjustment per day, you could use a value of the current day/month/year. * * @param {AdjustmentsBySku} adjustmentsBySku */ adjustBatch(adjustmentsBySku) { return this.performAdjustments(adjustmentsBySku, AdjustmentType.ADJUST); } /** * Sets multiple inventory adjustments for multiple SKU's. If an inventory item does not yet exist * for a given SKU, then a new inventory item is created for it. * * The `deduplicationKey` on each AdjustmentBySku should indicate a unique identifier which will prevent * another adjustment for the same deduplication key, inventory item, location, and status. For example, * if you only wanted to allow one adjustment per day, you could use a value of the current day/month/year. * * @param {AdjustmentsBySku} adjustmentsBySku */ setBatch(adjustmentsBySku) { return this.performAdjustments(adjustmentsBySku, AdjustmentType.SET); } performAdjustments(adjustmentsBySku, adjustmentType) { return __awaiter(this, void 0, void 0, function* () { const businessId = yield this.getBusinessIdFromLocation(adjustmentsBySku[0].adjustments[0].locationId); const adjustmentsBySkuPromises = adjustmentsBySku .map((adjustmentsBySku) => { return this.handleAdjustmentsBySku(adjustmentsBySku, businessId, adjustmentType); }); const promiseStateResults = yield Q.allSettled(adjustmentsBySkuPromises); let rejectedAdjustment = []; let fulfilledAdjustment = []; let failedToGetOrCreateInventoryItemCount = 0; let successfullyGottenOrCreatedInventoryItemCount = 0; for (const result of promiseStateResults) { if (result.state === 'rejected') { failedToGetOrCreateInventoryItemCount += 1; } else { successfullyGottenOrCreatedInventoryItemCount += 1; const adjustments = result.value; if (!adjustments) { throw new Error('No adjustments found for fulfilled promise'); } rejectedAdjustment = rejectedAdjustment.concat(adjustments.filter((result) => result.state === 'rejected').map(item => item.reason)); fulfilledAdjustment = fulfilledAdjustment.concat( // @ts-ignore adjustments.filter((result) => result.state === 'fulfilled').map(item => item.value)); } } const unknownStatuses = rejectedAdjustment.filter((reason) => reason instanceof UnknownStatusError_1.UnknownStatusError); const totalAttemptedAdjustments = fulfilledAdjustment.length + rejectedAdjustment.length - unknownStatuses.length; this.logger.info('Inventory adjustments completed.'); this.logger.info(`\tSkipped adjustments with unknown statuses: ${unknownStatuses.length}`); this.logger.info('\tSuccessfully retrieved or created inventory items: ' + `${successfullyGottenOrCreatedInventoryItemCount} / ${promiseStateResults.length}`); this.logger.info('\tSuccessful adjustments: ' + `${fulfilledAdjustment.length} / ${totalAttemptedAdjustments}`); if (rejectedAdjustment.length > 0 || failedToGetOrCreateInventoryItemCount > 0) { this.logger.warn('At least one adjustment failed, throwing error'); throw new Error('At least one adjustment failed'); } this.logger.info('Inventory quantity updates completed without error'); }); } handleAdjustmentsBySku(adjustmentsBySku, businessId, adjustmentType) { return __awaiter(this, void 0, void 0, function* () { let inventoryItem = yield this.getInventoryItem(adjustmentsBySku.sku, businessId); if (!inventoryItem) { inventoryItem = yield this.createInventoryItem(adjustmentsBySku.sku, businessId); } const allowedStatuses = Object.values(InventoryStatus_1.InventoryStatus); const adjustmentPromises = []; for (const adjustment of adjustmentsBySku.adjustments) { adjustmentPromises.push(this.sendAdjustmentRequest(adjustment, inventoryItem, allowedStatuses, adjustmentType)); } return Q.allSettled(adjustmentPromises); }); } getBusinessIdFromLocation(locationId) { return __awaiter(this, void 0, void 0, function* () { try { const location = yield this.locationsService.get(locationId); return location.businessId; } catch (error) { this.logger.error(error); throw new Error(`Failed to retrieve business ID from location: ${locationId}`); } }); } generateIdempotentKey(inventoryItemId, status, locationId, deduplicationKey) { return `${deduplicationKey}_${locationId}_${inventoryItemId}_${status}`; } getInventoryItem(sku, businessId) { return __awaiter(this, void 0, void 0, function* () { let inventoryItems; try { inventoryItems = yield this.inventoriesService.get(businessId, sku); } catch (error) { this.logger.error(`Failed to retrieve inventory item. SKU: ${sku} Business ID: ${businessId}`); this.logger.error(error); throw error; } if (inventoryItems.length > 1) { const ids = inventoryItems.map(inventoryItem => inventoryItem.id); const error = `Multiple inventory items found for a single SKU. SKU: ${sku} Found Inventory Item ID's: ${ids} Business ID: ${businessId}`; this.logger.error(error); throw new Error(error); } return inventoryItems[0]; }); } createInventoryItem(sku, businessId) { return __awaiter(this, void 0, void 0, function* () { try { const item = yield this.inventoriesService.create({ sku, businessId }); this.logger.info('Successfully created inventory item:' + `\n\tInventoryItemId: ${item.id}` + `\n\tSKU: ${item.sku}`); return item; } catch (error) { this.logger.error(`Failed to create new inventory item. SKU: ${sku} Business ID: ${businessId}`); this.logger.error(error); throw error; } }); } sendAdjustmentRequest(adjustment, inventoryItem, allowedStatuses, adjustmentType) { return __awaiter(this, void 0, void 0, function* () { if (!allowedStatuses.includes(adjustment.inventoryStatus)) { const errorMessage = `No matching status found. Skipping adjustment. InventoryItemId: ${inventoryItem.id} Status: ${adjustment.inventoryStatus} Expected one of the following statuses: ${allowedStatuses}`; this.logger.warn(errorMessage); throw new UnknownStatusError_1.UnknownStatusError(errorMessage); } const idempotentKey = this.generateIdempotentKey(inventoryItem.id, adjustment.inventoryStatus, adjustment.locationId, adjustment.deduplicationKey); const adjustmentRequest = { idempotentKey, locationId: adjustment.locationId, quantity: adjustment.quantity, inventoryItemId: inventoryItem.id, inventoryStatus: adjustment.inventoryStatus, aggregateChannelSync: !!adjustment.aggregateChannelSync }; if (adjustment.memo) { adjustmentRequest.memo = adjustment.memo; } try { let result; if (adjustmentType === AdjustmentType.SET) { result = yield this.inventoryItemQuantitiesService.set(adjustmentRequest); } else if (adjustmentType === AdjustmentType.ADJUST) { result = yield this.inventoryItemQuantitiesService.adjust(adjustmentRequest); } else { throw new Error(`Adjustment type "${adjustmentType}" is not configured.`); } this.logger.info(`Successfully set quantity. Inventory Item ID: ${adjustmentRequest.inventoryItemId} Location ID: ${adjustment.locationId} Quantity: ${adjustmentRequest.quantity} Status: ${adjustmentRequest.inventoryStatus}`); return result; } catch (error) { this.logger.error(`Failed to set quantity. Inventory Item ID: ${adjustmentRequest.inventoryItemId} Location ID: ${adjustment.locationId} Quantity: ${adjustmentRequest.quantity} Status: ${adjustmentRequest.inventoryStatus}`); this.logger.error(error); throw error; } }); } } exports.InventoryBatchAdjustmentsService = InventoryBatchAdjustmentsService; var AdjustmentType; (function (AdjustmentType) { AdjustmentType["ADJUST"] = "ADJUST"; AdjustmentType["SET"] = "SET"; })(AdjustmentType || (AdjustmentType = {})); //# sourceMappingURL=InventoryBatchAdjustmentsService.js.map