/** * Created by zhang on 2018/1/26. */ module.exports = { markUpdateProject:markUpdateProject, removeProjectMark:removeProjectMark, updateNodes:updateNodes, calcInstallationFee:calcInstallationFee, saveProperty: saveProperty, getDefaultColSetting: getDefaultColSetting, markProjectsToChange:markProjectsToChange, getBudgetSummayDatas:getBudgetSummayDatas, getGLJSummayDatas:getGLJSummayDatas }; let mongoose = require('mongoose'); let logger = require("../../../logs/log_helper").logger; let projectsModel = mongoose.model('projects'); let async_n = require("async"); let _ = require('lodash'); let ration_model = require('../models/ration'); let bill_model = require('../models/bills'); let consts = require('../models/project_consts'); let projectConsts = consts.projectConst; let ration_glj_model = mongoose.model('ration_glj'); let rationTemplateModel = mongoose.model('ration_template'); let project_glj_model = mongoose.model('glj_list'); let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade"); const uuidV1 = require('uuid/v1'); const gljUtil = require('../../../public/gljUtil'); let stdColSettingModel = mongoose.model('std_main_col_lib'); let decimal_facade = require('../../main/facade/decimal_facade'); const scMathUtil = require('../../../public/scMathUtil').getUtil(); const calcUtil = require('../../../public/calculate_util') const { fixedFlag } = require('../../../public/common_constants'); import GLJListModel from "../../glj/models/glj_list_model"; const projectDao = require('../../pm/models/project_model').project; async function calcInstallationFee(data) { let result={}; let projectGLJList = []; let billTasks = generateTasks(data.bills,data.useID); let rationTasks = generateTasks(data.ration,data.useID); if(billTasks.length>0){ await bill_model.model.bulkWrite(billTasks); } console.log(rationTasks); if(rationTasks.length>0){ await ration_model.model.bulkWrite(rationTasks); } //如果删除定额,需要删除对应的工料机 if(data.ration.delete.length>0){ let rationIDS = _.map(data.ration.delete,'ID'); await ration_glj_model.deleteMany({projectID: data.ration.delete[0].projectID, rationID: {"$in": rationIDS}});//删除定额工料机 } let rationGLJTasks = []; let updateList = []; if(data.ration.update.length>0){//如果有需要更新的定额工料机 for(let ur of data.ration.update){ for(let g of ur.glj){ let gTasks = { updateOne:{ filter:{ ID:g.ID, projectID:g.projectID }, update :{ quantity:g.quantity, rationItemQuantity:g.rationItemQuantity } } }; rationGLJTasks.push(gTasks); updateList.push(g); } } } if(rationGLJTasks.length>0){ await ration_glj_model.bulkWrite(rationGLJTasks); } let newGljList = []; if(data.ration.add.length>0){//新增的安装子目要增加对应的工料机 for(let nr of data.ration.add){ for(let tkey in nr.glj){ let [newRecode,projectGLJ] = await addInstallationGLJ(nr.glj[tkey]); newGljList.push(newRecode); } } } if(newGljList.length>0){ await ration_glj_model.insertMany(newGljList); } result.update = updateList; result.add = newGljList; return result; } async function addInstallationGLJ(glj) { glj.ID = uuidV1(); let [info,projectGLJ ] = await ration_glj_facade.getInfoFromProjectGLJ(glj); let newRecode = ration_glj_facade.createNewRecord(info); return [newRecode,projectGLJ]; } function generateTasks(data,userID) { let tasks=[]; let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: userID}; if(data.delete && data.delete.length > 0){ for(let bd of data.delete){ //原先是假删除,现在改成真删除 let task = { deleteOne:{ filter:{ ID:bd.ID, projectID:bd.projectID } } }; /* let task={ updateOne:{ filter:{ ID:bd.ID, projectID:bd.projectID }, update :{ deleteInfo:deleteInfo } } };*/ tasks.push(task); } } if(data.add && data.add.length > 0){ for(let n_data of data.add){ let task = { insertOne :{ document:n_data } }; tasks.push(task); } } return tasks; } async function updateNodes(datas){ let nodeGroups = _.groupBy(datas,'type'); let rationTasks = []; let billTasks = []; let rationGLJTasks = []; let projectGLJTasks = []; let projectTasks = []; let rationTemplateTasks = []; let asyncTasks = []; for(let type in nodeGroups){ for(let node of nodeGroups[type]){ if(type == projectConsts.BILLS){ billTasks.push(getTask(node)); }else if(type == projectConsts.RATION){ rationTasks.push(getTask(node)); }else if(type == projectConsts.RATION_GLJ){ rationGLJTasks.push(getTask(node)); }else if(type == projectConsts.PROJECTGLJ){ projectGLJTasks.push(getTask(node,'id')); }else if(type == projectConsts.PROJECT){ projectTasks.push(getTask(node)); }else if(type == projectConsts.RATION_TEMPLATE){ rationTemplateTasks.push(getTask(node)) } } } rationTasks.length>0?asyncTasks.push(ration_model.model.bulkWrite(rationTasks)):''; billTasks.length>0?asyncTasks.push(bill_model.model.bulkWrite(billTasks)):""; rationGLJTasks.length>0?asyncTasks.push(ration_glj_model.bulkWrite(rationGLJTasks)):""; projectGLJTasks.length>0?asyncTasks.push(project_glj_model.bulkWrite(projectGLJTasks)):""; projectTasks.length>0?asyncTasks.push(projectsModel.bulkWrite(projectTasks)):""; rationTemplateTasks.length>0?asyncTasks.push(rationTemplateModel.bulkWrite(rationTemplateTasks)):""; return asyncTasks.length>0?await Promise.all(asyncTasks):""; function getTask(node,idFiled = 'ID') { let task={ updateOne:{ filter:{}, update :_.cloneDeep(node.data) } }; task.updateOne.filter[idFiled] = node.data[idFiled];//现在复制项目也重新生成一个新的ID了,所以ID是唯一的 delete task.updateOne.update[idFiled];//防止误操作 return task; } } /*function updateNodes(datas,callback) { let tasks = []; for(let node of datas){ tasks.push(updateOne(node)) } async_n.parallel(tasks, function(err, results) { if (!err){ callback(0, '', results); } else{ console.log(err); callback(1, 'save project failed'+err.message, null); } }); function updateOne(node) { if(node.type == projectConsts.BILLS){ return function (asCallback) { bill_model.model.findOneAndUpdate({projectID: node.data.projectID, ID: node.data.ID,deleteInfo: null}, node.data,{new: true}, asCallback); } }else if(node.type ==projectConsts.RATION){ return function (asCallback) { ration_model.model.findOneAndUpdate({projectID: node.data.projectID, ID: node.data.ID,deleteInfo: null}, node.data,{new: true}, asCallback); } } } }*/ //data = {feeRateID:111111,projectID:1245}; type = feeRate async function markUpdateProject(data,type) { let query = {deleteInfo:null}; if(type=="feeRate"){//更改了费率 query['property.feeFile.id'] = data.feeRateID; } if(type=="unitFile"){//更改了单价文件 query['property.unitPriceFile.id'] = data.unitFileID;//unitPriceFile } let projects = await projectsModel.find(query); return await markProjectsToChange(projects,type,data.projectID,data.isInclude); } async function markProjectsToChange(projects,type,extProjectID,isInclude){ let tasks=[]; for(let p of projects){ if(isInclude!= true ){ if(extProjectID && p.ID===extProjectID) continue;//排除当前项目 } tasks.push(generateMarkTask(type,p.ID)); } return tasks.length>0 ? await projectsModel.bulkWrite(tasks):null; } async function removeProjectMark(projectID) { return await projectsModel.findOneAndUpdate({ID:projectID},{"$unset":{"changeMark":1}}); } function generateMarkTask(value,projectID) { let task = { updateOne:{ filter:{ ID:projectID }, update:{ changeMark:value } } }; return task } // {projectID: 5, propertyName: 'aaa', propertyValue: 1} function saveProperty(data, callback){ let obj = {}; let pn = 'property.' + data.propertyName; obj[pn] = data.propertyValue; projectsModel.update({"ID": data.projectID}, obj, function (err) { if (err) { logger.err(pn + ' save error: ' + err); callback(err, null) } else { logger.info(pn + ' saved.'); callback('', null); }} ); } async function getDefaultColSetting(libID){ return await stdColSettingModel.findOne({ID: libID, deleted: false}, '-_id main_tree_col'); } async function getBudgetSummayDatas(projectIDs,overWriteUrl){ try { let projects = []; let names = []; let prjTypeNames = []; let compilationScopes = []; let decimal = null; let isProgressiveType = false; for(let ID of projectIDs){ projects.push(await getBillsByProjectID(ID)) ; } if(projects.length == 0){ return []; } let mp = projects[0]; names.push(mp.name); prjTypeNames.push(mp.prjTypeName); compilationScopes.push(mp.compilationScope); if(projects.length == 1) decimal = await decimal_facade.getProjectDecimal(projectIDs[0]);//如果只有一个项目,则没走合并的那一步,decimal会为空,从面报错 for(let i = 1;i0) temTotalPrice = scMathUtil.roundForObj(tbill.billsTtlPrice + temTotalPrice,decimal.bills.totalPrice); if(c.children.length > 0) tbill.billsTtlPrice = sumChildren; } } return temTotalPrice; } function getBillDatas(bills,level,rootFlag) { let tem = { ID:bills.ID, billsName:bills.name, billsCode:bills.code, billsUnit:bills.unit, billsTtlAmt:bills.quantity, billsPrices:[], billsUnitPrices:[], rationCommons:[], billsAmounts:[], '技术经济指标':[], billsLevel:level, calcBase:bills.calcBase, billsMemos:bills.remark }; let total = 0; let rationTotal =0; let baseTotal = 0; for(let n of nameList){ let p = 0;//金额 let up =0;//单价 let ra = 0;//定额建安费 let bt = 0; //累计相关 if(bills.unitPrices[n]) up = scMathUtil.roundForObj(bills.unitPrices[n],decimal.bills.unitPrice); tem.billsUnitPrices.push(up); if(bills.prices[n]){ p = scMathUtil.roundForObj(bills.prices[n],decimal.bills.totalPrice); total = scMathUtil.roundForObj(p+total,decimal.process); } tem.billsPrices.push(p); if(bills.rationCommons[n]){ ra = scMathUtil.roundForObj(bills.rationCommons[n],decimal.bills.totalPrice); rationTotal = scMathUtil.roundForObj(ra+rationTotal,decimal.process); } tem.rationCommons.push(ra); if(bills.quantityMap[n] && parseFloat(bills.quantityMap[n]) !== 0){ tem.billsAmounts.push(bills.quantityMap[n]); tem['技术经济指标'].push(scMathUtil.roundForObj(p/bills.quantityMap[n],2)); }else { tem.billsAmounts.push(0); tem['技术经济指标'].push(scMathUtil.roundForObj(p,2)); } if(rootFlag == fixedFlag.MAINTENANCE_EXPENSES){//如果是第三部分下的子清单,才要计算累计的相关信息 if(bills.baseProgressiveFees[n]){ bt = scMathUtil.roundForObj(bills.baseProgressiveFees[n],decimal.bills.totalPrice); baseTotal = scMathUtil.roundForObj(bt+baseTotal,decimal.process); } } } tem.billsTtlPrice = scMathUtil.roundForObj(total,decimal.bills.totalPrice); if(progressiveInterval && isProgressiveType && rootFlag == fixedFlag.MAINTENANCE_EXPENSES){ let baseArr = calcUtil.getProgressive(bills.calcBase,overWrite?overWrite.progression:undefined); if(baseArr.length > 0){ let calcTotal = calcUtil.getProgressiveFee(baseTotal,baseArr[0],progressiveInterval,decimal.bills.totalPrice,overWrite?overWrite.deficiency:undefined); tem.billsTtlPrice = calcTotal; let rate = scMathUtil.roundForObj(calcTotal * 100/baseTotal,decimal.feeRate); tem.billsMemos = "费率:"+rate+"%"; //“费率:n%”,n为汇总后重算的金额/汇总后的基数 } } tem.rationTotal = scMathUtil.roundForObj(rationTotal,decimal.bills.totalPrice);//定额总建安费 tem['技术经济综合指标'] = (tem.billsTtlAmt && parseFloat(tem.billsTtlAmt) !== 0)?scMathUtil.roundForObj(tem.billsTtlPrice/tem.billsTtlAmt,2):scMathUtil.roundForObj(tem.billsTtlPrice,2); if(bills.flag == fixedFlag.TOTAL_COST) totalItem = tem; return tem } } async function mergeProject(main,sub) {//合并两个项目 let decimal = await decimal_facade.getProjectDecimal(main[0].projectID); let project = await projectsModel.findOne({ID:main[0].projectID}); let notMatchList = []; for(let s of sub){ //先找有没有相同的大项费用 let same = findTheSameItem(main,s); same?await mergeItem(same,s,decimal,project._doc):notMatchList.push(s);//如果找到,则合并,找不到就放在未匹配表 } for(let n of notMatchList){ main.push(n); } return decimal; } async function mergeItem(a,b,decimal,project) { let bqDecimal = await decimal_facade.getBillsQuantityDecimal(a.projectID,a.unit,project); a.quantity = a.quantity?scMathUtil.roundForObj(a.quantity,bqDecimal):0; b.quantity = b.quantity?scMathUtil.roundForObj(b.quantity,bqDecimal):0; a.quantity = scMathUtil.roundForObj(a.quantity+b.quantity,decimal.process); for(let name in b.prices){ a.prices[name] = b.prices[name]; a.rationCommons[name] = b.rationCommons[name]; a.quantityMap[name] = b.quantityMap[name]; a.unitPrices[name]=b.unitPrices[name]; a.baseProgressiveFees[name] = b.baseProgressiveFees[name]; } for(let name in a.quantityMap){ a.quantityMap[name] = a.quantityMap[name]?scMathUtil.roundForObj(a.quantityMap[name],bqDecimal):0; } await mergeChildren(a,b,decimal,project); } async function mergeChildren(a,b,decimal,project) { let notMatchList = []; if(a.children.length > 0 && b.children.length ==0){ return; }else if(a.children.length == 0 && b.children.length > 0){ a.children = b.children; return; } //=============剩下的是两者都有的情况 for(let s of b.children){ let same = findTheSameItem(a.children,s); same?await mergeItem(same,s,decimal,project):notMatchList.push(s);//如果找到,则合并,找不到就放在未匹配表 } for(let n of notMatchList){ let match = false;//符合插入标记 //对于未匹配的子项,如果是固定清单:第100章至700章清单的子项,要匹配名字中的数字来做排充 if(a.flag == fixedFlag.ONE_SEVEN_BILLS){ for(let i = 0;i< a.children.length;i++){ let m_name = a.children[i].name.replace(/[^0-9]/ig,""); let s_name = n.name.replace(/[^0-9]/ig,""); m_name = parseFloat(m_name); s_name = parseFloat(s_name); if(m_name&&s_name){ if(m_name == s_name){ await mergeItem(a.children[i],n,project); match = true; break; } if(m_name > s_name){//主节点名字中的数字大于被插节点,则被插节点放在主节点前面 a.children.splice(i,0,n); match = true; break; } } } }else {//其它的子项按编号进行排序 for(let i = 0;i< a.children.length ; i++){ let m_code = a.children[i].code; let s_code = n.code; if(m_code && s_code && m_code!=""&&s_code!=""){ if(m_code > s_code){ a.children.splice(i,0,n); match = true; break; } } } } if(match == false)a.children.push(n) //没有插入成功,直接放到最后面 } } function findTheSameItem(main,item) {//编号名称单位三个相同,认为是同一条清单 return _.find(main,function (tem) { return isEqual(tem.code,item.code)&&isEqual(tem.name,item.name)&&isEqual(tem.unit,item.unit); }) } function isEqual(a,b) {//粗略匹配,null undefind "" 认为相等 return getValue(a)==getValue(b); function getValue(t) { if(t==null||t==undefined||t=="") return null; return t; } } async function getBillsByProjectID(projectID){ let roots=[],parentMap={}; let bills = await bill_model.model.find({projectID: projectID}, '-_id');//取出所有清单 let project = await projectsModel.findOne({ID:projectID}); if(!project) throw new Error(`找不到项目:${projectID}`); let projectName = project.name; let author='';//编制人 let auditor='';//审核人 let compilationScope='';//编制范围 let engineering='';//养护类别 let progressiveType = 0;//累进计算类型 let progressiveInterval = null; if(project.property&&project.property.projectFeature){ for(let f of project.property.projectFeature){ if(f.key == 'author') author = f.value; if(f.key == 'auditor') auditor = f.value; if(f.key =='compilationScope') compilationScope = f.value; if(f.key == 'engineering') engineering = f.value; } if(project.property.progressiveType) progressiveType = project.property.progressiveType; progressiveInterval = project.property.progressiveInterval; } for(let b of bills){ let commonFee =_.find(b._doc.fees,{"fieldName":"common"}); let prices = {}; let quantityMap={}; let unitPrices ={}; let rationCommons={}; let baseProgressiveFees ={}; let rationFee = _.find(b._doc.fees,{"fieldName":"rationCommon"}); if(commonFee&&commonFee.totalFee) prices[projectName] = commonFee.totalFee; if(commonFee&&commonFee.unitFee) unitPrices[projectName] = commonFee.unitFee; if(rationFee&&rationFee.totalFee) rationCommons[projectName] = rationFee.totalFee; baseProgressiveFees[projectName] = b.baseProgressiveFee; quantityMap[projectName] = b.quantity; let flagIndex = _.find(b._doc.flags,{'fieldName':'fixed'}); let doc = {ID:b.ID,name:b.name,code:b.code,unit:b.unit,projectID:b.projectID, ParentID:b.ParentID,NextSiblingID:b.NextSiblingID,unitPrices:unitPrices,quantity:b.quantity,prices:prices,rationCommons:rationCommons,quantityMap:quantityMap,flag:flagIndex?flagIndex.flag:-99,remark:b.remark,calcBase:b.calcBase,baseProgressiveFees:baseProgressiveFees};//选取有用字段 if(b.ParentID == -1) roots.push(doc); parentMap[b.ParentID]?parentMap[b.ParentID].push(doc):parentMap[b.ParentID]=[doc]; }//设置子节点 for(let r of roots){ setChildren(r,parentMap,1); } roots = sortChildren(roots); return {name:projectName,roots:roots,author:author,auditor:auditor,compilationScope:compilationScope,ParentID:project.ParentID,prjTypeName:engineering,progressiveType:progressiveType,progressiveInterval:progressiveInterval} } function setChildren(bill,parentMap,level) { let children = parentMap[bill.ID]; if(children){ for(let c of children){ setChildren(c,parentMap,level+1) } bill.children = children; }else { bill.children = []; } } function sortChildren(lists) { let IDMap ={},nextMap = {}, firstNode = null,newList=[]; for(let l of lists){ if(l.children&&l.children.length > 0) l.children = sortChildren(l.children);//递规排序 IDMap[l.ID] = l; if(l.NextSiblingID!=-1) nextMap[l.NextSiblingID] = l; } for(let t of lists){ if(!nextMap[t.ID]){ //如果在下一节点映射没找到,则是第一个节点 firstNode = t; break; } } if(firstNode){ newList.push(firstNode); delete IDMap[firstNode.ID]; setNext(firstNode,newList); } //容错处理,如果链断了的情况,直接添加到后面 for(let key in IDMap){ if(IDMap[key]) newList.push(IDMap[key]) } return newList; function setNext(node,array) { if(node.NextSiblingID != -1){ let next = IDMap[node.NextSiblingID]; if(next){ array.push(next); delete IDMap[next.ID]; setNext(next,array); } } } } async function getGLJSummayDatas(projectIDs) { let projects = []; let names = []; let prjTypeNames = []; let compilationScopes = []; try { for(let ID of projectIDs){ projects.push(await getProjectData(ID)) ; } if(projects.length == 0){ return []; } let mp = projects[0]; for(let p of projects){ names.push(p.name); prjTypeNames.push(p.prjTypeName); p.gljList = await getProjectGLJData(p.ID,p.unitPriceFileId,mp.property); compilationScopes.push(p.compilationScope); } let mList = mergeGLJ(mp,projects,names,prjTypeNames); mList = gljUtil.sortProjectGLJ(mList,_); let summaryGLJDatas = getSummaryGLJDatas(mList,mp.property.decimal,names,prjTypeNames,compilationScopes); let parentProject = await projectsModel.findOne({ID:mp.ParentID}); let result = { prj: {}, SummaryGljAudit:{ "name": parentProject?parentProject.name:"", "编制": mp.author, "复核": mp.auditor, "编制范围":mp.compilationScope }, SummaryGljAuditDetail:summaryGLJDatas }; return result; }catch (e){ console.log(e); } } function getSummaryGLJDatas(gljList,decimal,nameList,prjTypeNames,compilationScopes) { let datas = [],qdecimal = decimal.glj.quantity,process = decimal.process; for(let tem of gljList){ let d = { code:tem.code, name:tem.name, type:tem.type, unit:tem.unit, specs:tem.specs, marketPrice:tem.marketPrice, prjNames:nameList, prjTypeNames:prjTypeNames, quantityList:[], '编制范围明细':compilationScopes }; let totalQuantity = 0; for(let n of nameList){ let q = tem.quantityMap[n]?scMathUtil.roundForObj(tem.quantityMap[n],qdecimal):0; totalQuantity = scMathUtil.roundForObj(q+totalQuantity,process); d.quantityList.push(q); } d.totalQuantity = scMathUtil.roundForObj(totalQuantity,qdecimal); datas.push(d); } return datas; } function mergeGLJ(mp,projects) { let gljMap = {},gljList=[]; for(let g of mp.gljList){ g.quantityMap={}; g.quantityMap[mp.name] = g.quantity; gljMap[gljUtil.getIndex(g)] = g; gljList.push(g); } for(let i = 1;i