"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findLoadableReleaseGoodByBin = void 0;
const warehouse_base_1 = require("@things-factory/warehouse-base");
const sales_base_1 = require("@things-factory/sales-base");
const worksheet_base_1 = require("@things-factory/worksheet-base");
/**
 * Custom error class for outbound order related errors
 * Covers both ReleaseGood and OrderInventory related errors
 */
class OutboundOrderError extends Error {
    constructor(message, code) {
        super(message);
        this.code = code;
        this.name = 'OutboundOrderError';
    }
}
/**
 * Builds a query to find a release good by order number
 */
const buildfindReleaseGoodByNameQuery = (tx, domainId, orderNo) => {
    return tx
        .getRepository(sales_base_1.ReleaseGood)
        .createQueryBuilder('releaseGood')
        .leftJoinAndSelect('releaseGood.bizplace', 'bizplace')
        .where('releaseGood.domain_id = :domainId', { domainId })
        .andWhere('releaseGood.name = :orderNo', { orderNo })
        .andWhere('releaseGood.status IN (:...status)', {
        status: [sales_base_1.ORDER_STATUS.LOADING, sales_base_1.ORDER_STATUS.PROCESSING, sales_base_1.ORDER_STATUS.PARTIAL_PROCESSING, sales_base_1.ORDER_STATUS.DONE]
    });
};
/**
 * Builds a query to find order inventories
 * @param tx - Entity manager
 * @param domainId - Domain ID
 * @param options - Optional filters: locationId for bin location, releaseGoodId for release good
 */
const buildOrderInventoryQuery = (tx, domainId, options) => {
    const queryBuilder = tx
        .getRepository(sales_base_1.OrderInventory)
        .createQueryBuilder('orderInventory')
        .leftJoinAndSelect('orderInventory.inventory', 'inventory')
        .leftJoinAndSelect('inventory.product', 'product')
        .leftJoinAndSelect('inventory.productDetail', 'productDetail')
        .leftJoinAndSelect('productDetail.productBarcodes', 'productBarcodes')
        .leftJoinAndSelect('productDetail.childProductDetail', 'childProductDetail')
        .leftJoinAndSelect('productDetail.parentProductDetails', 'parentProductDetails')
        .leftJoinAndSelect('orderInventory.binLocation', 'binLocation')
        .innerJoinAndSelect('orderInventory.releaseGood', 'releaseGood')
        .where('orderInventory.domain_id = :domainId', { domainId })
        .andWhere('orderInventory.status IN (:...orderInventoryStatus)', {
        orderInventoryStatus: [
            sales_base_1.ORDER_INVENTORY_STATUS.SORTING,
            sales_base_1.ORDER_INVENTORY_STATUS.LOADING,
            sales_base_1.ORDER_INVENTORY_STATUS.LOADED
        ]
    });
    if (options === null || options === void 0 ? void 0 : options.locationId) {
        queryBuilder.andWhere('orderInventory.bin_location_id = :locationId', { locationId: options.locationId });
    }
    if (options === null || options === void 0 ? void 0 : options.releaseGoodId) {
        queryBuilder.andWhere('orderInventory.release_good_id = :releaseGoodId', { releaseGoodId: options.releaseGoodId });
    }
    return queryBuilder.orderBy('orderInventory.createdAt', 'ASC');
};
/**
 * Transforms order inventory data into the required worksheet format
 * @param orderInventories - Array of order inventories
 * @param releaseGood - Optional release good (when scanning by order number)
 */
const transformToWorksheetFormat = (orderInventories, releaseGood) => {
    var _a;
    if (!orderInventories.length)
        return null;
    // Extract unique bin location names
    const uniqueBinLocations = [...new Set(orderInventories.map(oi => { var _a; return (_a = oi.binLocation) === null || _a === void 0 ? void 0 : _a.name; }).filter(Boolean))];
    // Get release good from first order inventory if not provided
    const firstReleaseGood = releaseGood || ((_a = orderInventories[0]) === null || _a === void 0 ? void 0 : _a.releaseGood);
    return {
        worksheetInfo: {
            worksheet: {
                id: null,
                name: null,
                type: null,
                status: null
            },
            bizplaceName: null,
            startedAt: null,
            releaseGood: firstReleaseGood
                ? {
                    id: firstReleaseGood.id,
                    name: firstReleaseGood.name,
                    attentionCompany: firstReleaseGood.attentionCompany,
                    attentionTo: firstReleaseGood.attentionTo,
                    deliveryAddress1: firstReleaseGood.deliveryAddress1,
                    deliveryAddress2: firstReleaseGood.deliveryAddress2,
                    status: firstReleaseGood.status,
                    refNo: firstReleaseGood.refNo,
                    refNo2: firstReleaseGood.refNo2,
                    refNo3: firstReleaseGood.refNo3,
                    rtm: firstReleaseGood.rtm
                }
                : null,
            binLocationName: uniqueBinLocations.join(', '),
            isReleaseGoodScan: !!releaseGood // Flag to indicate if this result came from scanning by release good
        },
        worksheetDetailInfos: orderInventories.map(oi => {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j;
            return ({
                id: null,
                name: null,
                batchId: oi.batchId,
                releaseQty: oi.releaseQty,
                pickedQty: oi.pickedQty,
                sortedQty: oi.sortedQty,
                status: oi.status,
                packingType: oi.packingType,
                packingSize: oi.packingSize,
                expirationDate: (_a = oi.inventory) === null || _a === void 0 ? void 0 : _a.expirationDate,
                binLocation: {
                    name: (_b = oi.binLocation) === null || _b === void 0 ? void 0 : _b.name
                },
                // Include releaseGood for each item to support filtering by release order in bin scan mode
                releaseGood: oi.releaseGood
                    ? {
                        id: oi.releaseGood.id,
                        name: oi.releaseGood.name,
                        attentionCompany: oi.releaseGood.attentionCompany,
                        attentionTo: oi.releaseGood.attentionTo,
                        deliveryAddress1: oi.releaseGood.deliveryAddress1,
                        deliveryAddress2: oi.releaseGood.deliveryAddress2,
                        status: oi.releaseGood.status,
                        refNo: oi.releaseGood.refNo,
                        refNo2: oi.releaseGood.refNo2,
                        refNo3: oi.releaseGood.refNo3
                    }
                    : null,
                productName: (_d = (_c = oi.inventory) === null || _c === void 0 ? void 0 : _c.product) === null || _d === void 0 ? void 0 : _d.name,
                productSku: (_f = (_e = oi.inventory) === null || _e === void 0 ? void 0 : _e.product) === null || _f === void 0 ? void 0 : _f.sku,
                conversion: (0, worksheet_base_1.computeConversionString)(oi.releaseQty, (_g = oi.inventory) === null || _g === void 0 ? void 0 : _g.productDetail),
                relatedOrderInv: {
                    id: oi.id,
                    batchId: oi.batchId,
                    packingType: oi.packingType,
                    refWorksheetId: oi.refWorksheetId,
                    packedQty: oi.packedQty,
                    releaseQty: oi.releaseQty,
                    product: ((_h = oi.inventory) === null || _h === void 0 ? void 0 : _h.product)
                        ? {
                            id: oi.inventory.product.id,
                            sku: oi.inventory.product.sku,
                            name: oi.inventory.product.name,
                            isInventoryDecimal: oi.inventory.product.isInventoryDecimal
                        }
                        : null,
                    productDetail: ((_j = oi.inventory) === null || _j === void 0 ? void 0 : _j.productDetail)
                        ? {
                            id: oi.inventory.productDetail.id,
                            productBarcodes: (oi.inventory.productDetail.productBarcodes || []).map((pb) => ({ gtin: pb.gtin }))
                        }
                        : null,
                    orderProduct: oi.orderProductId
                        ? {
                            id: oi.orderProductId
                        }
                        : null
                }
            });
        })
    };
};
/**
 * Finds loadable release goods by bin location
 * @param _ - GraphQL resolver parent object
 * @param orderNo - The order number or bin location name to search for
 * @param context - GraphQL context containing domain and transaction manager
 * @returns Promise<Object> - Transformed worksheet data structure
 * @throws OutboundOrderError if order is DONE or if no items found in bin
 */
exports.findLoadableReleaseGoodByBin = {
    async findLoadableReleaseGoodByBin(_, { orderNo }, context) {
        const { domain, tx } = context.state;
        let orderInventoryByBin = [];
        let foundReleaseGood = await buildfindReleaseGoodByNameQuery(tx, domain.id, orderNo).getOne();
        if (foundReleaseGood && foundReleaseGood.status == sales_base_1.ORDER_STATUS.DONE) {
            throw new OutboundOrderError('This order is DONE', 'ORDER_DONE');
        }
        else if (foundReleaseGood) {
            // Found release good by order number - query its order inventories and return worksheet format
            orderInventoryByBin = await buildOrderInventoryQuery(tx, domain.id, {
                releaseGoodId: foundReleaseGood.id
            }).getMany();
            if (orderInventoryByBin.length > 0) {
                // Pass the release good to indicate this is a release good scan
                return transformToWorksheetFormat(orderInventoryByBin, foundReleaseGood);
            }
            else {
                throw new OutboundOrderError('There is no item in this order', 'NO_ITEMS_IN_ORDER');
            }
        }
        else {
            // Try to find by bin location
            const binLocation = await tx.getRepository(warehouse_base_1.Location).findOne({
                where: { domain, name: orderNo }
            });
            if (binLocation) {
                orderInventoryByBin = await buildOrderInventoryQuery(tx, domain.id, { locationId: binLocation.id }).getMany();
            }
            if (orderInventoryByBin.length > 0) {
                return transformToWorksheetFormat(orderInventoryByBin);
            }
            else {
                throw new OutboundOrderError('There is no item in this bin', 'NO_ITEMS_IN_BIN');
            }
        }
    }
};
//# sourceMappingURL=find-loadable-release-good-by-bin.js.map