@1amageek/tradable
Version:
Cloud Firestore model framework for TypeScript - Google
383 lines • 21.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StockManager = exports.StockTransaction = void 0;
const FirebaseFirestore = require("@google-cloud/firestore");
const index_1 = require("./index");
class StockTransaction {
constructor(user, inventoryStock, sku, tradeTransaction) {
this.inventoryStocks = [];
this._User = user;
this._InventoryStock = inventoryStock;
this._SKU = sku;
this._TradeTransaction = tradeTransaction;
}
async commit() {
if (this.commitBlock) {
return this.commitBlock();
}
return [];
}
}
exports.StockTransaction = StockTransaction;
class StockManager {
constructor(user, inventoryStock, sku, tradeTransaction) {
this._User = user;
this._InventoryStock = inventoryStock;
this._SKU = sku;
this._TradeTransaction = tradeTransaction;
}
async reserve(order, orderItem, transaction) {
const orderID = order.id;
const skuID = orderItem.sku;
if (skuID) {
const sku = await new this._SKU(skuID, {}).fetch();
if (!sku) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] Invalid order ORDER/${orderID}. invalid SKU: ${skuID}`);
}
if (!sku.isAvailabled) {
throw new index_1.TradableError(index_1.TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is not availabled`);
}
this.delegate.reserve(order, orderItem, transaction);
}
}
async trade(order, transaction) {
const orderItems = order.items.objects();
const tasks = [];
for (const orderItem of orderItems) {
const skuID = orderItem.sku;
if (orderItem.type === index_1.OrderItemType.sku) {
if (!skuID) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] Invalid order ORDER/${order.id}, This order item is sku required.`);
}
const task = this._trade(order, orderItem, transaction);
tasks.push(task);
}
}
return await Promise.all(tasks);
}
async _trade(order, orderItem, transaction) {
const quantity = orderItem.quantity;
const orderID = order.id;
const skuID = orderItem.sku;
const sku = await new this._SKU(skuID, {}).fetch(transaction);
if (!sku) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] Invalid order ORDER/${orderID}. invalid SKU: ${skuID}`);
}
if (!sku.isAvailabled) {
throw new index_1.TradableError(index_1.TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is not availabled`);
}
const stockValue = sku.inventory.value;
const stockType = sku.inventory.type;
if (!stockType) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] ORDER/${orderID}. SKU: ${skuID}. Invalid StockType.`);
}
const stockTransaction = new StockTransaction(this._User, this._InventoryStock, this._SKU, this._TradeTransaction);
if (stockType === index_1.StockType.finite) {
const numberOfShardsLimit = sku.numberOfFetch * quantity;
const query = await sku.inventoryStocks.reference.where("isAvailabled", "==", true).limit(numberOfShardsLimit).get();
const inventoryStocks = query.docs.map((snapshot) => {
const stock = new this._InventoryStock(snapshot.id, {}).setData(snapshot.data());
stock.setParent(sku.inventoryStocks);
return stock;
});
if (inventoryStocks.length < quantity) {
throw new index_1.TradableError(index_1.TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is out of stock. stocks count ${inventoryStocks.length}`);
}
let tasks = [];
let stockIDs = inventoryStocks.map(stock => { return stock.id; });
for (let i = 0; i < quantity; i++) {
const numberOfShards = stockIDs.length;
if (numberOfShards > 0) {
const shardID = Math.floor(Math.random() * numberOfShards);
const inventoryStockID = stockIDs[shardID];
stockIDs.splice(shardID, 1);
const task = async () => {
return await sku.inventoryStocks.doc(inventoryStockID, this._InventoryStock).fetch(transaction);
};
tasks.push(task());
}
else {
throw new index_1.TradableError(index_1.TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is out of stock`);
}
}
const result = await Promise.all(tasks);
stockTransaction.inventoryStocks = result;
}
const purchasedBy = orderItem.purchasedBy;
const selledBy = orderItem.selledBy;
const seller = new this._User(selledBy, {});
const purchaser = new this._User(purchasedBy, {});
stockTransaction.commitBlock = () => {
let tradeTransactions = [];
for (let i = 0; i < quantity; i++) {
const tradeTransaction = new this._TradeTransaction();
tradeTransaction.type = index_1.TradeTransactionType.order;
tradeTransaction.selledBy = selledBy;
tradeTransaction.purchasedBy = purchasedBy;
tradeTransaction.order = orderID;
tradeTransaction.product = orderItem.product;
tradeTransaction.sku = skuID;
switch (stockType) {
case index_1.StockType.finite: {
const inventoryStock = stockTransaction.inventoryStocks[i];
if (inventoryStock.isAvailabled) {
const item = this.delegate.createItem(order, orderItem, inventoryStock.id, transaction);
tradeTransaction.item = item;
tradeTransaction.inventoryStock = inventoryStock.id;
transaction.set(inventoryStock.reference, {
"isAvailabled": false,
"item": item,
"order": orderID
}, { merge: true });
}
else {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidShard, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} InventoryStock/${inventoryStock.id} InventoryStock is not availabled`);
}
break;
}
case index_1.StockType.infinite: {
const item = this.delegate.createItem(order, orderItem, undefined, transaction);
tradeTransaction.item = item;
break;
}
case index_1.StockType.bucket: {
if (!stockValue) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] ORDER/${orderID}. SKU: ${skuID}. Invalid StockValue.`);
}
if (stockValue !== index_1.StockValue.outOfStock) {
const item = this.delegate.createItem(order, orderItem, undefined, transaction);
tradeTransaction.item = item;
}
else {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidShard, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} StockValue is out of stock.`);
}
break;
}
}
transaction.set(tradeTransaction.reference, tradeTransaction.value(), { merge: true });
transaction.set(seller.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true });
transaction.set(purchaser.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true });
tradeTransactions.push(tradeTransaction);
}
return tradeTransactions;
};
return stockTransaction;
}
// async trade(tradeInformation: TradeInformation, orderItem: OrderItem, transaction: FirebaseFirestore.Transaction) {
// const quantity: number = orderItem.quantity
// const numberOfShards: number = (tradeInformation.numberOfShards || 5) * quantity
// const orderID: string = tradeInformation.order
// const skuID: string = tradeInformation.sku
// const sku: SKU = new this._SKU(skuID, {})
// const query = sku.inventoryStocks.reference.where("isAvailabled", "==", true).limit(numberOfShards)
// const fetchResult = await Promise.all([
// sku.fetch(transaction),
// query.get()
// ])
// if (!sku) {
// throw new TradableError(TradableErrorCode.invalidArgument, `[Manager] Invalid order ORDER/${orderID}. invalid SKU: ${skuID}`)
// }
// if (!sku.isAvailabled) {
// throw new TradableError(TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is not availabled`)
// }
// const stockType = sku.inventory.type
// const stockTransaction: StockTransaction<
// Order,
// OrderItem,
// User,
// InventoryStock,
// SKU,
// TradeTransaction
// > = new StockTransaction(
// this._User,
// this._InventoryStock,
// this._SKU,
// this._TradeTransaction
// )
// const inventoryStocks = fetchResult[1].docs.map((snapshot) => {
// const stock: InventoryStock = new this._InventoryStock(snapshot.id, {}).setData(snapshot.data())
// stock.setParent(sku.inventoryStocks)
// return stock
// })
// if (stockType === StockType.finite) {
// if (inventoryStocks.length < quantity) {
// throw new TradableError(TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is out of stock`)
// }
// let tasks = []
// let stockIDs = inventoryStocks.map(stock => { return stock.id })
// for (let i = 0; i < quantity; i++) {
// const numberOfShards = stockIDs.length
// if (numberOfShards > 0) {
// const shardID = Math.floor(Math.random() * numberOfShards)
// const inventoryStockID = stockIDs[shardID]
// stockIDs.splice(shardID, 1)
// const task = async () => {
// return await sku.inventoryStocks.doc(inventoryStockID, this._InventoryStock).fetch(transaction)
// }
// tasks.push(task())
// } else {
// throw new TradableError(TradableErrorCode.outOfStock, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} SKU is out of stock`)
// }
// }
// const result = await Promise.all(tasks)
// stockTransaction.inventoryStocks = result
// }
// const purchasedBy: string = tradeInformation.purchasedBy
// const selledBy: string | undefined = tradeInformation.selledBy
// const seller: User = new this._User(selledBy, {})
// const purchaser: User = new this._User(purchasedBy, {})
// const stockValue: StockValue | undefined = sku.inventory.value
// if (!stockType) {
// throw new TradableError(TradableErrorCode.invalidArgument, `[Manager] ORDER/${orderID}. SKU: ${skuID}. Invalid StockType.`)
// }
// stockTransaction.commitBlock = () => {
// let tradeTransactions = []
// for (let i = 0; i < quantity; i++) {
// const tradeTransaction: TradeTransaction = new this._TradeTransaction()
// tradeTransaction.type = TradeTransactionType.order
// tradeTransaction.selledBy = selledBy
// tradeTransaction.purchasedBy = purchasedBy
// tradeTransaction.order = orderID
// tradeTransaction.product = tradeInformation.product
// tradeTransaction.sku = skuID
// switch (stockType) {
// case StockType.finite: {
// const inventoryStock = stockTransaction.inventoryStocks[i]
// if (inventoryStock.isAvailabled) {
// const item = this.delegate.createItem(tradeInformation, orderItem, inventoryStock.id, transaction)
// tradeTransaction.item = item
// tradeTransaction.inventoryStock = inventoryStock.id
// transaction.set(inventoryStock.reference, {
// "isAvailabled": false,
// "item": item,
// "order": orderID
// }, { merge: true })
// } else {
// throw new TradableError(TradableErrorCode.invalidShard, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} InventoryStock/${inventoryStock.id} InventoryStock is not availabled`)
// }
// break
// }
// case StockType.infinite: {
// const item = this.delegate.createItem(tradeInformation, orderItem, undefined, transaction)
// tradeTransaction.item = item
// break
// }
// case StockType.bucket: {
// if (!stockValue) {
// throw new TradableError(TradableErrorCode.invalidArgument, `[Manager] ORDER/${orderID}. SKU: ${skuID}. Invalid StockValue.`)
// }
// if (stockValue !== StockValue.outOfStock) {
// const item = this.delegate.createItem(tradeInformation, orderItem, undefined, transaction)
// tradeTransaction.item = item
// } else {
// throw new TradableError(TradableErrorCode.invalidShard, `[Manager] Invalid order ORDER/${orderID}. SKU/${skuID} StockValue is out of stock.`)
// }
// break
// }
// }
// transaction.set(tradeTransaction.reference, tradeTransaction.value(), { merge: true })
// transaction.set(seller.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true })
// transaction.set(purchaser.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true })
// tradeTransactions.push(tradeTransaction)
// }
// return tradeTransactions
// }
// return stockTransaction
// }
async cancel(order, orderItem, transaction) {
const orderID = order.id;
const skuID = orderItem.sku;
const purchasedBy = order.purchasedBy;
const selledBy = orderItem.selledBy;
const seller = new this._User(selledBy, {});
const purchaser = new this._User(purchasedBy, {});
const sku = new this._SKU(skuID, {});
const result = await Promise.all([sku.fetch(transaction), this.delegate.getItems(order, orderItem, transaction)]);
if (!sku) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] Invalid order ORDER/${orderID}. invalid SKU: ${skuID}`);
}
const items = result[1].docs;
const stockType = sku.inventory.type;
const stockTransaction = new StockTransaction(this._User, this._InventoryStock, this._SKU, this._TradeTransaction);
stockTransaction.commitBlock = () => {
let tradeTransactions = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const stockID = item.data()["inventoryStock"];
const tradeTransaction = new this._TradeTransaction();
tradeTransaction.type = index_1.TradeTransactionType.orderCancel;
tradeTransaction.selledBy = selledBy;
tradeTransaction.purchasedBy = purchasedBy;
tradeTransaction.order = orderID;
tradeTransaction.product = orderItem.product;
tradeTransaction.sku = skuID;
tradeTransaction.item = item.ref;
tradeTransaction.inventoryStock = stockID;
this.delegate.cancelItem(order, orderItem, item.ref, transaction);
if (stockType === index_1.StockType.finite) {
let inventoryStock = new this._InventoryStock(stockID);
inventoryStock.setParent(sku.inventoryStocks);
transaction.set(inventoryStock.reference, {
"isAvailabled": true,
"item": FirebaseFirestore.FieldValue.delete(),
"order": FirebaseFirestore.FieldValue.delete()
}, { merge: true });
}
transaction.set(tradeTransaction.reference, tradeTransaction.value(), { merge: true });
transaction.set(seller.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true });
transaction.set(purchaser.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true });
tradeTransactions.push(tradeTransaction);
}
return tradeTransactions;
};
return stockTransaction;
}
async itemCancel(order, orderItem, itemRef, transaction) {
const orderID = order.id;
const skuID = orderItem.sku;
const purchasedBy = order.purchasedBy;
const selledBy = order.selledBy;
const seller = new this._User(selledBy, {});
const purchaser = new this._User(purchasedBy, {});
const sku = await new this._SKU(skuID, {});
const inventoryStockQuery = sku.inventoryStocks.reference.where("item", "==", itemRef).limit(1);
const snapshot = await transaction.get(inventoryStockQuery);
const inventoryStocks = snapshot.docs;
if (!sku) {
throw new index_1.TradableError(index_1.TradableErrorCode.invalidArgument, `[Manager] Invalid order ORDER/${orderID}. invalid SKU: ${skuID}`);
}
const stockType = sku.inventory.type;
const stockTransaction = new StockTransaction(this._User, this._InventoryStock, this._SKU, this._TradeTransaction);
stockTransaction.commitBlock = () => {
let tradeTransactions = [];
const tradeTransaction = new this._TradeTransaction();
tradeTransaction.type = index_1.TradeTransactionType.orderChange;
tradeTransaction.selledBy = selledBy;
tradeTransaction.purchasedBy = purchasedBy;
tradeTransaction.order = orderID;
tradeTransaction.product = orderItem.product;
tradeTransaction.sku = skuID;
tradeTransaction.item = itemRef;
this.delegate.cancelItem(order, orderItem, itemRef, transaction);
if (stockType === index_1.StockType.finite) {
const stockID = inventoryStocks[0].id;
tradeTransaction.inventoryStock = stockID;
let inventoryStock = new this._InventoryStock(stockID);
inventoryStock.setParent(sku.inventoryStocks);
transaction.set(inventoryStock.reference, {
"isAvailabled": true,
"item": FirebaseFirestore.FieldValue.delete(),
"order": FirebaseFirestore.FieldValue.delete()
}, { merge: true });
}
transaction.set(tradeTransaction.reference, tradeTransaction.value(), { merge: true });
transaction.set(seller.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true });
transaction.set(purchaser.tradeTransactions.reference.doc(tradeTransaction.id), tradeTransaction.value(), { merge: true });
tradeTransactions.push(tradeTransaction);
return tradeTransactions;
};
return stockTransaction;
}
}
exports.StockManager = StockManager;
//# sourceMappingURL=stockManager.js.map