UNPKG

@1amageek/tradable

Version:

Cloud Firestore model framework for TypeScript - Google

383 lines 21.3 kB
"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