"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FinancialSpreadingService = exports.getShiftedMonth = void 0;
const joi_1 = __importDefault(require("joi"));
const mongoose_1 = __importDefault(require("mongoose"));
const dayjs_1 = __importDefault(require("dayjs"));
const lodash_1 = __importDefault(require("lodash"));
const decimal_js_1 = __importDefault(require("decimal.js"));
const db_data_helper_1 = require("../helpers/db-data.helper");
const FinancialSpreading_model_1 = require("../models/FinancialSpreading.model");
const FinancialSpreadingSheet_model_1 = require("../models/FinancialSpreadingSheet.model");
const getShiftedMonth = (month, shift) => {
    const nextMonth = (0, dayjs_1.default)().utcOffset(0).year(month.year).month(month.month - 1).add(shift, 'month');
    return { year: nextMonth.year(), month: nextMonth.month() + 1 };
};
exports.getShiftedMonth = getShiftedMonth;
const rowStyleMap = {
    orangeOnGray: { font: { italic: true, color: { rgb: 'EC5B00' } }, fill: { fgColor: { rgb: 'D3D3D3' } } },
    whiteOnBlack: { font: { color: { rgb: 'FFFFFF' } }, fill: { fgColor: { rgb: '000000' } } },
};
const rowStyles = {
    'GROSS_MARGIN': rowStyleMap.orangeOnGray,
    'OPERATING_MARGIN': rowStyleMap.orangeOnGray,
    'INCOME_FROM_OPERATIONS': rowStyleMap.whiteOnBlack,
    'PRE_TAX_INCOME': rowStyleMap.whiteOnBlack,
    'NET_INCOME': rowStyleMap.whiteOnBlack,
    'TOTAL_NON_CURRENT_ASSETS': rowStyleMap.whiteOnBlack,
    'TOTAL_CURRENT_LIABILITIES': rowStyleMap.whiteOnBlack,
    'LONG_TERM_LIABILITIES': rowStyleMap.whiteOnBlack,
    'TOTAL_CURRENT_ASSET': rowStyleMap.whiteOnBlack,
    'TOTAL_ASSETS': rowStyleMap.whiteOnBlack,
    'TOTAL_LIABILITIES': rowStyleMap.whiteOnBlack,
};
class FinancialSpreadingService {
    uploadsService;
    constructor(uploadsService) {
        this.uploadsService = uploadsService;
    }
    clearData(rawData, rawSheets) {
        const data = (0, db_data_helper_1.rawDataToDTO)(FinancialSpreading_model_1.FinancialSpreadingDTO, rawData);
        const sheets = (0, db_data_helper_1.rawDataToDTO)(FinancialSpreadingSheet_model_1.FinancialSpreadingSheetDTO, rawSheets);
        return { data, sheets };
    }
    async getLastMonth(borrowerId) {
        const lastMonthData = await FinancialSpreading_model_1.FinancialSpreading.aggregate([
            {
                $match: { 'borrowerId': new mongoose_1.default.Types.ObjectId(borrowerId) },
            }, {
                $sort: { 'year': -1, 'month': -1 },
            }, {
                $limit: 1,
            },
        ]);
        if (lastMonthData.length === 0) {
            return { year: 2024, month: 6 };
        }
        return (0, exports.getShiftedMonth)(lastMonthData[0], 1);
    }
    async addNewMonth(params) {
        const newMonth = await this.getLastMonth(params.borrowerId);
        await Promise.all([FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS, FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET].map(async (type) => {
            const allSheets = await this.getCreateAllBorrowerSheet({ ...params, financialSpreadingType: type });
            await Promise.all(allSheets.map(async (sheet) => {
                const modelInstance = new FinancialSpreading_model_1.FinancialSpreading({
                    borrowerId: params.borrowerId,
                    amount: 0,
                    sheetId: sheet._id, ...newMonth,
                });
                await modelInstance.save();
            }));
        }));
        return newMonth;
    }
    async clearMonth(params) {
        const monthAllSpreading = await FinancialSpreading_model_1.FinancialSpreading.find({
            borrowerId: params.borrowerId,
            month: params.selectedMonth.month,
            year: params.selectedMonth.year,
        });
        await Promise.all(monthAllSpreading.map(async (spreading) => {
            spreading.amount = 0;
            await spreading.save();
        }));
        const selectedMonth = { ...params.selectedMonth };
        return selectedMonth;
    }
    async resetAll(params) {
        await FinancialSpreading_model_1.FinancialSpreading.deleteMany({ borrowerId: params.borrowerId });
        await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.deleteMany({ borrowerId: params.borrowerId });
        const selectedMonth = { ...params.selectedMonth };
        return selectedMonth;
    }
    async resetSheets() {
        await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.deleteMany({});
        await FinancialSpreading_model_1.FinancialSpreading.deleteMany({});
    }
    async getFinancialSpreadingData(params, monthDeep = 3) {
        const validationRes = FinancialSpreading_model_1.financialSpreadingParamsValidationSchema.validate(params);
        if (validationRes.error) {
            console.error(validationRes.error);
            return { data: [], sheets: [] };
        }
        const range = Array.from({ length: monthDeep }, (_, i) => i + 1);
        const shifts = range.reduce((acc, curr) => {
            acc[`minus_${curr}`] = -curr;
            return acc;
        }, { 'amount': 0 });
        const rawSheets = await this.getCreateAllBorrowerSheet(params);
        const rawData = await Promise.all(rawSheets.map(async (sheet) => {
            const combinedData = {
                _id: '',
                borrowerId: params.borrowerId,
                sheetId: sheet._id.toString(),
                year: params.selectedMonth.year,
                month: params.selectedMonth.month,
                amount: 0,
                minus_1: 0,
                minus_2: 0,
                minus_3: 0,
            };
            await Promise.all(Object.entries(shifts).map(async ([shiftName, shiftValue]) => {
                const filter = {
                    borrowerId: params.borrowerId,
                    sheetId: sheet._id,
                    ...(0, exports.getShiftedMonth)(params.selectedMonth, shiftValue),
                };
                const foundData = await FinancialSpreading_model_1.FinancialSpreading.findOne(filter).lean();
                if (shiftValue === 0) {
                    combinedData._id = foundData ? foundData._id.toString() : `new_${sheet._id}`;
                }
                combinedData[shiftName] = foundData ? foundData.amount : 0;
            }));
            return combinedData;
        }));
        return this.clearData(rawData, rawSheets);
    }
    async updateFinancialSpreadingData(params, data, sheets) {
        const warnings = [];
        const editableSheets = sheets.filter((sheet) => !sheet.isTotal);
        const editableSheetIds = editableSheets.map((sheet) => sheet._id);
        const sheetsValidationResult = joi_1.default.array().items(FinancialSpreadingSheet_model_1.financialSpreadingSheetViewValidationSchema).validate(editableSheets, { abortEarly: false });
        if (sheetsValidationResult.error) {
            return { success: false, data, sheets, message: 'Invalid sheets' };
        }
        if (params.financialSpreadingType === FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET) {
            const totals = {};
            await Promise.all(editableSheets.map(async (sheet) => {
                const foundData = data.find((dataRow) => dataRow.sheetId === sheet._id);
                if (totals[sheet.rowType]) {
                    totals[sheet.rowType] = new decimal_js_1.default(totals[sheet.rowType]).add(foundData.amount).toNumber();
                }
                else {
                    totals[sheet.rowType] = foundData.amount;
                }
            }));
            const calculatedTotals = this.calculateBS(totals);
            if (calculatedTotals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.CHECK] !== 0) {
                warnings.push('Check must be equal 0');
            }
        }
        const originalSheets = await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.find({
            borrowerId: params.borrowerId,
            isTotal: false,
            financialSpreadingType: params.financialSpreadingType,
        });
        const redundantSheetIdSet = new Set(originalSheets.map((sheet) => sheet._id.toString()));
        const sheetMap = {};
        await Promise.all(editableSheets.map(async (sheet) => {
            if (sheet._id.includes('new_')) {
                const newSheet = await this.createNewSheet(sheet);
                sheetMap[sheet._id] = newSheet._id.toString();
            }
            else {
                redundantSheetIdSet.delete(sheet._id);
                const update = {
                    name: sheet.name,
                    order: sheet.order,
                    suborder: sheet.suborder,
                    rowType: sheet.rowType,
                };
                if ('isFixedCost' in sheet) {
                    update.isFixedCost = sheet.isFixedCost;
                }
                await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet
                    .findByIdAndUpdate(sheet._id, update, { new: true })
                    .lean();
            }
        }));
        const editableData = data.filter((dataRow) => editableSheetIds.includes(dataRow.sheetId));
        const validationResult = joi_1.default.array().items(FinancialSpreading_model_1.financialSpreadingViewValidationSchema).validate(editableData, { abortEarly: false });
        if (validationResult.error) {
            console.error(validationResult.error);
            return { success: false, data, sheets, message: 'Invalid data' };
        }
        await Promise.all(editableData.map(async (item) => {
            const { _id, ...rest } = item;
            const update = {
                amount: rest.amount,
            };
            if (_id.includes('new_')) {
                if (rest.sheetId.includes('new_')) {
                    rest.sheetId = sheetMap[rest.sheetId];
                }
                await FinancialSpreading_model_1.FinancialSpreading
                    .findOneAndUpdate({ borrowerId: rest.borrowerId, year: rest.year, month: rest.month, sheetId: rest.sheetId }, { $set: update }, { new: true, upsert: true });
            }
            else {
                await FinancialSpreading_model_1.FinancialSpreading
                    .findByIdAndUpdate(_id, { $set: update }, { new: true, upsert: true });
            }
        }));
        await this.deleteRedundantSheets(Array.from(redundantSheetIdSet));
        await this.calculateTotals(params);
        const warningsString = warnings.length > 0 ? `Data saved but has errors: ${warnings.join(', ')}` : '';
        return { success: true, message: warningsString };
    }
    async calculateTotals(params) {
        const allSheets = await this.getAllBorrowerSheets(params);
        const totalSheets = allSheets.filter((sheet) => sheet.isTotal);
        const rowSheets = allSheets.filter((sheet) => !sheet.isTotal);
        const totals = {};
        await Promise.all(totalSheets.map(async (totalSheet) => {
            const sumRows = rowSheets.filter((simpleSheet) => simpleSheet.rowType === totalSheet.rowType);
            const data = await FinancialSpreading_model_1.FinancialSpreading.find({
                borrowerId: params.borrowerId,
                year: params.selectedMonth.year,
                month: params.selectedMonth.month,
                sheetId: { $in: sumRows.map((row) => row._id) },
            }).lean();
            const totalAmount = data.reduce((acc, d) => new decimal_js_1.default(acc).add(d.amount).toNumber(), 0);
            await FinancialSpreading_model_1.FinancialSpreading.findOneAndUpdate({
                borrowerId: params.borrowerId,
                year: params.selectedMonth.year,
                month: params.selectedMonth.month,
                sheetId: totalSheet._id,
            }, { amount: totalAmount });
            totals[totalSheet.rowType] = totalAmount;
        }));
        const calculationMap = {
            [FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS]: this.calculatePL,
            [FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET]: this.calculateBS,
        };
        const updatedCalculation = calculationMap[params.financialSpreadingType](totals);
        await Promise.all(totalSheets.map(async (sheet) => {
            await FinancialSpreading_model_1.FinancialSpreading.findOneAndUpdate({
                borrowerId: params.borrowerId,
                year: params.selectedMonth.year,
                month: params.selectedMonth.month,
                sheetId: sheet._id,
            }, { amount: updatedCalculation[sheet.rowType] }, { upsert: true });
        }));
    }
    calculatePL(totals) {
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_PROFIT] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES]).sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.COST_OF_SALES]).toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_MARGIN] = totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES] === 0
            ? 0
            : new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_PROFIT]).div(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES]).mul(100).toDecimalPlaces(2).toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_PROFIT]).sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_EXPENSES]).toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_MARGIN] = totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES] === 0
            ? 0
            : new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS]).div(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES]).toDecimalPlaces(2).toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.PRE_TAX_INCOME] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS])
            .sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NON_OPERATING_EXPENSES])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NON_OPERATING_INCOME])
            .sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.FINANCING_COSTS])
            .sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NET_INCOME] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.PRE_TAX_INCOME]).sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.TAX]).toNumber();
        return totals;
    }
    calculateBS(totals) {
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_ASSET] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.CASH_EQUIVALENTS])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TRADE_RECEIVABLES])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.INVENTORY])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_CURRENT_ASSETS])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_NON_CURRENT_ASSETS] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.FIXED_ASSETS])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_NON_CURRENT_ASSETS])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_ASSETS] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_ASSET])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_NON_CURRENT_ASSETS])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_LIABILITIES] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.ACCOUNTS_PAYABLE])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_CURRENT_LIABILITIES])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.LONG_TERM_LIABILITIES] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_DEBT])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_LONG_TERM_LIABILITIES])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_LIABILITIES] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_LIABILITIES])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.LONG_TERM_LIABILITIES])
            .toNumber();
        const preCheck = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.RETAINED_EARNINGS])
            .add(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.EQUITY_RESERVES])
            .toNumber();
        totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.CHECK] = new decimal_js_1.default(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_ASSETS])
            .sub(totals[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_LIABILITIES])
            .sub(preCheck)
            .toNumber();
        return totals;
    }
    async createNewSheet(sheet) {
        const allDates = await FinancialSpreading_model_1.FinancialSpreading.aggregate([
            { $match: { borrowerId: new mongoose_1.default.Types.ObjectId(sheet.borrowerId) } },
            { $group: { _id: { year: '$year', month: '$month' } } },
        ]);
        const totalSheet = await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.findOne({
            'borrowerId': new mongoose_1.default.Types.ObjectId(sheet.borrowerId),
            'financialSpreadingType': sheet.financialSpreadingType,
            'rowType': sheet.rowType,
            'isTotal': true,
        }).lean();
        const lastSheets = await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.aggregate([
            {
                $match: {
                    'borrowerId': new mongoose_1.default.Types.ObjectId(sheet.borrowerId),
                    'financialSpreadingType': sheet.financialSpreadingType,
                    'rowType': sheet.rowType,
                    'isTotal': false,
                },
            },
        ]);
        const order = totalSheet.order;
        const suborder = lastSheets.length;
        const uniqueDates = allDates.map((r) => r._id);
        const { _id, ...rest } = sheet;
        const newSheet = new FinancialSpreadingSheet_model_1.FinancialSpreadingSheet({ ...rest, order, suborder });
        const newSheetData = await newSheet.save();
        await Promise.all(uniqueDates.map(async (uniqueDate) => {
            const newFinanceSpreading = new FinancialSpreading_model_1.FinancialSpreading({
                sheetId: newSheetData._id,
                borrowerId: sheet.borrowerId,
                amount: 0,
                ...uniqueDate,
            });
            await newFinanceSpreading.save();
        }));
        return newSheetData;
    }
    async deleteRedundantSheets(sheetIds) {
        await FinancialSpreading_model_1.FinancialSpreading.deleteMany({ sheetId: { $in: sheetIds.map((id) => new mongoose_1.default.Types.ObjectId(id)) } });
        await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.deleteMany({ _id: { $in: sheetIds.map((id) => new mongoose_1.default.Types.ObjectId(id)) } });
    }
    async getAllBorrowerSheets(params) {
        const allSheets = await FinancialSpreadingSheet_model_1.FinancialSpreadingSheet.find({
            borrowerId: params.borrowerId,
            financialSpreadingType: params.financialSpreadingType,
        }).lean();
        return lodash_1.default.sortBy(allSheets, ['order', 'isTotal', 'suborder']);
    }
    async getCreateAllBorrowerSheet(params) {
        const allSheets = await this.getAllBorrowerSheets(params);
        if (allSheets.length) {
            return allSheets;
        }
        const totalsMap = {
            [FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS]: FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal,
            [FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET]: FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal,
        };
        await Promise.all(Object.entries(FinancialSpreadingSheet_model_1.financeSpreadingLists[params.financialSpreadingType]).map(async ([totalKey, isCalculated], order) => {
            const newPartialRow = {
                borrowerId: new mongoose_1.default.Types.ObjectId(params.borrowerId),
                financialSpreadingType: params.financialSpreadingType,
                name: FinancialSpreadingSheet_model_1.financialSpreadingTotalDictionary[totalKey],
                rowType: totalsMap[params.financialSpreadingType][totalKey],
                isCalculated,
                order,
                suborder: 0,
            };
            if (!isCalculated) {
                const newRow = {
                    ...newPartialRow,
                    name: FinancialSpreadingSheet_model_1.financialSpreadingTotalDictionary[totalKey].toLowerCase(),
                    isTotal: false,
                };
                const newRowItem = new FinancialSpreadingSheet_model_1.FinancialSpreadingSheet(newRow);
                await newRowItem.save();
            }
            const newTotalRow = {
                ...newPartialRow,
                isTotal: true,
            };
            const newTotalRowItem = new FinancialSpreadingSheet_model_1.FinancialSpreadingSheet(newTotalRow);
            await newTotalRowItem.save();
        }));
        return this.getAllBorrowerSheets(params);
    }
    async getExcelFile(params) {
        const monthDeep = 11;
        const fullData = {
            [FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS]: [],
            [FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET]: [],
        };
        const range = Array.from({ length: monthDeep }, (_, i) => i + 1).reverse();
        const oldMonths = range.reduce((acc, shiftValue) => [...acc, (0, exports.getShiftedMonth)(params.selectedMonth, -shiftValue)], []);
        const monthsHeader = [...oldMonths, params.selectedMonth].map((month) => (0, dayjs_1.default)(new Date(month.year, month.month)).utcOffset(0).format('MM/YYYY'));
        const headers = ['', '', ...monthsHeader];
        const fullHeaders = headers.map((header) => ({
            v: header,
            s: { font: { bold: true } },
        }));
        const buildAggregatedRow = (title, rows) => {
            const oldData = range.map((r) => rows.reduce((sum, row) => new decimal_js_1.default(sum).add(row[`minus_${r}`] ?? 0).toNumber(), 0));
            const current = rows.reduce((sum, row) => new decimal_js_1.default(sum).add(row.amount).toNumber(), 0);
            return [
                { v: '', s: { font: { bold: true } } },
                { v: title, s: { font: { italic: true } } },
                ...oldData.map((v) => ({ v, t: 'n' })),
                { v: current, t: 'n' },
            ];
        };
        await Promise.all([FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS, FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET].map(async (financialSpreadingType) => {
            const financialSpreadingData = await this.getFinancialSpreadingData({ ...params, financialSpreadingType }, monthDeep);
            const sheetsView = financialSpreadingData.sheets;
            const data = financialSpreadingData.data;
            const sheetById = lodash_1.default.keyBy(sheetsView, '_id');
            const dataByRowType = lodash_1.default.groupBy(data, (row) => {
                const sheet = sheetById[row.sheetId];
                return sheet?.rowType;
            });
            const rows = [];
            for (const [rowType, rowsData] of Object.entries(dataByRowType)) {
                // ===== OPERATING EXPENSES (special case)
                if (rowType === FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_EXPENSES) {
                    const fixed = rowsData.filter(r => sheetById[r.sheetId]?.isFixedCost);
                    const nonFixed = rowsData.filter(r => !sheetById[r.sheetId]?.isFixedCost);
                    if (fixed.length) {
                        rows.push(buildAggregatedRow('Fixed operating expenses', fixed));
                    }
                    if (nonFixed.length) {
                        rows.push(buildAggregatedRow('Non-Fixed operating expenses', nonFixed));
                    }
                    continue;
                }
                // ===== DEFAULT ROW RENDERING
                rowsData.forEach((rowData) => {
                    const sheet = sheetById[rowData.sheetId];
                    if (!sheet)
                        return;
                    const oldData = range.map(r => rowData[`minus_${r}`]);
                    const baseRow = [
                        { v: sheet.isTotal ? sheet.name : '', s: { font: { bold: true } } },
                        { v: sheet.isTotal ? '' : sheet.name, s: { font: { italic: true } } },
                        ...oldData.map(v => ({ v, t: 'n' })),
                        { v: rowData.amount, t: 'n' },
                    ];
                    const style = sheet.isTotal ? rowStyles[sheet.rowType] ?? {} : {};
                    rows.push(baseRow.map(cell => ({
                        ...cell,
                        s: lodash_1.default.merge(cell.s, style),
                    })));
                });
            }
            fullData[financialSpreadingType] = [
                fullHeaders,
                ...rows,
            ];
        }));
        return await this.uploadsService.convertDataToFileWithStyleOld({
            'PROFIT LOSS': fullData[FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS],
            'BALANCE SHEET': fullData[FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET],
        }, { width: { 'PROFIT LOSS': { 'A': 5 }, 'BALANCE SHEET': { 'A': 5 } } });
    }
    async uploadData(data, params) {
        await Promise.all(data.map(async (item) => {
            const { _id, ...rest } = item;
            if (item.sheetId.includes('new_')) {
                const sheet = {
                    _id: null,
                    borrowerId: params.borrowerId,
                    financialSpreadingType: params.financialSpreadingType,
                    isCalculated: false,
                    isTotal: false,
                    name: item.title,
                    order: 0,
                    rowType: item.sheetType,
                    suborder: 0,
                };
                const newSheet = await this.createNewSheet(sheet);
                rest.sheetId = newSheet._id.toString();
            }
            await FinancialSpreading_model_1.FinancialSpreading
                .findOneAndUpdate({
                borrowerId: params.borrowerId,
                year: params.selectedMonth.year,
                month: params.selectedMonth.month,
                sheetId: rest.sheetId,
            }, { amount: rest.amount }, { upsert: true });
        }));
        await this.calculateTotals(params);
    }
}
exports.FinancialSpreadingService = FinancialSpreadingService;
