channelape-sdk
Version:
A client for interacting with ChannelApe's API
226 lines • 11.6 kB
JavaScript
;
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