/** * Created by zhang on 2018/2/9. */ //先导出后require可以解决循环引用问题 module.exports = { replaceRations: replaceRations, addNewRation:addNewRation, addMultiRation: addMultiRation, deleteMultiRation:deleteMultiRation, getSameSectionRations:getSameSectionRations, getExtendData:getExtendData, getDefaultProgramID:getDefaultProgramID, deleteSubListByQuery:deleteSubListByQuery, updateCoeAdjust:updateCoeAdjust }; let mongoose = require('mongoose'); const SearchDao = require('../../complementary_ration_lib/models/searchModel'); const GLJListModel = require("../../glj/models/glj_list_model"); const scMathUtil = require('../../../public/scMathUtil').getUtil(); let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade"); let glj_calculate_facade = require("../../ration_glj/facade/glj_calculate_facade"); let quantity_detail = require("../facade/quantity_detail_facade"); let ration_glj = mongoose.model('ration_glj'); let ration_coe = mongoose.model('ration_coe'); let ration_model = require('../models/ration'); let bill_model = require('../models/bills'); let decimal_facade = require('./decimal_facade'); let installationFeeModel = mongoose.model("installation_fee"); let rationInstallationModel = mongoose.model('ration_installation'); let rationTemplateModel = mongoose.model('ration_template'); const uuidV1 = require('uuid/v1'); let std_glj_lib_gljList_model = mongoose.model('std_glj_lib_gljList'); let complementary_glj_model = mongoose.model('complementary_glj_lib'); let rationItemModel = mongoose.model("std_ration_lib_ration_items"); let complementaryRationModel = mongoose.model('complementary_ration_items'); let coeMolde = mongoose.model('std_ration_lib_coe_list'); let compleCoeModel = mongoose.model('complementary_ration_coe_list'); let projectGLJModel = mongoose.model("glj_list"); let mixRatioModel = mongoose.model("mix_ratio"); let complementaryGljLibModel = mongoose.model('complementary_glj_lib'); let counterModel = mongoose.model('counter'); let unitPriceModel = mongoose.model('unit_price'); let chongqingOverWrite = require("../../../web/over_write/js/chongqing_2018.js"); let _= require('lodash'); const projectDao = require('../../pm/models/project_model').project; let projectModel = mongoose.model('projects'); async function addNewRation(data,compilation) { let query = data.itemQuery; let stdRation = null; let startTime = +new Date(); if(query){ let searchDao = new SearchDao(); stdRation = await searchDao.getRationItem(query.userID, compilation._id, [query.rationRepId],query.code, query.ID); if (stdRation && data.sessionUserID !== query.userID) { stdRation.owner = query.userID; } //data.newData.code = query.code; } let stdRationTime = +new Date(); console.log("取std定额时间-------------------------------"+(stdRationTime - startTime)); if(data.brUpdate.length>0){ await updateSerialNo(data.brUpdate); } let newRation =await insertNewRation(data.newData,data.defaultLibID,stdRation,data.calQuantity); let addRationGLJTime = +new Date(); console.log("插入新定额时间-------------------------------"+(addRationGLJTime - stdRationTime)); if(stdRation){ return await addRationSubList(stdRation,newRation,data.needInstall,compilation); }else { return {ration:newRation}; } } async function addMultiRation(datas,compilation) { let rst = []; for(let data of datas){ let r = await addNewRation(data,compilation); rst.push(r); } return rst; } async function deleteMultiRation(rations) {//这里是只有删除的情况,删除定额的同时删除定额下挂的其它子项目 if(rations.length > 0){//删除定额下的 let rationIDS = _.map(rations,'ID'); await deleteSubListByQuery({projectID:rations[0].projectID,rationID:{"$in": rationIDS}}); await ration_model.model.deleteMany({ID:{"$in": rationIDS}}); } } async function getSameSectionRations(data,userId,compilationId){ //let userId //要先根据定额获取所属章节的ID let from = data.from; //定额类型,是标准的还是用户定义的 let code = data.code; let libID = data.libID; let sectionId,rations=[]; if(from == 'std'){ let ration = await rationItemModel.findOne({rationRepId:libID,code:code},['sectionId']); sectionId = ration? ration.sectionId:null; }else { let ration = await complementaryRationModel.findOne({userId:userId,compilationId: compilationId,code:code},['sectionId']); sectionId = ration?ration.sectionId:null; } if(sectionId){ if (from == 'std') { rations = await rationItemModel.find({sectionId: sectionId}); } else { rations = await complementaryRationModel.find({userId: userId, sectionId: sectionId}); } rations = _.sortBy(rations,'code'); } return rations } async function updateSerialNo(serialNoUpdate){ let tasks=[]; for(let data of serialNoUpdate){ let task={ updateOne:{ filter:{ ID:data.ID, projectID:data.projectID }, update :{ serialNo:data.serialNo } } }; tasks.push(task); } await ration_model.model.bulkWrite(tasks); } async function insertNewRation(newData,defaultLibID,std,calQuantity) {//插入新的定额 let startTime = +new Date(); if(std){ if (std.owner) { // 别人分享的定额 newData.fromUser = std.owner; } newData.code = std.code; newData.name = std.name; newData.caption = std.caption; newData.unit = std.unit; newData.libID = std.rationRepId; newData.stdID = std.ID; newData.content = std.jobContent; newData.annotation = std.annotation; if (std.chapter) { newData.comments = std.chapter.explanation; newData.ruleText = std.chapter.ruleText; } newData.prefix = ''; newData.from = std.type === 'complementary' ? 'cpt' : 'std'; if(defaultLibID !== std.rationRepId){//定额是默认定额库中的时,只显示编号; newData.prefix = '借';//定额不是默认定额库中的、且不是补充定额库中的时, 在定额编号前显示“借”。 if(newData.from === 'cpt') newData.prefix = '补';//定额是补充定额库中的时,在定额编号前显示“补”; } if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID newData.programID = await getProgramForProject(newData.projectID); }else { newData.programID = std.feeType; } newData.manageFeeRate = std.manageFeeRate; newData.rationAssList = createRationAss(std); // calculate ration Quantity } if(calQuantity){ await CalculateQuantity(newData,newData.billsItemID,newData.projectID); } let addRationGLJTime = +new Date(); console.log("计算消耗量时间-------------------------------"+(addRationGLJTime - startTime)); await ration_model.model.insertMany(newData); return newData; /*ration_model.model.create(newData); return newData;*/ } async function replaceRations(userID,data,compilation) { let searchDao = new SearchDao(); let recodes = []; for(let recode of data.nodeInfo){ let stdRation = await searchDao.getRationItem(userID,compilation._id,data.libIDs,recode.newCode, null); if (stdRation && stdRation.userId && stdRation.userId !== userID) { stdRation.owner = stdRation.userId; } let newRecode = await replaceRation(recode,stdRation,data.defaultLibID,data.projectID,data.calQuantity,compilation,data.cleanzmhs); if(newRecode){ recodes.push(newRecode); }else { break; } } return recodes; } async function getDefaultProgramID(data) { let searchDao = new SearchDao(); let programID; let std = await searchDao.getRationItem(data.userID,data.compilationId,[data.libID],data.code, null); if(std == null||std ==undefined || std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID programID = await getProgramForProject(data.projectID); }else { programID = std.feeType; } return programID; } async function replaceRation(nodeInfo,stdRation,defaultLibID,projectID,calQuantity,compilation,cleanzmhs) { if(nodeInfo.newCode == null||nodeInfo.newCode ==""){//说明是删除编号,则要变成一条空定额 await deleRationSubRecode(projectID,nodeInfo.ID);//删除定额下挂的各种数据,如定额工料机等 return await setEmptyRation(projectID,nodeInfo.ID); }else if(stdRation){ await deleRationSubRecode(projectID,nodeInfo.ID,cleanzmhs);//删除定额下挂的各种数据,如定额工料机等 let newRation = await updateRation(stdRation,defaultLibID,nodeInfo.ID,nodeInfo.billsItemID,projectID,calQuantity,cleanzmhs);//生成并插入新的定额 return await addRationSubList(stdRation,newRation,nodeInfo.needInstall,compilation,cleanzmhs); }else { return null; } } async function addRationSubList(stdRation,newRation,needInstall,compilation,cleanzmhs=false) { let startTime = +new Date(); let [ration_gljs,projectGLJList] = await addRationGLJ(stdRation,newRation,compilation); let addRationGLJTime = +new Date(); console.log("添加定额工料机时间-----"+(addRationGLJTime - startTime)); let ration_coes = await addRationCoe(stdRation,newRation,compilation); let addRationCoeTime = +new Date(); console.log("添加定额coe时间-----"+(addRationCoeTime - addRationGLJTime)); let ration_installations = []; let ration_template = []; if(cleanzmhs == false){//清除子目换算即cleanzmh==true时 模板子目、安装增加费不用恢复成标准的 if(needInstall && stdRation.type == 'std'){//只有标准的定额才有安装增加费,补充的定额没有安装增加费 ration_installations = await addRationInstallFee(stdRation,newRation); } let addRationInstallFeeTime = +new Date(); console.log("添加定额install时间-----"+(addRationInstallFeeTime - addRationCoeTime)); //添加定额模板子目 ration_template = await addRationTemplate(stdRation,newRation); }else if(newRation.areaIncreaseFee == true){//清空子目换算时,如果有面积增加费,子目调整状态要生成 let t = await glj_calculate_facade.calculateQuantity({rationID:newRation.ID},true); newRation.adjustState = t.adjustState; } return {ration:newRation,ration_gljs:ration_gljs,ration_coes:ration_coes,ration_installations:ration_installations,ration_templates:ration_template?[ration_template]:[],projectGLJList:projectGLJList}; } async function addRationInstallFee(std,newRation) { let install_fee_list = []; if(std.hasOwnProperty('rationInstList') && std.rationInstList.length > 0){ let installFee = await installationFeeModel.findOne({'projectID': newRation.projectID}); if(!installFee) return;//如果没有找到项目对应的安装增加费,则不添加 for(let ri of std.rationInstList){ let feeItem = _.find(installFee.installFeeItem,{'ID':ri.feeItemId}); let section = _.find(installFee.installSection,{'ID':ri.sectionId}); if(feeItem&§ion){ let tem_r_i = { libID:installFee.libID, projectID:newRation.projectID, rationID:newRation.ID, feeItemId:feeItem.ID, sectionId:section.ID, itemName:feeItem.feeItem, feeType:feeItem.feeType, sectionName:section.name, unifiedSetting:1, ruleId:'' }; if(feeItem.isCal==1&§ion.feeRuleId&§ion.feeRuleId!=''){//勾选记取时并且有规则ID时才读取 let feeRule = _.find(installFee.feeRule,{'ID':section.feeRuleId}); if(feeRule){ tem_r_i.ruleId = feeRule.ID; } } tem_r_i.ID = uuidV1(); install_fee_list.push(tem_r_i); } } if(install_fee_list.length>0){ await rationInstallationModel.insertMany(install_fee_list); } } return install_fee_list; } async function addRationTemplate(std,newRation) { let templateList = []; if(std.hasOwnProperty('rationTemplateList') && std.rationTemplateList.length > 0){ for(let tem of std.rationTemplateList){ let re_ration = await rationItemModel.findOne({rationRepId:std.rationRepId,ID:tem.rationID}); if(re_ration){ let template = { billID:"", fxID:"", quantity:"0", coe:"0" }; template.code = re_ration.code; template.name = re_ration.name; template.type = tem.type; template.unit = re_ration.unit; template.billsLocation = tem.billsLocation; template.defaultLocation = tem.billsLocation; templateList.push(template) } } } if(templateList.length > 0){ let ration_template = {}; ration_template.ID = uuidV1(); ration_template.projectID = newRation.projectID; ration_template.rationID = newRation.ID; ration_template.createLocation = 1; //默认模板子目分别放在措施项目下 ration_template.templateList = templateList; await rationTemplateModel.create(ration_template); return ration_template; } return null; } async function addRationCoe(std,newRation,compilation) { let ration_coe_list = []; let seq = 0; let stdCoeIDs = [];//与comCoeIDs两者不共存 let comCoeIDs = []; let coeMap={}; if(std.hasOwnProperty('rationCoeList')&&std.rationCoeList.length>0){//添加标准库的工料机 for(let sub of std.rationCoeList){ std.type === 'std'?stdCoeIDs.push(sub.ID):comCoeIDs.push(sub.ID); } let libCoeList = []; let seqLibIDs = []; if (std.type === 'std'){ libCoeList = await coeMolde.find({'ID':{'$in':stdCoeIDs}}).lean(); seqLibIDs = stdCoeIDs; }else{ libCoeList = await compleCoeModel.find({'ID':{'$in':comCoeIDs}}).lean(); seqLibIDs = comCoeIDs; } for(let lib of libCoeList){ coeMap[lib.ID] = lib; } for(let ID of seqLibIDs){ let libCoe = coeMap[ID]; if(libCoe){ let newCoe = {}; newCoe.ID = uuidV1(); newCoe.coeID = ID; newCoe.seq = seq; newCoe.name = libCoe.name; newCoe.content = libCoe.content; newCoe.original_code = libCoe.original_code; newCoe.option_codes = libCoe.option_codes; newCoe.option_list = libCoe.option_list; newCoe.isAdjust=0; newCoe.coes = libCoe.coes; newCoe.rationID = newRation.ID; newCoe.projectID = newRation.projectID; seq++; ration_coe_list.push(newCoe); } } } let lastCoe = await getCustomerCoe(newRation.projectID,newRation.ID,seq,compilation);//这个地方加载overWrite需要一些时间 ration_coe_list.push(lastCoe); await ration_coe.insertMany(ration_coe_list); return ration_coe_list; } function getCustomerCoeData() { var coeList = [ {amount:1, operator:'*', gljCode:null, coeType:'定额'}, { amount:1, operator:'*', gljCode:null, coeType:'人工'}, { amount:1, operator:'*', gljCode:null, coeType:'材料'}, { amount:1, operator:'*', gljCode:null, coeType:'机械'}, { amount:1, operator:'*', gljCode:null, coeType:'主材'}, { amount:1, operator:'*', gljCode:null, coeType:'设备'} ]; return coeList; }; async function getCustomerCoe(projectID,rationID,seq,compilation){//取自定义乘系数,根据编办不同,内容可能不同 //生成默认的自定义乘系数 let lastCoe ={ coeID:-1, name : '自定义系数', content:'人工×1,材料×1,机械×1,主材×1,设备×1', isAdjust:1, seq:seq, rationID : rationID, projectID : projectID }; lastCoe.ID = uuidV1(); lastCoe.coes = getCustomerCoeData(); try { //查看编办中有没有重写路径 if(compilation.overWriteUrl && compilation.overWriteUrl!=""){ if(compilation.overWriteUrl.indexOf("chongqing_2018")!= -1){ console.log("重庆overwrite"); let overWrite = chongqingOverWrite if(overWrite.getCusCoeContent) lastCoe.content = overWrite.getCusCoeContent(); if(overWrite.getCustomerCoeData) lastCoe.coes = overWrite.getCustomerCoeData(); } } return lastCoe }catch (err){ console.log("读取自定义系数重写文件失败"); console.log(err.message); return lastCoe } } //对于多单价,多组成物消耗量的编办,通过这个方法获取单价、组成物消耗量的字段, function getExtendData(property,compilation) { return projectDao.getExtendData(property,compilation); } async function addRationGLJ(std,newRation,compilation) { let newRationGLJList = []; let rationGLJShowList = []; let projectGLJList = []; let gljKeyMap = {}; let mixRatioMap={}; let gljCodes=[]; let unitPriceFileId = 0; let property = await projectDao.getProjectProperty(newRation.projectID); if(property){ unitPriceFileId = property.unitPriceFile !== undefined ? property.unitPriceFile.id : 0; } let ext = getExtendData(property,compilation); let first = +new Date(); if(std.hasOwnProperty('rationGljList') && std.rationGljList.length > 0){ let stdGLJID =[];//标准工料机ID数组 let cptGLJID=[];//补充工料机ID数组 //let stdGLJID = _.map(std.rationGljList,'gljId'); for(let tem_g of std.rationGljList){ if(tem_g.type == 'complementary'){ cptGLJID.push(tem_g.gljId); }else { stdGLJID.push(tem_g.gljId); } } let stdGLJList = stdGLJID.length > 0 ? await std_glj_lib_gljList_model.find({'ID':{'$in':stdGLJID}}):[];//速度优化-------先一次性取出所有的工料机列表 let stdGLJMap = _.indexBy(stdGLJList, 'ID'); let cptGLJList = cptGLJID.length > 0 ? await complementary_glj_model.find({'userId':std.userId,'ID':{'$in':cptGLJID}}):[]; let cptGLJMap = _.indexBy(cptGLJList, 'ID'); let stdGLJMapTime = +new Date(); console.log("找到工料机映射表时间-------------------------------"+(stdGLJMapTime - first)); for(let sub of std.rationGljList){ let newGLJ = {}; newGLJ.ID = uuidV1(); newGLJ.projectID = newRation.projectID; newGLJ.GLJID = sub.gljId; newGLJ.rationID = newRation.ID; newGLJ.billsItemID = newRation.billsItemID; newGLJ.rationItemQuantity = sub.consumeAmt; newGLJ.quantity = sub.consumeAmt; newGLJ.glj_repository_id = std.rationRepId; let std_glj = null; if(sub.type == 'complementary'){//有可能来自标准工料机库或补充工料机库 std_glj = cptGLJMap[sub.gljId]; newGLJ.from = 'cpt'; }else { std_glj = stdGLJMap[sub.gljId]; newGLJ.from = 'std'; } //多单价情况处理 if(ext && ext.priceField && std_glj && std_glj.priceProperty && std_glj.priceProperty[ext.priceField] !== null && std_glj.priceProperty[ext.priceField] !== undefined){ std_glj.basePrice = std_glj.priceProperty[ext.priceField]; } if(std_glj){ newGLJ.name = std_glj.name; newGLJ.code = std_glj.code; newGLJ.original_code = std_glj.code; newGLJ.unit = std_glj.unit; newGLJ.specs = std_glj.specs; newGLJ.model = std_glj.model; newGLJ.basePrice = std_glj.basePrice; newGLJ.marketPrice = std_glj.basePrice; newGLJ.taxRate= std_glj.taxRate; newGLJ.shortName = std_glj.shortName; newGLJ.type = std_glj.gljType; newGLJ.repositoryId = std_glj.repositoryId; newGLJ.adjCoe = std_glj.adjCoe; newGLJ.materialType = std_glj.materialType; newGLJ.materialCoe = std_glj.materialCoe; newGLJ.materialIndexType = std_glj.materialIndexType; newGLJ.materialIndexUnit = std_glj.materialIndexUnit; newGLJ.materialIndexCoe = std_glj.materialIndexCoe; newGLJ.createType = 'normal'; let tindex = getIndex(newGLJ); if(std_glj.component && std_glj.component.length > 0) mixRatioMap[tindex] = std_glj.component; let tdata = ration_glj_facade.getGLJSearchInfo(newGLJ); gljKeyMap[tindex] = tdata; gljCodes.push(tdata.code); newRationGLJList.push(newGLJ); // let [info,projectGLJ] = await ration_glj_facade.getInfoFromProjectGLJ(newGLJ,unitPriceFileId,ext); // newGLJ = ration_glj_facade.createNewRecord(info); // newRationGLJList.push(newGLJ); // rationGLJShowList.push(info); // projectGLJList.push(projectGLJ) } //let InfoFromProjectGLJ = +new Date(); //console.log("找到项目工料机时间-------------------------------"+(InfoFromProjectGLJ - std_gljTime)); } [newRationGLJList, projectGLJList] = await getProjectGLJinfo(newRation.projectID,newRationGLJList,gljKeyMap,gljCodes,mixRatioMap,unitPriceFileId,ext); } let before = +new Date(); console.log("总查询时间为-------------------------------"+(before-first)); if(newRationGLJList.length>0){ await ration_glj.insertMany(newRationGLJList); } let after = +new Date(); console.log("实际插入时间为-------------------------------"+(after-before)); console.log("总操作时间为-------------------------------"+(after-first)); return [newRationGLJList,projectGLJList] } async function getProjectGLJinfo(projectID,t_newRationGLJList,gljKeyMap,gljCodes,mixRatioMap,unitPriceFileId,ext){//批量插入或查找项目工料机信息 //先根据工料机编号在项目工料机中查找工料机是否存在 let projectGLJMap={}; let projectGLJList = []; let newProjectGLJList=[];//工料机ID要重新去取 let connectKeyList = []; let newRationGLJList = []; let gljListModel = new GLJListModel(); let t_projectGLJList = await projectGLJModel.find({'project_id':projectID,'code':{'$in':gljCodes}}).lean(); for(let pg of t_projectGLJList){ let pindex = getIndex(pg); projectGLJMap[pindex] = pg; } for(let key in gljKeyMap){ if(!projectGLJMap[key]){//如果项目工料机不存在,则添加 newProjectGLJList.push(gljKeyMap[key]); projectGLJMap[key] = gljKeyMap[key]; } //查看组成物 if(gljListModel.ownCompositionTypes.indexOf(gljKeyMap[key].type)!=-1){//有组成物的类型 connectKeyList.push(key); } } let [existMixRatioMap,mixRatioInsertData,missCodeList] = await getMixRatioInfo(projectID,projectGLJMap,newProjectGLJList,mixRatioMap,connectKeyList,unitPriceFileId,ext); if(missCodeList.length > 0) gljCodes = gljCodes.concat(missCodeList); let [unitPriceMap,newUnitPriceList] = await getUnitPriceData(newProjectGLJList,gljCodes,unitPriceFileId); if(newUnitPriceList.length > 0) await unitPriceModel.insertMany(newUnitPriceList); if(mixRatioInsertData.length > 0) await mixRatioModel.insertMany(mixRatioInsertData); //插入项目工料机 if(newProjectGLJList.length > 0){ await setIDfromCounter("glj_list",newProjectGLJList); await projectGLJModel.insertMany(newProjectGLJList); } //组装数据 for(let ration_glj of t_newRationGLJList){ let rkey = getIndex(ration_glj); let pglj = projectGLJMap[rkey]; let subList = []; setUnitPrice(pglj,unitPriceMap); if(existMixRatioMap[rkey]){//如果有组成物 for(let m of existMixRatioMap[rkey]){ let mpglj = projectGLJMap[getIndex(m)] if(mpglj){ let cglj = _.clone(mpglj); setUnitPrice(cglj,unitPriceMap); cglj.ratio_data = m; subList.push(cglj); }else{ throw `组成物${m.name}对应的项目工料机没有找到`; } } pglj.subList =subList; } ration_glj.projectGLJID = pglj.id; newRationGLJList.push(ration_glj_facade.createNewRecord(ration_glj)); projectGLJList.push(pglj); } return [newRationGLJList, projectGLJList]; function setUnitPrice(p,unitPriceMap){ p.unit_price = unitPriceMap[getIndex(p)]; } } async function getUnitPriceData(newProjectGLJList,gljCodes,unitPriceFileId){ let unitPriceMap = {}; let newUnitPriceList = []; let unitPriceList = await unitPriceModel.find({unit_price_file_id: unitPriceFileId,'code':{'$in':gljCodes}}).lean(); for(let u of unitPriceList){ unitPriceMap[getIndex(u)]=u; } for(let np of newProjectGLJList){ let pkey = getIndex(np); if(unitPriceMap[pkey]) continue; let insertData = { code: np.code, base_price: np.base_price, market_price: np.market_price, unit_price_file_id: unitPriceFileId, name: np.name, taxRate:np.taxRate, specs:np.specs?np.specs:'', original_code:np.original_code, unit:np.unit?np.unit:'', type: np.type, short_name: np.shortName !== undefined ? np.shortName : '', glj_id: np.glj_id, is_add:0, grossWeightCoe:np.grossWeightCoe, purchaseStorageRate:np.purchaseStorageRate, offSiteTransportLossRate:np.offSiteTransportLossRate, handlingLossRate:np.handlingLossRate }; if(np.from=='cpt') insertData.is_add=1;//如果是来自补充工料机,则都添加新增标记 if(insertData.code != insertData.original_code) insertData.is_add=1;//添加的时候如果是复制整块来的,可能在源项目中是新增的工料机,这里也要添上(暂时可能还用不到) newUnitPriceList.push(insertData); unitPriceMap[pkey] = insertData; } if(newUnitPriceList.length > 0) await setIDfromCounter("unit_price",newUnitPriceList); return [unitPriceMap,newUnitPriceList]; } async function getMixRatioInfo(projectID,projectGLJMap,newProjectGLJList,mixRatioMap,connectKeyList,unitPriceFileId,ext){//取组成物信息,得到缺少的组成物情况 let missCodeList = []; //所有组成物信息的编码,用来统一查询对应的项目工料机是否存在 let existMixRatioMap ={}; let codeMap={};//用来去重 let mixRatioInsertData = []; // 1. 先检查现在的组成物表中,是否有相关信息 - 生成映射记录 if(connectKeyList.length > 0){//有组成物的话从数据库中取出组成物信息 let mixRatioList = await mixRatioModel.find({'unit_price_file_id': unitPriceFileId,'connect_key': {'$in':connectKeyList}}).lean(); for(let m of mixRatioList){ //组成物信息分组,查看哪些是已经存在的 existMixRatioMap[m.connect_key]?existMixRatioMap[m.connect_key].push(m):existMixRatioMap[m.connect_key]=[m]; //查看组成物对应的项目工料机是否存在,如果不存在,要插入项目工料机 let mkey = getIndex(m); if(!projectGLJMap[mkey] && !codeMap[m.code]){//如果之前查出来的项目工料机中不包含组成物的信息,要加到missCode里面再查找一次项目工料机看是否存在 missCodeList.push(m.code); codeMap[m.code] = true; } } // 2 将第一步得到的映射表 与在标准库查询父工料机得到的映射表对比,得出哪些组物成还需要添加,获得库ID let stdIDs = []; let comIDs = []; let missMixRatioGroup = []; for(let ck of connectKeyList){//查看项目中组成物信息是否已经存在,如果不存在,则用插定额时获取的组成物信息从数据库中获取 if(!existMixRatioMap[ck] && mixRatioMap[ck] && mixRatioMap[ck].length > 0){//组成物信息不存在 let pglj = projectGLJMap[ck];//取出父数据 let from = pglj.from === undefined|| pglj.from ===null || pglj.from === ""?'std' : pglj.from; for(let c of mixRatioMap[ck]){ if(from == "std"){//标准的工料机只来自标准的 stdIDs.push(c.ID); }else{ c.isStd?stdIDs.push(c.ID):comIDs.push(c.ID); } } missMixRatioGroup.push({'connect_key':ck,'list':mixRatioMap[ck],'from':from}); } } //3.统一查询所有组成物在标准库中的详细信息 let stdMixMap = {}; //整理需插入的组成物列表的数据 //来自标准工料机 if(stdIDs.length > 0){ stdIDs = _.uniq(stdIDs);//去重 let stdMixList = await std_glj_lib_gljList_model.find({'ID':{'$in':stdIDs}}).lean(); for(let sm of stdMixList){ stdMixMap[sm.ID] = sm; let skey = getIndex(sm,['code','name','specs','unit','gljType']); if(!projectGLJMap[skey] && !codeMap[sm.code]){ missCodeList.push(sm.code); codeMap[sm.code] = true; } } } //来自组成物工料机 let comMixMap = {}; if(comIDs.length > 0){ comIDs = _.uniq(comIDs);//去重 let comMixList = await complementaryGljLibModel.find({'ID':{'$in':comIDs}}).lean(); for(let cm of comMixList){ comMixMap[cm.ID] = cm; let ckey = getIndex(cm,['code','name','specs','unit','gljType']); if(!projectGLJMap[ckey] && codeMap[cm.code]){ missCodeList.push(cm.code); codeMap[cm.code] = true; } } } //4.生成需要插入组成物表的数据 for(let mg of missMixRatioGroup){//整理需要插入组成物列表的数据 for(let tc of mg.list){ let consumpiton = tc.consumeAmt; //只有标准的工料机的组成物才会有多单价、多组成物消耗量的情况 if(mg.from == 'std' && ext && ext.quantityField &&( tc.consumeAmtProperty[ext.quantityField]!= undefined && tc.consumeAmtProperty[ext.quantityField]!=null)){ consumpiton = tc.consumeAmtProperty[ext.quantityField]; } let mfrom = mg.from == 'std' || tc.isStd?'std':'cpt'; let tmp = mfrom == 'std'?stdMixMap[tc.ID]:comMixMap[tc.ID];//取出之前库中查到的工料机 let mixRatioData = { consumption: consumpiton, glj_id: tmp.ID, unit_price_file_id: unitPriceFileId, connect_key: mg.connect_key, type: tmp.gljType, code: tmp.code, specs:tmp.specs?tmp.specs:"", name:tmp.name, unit:tmp.unit?tmp.unit:'', from:mfrom }; mixRatioInsertData.push(mixRatioData); } } if(mixRatioInsertData.length > 0) await setIDfromCounter("mix_ratio",mixRatioInsertData,existMixRatioMap,'connect_key'); //await mixRatioModel.insertMany(mixRatioInsertData); 因为没有事务添加组成物数据要放在添加单价文件数据之后 //5.查询组成物对应的项目工料机是否存在,如果不存在,生成项目工料机信息 let projectGLJList = await projectGLJModel.find({'project_id':projectID,'code':{'$in':missCodeList}}).lean(); for(let pg of projectGLJList){ let pindex = getIndex(pg); projectGLJMap[pindex] = pg; } let lessMix = [];//组成物表存在,项目工料机不存在的数据 let lessMixMap = {};//防止重复添加 for(let connect_key in existMixRatioMap){ let mixRatios = existMixRatioMap[connect_key]; for(let m of mixRatios){ let mk = getIndex(m); if(!projectGLJMap[mk] && !lessMixMap[mk]){//如果组成物对应的项目工料机不存在 let nglj = null; if(m.from == 'std'){//这里有值,说明是刚添加到组成物文件中的数据 nglj = stdMixMap[m.glj_id]; }else if(m.from == 'cpt'){//这里有值,说明是刚添加到组成物文件中的数据 nglj = comMixMap[m.glj_id]; } if(nglj){ nglj.from = m.from; let np = getProjectGLJNewData(nglj,projectID,ext); newProjectGLJList.push(np); projectGLJMap[mk] = np; }else{//这里没找到,说明是组成物文件里有,但是项目工料机没有的数据 lessMix.push(m); } lessMixMap[mk] = true;//只要处理过一次,就不用再重新处理了,机械组成物,比如柴油这些,会出现多次 } } } //6. 组成物文件里有,但是项目工料机没有的数据(共用单价文件等情况产生) let lessIDList=[]; let uniqMap ={};//去重 let lessStdMix = [];//防止组成物中改了名称等,但是通过glj_id取出来的是还没改前的原始数据 if(lessMix.length > 0){ for(let lm of lessMix){ let parentglj = projectGLJMap[lm.connect_key]; if(!parentglj) throw `含有组成物工料机${lm.connect_key},没有找到,添加定额失败`; if((parentglj.from == "std" || lm.from == "std") && lm.code!="80CCS"){//车船税特殊处理 if(!uniqMap[lm.glj_id]){ lessIDList.push(lm.glj_id); uniqMap[lm.glj_id] = lm; } lessStdMix.push(lm); }else {//来自组成物的直接设置 lm.from = 'cpt'; lm.gljType = lm.type; let t_mg = getProjectGLJNewData(lm,projectID); newProjectGLJList.push(t_mg); projectGLJMap[getIndex(lm)] = t_mg; } } } if(lessIDList.length > 0){ let less_stds = await std_glj_lib_gljList_model.find({'ID':{'$in':lessIDList}}).lean(); let less_stds_map = {}; for(let les of less_stds){ less_stds_map[les.ID] = les; } for(let t_l_m of lessStdMix){ let t_nglj = less_stds_map[t_l_m.glj_id]; t_nglj.from = 'std'; //防止组成物中改了名称等,但是通过glj_id取出来的是还没改前的原始数据 t_nglj.name = t_l_m.name; t_nglj.code = t_l_m.code; t_nglj.gljType = t_l_m.type; t_nglj.specs = t_l_m.specs; t_nglj.unit = t_l_m.unit; let t_np = getProjectGLJNewData(t_nglj,projectID,ext); newProjectGLJList.push(t_np); projectGLJMap[getIndex(t_l_m)] = t_np; } } } return [existMixRatioMap,mixRatioInsertData,missCodeList] } function getProjectGLJNewData(tmp,projectId,ext){ let gljData = { glj_id: tmp.ID, repositoryId:tmp.repositoryId, project_id: projectId, code: tmp.code, name: tmp.name, specs: tmp.specs?tmp.specs:'', unit: tmp.unit === undefined ? '' : tmp.unit, type: tmp.gljType, adjCoe:tmp.adjCoe, original_code:tmp.code, materialType: tmp.materialType, //三材类别 materialCoe: tmp.materialCoe, base_price: tmp.basePrice, market_price: tmp.basePrice, is_evaluate:0, is_eval_material:0, no_tax_eqp:0, is_adjust_price:0, is_main_material:0, is_contractor_material:0, supply_quantity:0, supply:0, from:tmp.from?tmp.from:"std" }; // 现在定额库可以引用其他费用定额的,比如广东可能套用部颁的定额,因此就算广东费用定额是多单价的,也可能会引用单个单价的人材机 if(gljData.from == 'std' && ext && ext.priceField &&( tmp.priceProperty && tmp.priceProperty[ext.priceField]!= undefined && tmp.priceProperty[ext.priceField]!=null)){ const basePrice = scMathUtil.roundTo(tmp.priceProperty[ext.priceField],-6); gljData.base_price = basePrice; gljData.market_price = basePrice; } return gljData; } async function setIDfromCounter(name,list,map,keyfield){//map,keyfield let update = {$inc: {sequence_value: list.length}}; let condition = {_id: name}; let options = {new: true}; // 先查找更新 let counter = await counterModel.findOneAndUpdate(condition, update, options); let firstID = counter.sequence_value - (list.length - 1); for(let a of list){ a.id = firstID; firstID+=1 if(map && keyfield){ let key = a[keyfield]; map[key]?map[key].push(a):map[key]=[a] } } } async function deleRationSubRecode(projectID,rationID,cleanzmhs=false) {//删除挂在定额下的数据,如工程量明细,定额工料机等 let delete_query={projectID: projectID, rationID: rationID}; //删除工程量明细 await deleteSubListByQuery(delete_query,cleanzmhs) ; } async function deleteSubListByQuery(delete_query,cleanzmhs=false) { if(cleanzmhs == false){//清空子目换算即cleanzmh==true时不需要清空工程量明细、模板关联子目、安装增加费 await quantity_detail.deleteByQuery(delete_query) ;//删除工程量明细 await rationInstallationModel.deleteMany(delete_query);//删除安装增加费 await rationTemplateModel.deleteMany(delete_query);//删除模板关联子目 } //to do稳定土也要删除 await ration_coe.deleteMany(delete_query);//删除附注条件 await ration_glj.deleteMany(delete_query);//删除定额工料机 } async function updateCoeAdjust(data,compilation) { let replace = [],projectGLJList=[]; await ration_coe.update({ID:data.ID},data.doc); //添加单个工料机的情况 if (data.add.length > 0){ let [tg,pl] = await ration_glj_facade.insertAddTypeGLJ(data.add,compilation); if(pl.length > 0) projectGLJList = projectGLJList.concat(pl); } if(data.delete.length > 0) await ration_glj_facade.deleteGLJ(data.delete); //替换工料机的情况 if (data.replace.length > 0){ for(let r of data.replace){ let [rg,pg] = await ration_glj_facade.replaceGLJByData(r,compilation); replace.push(rg) ; projectGLJList.push(pg); } } let cal_result = await glj_calculate_facade.calculateQuantity({projectID:data.projectID,rationID:data.rationID},null,true); let coe = { query:{ID:data.ID,projectID:data.projectID}, doc:data.doc }; let ration_glj ={ quantityRefresh:true, glj_result:cal_result.glj_result }; let ration = { ID:cal_result.rationID, adjustState:cal_result.adjustState, name:cal_result.rationName }; return {coe:coe,ration_glj:ration_glj,ration:ration,add:data.add,delete:data.delete,replace:replace,projectGLJList:projectGLJList} } async function updateRation(std,defaultLibID,rationID,billsItemID,projectID,calQuantity,cleanzmh=false) { // insertNewRation let ration ={}; if (std.owner) { // 别人分享的定额 ration.fromUser = std.owner; } ration.code = std.code; ration.name = std.name; ration.caption = std.caption; ration.unit = std.unit; if (std.type === 'std') { ration.libID = std.rationRepId; ration.stdID = std.ID; } ration.content = std.jobContent; ration.manageFeeRate = std.manageFeeRate; ration.adjustState = ''; ration.isFromDetail=0; ration.isSubcontract=false; ration.fees=[]; if (std.chapter) { ration.comments = std.chapter.explanation; ration.ruleText = std.chapter.ruleText; } ration.from = std.type === 'complementary' ? 'cpt' : 'std'; //定额前缀 none:0, complementary:1, borrow: 2 ration.prefix = ''; if(parseInt(defaultLibID) !== std.rationRepId){//定额是默认定额库中的时,只显示编号; ration.prefix = '借';//定额不是默认定额库中的、且不是补充定额库中的时, 在定额编号前显示“借”。 if(ration.from === 'cpt') ration.prefix = '补';//定额是补充定额库中的时,在定额编号前显示“补”; } ration.rationAssList = createRationAss(std);//生成辅助定额 if(cleanzmh==false){//如果是清空子目换算,即cleanzmh==true 保留定额工程量、工程量表达式、含量(分解系数)、取费专业(取费类别) if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID ration.programID = await getProgramForProject(projectID); }else { ration.programID = std.feeType; } if(calQuantity){ await CalculateQuantity(ration,billsItemID,projectID); } } let unsetObject = { "marketUnitFee":1, 'marketTotalFee':1, "maskName":1 }; let newRation = await ration_model.model.findOneAndUpdate({ID:rationID,projectID:projectID},{"$set":ration,"$unset":unsetObject},{new: true});//; return newRation; } async function setEmptyRation(projectID,rationID){ let ration ={}; ration.code = ""; ration.name = ""; ration.caption = ""; ration.unit = ""; ration.libID = null; ration.content = ""; ration.adjustState = ''; ration.isFromDetail=0; ration.isSubcontract=false; ration.fees=[]; ration.comments = ""; ration.ruleText = ""; ration.quantity=""; ration.contain=""; ration.quantityEXP=""; ration.from = 'std'; //定额前缀 none:0, complementary:1, borrow: 2 ration.prefix = ''; ration.rationAssList = []; ration.marketUnitFee =""; ration.marketTotalFee =""; ration.maskName = ""; ration.targetTotalFee =''; ration.targetUnitFee = ""; ration.deleteInfo = null; ration.quantityCoe = {}; ration.rationQuantityCoe=""; ration.tenderQuantity = ""; ration.programID = null; let newRation = await ration_model.model.findOneAndUpdate({ID:rationID,projectID:projectID},{"$set":ration},{new: true});//; return {ration:newRation,ration_gljs:[],ration_coes:[],ration_installs:[]}; } function createRationAss(std) { let rationAssList = [];//生成辅助定额 if(std.hasOwnProperty('rationAssList')&&std.rationAssList.length>0){ let assGroup = _.groupBy(std.rationAssList,'name'); for(let key in assGroup){ let assList = assGroup[key]; let ass = assList[0]; ass._doc.actualValue = ass.stdValue; ass._doc.isAdjust = 0; if(_.isString(ass._doc.assistCode)) ass._doc.assistCode = ass._doc.assistCode.replace("\n",""); if(_.isString(ass._doc.thirdRationCode)) ass._doc.thirdRationCode = ass._doc.thirdRationCode.replace("\n",""); if(assList.length > 1){ ass._doc.groupList = JSON.parse(JSON.stringify(assList)) ; ass._doc.maxValue = assList[assList.length-1]._doc.maxValue; } rationAssList.push(ass); } } return rationAssList; } async function CalculateQuantity (ration,billsItemID,projectID) { // calculate ration Quantity let project = await projectModel.findOne({ID:projectID}); let decimalObject =await decimal_facade.getProjectDecimal(projectID,project); let quantity_decimal = (decimalObject&&decimalObject.ration&&decimalObject.ration.quantity)?decimalObject.ration.quantity:3; let pbill = await bill_model.model.findOne({projectID:projectID,ID:billsItemID}); let t_unit = ration.unit?ration.unit.replace(/^\d+/,""):""; if(t_unit!=pbill.unit){//如果定额工程量的单位去除前面的数字后不等于清单单位,定额工程量保持不变 return ; } let billsQuantity = pbill.quantity ? pbill.quantity : 0; let bill_decimal = await decimal_facade.getBillsQuantityDecimal(projectID,pbill.unit,project); billsQuantity=scMathUtil.roundForObj(billsQuantity,bill_decimal); ration.quantityEXP="QDL"; ration.quantity = scMathUtil.roundForObj(billsQuantity / FilterNumberFromUnit(ration.unit),quantity_decimal);//不管是否打勾都做转换 ration.contain = scMathUtil.roundForObj(ration.quantity/billsQuantity,6); }; async function getProgramForProject(projectID){ let project = await projectModel.findOne({ID:projectID}); return project.property.engineering; } function FilterNumberFromUnit (unit) { let reg = new RegExp('^[0-9]+'); if (reg.test(unit)) { return parseInt(unit.match(reg)[0]); } else { return 1; } }; function getIndex(obj,tpops){ let pops = tpops?tpops:['code','name','specs','unit','type']; let t_index = ''; let k_arr=[]; for(let p of pops){ let tmpK = (obj[p]==undefined||obj[p]==null||obj[p]=='')?'null':obj[p]; k_arr.push(tmpK); } t_index=k_arr.join("|-|"); return t_index; }