Просмотр исходного кода

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/YangHuCost

TonyKang 5 лет назад
Родитель
Сommit
0746518752
42 измененных файлов с 954 добавлено и 178 удалено
  1. 1 0
      config/gulpConfig.js
  2. 1 0
      logs/log4js.json
  3. 1 1
      modules/all_models/compleRation_ration.js
  4. 1 1
      modules/all_models/compleRation_section.js
  5. 1 0
      modules/all_models/mix_ratio.js
  6. 1 1
      modules/all_models/stdRation_coe.js
  7. 2 2
      modules/all_models/stdRation_ration.js
  8. 1 1
      modules/all_models/stdRation_section.js
  9. 11 5
      modules/common/const/bills_fixed.js
  10. 4 4
      modules/complementary_ration_lib/models/searchModel.js
  11. 10 4
      modules/glj/models/glj_list_model.js
  12. 431 19
      modules/main/facade/ration_facade.js
  13. 12 8
      modules/pm/controllers/pm_controller.js
  14. 37 42
      modules/pm/models/project_model.js
  15. 2 2
      package.json
  16. 12 0
      pm2_import.json
  17. 9 0
      pm2_server.json
  18. 138 0
      public/calculate_util.js
  19. 1 0
      public/common_util.js
  20. 1 1
      public/web/gljUtil.js
  21. 30 1
      public/web/id_tree.js
  22. 2 2
      public/web/tree_sheet/tree_sheet_controller.js
  23. 2 2
      public/web/tree_sheet/tree_sheet_helper.js
  24. 1 0
      web/building_saas/main/html/main.html
  25. 4 2
      web/building_saas/main/js/controllers/project_controller.js
  26. 30 1
      web/building_saas/main/js/models/cache_tree.js
  27. 10 10
      web/building_saas/main/js/models/calc_base.js
  28. 14 14
      web/building_saas/main/js/models/calc_program.js
  29. 2 1
      web/building_saas/main/js/models/fee_rate.js
  30. 6 0
      web/building_saas/main/js/models/main_consts.js
  31. 27 9
      web/building_saas/main/js/models/project_glj.js
  32. 5 3
      web/building_saas/main/js/models/ration.js
  33. 4 4
      web/building_saas/main/js/views/fee_rate_view.js
  34. 4 7
      web/building_saas/main/js/views/glj_view.js
  35. 9 7
      web/building_saas/main/js/views/project_view.js
  36. 19 7
      web/building_saas/main/js/views/std_billsGuidance_lib.js
  37. 4 4
      web/building_saas/main/js/views/zmhs_view.js
  38. 1 1
      web/common/components/share/index.js
  39. 6 6
      web/over_write/js/anhui_2019.js
  40. 5 5
      web/over_write/js/neimeng_2019.js
  41. 91 0
      web/over_write/js/nongcun_2020.js
  42. 1 1
      web/over_write/js/zhejiang_2005.js

+ 1 - 0
config/gulpConfig.js

@@ -84,6 +84,7 @@ module.exports = {
         'public/web/socket/connection.js',
         'public/web/uuid.js',
         'public/web/sheet/sheet_common.js',
+        'public/calculate_util.js',
         'web/building_saas/main/js/models/calc_program.js',
         'web/building_saas/main/js/models/calc_base.js',
         'web/building_saas/main/js/views/calc_program_manage.js',

+ 1 - 0
logs/log4js.json

@@ -1,4 +1,5 @@
 {
+  "disableClustering": true, 
   "customBaseDir" :"../logs/YangHuCost/",
   "customDefaultAtt" :{
     "type": "dateFile",

+ 1 - 1
modules/all_models/compleRation_ration.js

@@ -41,7 +41,7 @@ const compleRationSchema = new Schema({
     userId: String,
     compilationId: String,
     rationRepId: Number,
-    ID:Number,
+    ID:{type: Number,index: true},
     code: String,
     name: String,
     unit: String,

+ 1 - 1
modules/all_models/compleRation_section.js

@@ -17,7 +17,7 @@ const compleRationSectionTreeSchema = new Schema({
     name: String,
     //是否是同层第一个节点
    // isFirst: Boolean,
-    ID: String,
+    ID: {type: String,index: true},
     NextSiblingID: String,
     ParentID: String,
     deleteInfo: deleteSchema,

+ 1 - 0
modules/all_models/mix_ratio.js

@@ -46,6 +46,7 @@ let modelSchema = {
     code: String,
     // 工料机类型
     type: Number,
+    from:String,
     model: Number// 机型
 };
 mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));

+ 1 - 1
modules/all_models/stdRation_coe.js

@@ -19,7 +19,7 @@ const coeSchema = new Schema({
 
 const coeListSchema = new Schema({
     libID: Number,                      // 所属定额定ID
-    ID: Number,                         // 系数ID(流水号ID)
+    ID: {type: Number,index: true},                         // 系数ID(流水号ID)
     serialNo: Number,                  //编号
     name: String,                       // 名称
     content: String,                    // 说明

+ 2 - 2
modules/all_models/stdRation_ration.js

@@ -32,8 +32,8 @@ const rationInstSchema = new Schema({
 },{_id: false});
 
 const rationItemSchema = new Schema({
-    ID:Number,
-    code: String,
+    ID:{type: Number,index: true},
+    code: {type: String,index: true},
     name: String,
     unit: String,
     basePrice: Number,

+ 1 - 1
modules/all_models/stdRation_section.js

@@ -6,7 +6,7 @@ const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 const rationChapterTreeSchema = new Schema({//章节树  //生成唯一id改为sectionID  改成string
     rationRepId: Number,
-    ID:Number,
+    ID:{type: Number,index: true},
     ParentID:Number,
     NextSiblingID:Number,
     name: String,

+ 11 - 5
modules/common/const/bills_fixed.js

@@ -47,21 +47,27 @@ const fixedFlag = {
     //计日工 daywork labor
     DAYWORK_LABOR: 22,
     //劳务
-    LABOUR_SERVICE:23,
+    LABOUR_SERVICE: 23,
     //材料
-    MATERIAL:24,
+    MATERIAL: 24,
     //施工机械
-    CONSTRUCTION_MACHINE:25,
+    CONSTRUCTION_MACHINE: 25,
     //暂列金额
-    PROVISIONAL:26,
+    PROVISIONAL: 26,
     //安全生产费
-    SAFE_COST:27,
+    SAFE_COST: 27,
     //100章清单
     ONE_HUNDRED_BILLS: 28,
     // 一二三部分合计
     ONE_TO_THREE_TOTAL: 29,
     // 前期工作费
     PRELIMINARY_WORK: 30,
+    // 小修费
+    MINOR_REPAIR_FEE: 31,
+    // 预防养护费
+    PREVENTIVE_MAINTENANCE_FEE: 32,
+    // 修复养护费
+    REPAIR_MAINTENANCE_FEE: 33,
 };
 
 export default fixedFlag;

+ 4 - 4
modules/complementary_ration_lib/models/searchModel.js

@@ -26,16 +26,16 @@ class SearchDao{
                 ration = await this.getCompleRation(userId,compilationId,code,ID);
             }else {
                 firstLib = parseInt(firstLib);
-                let firstQuery = {rationRepId: firstLib, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
+                let firstQuery = {rationRepId: firstLib, code: code};
                 if(ID){
-                    firstQuery = {ID: ID, $or: [{isDeleted: null}, {isDeleted: false}]};
+                    firstQuery = {ID: ID};
                 }
                 ration = await this.getStdRation(firstQuery);
             }
             if(ration == null){//选中的定额库或者默认的定额库中没有找到定额,才走常规的流程查找其它定额库
-                let stdQuery = {rationRepId: {$in: otherLibs}, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
+                let stdQuery = {rationRepId: {$in: otherLibs}, code: code};
                 if(ID){
-                    stdQuery = {ID: ID, $or: [{isDeleted: null}, {isDeleted: false}]};
+                    stdQuery = {ID: ID};
                 }
                 ration = await this.getStdRation(stdQuery);
                 if(ration == null) ration = await this.getCompleRation(userId,compilationId,code,ID);

+ 10 - 4
modules/glj/models/glj_list_model.js

@@ -670,6 +670,7 @@ class GLJListModel extends BaseModel {
                 code: tmp.code,
                 specs:tmp.specs?tmp.specs:"",
                 name:tmp.name,
+                from:tmp.from,
                 unit:tmp.unit?tmp.unit:''
             };
             mixRatioInsertData.push(mixRatioData);
@@ -829,7 +830,7 @@ class GLJListModel extends BaseModel {
         let gljInsertData = [];
         for(let mk in ratioMap){//找出缺少的工料机
             if(!matchGljs[mk]){
-                lessMix.push(ratioMap[mk]);
+                lessMix.push(ratioMap[mk]);     
                 lessIDList.push(ratioMap[mk].glj_id);
             }
         }
@@ -844,9 +845,14 @@ class GLJListModel extends BaseModel {
                 }
             }else {
                 for(let m of lessMix){
-                    m.from = 'cpt';
-                    let newProjctData =  this.getProjectGLJNewData(m,projectId);
-                    gljInsertData.push(newProjctData);
+                  let mt = m;
+                  if(m.from == "std"){
+                    mt = await gljListModel.model.findOne({ID:m.glj_id}).lean();
+                  }else{
+                    mt.from = 'cpt';
+                  }
+                  let newProjctData =  this.getProjectGLJNewData(mt,projectId);
+                  gljInsertData.push(newProjctData);
                 }
             }
 

+ 431 - 19
modules/main/facade/ration_facade.js

@@ -3,6 +3,8 @@
  */
 let mongoose = require('mongoose');
 import SearchDao from '../../complementary_ration_lib/models/searchModel';
+import GLJListModel from "../../glj/models/glj_list_model";
+import e from 'express';
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
 let gljUtil = require('../../../public/gljUtil');
 let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade");
@@ -26,11 +28,18 @@ let originaltCalcModel = mongoose.model('original_calc');
 
 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 _= require('lodash');
 const projectDao = require('../../pm/models/project_model').project;
 let projectModel = mongoose.model('projects');
+let unitPriceModel = mongoose.model('unit_price');
+let unitPriceFileModel = mongoose.model('unit_price_file');
+let vvTaxModel =  mongoose.model("std_vehicleVesselTax_items");
+
 const fs = require('fs');
 
 module.exports = {
@@ -248,8 +257,8 @@ async function insertNewRation(newData,defaultLibID,std,calQuantity) {//插入
     }
     let addRationGLJTime = +new Date();
     console.log("计算消耗量时间-------------------------------"+(addRationGLJTime - startTime));
-    let newRation = await ration_model.model.create(newData);
-    return newRation;
+    await ration_model.model.insertMany([newData]);
+    return newData;
     /*ration_model.model.create(newData);
     return newData;*/
 }
@@ -394,6 +403,9 @@ async function addRationTemplate(std,newRation) {
 async function addRationCoe(std,newRation,compilation) {
     let ration_coe_list = [];
     let seq = 0;
+    let stdCoeIDs = [];
+    let coeMap={}
+
     if(std.hasOwnProperty('rationCoeList')&&std.rationCoeList.length>0){//添加标准库的工料机
         for(let sub of std.rationCoeList){
             let libCoe;
@@ -455,12 +467,12 @@ async function getCustomerCoe(projectID,rationID,seq,compilation){//取自定义
     lastCoe.ID = uuidV1();
     lastCoe.coes = getCustomerCoeData();
     try {
-    //查看编办中有没有重写路径
-     if(compilation.overWriteUrl && compilation.overWriteUrl!=""){
+    //查看编办中有没有重写路径  -- 养护中暂时没用到
+     /* if(compilation.overWriteUrl && compilation.overWriteUrl!=""){
          let overWrite = require("../../.."+compilation.overWriteUrl);
          if(overWrite.getCusCoeContent) lastCoe.content = overWrite.getCusCoeContent();
          if(overWrite.getCustomerCoeData) lastCoe.coes = overWrite.getCustomerCoeData();
-     }
+     } */
      return lastCoe
    }catch (err){
        console.log("读取自定义系数重写文件失败");
@@ -481,6 +493,9 @@ async function addRationGLJ(std,newRation,compilation,isMaterial,connect_key) {
     let newRationGLJList = [];
     let rationGLJShowList = [];
     let projectGLJList = [];
+    let gljKeyMap = {};
+    let mixRatioMap={};
+    let gljCodes=[];
     let unitPriceFileId = 0;
     let property = await projectDao.getProjectProperty(newRation.projectID);
     if(property){
@@ -499,9 +514,9 @@ async function addRationGLJ(std,newRation,compilation,isMaterial,connect_key) {
                 stdGLJID.push(tem_g.gljId);
              }
         }
-       let stdGLJList = stdGLJID.length > 0 ? await std_glj_lib_gljList_model.find({'ID':{'$in':stdGLJID}}):[];//速度优化-------先一次性取出所有的工料机列表
+       let stdGLJList = stdGLJID.length > 0 ? await std_glj_lib_gljList_model.find({'ID':{'$in':stdGLJID}}).lean():[];//速度优化-------先一次性取出所有的工料机列表
        let stdGLJMap = _.indexBy(stdGLJList, 'ID');
-       let cptGLJList =  cptGLJID.length > 0 ? await complementary_glj_model.find({'userId':std.userId,'ID':{'$in':cptGLJID}}):[];
+       let cptGLJList =  cptGLJID.length > 0 ? await complementary_glj_model.find({'userId':std.userId,'ID':{'$in':cptGLJID}}).lean():[];
        let cptGLJMap = _.indexBy(cptGLJList, 'ID');
         let stdGLJMapTime = +new Date();
         console.log("找到工料机映射表时间-------------------------------"+(stdGLJMapTime - first));
@@ -527,29 +542,414 @@ async function addRationGLJ(std,newRation,compilation,isMaterial,connect_key) {
             let std_glj = getStdGlj(sub,stdGLJMap,cptGLJMap,{},ext);
             if(std_glj){
                 ration_glj_facade.setPropertyFromStd(newGLJ,std_glj);
-                let [info,projectGLJ] =  await ration_glj_facade.getInfoFromProjectGLJ(newGLJ,unitPriceFileId,ext);
-                newGLJ = ration_glj_facade.createNewRecord(info);
+                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);
-                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 InfoFromProjectGLJ = +new Date();
+        console.log("找到项目工料机时间-------------------------------"+(InfoFromProjectGLJ - stdGLJMapTime));
+
     }
-    let before = +new Date();
-    console.log("总查询时间为-------------------------------"+(before-first));
     if(isMaterial == true) return [newRationGLJList,projectGLJList];//如果是材料计算的工料机,这里返回就可以了
 
     if(newRationGLJList.length>0){
         await ration_glj.insertMany(newRationGLJList);
     }
     let after = +new Date();
-    console.log("实际插入时间为-------------------------------"+(after-before));
     console.log("总操作时间为-------------------------------"+(after-first));
-    return [rationGLJShowList,projectGLJList];
+    return [newRationGLJList,projectGLJList];
+}
+
+async function getProjectGLJinfo(projectID,t_newRationGLJList,gljKeyMap,gljCodes,mixRatioMap,unitPriceFileId,ext){//批量插入或查找项目工料机信息
+  //先根据工料机编号在项目工料机中查找工料机是否存在
+  let projectGLJMap={};
+  let projectGLJList = [];
+  let newProjectGLJList=[];//工料机ID要重新去取
+  let connectKeyList = [];
+  let CCSMap = {keyMap:{},codes:[]};//需要添加车船税的机械台班
+  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(gljKeyMap[key].type == 301){//如果是机械台班,需看看有没有车船税
+        CCSMap.keyMap[key] = true;
+        CCSMap.codes.push(gljKeyMap[key].code);
+      }
+    }
+    //查看组成物
+    if(gljListModel.ownCompositionTypes.indexOf(gljKeyMap[key].type)!=-1){//有组成物的类型
+      connectKeyList.push(key);
+    }
+  }
+
+
+  let [existMixRatioMap,mixRatioInsertData,missCodeList] = await getMixRatioInfo(projectID,projectGLJMap,newProjectGLJList,mixRatioMap,connectKeyList,unitPriceFileId,CCSMap,ext);
+  if(missCodeList.length > 0) gljCodes = gljCodes.concat(missCodeList);
+ 
+  //处理车般税相关,车船税是添加项目时,项目工料机,和单价文件里自动添加的 --- todo
+ 
+  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,
+      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,CCSMap,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);
+        }
+    }
+
+    //4.5 处理车船税问题,查询机械台班是否需要添加车船税
+    if(CCSMap.codes.length > 0){
+      let unitFileInfo = await unitPriceFileModel.findOne({id:unitPriceFileId}).lean();
+      if(unitFileInfo.vvTaxFileID && unitFileInfo.vvTaxFileID!=""){
+        let needCCS = false;
+        let items =  await vvTaxModel.find({libID:unitFileInfo.vvTaxFileID,'code':{'$in':CCSMap.codes}}).lean();
+        for(let i of items){
+          let ikey = getIndex(i);
+          if(CCSMap.keyMap[ikey]){
+            needCCS = true;
+            mixRatioInsertData.push(gljUtil.getBaseCCSMixRatio(unitPriceFileId,i.vehicleVesselTax,ikey))
+          }
+        }
+        if(needCCS && !codeMap["80CCS"]) missCodeList.push("80CCS");
+      }
+    }
+
+
+    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,
+      from:tmp.from?tmp.from:"std"
+  };
+  if(gljData.from == 'std' && ext && ext.priceField &&( tmp.priceProperty[ext.priceField]!= undefined && tmp.priceProperty[ext.priceField]!=null)){
+    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]
+      }
+  }
+
 }
 
+
 function getStdGlj(sub,stdGLJMap,cptGLJMap,newGLJ,ext) {
     let std_glj = null;
     if(sub.type == 'complementary'){//有可能来自标准工料机库或补充工料机库
@@ -767,4 +1167,16 @@ function FilterNumberFromUnit (unit) {
     } 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;
+}

+ 12 - 8
modules/pm/controllers/pm_controller.js

@@ -83,14 +83,18 @@ module.exports = {
         });
     },
     updateProjects: async function (req, res) {
-        let data = JSON.parse(req.body.data);
-        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation, data.updateData, function (err, message, data) {
-            if (err === 0) {
-                callback(req, res, err, message, data);
-            } else {
-                callback(req, res, err, message, null);
-            }
-        });
+        try {
+            let data = JSON.parse(req.body.data);
+            await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation, data.updateData, function (err, message, data) {
+                if (err === 0) {
+                    callback(req, res, err, message, data);
+                } else {
+                    callback(req, res, err, message, null);
+                }
+            });
+        } catch (err) {
+            callback(req, res, 1, String(err), null);
+        }
     },
     // CSL, 2017-12-14 该方法用于项目属性:提交保存混合型数据,这些数据来自不同的表,包括projects.property、ration、bills、labour_coes.
     updateMixDatas: async function(req, res){

+ 37 - 42
modules/pm/models/project_model.js

@@ -255,51 +255,46 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                 data.updateData['deleteInfo'] = deleteInfo;
                 //Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
                 //update
-                try {
-                    if (data.updateData.projType === projectType.project) {
-                        let tenders = await Projects.find({userID: userId, ParentID: data.updateData.ID});
-                        if (tenders.length > 0) {
-                            await UnitPriceFiles.update({
-                                user_id: userId,
-                                root_project_id: data.updateData.ID
-                            }, {$set: {deleteInfo: deleteInfo}}, {multi: true});
-                            await FeeRateFiles.update({
-                                userID: userId,
-                                rootProjectID: data.updateData.ID
-                            }, {$set: {deleteInfo: deleteInfo}}, {multi: true});
-                            await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
-                        } else {//true
-                            await UnitPriceFiles.remove({user_id: userId, root_project_id: data.updateData.ID});
-                            await FeeRateFiles.remove({userID: userId, rootProjectID: data.updateData.ID});
-                            //await Projects.update({userID: userId, NextSiblingID: data.updateData.ID, deleteInfo: null}, {$set: {NextSiblingID: data.NextSiblingID}});
-                            await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                if (data.updateData.projType === projectType.project) {
+                    let tenders = await Projects.find({ userID: userId, ParentID: data.updateData.ID });
+                    if (tenders.length > 0) {
+                        await UnitPriceFiles.update({
+                            user_id: userId,
+                            root_project_id: data.updateData.ID
+                        }, { $set: { deleteInfo: deleteInfo } }, { multi: true });
+                        await FeeRateFiles.update({
+                            userID: userId,
+                            rootProjectID: data.updateData.ID
+                        }, { $set: { deleteInfo: deleteInfo } }, { multi: true });
+                        await Projects.update({ userID: userId, ID: data.updateData.ID }, data.updateData, updateAll);
+                    } else {//true
+                        await UnitPriceFiles.remove({ user_id: userId, root_project_id: data.updateData.ID });
+                        await FeeRateFiles.remove({ userID: userId, rootProjectID: data.updateData.ID });
+                        //await Projects.update({userID: userId, NextSiblingID: data.updateData.ID, deleteInfo: null}, {$set: {NextSiblingID: data.NextSiblingID}});
+                        await Projects.remove({ userID: userId, ID: data.updateData.ID }, updateAll);
+                    }
+                } else if (data.updateData.projType === projectType.tender) {//fake
+                    let delTender = await Projects.findOne({ userID: userId, ID: data.updateData.ID });
+                    //如果这个单位工程用到的费率文件、单价文件,没有被其他的单位工程使用,则应该一起跟随删除
+                    if (delTender) {
+                        let unitPriceFile = delTender.property.unitPriceFile;
+                        let feeRateFile = delTender.property.feeFile;
+                        let usedUFTenders = await Projects.find(
+                            { userID: userId, $or: [{ deleteInfo: null }, { 'deleteInfo.deleted': false }], 'property.unitPriceFile.id': unitPriceFile.id });
+                        if (usedUFTenders.length === 1) {
+                            await UnitPriceFiles.update({ id: unitPriceFile.id }, { $set: { deleteInfo: deleteInfo } });
                         }
-                    } else if (data.updateData.projType === projectType.tender) {//fake
-                        let delTender = await Projects.findOne({userID: userId, ID: data.updateData.ID});
-                        //如果这个单位工程用到的费率文件、单价文件,没有被其他的单位工程使用,则应该一起跟随删除
-                        if(delTender){
-                            let unitPriceFile = delTender.property.unitPriceFile;
-                            let feeRateFile = delTender.property.feeFile;
-                            let usedUFTenders = await Projects.find(
-                                {userID: userId, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], 'property.unitPriceFile.id': unitPriceFile.id});
-                            if(usedUFTenders.length === 1){
-                                await UnitPriceFiles.update({id: unitPriceFile.id}, {$set: {deleteInfo: deleteInfo}});
-                            }
-                            let usedFRTenders = await Projects.find(
-                                {userID: userId, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], 'property.feeFile.id': feeRateFile.id});
-                            if(usedFRTenders.length === 1){
-                                await FeeRateFiles.update({ID: feeRateFile.id}, {$set: {deleteInfo: deleteInfo}});
-                            }
-                            await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                        let usedFRTenders = await Projects.find(
+                            { userID: userId, $or: [{ deleteInfo: null }, { 'deleteInfo.deleted': false }], 'property.feeFile.id': feeRateFile.id });
+                        if (usedFRTenders.length === 1) {
+                            await FeeRateFiles.update({ ID: feeRateFile.id }, { $set: { deleteInfo: deleteInfo } });
                         }
+                        await Projects.update({ userID: userId, ID: data.updateData.ID }, data.updateData, updateAll);
+                    }
 
-                    } else if (data.updateData.projType === projectType.folder) {//true
-                        await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
-                    } else throw '未知文件类型,删除失败!';
-                }
-                catch (error) {
-                    callback(1, error, null);
-                }
+                } else if (data.updateData.projType === projectType.folder) {//true
+                    await Projects.remove({ userID: userId, ID: data.updateData.ID }, updateAll);
+                } else throw '未知文件类型,删除失败!';
             }
             else {
                 hasError = true;

+ 2 - 2
package.json

@@ -62,9 +62,9 @@
   },
   "scripts": {
     "start": "C:\\Users\\mai\\AppData\\Roaming\\npm\\babel-node.cmd server.js",
-    "server": "node --max-old-space-size=2048 server.js",
+    "server": "pm2-docker pm2_server.json",
     "socket": "node socket.js",
-    "import": "node --max-old-space-size=2048 importserver.js",
+    "import": "pm2-docker pm2_import.json",
     "dev_server":"SET NODE_ENV=qa&& babel-node server.js",
     "dev_socket":"SET NODE_ENV=qa&& babel-node socket.js",
     "dev_import":"SET NODE_ENV=qa&& babel-node importserver.js"

+ 12 - 0
pm2_import.json

@@ -0,0 +1,12 @@
+{
+  "apps": {
+      "name": "constructionimport",
+      "script": "importserver.js",
+      "output": "/home/logs/out.log",
+      "error": "/home/logs/err.log",
+      "log_date_format": "YYYY-MM-DD HH:mm Z",
+      "exec_mode": "cluster_mode",                
+      "instances": 3, 
+      "merge_logs": true
+    }
+}

+ 9 - 0
pm2_server.json

@@ -0,0 +1,9 @@
+{
+  "apps": {
+      "name": "constructionimport",
+      "script": "server.js",
+      "node_args":"--max-old-space-size=2048",
+      "exec_mode": "cluster_mode",                
+      "instances": 3
+    }
+}

+ 138 - 0
public/calculate_util.js

@@ -0,0 +1,138 @@
+'use strict';
+
+((factory) => {
+    if (typeof module !== 'undefined') {
+        const scMathUtil = require('./scMathUtil').getUtil();
+        module.exports = factory(scMathUtil);
+    } else {
+        window.calculateUtil = factory(scMathUtil);
+    }
+})((scMathUtil) => {
+    function standar(exp) {
+        //去空格
+        exp = exp.replace(/\s/g, '');
+        //( to (
+        exp = exp.replace(/(/g, '(');
+        //)to )
+        exp = exp.replace(/)/g, ')');
+        //,to ,
+        exp = exp.replace(/,/g, ',');
+        //f to F
+        exp = exp.replace(new RegExp('f', 'g'), 'F');
+        return exp;
+    }
+    /**
+     * 获取累进办法计算的金额
+     * @param {Number} baseFee - 基准金额
+     * @param {String} name - 使用累进计算的基数名称(需要与累进库中的名称匹配)
+     * @param {Array} progressiveData - 项目的累进数据(property.progressiveInterval)
+     * @param {Number} decimal - 精度
+     * @return {Number}
+     */
+    function getProgressiveFee(baseFee, name, progressiveData, decimal) {
+        if (!progressiveData) {
+            throw '该项目不存在累进区间数据';
+        }
+        //根据基数名称匹配累进库数据,标准化以免(())等不同导致不匹配
+        const matchProgressiveData = progressiveData.find(item => standar(item.name) === standar(name));
+        if (!matchProgressiveData) {
+            throw `计算基数{${name}}不存在累进区间数据`;
+        }
+        // 将原始数据转换成方便处理的数据:[{feeRate: xx, min: 0, max: 200, minOpr: '(', maxOpr: ']'}]
+        const progression = matchProgressiveData.progression.map(item => {
+            // item.interval内容: eg (0,200]、[300,500) [1000,+)....
+            const interval = standar(item.interval);
+            // ( => 大于 [ => 大于等于 ) => 小于 ] => 小于等于
+            const minReg = /([\(\[])(\d+)/;
+            const minMatch = minReg.exec(interval);
+            if (!minMatch || !minMatch[1] || !minMatch[2]) {
+                throw `计算基数{${name}}累进区间数据错误`;
+            }
+            const minOpr = minMatch[1];
+            // 后台数据单位为万元,这里转为为元
+            const min = parseFloat(minMatch[2]) * 10000;
+            const maxReg = /[\,,]([\d\+]+)([\)\]])/;
+            const maxMatch = maxReg.exec(interval);
+            if (!maxMatch || !maxMatch[1] || !maxMatch[2]) {
+                throw `计算基数{${name}}累进区间数据错误`;
+            }
+            const max = maxMatch[1] === '+' ? 'infinity' : parseFloat(maxMatch[1]) * 10000;
+            const maxOpr = maxMatch[2];
+            return {
+                feeRate: item.feeRate,
+                min,
+                minOpr,
+                max,
+                maxOpr
+            }
+        });
+        progression.sort((a, b) => a.min - b.min);
+        // 基数所在区间
+        const withinData = progression.find(item => {
+            const oprMiddle = item.max === 'infinity' ? '+' : '';
+            const oprLink = item.minOpr + oprMiddle + item.maxOpr;
+            switch (oprLink) {
+                case '()':
+                    return baseFee > item.min && baseFee < item.max;
+                case '(]':
+                    return baseFee > item.min && baseFee <= item.max;
+                case '[)':
+                    return baseFee >= item.min && baseFee < item.max;
+                case '[]':
+                    return baseFee >= item.min && baseFee <= item.max;
+                case '(+)':
+                case '(+]':
+                    return baseFee > item.min;
+                case '[+)':
+                case '[+]':
+                    return baseFee >= item.min;
+                default:
+                    return false;
+            }
+        });
+        if (!withinData) {
+            return 0;
+        }
+        // 累进计算
+        let fee = 0;
+        //累进之前的区间
+        for (let i = 0; i < progression.indexOf(withinData); i++) {
+            const perData = progression[i];
+            fee += (perData.max - perData.min) * perData.feeRate * 0.01;
+        }
+        //累进所在区间
+        fee += (baseFee - withinData.min) * withinData.feeRate * 0.01;
+        return scMathUtil.roundForObj(fee, decimal);
+    }
+    
+    // 所有编办的累进基数(如果以后编办基数有冲突,则此判断方法不可用了。如在编办A、B均有计算x,且A中x为累进基数,B中x为非累进基数)
+    const progression = [
+        '施工场地建设费', '养护单位(业主)管理费', '信息化费', '路线工程监理费', '独立桥梁隧道工程监理费', // 重庆2018
+        '设计文件审查费', '路线勘察设计费', '独立桥梁隧道维修加固勘察设计费', '招标代理及标底(最高投标限价)编制费',
+        '养护单位管理费', '养护项目信息化费', '工程监理费', '前期工作费', // 安徽2019
+        '养护单位项目管理费', // 内蒙2019
+        '养护工程管理费' // 浙江2005
+    ];
+
+    /**
+     * 判断该基数是否包含累进基数
+     * @param {String} calcBase - 计算基数
+     * @return {Boolean}
+     */
+    function isProgressive(calcBase) {
+        if (typeof calcBase !== 'string') {
+            return false;
+        }
+        const reg = /{[^}]+}/g;
+        const matched = calcBase.match(reg);
+        if (!matched) {
+            return false;
+        }
+        return matched.some(mStr => progression.some(pStr => `{${pStr}}` === mStr));
+    }
+
+    return {
+        getProgressiveFee,
+        isProgressive,
+    };
+});

+ 1 - 0
public/common_util.js

@@ -43,6 +43,7 @@
             return sorted;
         }
     }
+
     return {
         isDef,
         isEmptyVal,

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
public/web/gljUtil.js


+ 30 - 1
public/web/id_tree.js

@@ -406,7 +406,36 @@ var idTree = {
             }
             return success;
         };
-
+        // 节点所属固定ID
+        Node.prototype.getFlag = function () {
+            if (!this.data || !this.data.flags || !this.data.flags[0] || !this.data.flags[0].flag) {
+                return 0;
+            }
+            return this.data.flags[0].flag;
+        };
+        // 节点所属固定ID
+        Node.prototype.belongToFlag = function () {
+            let node = this;
+            while (node) {
+                if (node.data && node.data.flags && node.data.flags[0] && node.data.flags[0].flag) {
+                    return node.data.flags[0].flag;
+                }
+                node = node.parent;
+            }
+            return null;
+        };
+        // 节点是否属于某些固定ID,会一直向上找,直到找到或到达顶层
+        Node.prototype.isBelongToFlags = function (flags) {
+            let node = this;
+            while (node) {
+                const flag = node.getFlag();
+                if (flags.includes(flag)) {
+                    return true;
+                }
+                node = node.parent;
+            }
+            return false;
+        }
         var Tree = function (setting) {
             this.nodes = {};
             this.roots = [];

+ 2 - 2
public/web/tree_sheet/tree_sheet_controller.js

@@ -201,10 +201,10 @@ var TREE_SHEET_CONTROLLER = {
             }
         };
 
-        controller.prototype.refreshTreeNode = function (nodes, recursive) {
+        controller.prototype.refreshTreeNode = function (nodes,recursive,autoFit) {
             var that = this;
             TREE_SHEET_HELPER.massOperationSheet(this.sheet, function () {
-                TREE_SHEET_HELPER.refreshTreeNodeData(that.setting, that.sheet, nodes, recursive)
+                TREE_SHEET_HELPER.refreshTreeNodeData(that.setting, that.sheet, nodes, recursive,autoFit)
             })
         }
 

+ 2 - 2
public/web/tree_sheet/tree_sheet_helper.js

@@ -109,7 +109,7 @@ var TREE_SHEET_HELPER = {
             }
         })
     },
-    refreshTreeNodeData: function (setting, sheet, nodes, recursive) {
+    refreshTreeNodeData: function (setting, sheet, nodes, recursive,autoFit=true) {
         nodes.forEach(function (node) {
             let iRow = node.serialNo();
             if(setting.emptyRowHeader){
@@ -208,7 +208,7 @@ var TREE_SHEET_HELPER = {
                     cell.locked(typeof projectReadOnly !== 'undefined' && projectReadOnly ? true : false);
                 }
             });
-            if(setting.setAutoFitRow){
+            if(autoFit==true && setting.setAutoFitRow){
                 setting.setAutoFitRow(sheet,node)//自动行高功能比较费时,400行,启用和不启用相差2秒左右
             }
             if (recursive) {

+ 1 - 0
web/building_saas/main/html/main.html

@@ -1977,6 +1977,7 @@
     <script type="text/javascript" src="/web/building_saas/main/js/models/ration_template.js"></script>
     <!--<script type="text/javascript" src="/web/building_saas/main/js/models/volume_price.js"></script>-->
     <script type="text/javascript" src="/web/building_saas/main/js/models/labour_coe.js"></script>
+    <script type="text/javascript" src="/public/calculate_util.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/models/calc_program.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/models/calc_base.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/models/installation_fee.js"></script>

+ 4 - 2
web/building_saas/main/js/controllers/project_controller.js

@@ -4,7 +4,7 @@
 
 ProjectController = {
     /* sc: tree_sheet_controller */
-    syncDisplayNewNode: function (sc, newNode) {
+    syncDisplayNewNode: function (sc, newNode,callback) {
         TREE_SHEET_HELPER.massOperationSheet(sc.sheet, function () {
             var sels = sc.sheet.getSelections();
             sc.sheet.addRows(newNode.serialNo(), 1);
@@ -14,9 +14,10 @@ ProjectController = {
             //不显示到中间
             //sc.sheet.showRow(newNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
             cbTools.refreshFormulaNodes();
+            if(callback) callback();
         });
     },
-    syncDisplayNewNodes: function (sc, newNodes,withOutSelect=false) {//withOutSelect 不需要自动选中,外面自已处理
+    syncDisplayNewNodes: function (sc, newNodes,withOutSelect=false,callback) {//withOutSelect 不需要自动选中,外面自已处理
         TREE_SHEET_HELPER.massOperationSheet(sc.sheet, function () {
             var sels = sc.sheet.getSelections();
             newNodes.sort(function (a, b) {
@@ -42,6 +43,7 @@ ProjectController = {
             }
 
             cbTools.refreshFormulaNodes();
+            if(callback) callback();
         });
     },
     syncDisplayNewRationGljNode:function (sc,newNode) {

+ 30 - 1
web/building_saas/main/js/models/cache_tree.js

@@ -302,7 +302,36 @@ var cacheTree = {
             }
             return node;
         };
-
+        // 节点所属固定ID
+        Node.prototype.getFlag = function () {
+            if (!this.data || !this.data.flags || !this.data.flags[0] || !this.data.flags[0].flag) {
+                return 0;
+            }
+            return this.data.flags[0].flag;
+        };
+        // 节点所属固定ID
+        Node.prototype.belongToFlag = function () {
+            let node = this;
+            while (node) {
+                if (node.data && node.data.flags && node.data.flags[0] && node.data.flags[0].flag) {
+                    return node.data.flags[0].flag;
+                }
+                node = node.parent;
+            }
+            return null;
+        };
+        // 节点是否属于某些固定ID,会一直向上找,直到找到或到达顶层
+        Node.prototype.isBelongToFlags = function (flags) {
+            let node = this;
+            while (node) {
+                const flag = node.getFlag();
+                if (flags.includes(flag)) {
+                    return true;
+                }
+                node = node.parent;
+            }
+            return false;
+        }
         var Tree = function (owner) {
             this.owner = owner;
             this.nodes = {};

+ 10 - 10
web/building_saas/main/js/models/calc_base.js

@@ -370,7 +370,7 @@ let cbTools = {
     refreshFormulaNodes: function () {
         try {
             let nodes = this.getFormulaNodes();
-            if (nodes.length > 0) projectObj.mainController.refreshTreeNode(nodes);
+            if (nodes.length > 0) projectObj.mainController.refreshTreeNode(nodes,false,false);
         } catch (err) {
             alert('公式引用行号显示刷新失败:' + err.message);
         }
@@ -601,58 +601,58 @@ let baseFigureTemplate = {
         //使用累进办法计算,基数为{定额建筑安装工程费(不含定额设备购置费及专项费用)}
         'SGCDJSF': function (tender) {
             let baseFee = this['DEJZAZGCFBHSBZX'](tender);
-            return cbTools.getProgressiveFee(baseFee, '施工场地建设费');
+            return calculateUtil.getProgressiveFee(baseFee, '施工场地建设费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         //{养护单位(业主)管理费}
         // 使用累进办法计算,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'YHDWYZGLF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '养护单位(业主)管理费');
+            return calculateUtil.getProgressiveFee(baseFee, '养护单位(业主)管理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         //{信息化费}
         // 使用累进办法计算,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'XXHF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '信息化费');
+            return calculateUtil.getProgressiveFee(baseFee, '信息化费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         //{路线工程监理费}
         //使用累进办法计算,不足2万按2万,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'LXGCJLF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender),
-                fee = cbTools.getProgressiveFee(baseFee, '路线工程监理费');
+                fee = calculateUtil.getProgressiveFee(baseFee, '路线工程监理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 20000 ? 20000 : fee;
         },
         //{独立桥梁隧道工程监理费}
         //使用累进办法计算,不足2万按2万,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'QLSDGCJLF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender),
-                fee = cbTools.getProgressiveFee(baseFee, '独立桥梁隧道工程监理费');
+                fee = calculateUtil.getProgressiveFee(baseFee, '独立桥梁隧道工程监理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 20000 ? 20000 : fee;
         },
         //{设计文件审查费}
         // 使用累进办法计算,不足3千按3千,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'SJWJSCF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender),
-                fee = cbTools.getProgressiveFee(baseFee, '设计文件审查费');
+                fee = calculateUtil.getProgressiveFee(baseFee, '设计文件审查费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 3000 ? 3000 : fee;
         },
         //{路线勘察设计费}
         // 使用累进办法计算,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'LXKCSJF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '路线勘察设计费');
+            return calculateUtil.getProgressiveFee(baseFee, '路线勘察设计费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         //{独立桥梁隧道维修加固勘察设计费}
         // 使用累进办法计算,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'QLSDKCSJF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '独立桥梁隧道维修加固勘察设计费');
+            return calculateUtil.getProgressiveFee(baseFee, '独立桥梁隧道维修加固勘察设计费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         //{招标代理及标底(最高投标限价)编制费} (招标代理及标底编制费ZBDLJBDBZF)
         // 使用累进办法计算,计算基数为{定额建筑安装工程(其中定额设备购置费按 40%计)}
         'ZBDLJBDBZF': function (tender) {
             let baseFee = this['DEJZAZGCSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '招标代理及标底(最高投标限价)编制费');
+            return calculateUtil.getProgressiveFee(baseFee, '招标代理及标底(最高投标限价)编制费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         //{价差预备费}
         //以建筑安装工程费为基数

+ 14 - 14
web/building_saas/main/js/models/calc_program.js

@@ -1474,16 +1474,16 @@ class CalcProgram {
 
 
         // 存储费率临时数据,报表用。
-        if (me.saveForReports.length > 0){
-            let saveDatas = {};
-            saveDatas.projectID = projectObj.project.projectInfo.ID;
-            saveDatas.calcItems = me.saveForReports;
-            CommonAjax.post('/calcProgram/saveCalcItems', saveDatas, function (result) {
-                if (result){
-                    me.saveForReports = [];
-                };
-            });
-        };
+        // if (me.saveForReports.length > 0){
+        //     let saveDatas = {};
+        //     saveDatas.projectID = projectObj.project.projectInfo.ID;
+        //     saveDatas.calcItems = me.saveForReports;
+        //     CommonAjax.post('/calcProgram/saveCalcItems', saveDatas, function (result) {
+        //         if (result){
+        //             me.saveForReports = [];
+        //         };
+        //     });
+        // };
     };
 
     compilePublics(){
@@ -1583,9 +1583,9 @@ class CalcProgram {
                     let cfr = me.compiledFeeRates[item.feeRateID];
                     item.feeRate = cfr ? cfr.rate : 100;
 
-                    if (!orgFeeRate || (orgFeeRate && orgFeeRate != item.feeRate)){
-                        me.saveForReports.push({templatesID: template.ID, calcItem: item});
-                    }
+                    // if (!orgFeeRate || (orgFeeRate && orgFeeRate != item.feeRate)){
+                    //     me.saveForReports.push({templatesID: template.ID, calcItem: item});
+                    // }
                 };
 
                 // 字段名映射
@@ -1910,7 +1910,7 @@ class CalcProgram {
             if(callback){
                 callback(data);
             };
-            projectObj.mainController.refreshTreeNode(treeNodes);
+            projectObj.mainController.refreshTreeNode(treeNodes,false,false);
             // 批量树结点计算后,计算程序早已物是人非,所以这里要重新计算一下。警告:第二个参数千万不能改成3,否则死循环!
             if (activeSubSheetIsCalcProgram())
                 calcProgramObj.refreshCalcProgram(projectObj.project.mainTree.selected, 2);

+ 2 - 1
web/building_saas/main/js/models/fee_rate.js

@@ -213,7 +213,7 @@ var FeeRate = {
            this.onFeeRatesChange([{rateID:rateID,value:value}]);
         };
 
-        FeeRate.prototype.onFeeRatesChange = function (infos) {//{rateID:'AAAA',value:23}
+        FeeRate.prototype.onFeeRatesChange =async function (infos) {//{rateID:'AAAA',value:23}
             let node = project.mainTree.selected;
             let me = this;
             let rateMap = {};
@@ -227,6 +227,7 @@ var FeeRate = {
                     calcProgramObj.refreshCalcProgram(node, 3);
                 }
             }
+            await project.projectGLJ.calcAllWhenFeeRateChange();
             project.calcProgram.calcAllNodesAndSave(calcAllType.catAll,function () {
                 $.bootstrapLoading.start();
                 project.markUpdateProject({projectID:project.ID(),feeRateID:me.getActivateFeeRateFileID()},"feeRate",function () {

+ 6 - 0
web/building_saas/main/js/models/main_consts.js

@@ -271,6 +271,12 @@ const fixedFlag = {
     ONE_TO_THREE_TOTAL: 29,
     // 前期工作费
     PRELIMINARY_WORK: 30,
+    // 小修费
+    MINOR_REPAIR_FEE: 31,
+    // 预防养护费
+    PREVENTIVE_MAINTENANCE_FEE: 32,
+    // 修复养护费
+    REPAIR_MAINTENANCE_FEE: 33,
 };
 
 const gljKeyArray =['code','name','specs','unit','type'];

+ 27 - 9
web/building_saas/main/js/models/project_glj.js

@@ -336,7 +336,7 @@ ProjectGLJ.prototype.updateCalcMaterial =async function (projectGLJ,updateField,
     }
 };
 
-ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs) {//当材料发生改变时重算所有计算材料的价格
+ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs,isFromFeeRate) {//当材料发生改变时重算所有计算材料的价格
     let priceMap = {};
     for(let unitPrice of unitPriecs){
         if(unitPrice && gljUtil.isDef(unitPrice.doc.market_price)) priceMap[unitPrice.projectGLJID] = unitPrice.doc.market_price;
@@ -347,6 +347,9 @@ ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs) {//当材料发生
     let sumOriginalUpdateMap = {};//存放所有材料下的原价更新信息;
 
     for(let g of this.datas.gljList){
+        if(g.code == "2001001"){
+          console.log("calcMat")
+        }
         if(g.quantity !== 0 && g.quantity !== '0'  && g.unit_price.calcMaterial == 1&&!priceMap[g.id]){//工料机本身不包含在要计算的列表内
             //先判断要不要重算,只有包含相关工料机的定额的材料,才需要重新计算
             let fOrPDataMap = {};
@@ -371,7 +374,7 @@ ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs) {//当材料发生
             if(originalListMap[tIndex]){ //运费重算
                 let originalUpdateMap = {};
                 for(let o of originalListMap[tIndex]){
-                    let calo = ifNeedReCalc(o,priceMap);
+                    let calo = ifNeedReCalc(o,priceMap,isFromFeeRate);
                     if(calo){
                         let o_task = this.calcEachFreightOrPrice(o,"price",priceMap);
                         if(o_task){
@@ -402,8 +405,9 @@ ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs) {//当材料发生
     return [unitPriecs,sumUpdateMap];
 
 
-    function ifNeedReCalc(obj,IDMap) {
+    function ifNeedReCalc(obj,IDMap,isFromFeeRate = false) {
         let calcf = false;
+        if(isFromFeeRate == true) return true;//如果是从费率过来的,默认自动计算
         if(obj.ration_gljs){
             for(let rg of  obj.ration_gljs){
                 if(IDMap[rg.projectGLJID]){//判断是否关联了该项目工料机
@@ -569,14 +573,14 @@ ProjectGLJ.prototype.priceCalc = function (glj,dataMap,tpriceList) {
 };
 
 
-ProjectGLJ.prototype.m_updateUnitPrice = function (datas) {//批量更新
+ProjectGLJ.prototype.m_updateUnitPrice = function (datas,isFromFeeRate) {//批量更新
     let me = this;
     let gljList = [];
     for(let d of datas){
         let g = updateUnit(d.projectGLJID,d);
         if(g) gljList.push(g);
     }
-    this.calcQuantity();
+    if(isFromFeeRate == false)this.calcQuantity();
     //刷新项目工料机表显示
     projectGljObject.refreshDataSheet();
     //重新计算相关节点
@@ -591,7 +595,7 @@ ProjectGLJ.prototype.m_updateUnitPrice = function (datas) {//批量更新
     //刷新定额工料机
     gljOprObj.refreshView();
     //socket推送更新信息
-    projectGljObject.onUnitFileChange(true);
+    projectGljObject.onUnitFileChange(isFromFeeRate?false:true);//如果是从更新费率过来的,不用在项目属性上做标记,费率那里已经自动计算所有节点了
 
 
     function updateUnit(id,unitPrice) {
@@ -724,14 +728,14 @@ ProjectGLJ.prototype.refreshEctrovalenceCache = function (updateMap) {
 };
 
 
-ProjectGLJ.prototype.refreshMaterialCalcCache = function (updateMap){
+ProjectGLJ.prototype.refreshMaterialCalcCache = function (updateMap,isFromFeeRate = false){//从费率计算过来的时候,费率那里已经做了自动计算所有节点了
    if(updateMap["freight"]) updateList(this.datas.freightList,updateMap["freight"]);
    if(updateMap["price"]) updateList(this.datas.originalList,updateMap["price"]);
    if(updateMap["unitPrice"]) {
       if(Array.isArray(updateMap["unitPrice"])){
-          this.m_updateUnitPrice(updateMap["unitPrice"]);
+          this.m_updateUnitPrice(updateMap["unitPrice"],isFromFeeRate);
       }else {
-          this.m_updateUnitPrice([updateMap["unitPrice"]]);
+          this.m_updateUnitPrice([updateMap["unitPrice"]],isFromFeeRate);
       }
    }
 
@@ -811,6 +815,20 @@ ProjectGLJ.prototype.changeAssistProductionFeeRate = async function (newFeeRate)
     }
 };
 
+ProjectGLJ.prototype.calcAllWhenFeeRateChange = async function(){//当高原相关费率改变时,重新计算所有材料
+  try {
+    let [unitPrices,sumMap] = this.calcAllMaterial([],true);
+    if(unitPrices.length > 0){
+      sumMap["unitPrice"] = unitPrices;
+      let result = await ajaxPost('/glj/updateMaterialCalcTasks',sumMap);
+      this.refreshMaterialCalcCache(sumMap,true);
+      materialCalcObj.showDatas();
+    }
+}catch (err){
+    console.log(err);
+}
+}
+
 ProjectGLJ.prototype.loadFreightAndOriginalData = function(originalList,freightList){
   if(freightList) this.datas.freightList = freightList;
   if(originalList) this.datas.originalList = originalList;

+ 5 - 3
web/building_saas/main/js/models/ration.js

@@ -632,9 +632,11 @@ var Ration = {
                 newNode.source = newSource;
                 newNode.sourceType = project.Ration.getSourceType();
                 newNode.data = newSource;
-                ProjectController.syncDisplayNewNode(sheetController, newNode);
-                project.ration_glj.addToMainTree(data.ration_gljs);
-                projectObj.mainController.refreshTreeNode([newNode], false);
+                ProjectController.syncDisplayNewNode(sheetController, newNode,function(){
+                  project.ration_glj.addToMainTree(data.ration_gljs);
+                  projectObj.mainController.refreshTreeNode([newNode], false);
+                });
+                
             }
         };
         ration.prototype.addNewRationFast = function (rationType,callback) {

+ 4 - 4
web/building_saas/main/js/views/fee_rate_view.js

@@ -528,7 +528,7 @@ var feeRateObject={
         }
         if(updateDatas.length > 0){
             $.bootstrapLoading.start();
-            feeRate.updateFeeRatesByIDs(updateDatas,function () {
+            feeRate.updateFeeRatesByIDs(updateDatas,async function () {
                 let feerateInfo = [];
                 for(let u of updateDatas){
                     let row = _.findIndex(me.mainFeeRateData,{'ID':u.rateID})
@@ -540,7 +540,7 @@ var feeRateObject={
                         }
                     }
                 }
-                feerateInfo.length > 0 ?feeRate.onFeeRatesChange(feerateInfo):'';
+                feerateInfo.length > 0 ?await feeRate.onFeeRatesChange(feerateInfo):'';
                 $.bootstrapLoading.end();
             })
         }
@@ -638,7 +638,7 @@ var feeRateObject={
          }
         if(updateDatas.length > 0){
             $.bootstrapLoading.start();
-            feeRate.updateFeeRatesByIDs(updateDatas,function () {
+            feeRate.updateFeeRatesByIDs(updateDatas,async function () {
                 let feerateInfo = [];
                 for(let u of updateDatas){
                     feerateInfo.push({rateID:u.rateID,value:u.doc["rate"]});
@@ -646,7 +646,7 @@ var feeRateObject={
                 subRateObject.showSubRateData();
                 me.showMainFeeRateData();
                 if(feerateInfo.length > 0){
-                    feeRate.onFeeRatesChange(feerateInfo);
+                    await feeRate.onFeeRatesChange(feerateInfo);
                 }else {
                     $.bootstrapLoading.end();
                 }

+ 4 - 7
web/building_saas/main/js/views/glj_view.js

@@ -488,6 +488,8 @@ var gljOprObj = {
         }
     },
     showDataIfRationSelect: function (node,selectedNodeId) {
+        this.sheet.suspendPaint();
+        this.sheet.suspendEvent();
         var isShow = false;
         if(projectReadOnly && this.setting.view.lockColumns){
             this.setting.view.lockColumns = null;
@@ -531,9 +533,8 @@ var gljOprObj = {
             this.clearSheetData();
             MaterialController.hideReplaceDiv();
         }
-        //子目换算
-        //zmhs_obj.showZMHSData(node);
-        //   $('#dropdown').hide();
+        this.sheet.resumeEvent();
+        this.sheet.resumePaint();
     },
     showMixRatio:function (node) {//显示组成物到定额工料机
         let mixRatioMap = projectObj.project.projectGLJ.datas.mixRatioMap;
@@ -628,8 +629,6 @@ var gljOprObj = {
         return codeMap;
     },
     initRationTree: function (init,codeMap) {
-        this.sheet.suspendPaint();
-        this.sheet.suspendEvent();
         this.sheet.setRowCount(this.sheetData.length >30?this.sheetData.length:30);
         for (var i = 0; i < this.sheetData.length; i++) {
             let options = this.getCodeOptions(this.sheetData[i],codeMap);
@@ -641,8 +640,6 @@ var gljOprObj = {
                 }
             }
         }
-        this.sheet.resumeEvent();
-        this.sheet.resumePaint();
     },
     getCodeOptions:function (recode,codeMap) {
         let options = [];

+ 9 - 7
web/building_saas/main/js/views/project_view.js

@@ -246,13 +246,15 @@ var projectObj = {
     setActiveCell(field, moveScroll){
         projectObj.mainSpread.focus(true);
         let mainSheet = projectObj.mainSpread.getActiveSheet();
-        let fieldCol = colSettingObj.getColByField(field);
-        if(fieldCol){
-            if(moveScroll){
-                mainSheet.showColumn(fieldCol, GC.Spread.Sheets.HorizontalPosition.center);
-            }
-            mainSheet.setActiveCell(projectObj.project.mainTree.selected.serialNo(), fieldCol);
-        }
+        TREE_SHEET_HELPER.massOperationSheet(mainSheet,function(){
+          let fieldCol = colSettingObj.getColByField(field);
+          if(fieldCol){
+              if(moveScroll){
+                  mainSheet.showColumn(fieldCol, GC.Spread.Sheets.HorizontalPosition.center);
+              }
+              mainSheet.setActiveCell(projectObj.project.mainTree.selected.serialNo(), fieldCol);
+          }
+        });
     },
     //获取粘贴更改的单元格(粘贴时,跳过隐藏行)
     checkSpreadChangedCells: function (info) {

+ 19 - 7
web/building_saas/main/js/views/std_billsGuidance_lib.js

@@ -130,10 +130,22 @@ const billsGuidance = (function () {
             }
         }
     };
+    // 获取对比树片段数据的方法,此方法可能会被覆盖,方法存在一个对象中,使得外部可以覆盖相关方法
+    // 在农村公路2020中,主树对比片段与这里的逻辑是不相同的
+    const overwrite = {
+        getFragment() {
+            return { parent: null, mainTreeFragment: projectObj.project.Bills.tree.roots }
+        }
+    };
     //插入清单
     function insertBills(lowestNodes) {
         let selTree = getSelTree(lowestNodes);
-        let compareData = compareTree(projectObj.project.Bills.tree, selTree);
+        const { errMsg, parent, mainTreeFragment } = overwrite.getFragment();
+        if (errMsg) {
+            alert(errMsg);
+            return;
+        }
+        let compareData = compareTree(parent, mainTreeFragment, selTree.roots);
         let sheet = projectObj.mainSpread.getActiveSheet(),
             row = sheet.getActiveColumnIndex(),
             col = sheet.getActiveColumnIndex();
@@ -260,17 +272,17 @@ const billsGuidance = (function () {
     function getMatchContent(node) {
         return `${node.data.code ? node.data.code : '*'}-${node.data.name ? node.data.name : '*'}-${node.data.unit ? node.data.unit: '*'}`;
     }
-    //**对比清单主树与选中树,获取需要更新和插入的数据**
-    //@param {Object}mainTree {Object}selTree
-    function compareTree(billsTree, selTree) {
+    //**对比清单主树节点片段与选中树节点片段,获取需要更新和插入的数据**
+    //@param {Object}parent - 主树片段的父节点 {Array}mainTreeFragment {Array}stdTreeFragment
+    function compareTree(parent, mainTreeFragment, stdTreeFragment) {
         //需要插入、更新的数据
         let postData = [],
             //跟树结构自动定位至的清单节点(最近匹配到的节点)
             locateNode = null;
-        if (!billsTree || !selTree) {
+        if (!mainTreeFragment || !stdTreeFragment) {
             return postData;
         }
-        comparePeer(null, billsTree.roots, selTree.roots);
+        comparePeer(parent, mainTreeFragment, stdTreeFragment);
         return {postData, locateNode};
         /*
         * 该清单节点是否可以继续往下递归匹配,即该节点是否还可插入子项(在该层匹配到的时候判断)
@@ -1162,7 +1174,7 @@ const billsGuidance = (function () {
         }
     }
 
-    return {initViews, bindBtn, refreshWorkBook, setColumnWidthByRate, locateAtBills, bills, elfItem};
+    return {initViews, bindBtn, refreshWorkBook, setColumnWidthByRate, locateAtBills, bills, elfItem, overwrite};
 })();
 
 $(document).ready(function(){

+ 4 - 4
web/building_saas/main/js/views/zmhs_view.js

@@ -147,6 +147,8 @@ let zmhs_obj = {
         let selected = node?node:projectObj.project.mainTree.selected;
         let ration_coe = projectObj.project.ration_coe;
         let coeList = [];
+        this.coeSheet.suspendPaint();
+        this.coeSheet.suspendEvent();
         if(selected&&selected.sourceType == "ration"){
             let ration = selected.data;
             let assList = this.getAssList(node); //2019-01-23 新需求,将辅助定额合并到一个表显示  -- 20191206
@@ -158,8 +160,6 @@ let zmhs_obj = {
         this.coeSheet.setRowCount(0);
         sheetCommonObj.showData(this.coeSheet, this.coeSetting,coeList);
         if (coeList.length > 0) {
-            this.coeSheet.suspendPaint();
-            this.coeSheet.suspendEvent();
             for(let i =0;i<coeList.length;i++ ){
                 if(gljUtil.isDef(coeList[i].option_codes)&&coeList[i].option_codes!=""){
                     this.getComboBoxForCodes(coeList[i],i);//设置可选类型的下拉框
@@ -171,8 +171,6 @@ let zmhs_obj = {
                     this.coeSheet.setCellType(i, 1, sheetCommonObj.getCustomerCoeCellType(this.generateHtmlString,this.bindCusEditorValue,this.updateCusCoeAfterEditor), GC.Spread.Sheets.SheetArea.viewport);
                 }
             }
-            this.coeSheet.resumeEvent();
-            this.coeSheet.resumePaint();
         }
         this.coeSheetData = coeList;
         if(projectReadOnly){
@@ -181,6 +179,8 @@ let zmhs_obj = {
         if(preSelections){//定位光标到之前的位置
             this.coeSheet.setSelection(preSelections[0].row,preSelections[0].col,preSelections[0].rowCount,preSelections[0].colCount);
         }
+        this.coeSheet.resumeEvent();
+        this.coeSheet.resumePaint();
     },
     showCusData:function (node) {
         let selected = node?node:projectObj.project.mainTree.selected;

+ 1 - 1
web/common/components/share/index.js

@@ -180,7 +180,7 @@ const SHARE_TO = (() => {
                                                 <label class="custom-control-label" for="allow-copy">允许拷贝</label>
                                             </div>
                                             <div class="custom-control custom-checkbox">
-                                                <input type="checkbox" class="custom-control-input" id="allow-edit" checked="">
+                                                <input type="checkbox" class="custom-control-input" id="allow-edit">
                                                 <label class="custom-control-label" for="allow-edit">允许编辑</label>
                                             </div>
                                         </div>

+ 6 - 6
web/over_write/js/anhui_2019.js

@@ -268,34 +268,34 @@ if (typeof baseFigureTemplate !== 'undefined') {
         // 施工场地建设费 算法:以{定额建筑安装工程费(不含定额设备购置费及专项管理费) }为基数,采用累进办法计算。
         SGCDJSF(tender) {
             const baseFee = this['DEJZAZGCFBHSBZXGLF'](tender);
-            return cbTools.getProgressiveFee(baseFee, '施工场地建设费');
+            return calculateUtil.getProgressiveFee(baseFee, '施工场地建设费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 养护单位管理费 算法:以{定额建筑安装工程费(不含专项管理费) }为基数,采用累进办法计算。
         YHDWGLF(tender) {
             const baseFee = this['DEJZAZGCFBHZXGLF'](tender);
-            return cbTools.getProgressiveFee(baseFee, '养护单位管理费');
+            return calculateUtil.getProgressiveFee(baseFee, '养护单位管理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 养护项目信息化费 算法:以{定额建筑安装工程费}为基数,采用累进办法计算。(不足20000元时按20000元计算)
         YHXMXXHF(tender) {
             const baseFee = this['DEJZAZGCF'](tender);
-            const fee = cbTools.getProgressiveFee(baseFee, '养护项目信息化费');
+            const fee = calculateUtil.getProgressiveFee(baseFee, '养护项目信息化费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 20000 ? 20000 : fee;
         },
         // 工程监理费 算法:以{定额建筑安装工程费(不含专项管理费) }为基数,采用累进办法计算。(不足20000元时按20000元计算)
         GCJLF(tender) {
             const baseFee = this['DEJZAZGCFBHZXGLF'](tender);
-            const fee = cbTools.getProgressiveFee(baseFee, '工程监理费');
+            const fee = calculateUtil.getProgressiveFee(baseFee, '工程监理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 20000 ? 20000 : fee;
         },
         // 设计文件审查费 算法:以{定额建筑安装工程费(不含专项管理费) }为基数,采用累进办法计算。
         SJWJSCF(tender) {
             const baseFee = this['DEJZAZGCFBHZXGLF'](tender);
-            return cbTools.getProgressiveFee(baseFee, '设计文件审查费');
+            return calculateUtil.getProgressiveFee(baseFee, '设计文件审查费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 前期工作费 算法:以{定额建筑安装工程费(不含专项管理费) }为基数,采用累进办法计算。(不足30000元时按30000元计算)
         QQGZF(tender) {
             const baseFee = this['DEJZAZGCFBHZXGLF'](tender);
-            const fee = cbTools.getProgressiveFee(baseFee, '前期工作费');
+            const fee = calculateUtil.getProgressiveFee(baseFee, '前期工作费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 30000 ? 30000 : fee;
         },
         /*  价差预备费 算法:以建筑安装工程费为基数,按设计文件编制年始至养护项目工程竣工年终的年数和年工程造价增涨率计算。

+ 5 - 5
web/over_write/js/neimeng_2019.js

@@ -238,28 +238,28 @@ if (typeof baseFigureTemplate !== 'undefined') {
         // 养护单位项目管理费:以累进办法计算,计算基数为“定额建筑安装工程费(定额设备购置费按40%计)
         YHDWXMGLF(tender) {
             const baseFee = this['DEJZAZGCFSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '养护单位项目管理费');
+            return calculateUtil.getProgressiveFee(baseFee, '养护单位项目管理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 信息化费:以累进办法计算,计算基数为“定额建筑安装工程费(定额设备购置费按40%计)”
         XXHF(tender) {
             const baseFee = this['DEJZAZGCFSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '信息化费');
+            return calculateUtil.getProgressiveFee(baseFee, '信息化费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 工程监理费:以累进办法计算,计算基数为“定额建筑安装工程费(定额设备购置费按40%计)
         GCJLF(tender) {
             const baseFee = this['DEJZAZGCFSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '工程监理费');
+            return calculateUtil.getProgressiveFee(baseFee, '工程监理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 设计文件审查费:以累进办法计算,计算基数为“定额建筑安装工程费(定额设备购置费按40%计)”;设计文件审查费不足3000元的,按3000元计算
         SJWJSCF(tender) {
             const baseFee = this['DEJZAZGCFSBSS'](tender);
-            const fee = cbTools.getProgressiveFee(baseFee, '设计文件审查费');
+            const fee = calculateUtil.getProgressiveFee(baseFee, '设计文件审查费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
             return fee > 0 && fee < 3000 ? 3000 : fee;
         },
         // 前期工作费:以累进办法计算,计算基数为“定额建筑安装工程费(定额设备购置费按40%计)
         QQGZF(tender) {
             const baseFee = this['DEJZAZGCFSBSS'](tender);
-            return cbTools.getProgressiveFee(baseFee, '前期工作费');
+            return calculateUtil.getProgressiveFee(baseFee, '前期工作费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 价差预备费
         JCYBF(tender) {

+ 91 - 0
web/over_write/js/nongcun_2020.js

@@ -0,0 +1,91 @@
+// 农村公路养护2020
+
+// 覆盖标准清单添加逻辑(实质上是覆盖获取对比节点片段方法)
+(() => {
+    const functionMap = {
+        /**
+         * 获取操作<<农村公路养护预算费用库>>时的节点片段
+         * 片段为主树根节点(与其他费用定额的添加逻辑一致)
+         */
+        budeget() {
+            return { parent: null, mainTreeFragment: projectObj.project.Bills.tree.roots };
+        },
+        /**
+         * 获取操作<<农村公路小修工程量清单库>>时的节点片段
+         * 1.主树焦点行必须在固定清单“小修费”的子节点xx项目清单范围内,范围内指清单本身及其所有后代节点
+         *   小修费下某清单为xx项目清单的判定规则:小修费下名称不为“道路”、“桥梁”、“隧道”的清单
+         * 2.若焦点行在无效范围中,插入时弹出提示
+         */
+        minorRepair() {
+            const selected = projectObj.project.mainTree.selected;
+            // 获取焦点所属的小修费下的xx项目(同时不为“道路”、“桥梁”、“隧道”)
+            const targetNode = getTargetNode(selected);
+            if (!targetNode) {
+                return { errMsg: '当前位置不可添加小修工程量清单,请选中小修费下的项目名称,再添加工程量清单。' };
+            }
+            return { parent: targetNode, mainTreeFragment: targetNode.children || [] };
+
+            function getTargetNode(node) {
+                const exclusion = ['道路', '桥梁', '隧道'];
+                while (node) {
+                    const isTheNode = node.parent && node.parent.getFlag() === fixedFlag.MINOR_REPAIR_FEE && !exclusion.includes(node.data.name);
+                    if (isTheNode) {
+                        return node;
+                    }
+                    node = node.parent;
+                }
+                return null;
+            }
+        },
+        /**
+         * 获取操作<<农村公路养护工程工程量清单>>的节点片段
+         * 1.主树焦点行必须在固定清单“预防养护费”或者“修复养护费”下的建筑安装工程费清单范围内(多少层都可以,子代、孙子代...),范围内指清单本身及其所有后代节点
+         * 2.若焦点行在无效范围中,插入时弹出提示
+         */
+        maintain() {
+            const selected = projectObj.project.mainTree.selected;
+            const targetNode = getTargetNode(selected);
+            if (!targetNode) {
+                return { errMsg: '当前位置不可添加养护工程量清单,请选择中建筑安装工程费,再添加工程量清单。' };
+            }
+            return { parent: targetNode, mainTreeFragment: targetNode.children || [] };
+
+            function getTargetNode(node) {
+                const targetName = '建筑安装工程费';
+                while (node) {
+                    const isTheNode = node.data.name === targetName && node.isBelongToFlags([fixedFlag.PREVENTIVE_MAINTENANCE_FEE, REPAIR_MAINTENANCE_FEE]);
+                    if (isTheNode) {
+                        return node;
+                    }
+                    node = node.parent;
+                }
+                return null;
+            }
+        }
+    };
+    // 获取当前操作的清单库的方法
+    function getFragmentFunction() {
+        const selLibText = $('#stdBillsGuidanceLibSelect').text();
+        if (/农村公路养护预算费用/.test(selLibText)) {
+            return functionMap.budeget;
+        } else if (/农村公路小修工程量清单/.test(selLibText)) {
+            return functionMap.minorRepair;
+        } else if (/农村公路养护工程工程量清单/.test(selLibText)) {
+            return functionMap.maintain;
+        } else {
+            return null;
+        }
+    }
+    // 获取对比的主树节点片段
+    function overwrite() {
+        if (typeof billsGuidance !== 'undefined') {
+            // 不同的清单库插入获取片段都不同,先获取是哪个库进行操作
+            const func = getFragmentFunction();
+            if (func) {
+                billsGuidance.overwrite.getFragment = func;
+            }
+        }
+    }
+
+    overwrite();
+})();

+ 1 - 1
web/over_write/js/zhejiang_2005.js

@@ -139,7 +139,7 @@ if (typeof baseFigureTemplate !== 'undefined') {
         // 养护工程管理经费:取清单固定类别是“建筑安装工程费”金额为基数,采用累进办法计算
         YHGCGLJF(tender) {
             const baseFee = this['GLYHGCF'](tender);
-            return cbTools.getProgressiveFee(baseFee, '养护工程管理费');
+            return calculateUtil.getProgressiveFee(baseFee, '养护工程管理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice);
         },
         // 一二三部分合计:取清单固定类别是“一二三部分合计”的金额
         YESBFHJ(tender) {