"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addReleaseGoodProducts = void 0;
const typeorm_1 = require("typeorm");
const product_base_1 = require("@things-factory/product-base");
const sales_base_1 = require("@things-factory/sales-base");
const warehouse_base_1 = require("@things-factory/warehouse-base");
const worksheet_base_1 = require("@things-factory/worksheet-base");
const integration_lmd_1 = require("@things-factory/integration-lmd");
exports.addReleaseGoodProducts = {
    async addReleaseGoodProducts(_, { name, orderInventories, existingOrderInventories }, context) {
        var _a, _b, _c;
        try {
            const { tx, user, domain } = context.state;
            let releaseGood = await tx.getRepository(sales_base_1.ReleaseGood).findOne({
                where: { domain, name: name },
                relations: ['bizplace', 'orderProducts', 'lastMileDelivery']
            });
            const bizplace = releaseGood.bizplace;
            let pickingWorksheet = await tx.getRepository(worksheet_base_1.Worksheet).findOne({
                where: { releaseGood, type: worksheet_base_1.WORKSHEET_TYPE.PICKING }
            });
            let loadingWorksheet = await tx.getRepository(worksheet_base_1.Worksheet).findOne({
                where: { releaseGood, type: worksheet_base_1.WORKSHEET_TYPE.LOADING }
            });
            if (existingOrderInventories) {
                for (let oi of existingOrderInventories) {
                    // map input to OrderInventory Object
                    let curOrderInv = Object.assign({}, oi);
                    curOrderInv.domain = domain;
                    curOrderInv.bizplace = bizplace;
                    curOrderInv.releaseGood = releaseGood;
                    curOrderInv.productDetail = await tx.getRepository(product_base_1.ProductDetail).findOne(oi.productDetail.id);
                    let existingOrderInv;
                    if ((_a = curOrderInv === null || curOrderInv === void 0 ? void 0 : curOrderInv.inventory) === null || _a === void 0 ? void 0 : _a.id) {
                        let foundInv = await tx.getRepository(warehouse_base_1.Inventory).findOne(curOrderInv.inventory.id);
                        curOrderInv.inventory = foundInv;
                        existingOrderInv = await tx.getRepository(sales_base_1.OrderInventory).findOne({
                            where: {
                                releaseGood,
                                productDetail: curOrderInv.productDetail,
                                batchId: curOrderInv.batchId,
                                inventory: foundInv
                            }
                        });
                        if (foundInv && foundInv.lockedQty == 0) {
                            foundInv.qty += Number(existingOrderInv.releaseQty);
                            foundInv.uomValue += Number(existingOrderInv.releaseUomValue);
                            foundInv.lockedQty = Number(existingOrderInv.releaseQty);
                            foundInv.lockedUomValue = Number(existingOrderInv.releaseUomValue);
                        }
                        if (curOrderInv.releaseQty < existingOrderInv.pickedQty) {
                            throw new Error(`${curOrderInv.inventory.palletId} has already picked ${existingOrderInv.pickedQty}`);
                        }
                        if (curOrderInv.releaseQty === 0) {
                            foundInv.lockedQty = Number(foundInv.lockedQty) - Number(existingOrderInv.releaseQty);
                            foundInv.lockedUomValue = Number(foundInv.lockedUomValue) - Number(existingOrderInv.releaseUomValue);
                            await tx.getRepository(warehouse_base_1.Inventory).save(foundInv);
                            if (existingOrderInv) {
                                // if found existing OrderInventory and worksheetDetail then qty, uomValue, and status are updated
                                curOrderInv = Object.assign(Object.assign({}, existingOrderInv), { releaseQty: curOrderInv.releaseQty, releaseUomValue: curOrderInv.releaseUomValue, lockedUomValue: curOrderInv.lockedUomValue, status: sales_base_1.ORDER_INVENTORY_STATUS.CANCELLED });
                                let existingWorksheetDetail = await tx.getRepository(worksheet_base_1.WorksheetDetail).findOne({
                                    where: {
                                        worksheet: pickingWorksheet,
                                        type: worksheet_base_1.WORKSHEET_TYPE.PICKING,
                                        targetInventory: curOrderInv
                                    }
                                });
                                if (existingWorksheetDetail) {
                                    existingWorksheetDetail = Object.assign(Object.assign({}, existingWorksheetDetail), { status: worksheet_base_1.WORKSHEET_STATUS.CANCELLED });
                                    await tx.getRepository(worksheet_base_1.WorksheetDetail).save(existingWorksheetDetail);
                                }
                                await tx.getRepository(sales_base_1.OrderInventory).save(curOrderInv);
                            }
                        }
                        else if (curOrderInv.releaseQty == existingOrderInv.pickedQty) {
                            curOrderInv = Object.assign(Object.assign({}, existingOrderInv), { releaseQty: curOrderInv.releaseQty, releaseUomValue: curOrderInv.releaseUomValue, lockedUomValue: curOrderInv.lockedUomValue, status: sales_base_1.ORDER_INVENTORY_STATUS.PICKED });
                            let existingWorksheetDetail = await tx.getRepository(worksheet_base_1.WorksheetDetail).findOne({
                                where: {
                                    targetInventory: curOrderInv
                                }
                            });
                            existingWorksheetDetail = Object.assign(Object.assign({}, existingWorksheetDetail), { status: worksheet_base_1.WORKSHEET_STATUS.PICKED });
                            await tx.getRepository(worksheet_base_1.WorksheetDetail).save(existingWorksheetDetail);
                            await tx.getRepository(sales_base_1.OrderInventory).save(curOrderInv);
                        }
                        else {
                            const qtyDelta = Number(curOrderInv.releaseQty) - Number(existingOrderInv.releaseQty);
                            const uomDelta = Number(curOrderInv.releaseUomValue) - Number(existingOrderInv.releaseUomValue);
                            const updateResult = await tx
                                .getRepository(warehouse_base_1.Inventory)
                                .createQueryBuilder()
                                .update(warehouse_base_1.Inventory)
                                .set({
                                lockedQty: () => `GREATEST(COALESCE("locked_qty", 0) + :qtyDelta::numeric, 0)`,
                                lockedUomValue: () => `GREATEST(COALESCE("locked_uom_value", 0) + :uomDelta::numeric, 0)`,
                                updater: user
                            })
                                .setParameter('qtyDelta', qtyDelta)
                                .setParameter('uomDelta', uomDelta)
                                .where(qtyDelta > 0
                                ? 'id = :id AND qty >= COALESCE(locked_qty, 0) + :newQty'
                                : 'id = :id', Object.assign({ id: foundInv.id }, (qtyDelta > 0 ? { newQty: qtyDelta } : {})))
                                .execute();
                            if (updateResult.affected === 0) {
                                throw new Error(`Insufficient inventory for pallet ${foundInv.palletId}`);
                            }
                            foundInv = await tx.getRepository(warehouse_base_1.Inventory).findOne(foundInv.id);
                            if (existingOrderInv) {
                                // if found existing OrderInventory and worksheetDetail then qty, uomValue, and status are updated
                                curOrderInv = Object.assign(Object.assign({}, existingOrderInv), { releaseQty: curOrderInv.releaseQty, releaseUomValue: curOrderInv.releaseUomValue, status: pickingWorksheet ? sales_base_1.ORDER_INVENTORY_STATUS.PICKING : sales_base_1.ORDER_INVENTORY_STATUS.PENDING });
                                let existingWorksheetDetail = await tx.getRepository(worksheet_base_1.WorksheetDetail).findOne({
                                    where: {
                                        worksheet: pickingWorksheet,
                                        type: worksheet_base_1.WORKSHEET_TYPE.PICKING,
                                        targetInventory: existingOrderInv
                                    }
                                });
                                if (existingWorksheetDetail) {
                                    if (curOrderInv.releaseQty != existingOrderInv.releaseQty) {
                                        existingWorksheetDetail = Object.assign(Object.assign({}, existingWorksheetDetail), { status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED });
                                        await tx.getRepository(worksheet_base_1.WorksheetDetail).save(existingWorksheetDetail);
                                    }
                                }
                                await tx.getRepository(sales_base_1.OrderInventory).save(curOrderInv);
                            }
                        }
                        if (pickingWorksheet) {
                            // if has picking worksheet, status will be deactivated to prevent any action from warehouse operator until office admin has confirmation
                            pickingWorksheet = Object.assign(Object.assign({}, pickingWorksheet), { status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED });
                            await tx.getRepository(worksheet_base_1.Worksheet).save(pickingWorksheet);
                        }
                        if (loadingWorksheet) {
                            // if has loading worksheet, status will be deactivated to prevent any action from warehouse operator until office admin has confirmation
                            loadingWorksheet = Object.assign(Object.assign({}, loadingWorksheet), { status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED });
                            await tx.getRepository(worksheet_base_1.Worksheet).save(loadingWorksheet);
                        }
                    }
                }
            }
            if (orderInventories.length > 0) {
                for (let oi of orderInventories) {
                    // map input to OrderInventory Object
                    let newOrderInv = Object.assign({}, oi);
                    newOrderInv.domain = domain;
                    newOrderInv.bizplace = bizplace;
                    newOrderInv.batchId = oi.batchId;
                    newOrderInv.packingType = oi.packingType;
                    newOrderInv.status = pickingWorksheet ? sales_base_1.ORDER_INVENTORY_STATUS.PICKING : sales_base_1.ORDER_INVENTORY_STATUS.PENDING;
                    newOrderInv.name = sales_base_1.OrderNoGenerator.orderInventory();
                    newOrderInv.releaseGood = releaseGood;
                    newOrderInv.creator = user;
                    newOrderInv.updater = user;
                    let existingOrderInv;
                    let existingOrderProduct = await tx.getRepository(sales_base_1.OrderProduct).findOne({
                        where: {
                            domain,
                            releaseGood,
                            batchId: oi.batchId,
                            productDetail: newOrderInv.productDetail
                        }
                    });
                    // check if it is release by inventory (has inventory value) or product
                    if ((_b = newOrderInv.inventory) === null || _b === void 0 ? void 0 : _b.id) {
                        // if release by inventory, then quantity and uomValue values are updated
                        let foundInv = await tx
                            .getRepository(warehouse_base_1.Inventory)
                            .findOne({ where: { id: newOrderInv.inventory.id }, relations: ['productDetail'] });
                        newOrderInv.inventory = foundInv;
                        newOrderInv.uom = (oi === null || oi === void 0 ? void 0 : oi.uom) || foundInv.uom;
                        newOrderInv.batchIdRef = (oi === null || oi === void 0 ? void 0 : oi.batchIdRef) || foundInv.batchIdRef;
                        if (foundInv.productDetail) {
                            newOrderInv.uomValue = foundInv.productDetail.uomValue;
                            delete foundInv.productDetail;
                        }
                        // check for existing released OrderInventory specifying product, batchId, packingType, and inventory
                        existingOrderInv = await tx.getRepository(sales_base_1.OrderInventory).findOne({
                            where: {
                                releaseGood,
                                productDetail: newOrderInv.productDetail,
                                batchId: newOrderInv.batchId,
                                inventory: foundInv
                            }
                        });
                        const lockResult = await tx
                            .getRepository(warehouse_base_1.Inventory)
                            .createQueryBuilder()
                            .update(warehouse_base_1.Inventory)
                            .set({
                            lockedQty: () => `COALESCE("locked_qty", 0) + :releaseQty::numeric`,
                            lockedUomValue: () => `COALESCE("locked_uom_value", 0) + :releaseUomValue::numeric`,
                            updater: user
                        })
                            .setParameter('releaseQty', newOrderInv.releaseQty)
                            .setParameter('releaseUomValue', newOrderInv.releaseUomValue)
                            .where('id = :id AND qty >= COALESCE(locked_qty, 0) + :newQty', {
                            id: foundInv.id,
                            newQty: newOrderInv.releaseQty
                        })
                            .execute();
                        if (lockResult.affected === 0) {
                            throw new Error(`Insufficient inventory for pallet ${foundInv.palletId}`);
                        }
                        // Re-read foundInv for downstream use
                        foundInv = await tx.getRepository(warehouse_base_1.Inventory).findOne(foundInv.id);
                        let newOrderProduct = Object.assign(new sales_base_1.OrderProduct(), newOrderInv);
                        newOrderProduct = Object.assign(Object.assign({}, newOrderProduct), { packQty: 0, actualPackQty: 0, palletQty: 0, actualPalletQty: 0, status: sales_base_1.ORDER_PRODUCT_STATUS.ASSIGNED });
                        if (!existingOrderProduct)
                            newOrderProduct = await tx.getRepository(sales_base_1.OrderProduct).save(newOrderProduct);
                        newOrderInv.orderProduct = existingOrderProduct ? existingOrderProduct : newOrderProduct;
                    }
                    else {
                        // check for existing released OrderInventory specifying product, batchId and packingType
                        existingOrderInv = await tx.getRepository(sales_base_1.OrderInventory).findOne({
                            where: {
                                releaseGood,
                                productDetail: newOrderInv.productDetail,
                                batchId: newOrderInv.batchId
                            }
                        });
                    }
                    if (existingOrderInv) {
                        // if found existing OrderInventory and worksheetDetail then qty, uomValue, and status are updated
                        newOrderInv = Object.assign(Object.assign({}, existingOrderInv), { releaseQty: existingOrderInv.status === sales_base_1.ORDER_INVENTORY_STATUS.REPLACED
                                ? newOrderInv.releaseQty
                                : existingOrderInv.releaseQty + newOrderInv.releaseQty, releaseUomValue: sales_base_1.ORDER_INVENTORY_STATUS.REPLACED
                                ? newOrderInv.releaseUomValue
                                : existingOrderInv.releaseUomValue + newOrderInv.releaseUomValue, status: existingOrderInv.status === sales_base_1.ORDER_INVENTORY_STATUS.CANCELLED
                                ? pickingWorksheet
                                    ? sales_base_1.ORDER_INVENTORY_STATUS.PICKING
                                    : sales_base_1.ORDER_INVENTORY_STATUS.PENDING
                                : existingOrderInv.status });
                        let existingWorksheetDetail = await tx.getRepository(worksheet_base_1.WorksheetDetail).findOne({
                            where: {
                                worksheet: pickingWorksheet,
                                type: worksheet_base_1.WORKSHEET_TYPE.PICKING,
                                targetInventory: existingOrderInv
                            }
                        });
                        if (existingWorksheetDetail) {
                            existingWorksheetDetail = Object.assign(Object.assign({}, existingWorksheetDetail), { targetProduct: newOrderInv.orderProduct, status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED });
                            await tx.getRepository(worksheet_base_1.WorksheetDetail).save(existingWorksheetDetail);
                        }
                    }
                    let mappedOrderInv = data => {
                        let mapOrdInv = Object.assign(new sales_base_1.OrderInventory(), {
                            name: data.name,
                            type: data.type,
                            releaseQty: data.releaseQty,
                            releaseUomValue: data.releaseUomValue,
                            batchId: data.batchId,
                            batchIdRef: data.batchIdRef,
                            packingType: data.packingType,
                            uom: data.uom,
                            product: data.product,
                            productDetail: data.productDetail,
                            inventory: data.inventory,
                            status: data.status,
                            releaseGood: data.releaseGood,
                            orderProduct: data.orderProduct,
                            bizplace: data.bizplace,
                            domain: domain,
                            creator: user,
                            updater: user
                        });
                        return mapOrdInv;
                    };
                    if (releaseGood.status === 'PENDING') {
                        let savedOrderInv = mappedOrderInv(newOrderInv);
                        savedOrderInv = await tx.getRepository(sales_base_1.OrderInventory).save(savedOrderInv);
                    }
                    if (pickingWorksheet) {
                        let savedOrderInv = mappedOrderInv(newOrderInv);
                        if (newOrderInv === null || newOrderInv === void 0 ? void 0 : newOrderInv.id) {
                            // newOrderInv will have id if it was updated from existingOrderInv
                            // this solution will remove ix_order-inventory_0 unique error
                            // ** please revise this resolver in future. looks like there's few
                            //    things that can refactor
                            savedOrderInv.id = newOrderInv.id;
                        }
                        savedOrderInv = await tx.getRepository(sales_base_1.OrderInventory).save(savedOrderInv);
                        if (!existingOrderInv && pickingWorksheet) {
                            // if this is a new orderInventory and has existing worksheet then generate a new worksheet detail for it
                            await (0, worksheet_base_1.generatePickingWorksheetDetail)(tx, domain, user, pickingWorksheet, savedOrderInv);
                        }
                        // if has picking worksheet, status will be deactivated to prevent any action from warehouse operator until office admin has confirmation
                        await tx.getRepository(worksheet_base_1.Worksheet).update(pickingWorksheet.id, { status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED });
                    }
                }
            }
            if (loadingWorksheet) {
                // if has loading worksheet, status will be deactivated to prevent any action from warehouse operator until office admin has confirmation
                loadingWorksheet = Object.assign(Object.assign({}, loadingWorksheet), { status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED });
                await tx.getRepository(worksheet_base_1.Worksheet).save(loadingWorksheet);
            }
            // Update Order Product Release Qty
            let rgOrderProducts = await tx
                .getRepository(sales_base_1.OrderProduct)
                .find({ where: { domain, releaseGood } });
            await Promise.all(rgOrderProducts.map(async (op) => {
                const orderInventories = await tx.getRepository(sales_base_1.OrderInventory).find({
                    where: {
                        domain,
                        releaseGood,
                        orderProduct: op,
                        status: (0, typeorm_1.Not)((0, typeorm_1.In)([
                            sales_base_1.ORDER_INVENTORY_STATUS.TERMINATED,
                            sales_base_1.ORDER_INVENTORY_STATUS.CANCELLED,
                            sales_base_1.ORDER_INVENTORY_STATUS.PENDING_CANCEL
                        ]))
                    }
                });
                let totalReleaseQty = orderInventories.reduce((total, currentValue) => {
                    total += currentValue.releaseQty;
                    return total;
                }, 0);
                let totalReleaseUomValue = orderInventories.reduce((total, currentValue) => {
                    total += currentValue.releaseUomValue;
                    return total;
                }, 0);
                let updateOPInfo = {
                    releaseQty: totalReleaseQty,
                    releaseUomValue: totalReleaseUomValue
                };
                if (totalReleaseQty <= 0) {
                    updateOPInfo.status = sales_base_1.ORDER_PRODUCT_STATUS.TERMINATED;
                }
                await tx.getRepository(sales_base_1.OrderProduct).update({ id: op.id }, updateOPInfo);
            }));
            if ((releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.lastMileDelivery) && ((_c = releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.lastMileDelivery) === null || _c === void 0 ? void 0 : _c.isTms)) {
                // if releaseGood has last mile delivery, then update the last mile delivery with new order inventories
                const tmsService = (0, integration_lmd_1.getTmsService)(releaseGood.lastMileDelivery.platform);
                tmsService.updateShipment(releaseGood);
            }
        }
        catch (error) {
            throw error;
        }
    }
};
//# sourceMappingURL=add-release-good-products.js.map