/** * Created by zhang on 2018/1/22. */ let mongoose = require('mongoose'); let projectConst = require('../models/project_consts'); const rationType = projectConst.rationType; const rationKeyArray = projectConst.rationKeyArray; const gljKeyArray = projectConst.gljKeyArray; let quantity_detail = require("./quantity_detail_facade"); let quantity_detail_model = mongoose.model('quantity_detail'); let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade"); let Bills_Lib = mongoose.model('std_bills_lib_bills'); let ration_Model = mongoose.model('ration'); let ration_glj_Model = mongoose.model('ration_glj'); let ration_coe_Model = mongoose.model('ration_coe'); const projectModel = mongoose.model('projects'); let ration_installation_Model = mongoose.model('ration_installation'); let ration_template_Model = mongoose.model('ration_template'); let bill_Model = require('../models/bills').model; let billsLibDao = require("../../bills_lib/models/bills_lib_interfaces"); const pmFacade = require('../../pm/facade/pm_facade'); const { getSortedTreeData, getEngineeringFeeType } = require('../../../public/common_util'); const { billType, constructionFeeNodeID, constructionEquipmentFeeNodeID, BudgetArea, fixedFlag } = require('../../../public/common_constants'); const scMathUtil = require('../../../public/scMathUtil').getUtil(); const uuidV1 = require('uuid/v1'); const equipmentFacade = require('../../equipment_purchase/facade/equipment_purchase_facade'); const gatherModel = mongoose.model('gather_calc_programs'); const GLJController = require("../../glj/controllers/glj_controller"); const GLJListModel = require('../../glj/models/glj_list_model'); let _ = require("lodash"); module.exports={ getSectionInfo : async function (data) { let conditions=[]; let fxList=[]; let sectionInfo ={}; for(let libID in data){ let codes=[]; let temp={}; codes= _.keys(data[libID]); temp['billsLibId']=libID; temp['code'] = {"$in": codes}; conditions.push(temp); } if(conditions.length>0){ fxList = await Bills_Lib.find({"$or":conditions}); } if(fxList.length>0){ let sectionIDs = {}; for(let f of fxList){ if(f._doc.sectionInfo){ f._doc.sectionInfo.first?sectionIDs[f._doc.sectionInfo.first]=true:""; f._doc.sectionInfo.second?sectionIDs[f._doc.sectionInfo.second]=true:""; f._doc.sectionInfo.third?sectionIDs[f._doc.sectionInfo.third]=true:""; } } let IDList = _.keys(sectionIDs); let sectionList = await Bills_Lib.find({'ID':{'$in':IDList}}); let sectionMap = _.mapKeys(sectionList,'ID'); let fxMap = _.mapKeys(fxList,'code'); sectionInfo={ fxMap:fxMap, sectionMap :sectionMap } return sectionInfo; } return null; }, reorganizeFBFX:async function(data){ let result = {}; let tasks = generateBillTasks(data); if(data.delete && data.delete.length > 0){ let qd_query={projectID: data.projectID, billID: {"$in": data.delete}}; await quantity_detail.deleteByQuery(qd_query) ; } if(tasks.length > 0){ result =await bill_Model.bulkWrite(tasks); } return result; }, pasteBlock : async function(data,compilation){ let pasteTasks = [ pasteRationsAndRationGLJ(data.rations,data.ration_gljs,compilation),//这一步会花费比较多时间 pasteOtherData(data) ]; let [rationsAndRationGLJ,resultMap] = await Promise.all(pasteTasks); let gljController = new GLJController(); // let projectGLJ = await gljController.getProjectGLJsByProjectID(data.projectID); resultMap.rations = rationsAndRationGLJ.rations; resultMap.ration_gljs = rationsAndRationGLJ.new_ration_gljs; resultMap.projectGLJList = rationsAndRationGLJ.projectGLJList; // resultMap.gljData = projectGLJ.data; return resultMap; }, createNewBills:async function(newDatas){ // billsLibDao.getStdBillsByCode 从这里取数据 let results = []; for(let bills of newDatas){ let stdB = await billsLibDao.getStdBillsByCode({billsLibId:bills.billsLibId,code:bills.billsLocation}); if(stdB){ stdB = JSON.parse(JSON.stringify(stdB)); if(!bills.unit && /\//.test(stdB.unit)){ bills.unit = stdB.unit.split(/\//)[0]; } //处理项目特征和工作内容,现在默认都添加到项目特征列中 stdB.itemCharacterText = "[项目特征]\n[工作内容]\n"+stdB.jobContentText; stdB.jobContentText=""; bills = _.merge(stdB,bills); } delete bills.billsLocation; results.push(bills); } if(results.length > 0){ await bill_Model.create(results); } return results; }, insertBills: async function(datas) { let bulks = []; for (let data of datas) { if (data.updateType === 'update') { bulks.push({updateOne: {filter: {ID: data.updateData.ID}, update: {NextSiblingID: data.updateData.NextSiblingID}}}); } else { bulks.push({insertOne: {document: data.updateData}}); } } if (bulks.length > 0) { await bill_Model.bulkWrite(bulks); } }, // 获取单位工程ID 工程费用(费用汇总里的totalFee) 映射表 getUnitsBudgetMap: async function (unitIDs) { const rst = {}; const gatherData = await gatherModel.find({ projectID: { $in: unitIDs } }, '-_id projectID totalFee').lean(); const gatherMap = {}; gatherData.forEach(item => gatherMap[item.projectID] = item.totalFee || 0); unitIDs.forEach(unitID => { rst[unitID] = gatherMap[unitID] || 0; }); return rst; }, // 获取设备购置费 getUnitsEquipmentMap: async function (unitIDs) { return await equipmentFacade.getEquipmentTotalCost(unitIDs); }, // 获取概算汇总初始化数据 initialBudgetSummary: async function (constructionID) { const treeData = await this.getBudgetSummary(constructionID); const construction = await projectModel.findOne({ ID: constructionID }, { 'property.costGrowthRate': 1, 'property.growthPeriod': 1 }).lean(); const costGrowthRate = construction && construction.property && construction.property.costGrowthRate || 0; const growthPeriod = construction && construction.property && construction.property.growthPeriod || 0; return { treeData, costGrowthRate, growthPeriod, } }, // 项目管理等需要获取概算汇总项目数据映射 getBudgetSummaryInfo: async function (constructionIDs) { const map = {}; const task = []; for (const constructionID of constructionIDs) { task.push(this.getBudgetSummary(constructionID, true)); } const summaryResult = await Promise.all(task); summaryResult.forEach(items => { items.forEach(item => { if (item.orgProjectID) { map[item.orgProjectID] = item; } }); }); return map; }, // 获取概算汇总数据(拍好序的) // 如果isSimple为true,只进行工程费用部分汇总,只返回工程费用部分数据 getBudgetSummary: async function (constructionID, isSimple = false) { // 获取建设项目清单数据(工程建设其他费用 - 建设项目总概算)部分 let sortedOtherFeeBills = []; if (!isSimple) { const constructionOtherFeeBills = await bill_Model.find({ projectID: constructionID }).lean(); sortedOtherFeeBills = getSortedTreeData('-1', constructionOtherFeeBills); sortedOtherFeeBills.forEach(item => item.area = BudgetArea.CONSTRUCTION_OTHER_FEE); } else { sortedOtherFeeBills = await bill_Model.find({ projectID: constructionID, 'flags.flag': fixedFlag.CONSTRUCTION_BUDGET }).lean(); } // 获取工程费用数据 const constructionFeeBills = await this.getConstructionFeeData(constructionID, (sortedOtherFeeBills[0] && sortedOtherFeeBills[0].ID) || '-1'); // 汇算工程费用 await this.summarizeData(constructionFeeBills); const rst = [...constructionFeeBills, ...sortedOtherFeeBills]; const totalItem = rst.find(item => item.flags && item.flags[0] && item.flags[0].flag === fixedFlag.CONSTRUCTION_BUDGET); let totalFee = 0; if (totalItem) { const totalFeeItem = totalItem.fees && totalItem.fees.find(f => f.fieldName === 'common'); totalFee = totalFeeItem ? +totalFeeItem.totalFee : 0; } // 方便报表取数据,规范数据 rst.forEach(item => { item.code = item.code || ''; item.name = item.name || ''; item.unit = item.unit || ''; item.quantity = item.quantity || ''; const buildingFeeItem = item.fees && item.fees.find(f => f.fieldName === 'building'); const installFeeItem = item.fees && item.fees.find(f => f.fieldName === 'installation'); const equipmentFeeItem = item.fees && item.fees.find(f => f.fieldName === 'equipment'); const otherFeeItem = item.fees && item.fees.find(f => f.fieldName === 'other'); const totalFeeItem = item.fees && item.fees.find(f => f.fieldName === 'common'); item.buildingFee = buildingFeeItem ? buildingFeeItem.totalFee : 0; item.installationFee = installFeeItem ? installFeeItem.totalFee : 0; item.equipmentFee = equipmentFeeItem ? equipmentFeeItem.totalFee : 0; item.otherFee = otherFeeItem ? otherFeeItem.totalFee : 0; item.totalFee = totalFeeItem ? totalFeeItem.totalFee : 0; // 计算占总投资比例 const rate = totalFee ? scMathUtil.roundForObj(item.totalFee / totalFee, 4) : 0; item.rate = rate * 100; // 转换为百分比 }); return rst; }, // 获取工程费用数据,作为概算汇总数据的拼接树数据 getConstructionFeeData: async function (constructionID, nextID) { const projects = await pmFacade.getPosterityProjects([constructionID], true, { _id: 0, ID: 1, ParentID: 1, NextSiblingID: 1, name: 1, projType: 1, 'property.engineeringName': 1}); const construction = projects.find(p => p.ID === constructionID); const items = getSortedTreeData(construction.ParentID, projects); // 转换为uuid const IDMap = {}; items.forEach((item, index) => { //保留项目ID item.orgProjectID = item.ID; if (index == 0) { IDMap[item.ID] = constructionFeeNodeID; } else { IDMap[item.ID] = uuidV1() } }); // 将项目调整为工程费用数据 let curConstructionID; let curSingleNo = 0; let curSingleID; let curUnitNo = 0; // let latestSingleNode; items.forEach((item, index) => { item.area = BudgetArea.CONSTRUCTION_FEE; item.ID = IDMap[item.ID]; item.ParentID = IDMap[item.ParentID] || '-1'; item.NextSiblingID = IDMap[item.NextSiblingID] || '-1'; if (index === 0) { curConstructionID = item.ID; item.type = billType.DXFY; item.flags = [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_FEE }]; item.code = '1'; item.name = '工程费用'; item.NextSiblingID = nextID; } else { item.type = billType.BILL; if (item.ParentID === curConstructionID) { // latestSingleNode = item; curSingleNo += 1; curUnitNo = 0; curSingleID = item.ID; item.code = `1.${curSingleNo}` } else if (item.ParentID === curSingleID) { curUnitNo += 1; item.code = `1.${curSingleNo}.${curUnitNo}` } } }); /* const constructionFeeNode = items[0]; // 设备及工器具购置费 const constructionEquipmentNode = { type: billType.BILL, area: BudgetArea.CONSTRUCTION_FEE, flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_EQUIPMENT_FEE }], ID: constructionEquipmentFeeNodeID, ParentID: constructionFeeNode.ID, NextSiblingID: '-1', code: `1.${curSingleNo + 1}`, name: '设备及工器具购置费', }; latestSingleNode.NextSiblingID = constructionEquipmentNode.ID; items.push(constructionEquipmentNode); */ return items; }, // 汇算数据 summarizeData: async function (items) { const processDecimal = 6; const construction = items.find(item => item.projType === pmFacade.projectType.project); if (!construction) { return; } const singles = items.filter(item => item.projType === pmFacade.projectType.engineering); const units = items.filter(item => item.projType === pmFacade.projectType.tender); if (!singles.length || !units.length) { return; } const unitProjectIDs = units.map(unit => unit.orgProjectID); // 获取第一个单位工程的小数位数(清单合价) const theUnit = await projectModel.findOne({ ID: unitProjectIDs[0] }, { _id: 0, 'property.decimal': 1 }).lean(); const decimal = theUnit && theUnit.property && theUnit.property.decimal && theUnit.property.decimal.bills && theUnit.property.decimal.bills.totalPrice || 2; // 获取单位工程 -工程费用映射表 const unitBudgetMap = await this.getUnitsBudgetMap(unitProjectIDs); // 获取单位工程 - 设备购置费映射表 const unitEquipmentMap = await this.getUnitsEquipmentMap(unitProjectIDs); // 汇算 const constructionFeeObj = { total: 0, building: 0, installation: 0, equipment: 0 }; for (const single of singles) { const singleFeeObj = { total: 0, building: 0, installation: 0, equipment: 0 }; const refUnits = units.filter(unit => unit.ParentID === single.ID); for (const unit of refUnits) { const unitFee = +unitBudgetMap[unit.orgProjectID]; // 费用汇总算出来的值 const unitEquipmentFee = +unitEquipmentMap[unit.orgProjectID]; // 设备购置窗口的值 const unitTotalFee = scMathUtil.roundForObj(unitFee + unitEquipmentFee, decimal); // 费用汇总算出来的值 + 设备购置值 // 汇算到单项工程 singleFeeObj.total = scMathUtil.roundForObj(singleFeeObj.total + unitTotalFee, processDecimal); singleFeeObj.equipment = scMathUtil.roundForObj(singleFeeObj.equipment + unitEquipmentFee, processDecimal); const unitFeeObj = { total: unitTotalFee || 0, building: 0, installation: 0, equipment: unitEquipmentFee || 0 }; // 建筑工程费、安装工程费 const feeType = getEngineeringFeeType(unit.property && unit.property.engineeringName || ''); if (feeType) { unitFeeObj[feeType] = unitFee; singleFeeObj[feeType] = scMathUtil.roundForObj(singleFeeObj[feeType] + unitFee, processDecimal); } unit.fees = feeObj2Fees(unitFeeObj); } single.fees = feeObj2Fees(singleFeeObj); // 汇算到建设项目 constructionFeeObj.total = scMathUtil.roundForObj(constructionFeeObj.total + singleFeeObj.total, processDecimal); constructionFeeObj.building = scMathUtil.roundForObj(constructionFeeObj.building + singleFeeObj.building, processDecimal); constructionFeeObj.installation = scMathUtil.roundForObj(constructionFeeObj.installation + singleFeeObj.installation, processDecimal); constructionFeeObj.equipment = scMathUtil.roundForObj(constructionFeeObj.equipment + singleFeeObj.equipment, processDecimal); } construction.fees = feeObj2Fees(constructionFeeObj); // 获取设备购置费(旧) /* const equipmentFee = await this.getEquipmentFee(unitProjectIDs); const equipmentItem = items.find(item => item.flags && item.flags[0] && item.flags[0].flag === fixedFlag.CONSTRUCTION_EQUIPMENT_FEE); const equipmentFeeObj = { fieldName: 'equipment', totalFee: equipmentFee }; if (equipmentItem) { equipmentItem.fees = [equipmentFeeObj]; } construction.fees.push(equipmentFeeObj); */ function feeObj2Fees(feeObj) { return [ { fieldName: 'common', totalFee: scMathUtil.roundForObj(feeObj.total, decimal) }, { fieldName: 'building', totalFee: scMathUtil.roundForObj(feeObj.building, decimal) }, { fieldName: 'installation', totalFee: scMathUtil.roundForObj(feeObj.installation, decimal) }, { fieldName: 'equipment', totalFee: scMathUtil.roundForObj(feeObj.equipment, decimal) }, ]; } }, bulkOperation: async function (bulkData) { const bulks = []; const projectBulks = []; bulkData.forEach(item => { if (item.type === 'new') { bulks.push({ insertOne: { document: item.data } }); } else if (item.type === 'update') { bulks.push({ updateOne: { filter: { ID: item.data.ID }, update: { $set: item.data } } }); } else if (item.type === 'updateProject') { projectBulks.push({ updateOne: { filter: { ID: item.data.ID }, update: { $set: item.data } } }); } else { bulks.push({ deleteOne: { filter: { ID: item.data.ID } } }); } }); if (bulks.length) { await bill_Model.bulkWrite(bulks); } if (projectBulks.length) { await projectModel.bulkWrite(projectBulks); } } }; async function pasteOtherData(data) { let bills = data.bills; let quantity_details = data.quantity_details; let ration_coes = data.ration_coes; let ration_installations = data.ration_installations; let ration_templates = data.ration_templates; let updateData = data.updateData; let uModel = null; let tasks = []; //生成更新任务 for(let u of updateData){ if(u.type == "bills"){ uModel = bill_Model; }else { uModel = ration_Model; } let tem = { updateOne:{ filter:u.query, update :u.doc } }; tasks.push(tem); } bills.length > 0 ? await insertMany(bills,bill_Model):''; quantity_details.length > 0 ? await insertMany(quantity_details,quantity_detail_model):''; ration_coes.length > 0 ? await insertMany(ration_coes,ration_coe_Model):''; ration_installations.length > 0 ? await insertMany(ration_installations,ration_installation_Model):''; ration_templates.length > 0? await insertMany(ration_templates,ration_template_Model):''; tasks.length>0?await uModel.bulkWrite(tasks):''; return {bills:bills,quantity_details:quantity_details,ration_coes:ration_coes,ration_installations:ration_installations,ration_templates:ration_templates,updateData:updateData} } async function pasteRationsAndRationGLJ (rations,ration_gljs,compilation) { let projectGljModel = new GLJListModel(); let gljMap = {}, new_ration_gljs=[],projectID=null,projectGLJList =[]; if(rations.length > 0) projectID = rations[0].projectID; if(projectID==null && ration_gljs.length > 0) projectID = ration_gljs[0].projectID; if(projectID == null) return {rations:rations,new_ration_gljs:new_ration_gljs}; let [unitFileId,ext] = await ration_glj_facade.prepareExtData(projectID,compilation); //先根据定额类型的工料机,插入到项目工料机中 for(let r of rations){ if(r.type == rationType.gljRation){ let projectGLJ = await getProjectGLJ(r,rationKeyArray,gljMap,unitFileId,ext); projectGLJList.push(projectGLJ); } } for(let rg of ration_gljs){ let projectGLJ = await getProjectGLJ(rg,gljKeyArray,gljMap,unitFileId,ext); let temRecord = ration_glj_facade.createNewRecord(rg); rg.rcode?temRecord.rcode = rg.rcode:''; rg.hasOwnProperty("customQuantity")?temRecord.customQuantity = rg.customQuantity:''; new_ration_gljs.push(temRecord); projectGLJList.push(projectGLJ); } rations.length>0?await insertMany(rations,ration_Model):''; new_ration_gljs.length>0?await insertMany(new_ration_gljs,ration_glj_Model):''; return {rations:rations,new_ration_gljs:new_ration_gljs,projectGLJList:projectGLJList}; async function getProjectGLJ (glj,keyArray,gljMap,unitFileId,ext) { let keyIndex = projectGljModel.getIndex(glj,keyArray); let pgljResult = null; if(gljMap[keyIndex]){ pgljResult = gljMap[keyIndex] }else { let p_glj = ration_glj_facade.getGLJSearchInfo(glj); pgljResult = await projectGljModel.addList(p_glj,unitFileId,ext);//逐条添加到项目工料机 gljMap[keyIndex] = pgljResult; } setResult(glj,pgljResult); return pgljResult } function setResult(glj,result ) { let typeString = result.type+''; glj.marketPrice = result.unit_price.market_price; glj.adjustPrice = result.unit_price.base_price; glj.basePrice = result.unit_price.base_price; glj.isAdd = result.unit_price.is_add; glj.projectGLJID = result.id; if (typeString.startsWith("2")||typeString=='4'||typeString=='5') {//只有材料类型才显示是否暂估 glj.isEstimate = result.is_evaluate; } } } function generateBillTasks(data) { let tasks=[]; let user_id = data.user_id,projectID = data.projectID; let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: user_id}; if(data.delete && data.delete.length > 0){ for(let d_ID of data.delete){ //原先是假删除,现在改成真删除 let task = { deleteOne:{ filter:{ ID:d_ID, projectID:projectID } } }; /* let task={ updateOne:{ filter:{ ID:d_ID, projectID:projectID }, update :{ deleteInfo:deleteInfo } } };*/ tasks.push(task); } } if(data.update && data.update.length > 0){ for(let u_data of data.update){ let task ={ updateOne:{ filter : { ID:u_data.ID, projectID:projectID }, update : u_data.data } }; tasks.push(task); } } if(data.create && data.create.length > 0){ for(let n_data of data.create){ let task = { insertOne :{ document:n_data } }; tasks.push(task); } } return tasks; } async function insertMany(datas,model) { let tem = []; console.log(datas); while (datas.length>1000){//因为mongoose限制了批量插入的条数为1000.所以超出限制后需要分批插入 let newList = datas.splice(0,1000);//一次插入1000条 await model.insertMany(newList); tem = tem.concat(newList); } await model.insertMany(datas); if(tem.length > 0) datas.push(...tem);//还原数组 }