Jelajahi Sumber

建筑计算接口拆分(从养护同步)

chenshilong 5 tahun lalu
induk
melakukan
b0d1abc65a
1 mengubah file dengan 243 tambahan dan 339 penghapusan
  1. 243 339
      web/building_saas/main/js/models/calc_program.js

+ 243 - 339
web/building_saas/main/js/models/calc_program.js

@@ -3,92 +3,6 @@
  * 计算程序。所有定额、清单、父清单的计算都从此入。
  */
 
-/*  新版GLD 取消了默认清单模板,所以这里废弃。先留着,预防不时之需。
-let defaultBillTemplate = {
-    ID: 15,
-    name: "清单公式",
-    calcItems: [
-        {
-            ID: 1,
-            code: "1",
-            name: "定额直接费",
-            dispExpr: "F2+F3+F4",
-            statement: "人工费+材料费+机械费",
-            feeRate: null,
-            memo: ''
-        },
-        {
-            ID: 2,
-            code: "1.1",
-            name: "人工费",
-            dispExpr: "HJ",
-            statement: "合计",
-            feeRate: 50,
-            fieldName: 'labour',
-            memo: ''
-        },
-        {
-            ID: 3,
-            code: "1.2",
-            name: "材料费",
-            dispExpr: "HJ",
-            statement: "合计",
-            feeRate: 30,
-            fieldName: 'material',
-            memo: ''
-        },
-        {
-            ID: 4,
-            code: "1.3",
-            name: "机械费",
-            dispExpr: "HJ",
-            statement: "合计",
-            feeRate: 20,
-            fieldName: 'machine',
-            memo: ''
-        },
-        {
-            ID: 5,
-            code: "2",
-            name: "企业管理费",
-            dispExpr: "F1",
-            statement: "定额直接费",
-            feeRate: null,
-            fieldName: 'manage',
-            memo: ''
-        },
-        {
-            ID: 6,
-            code: "3",
-            name: "利润",
-            dispExpr: "F1",
-            statement: "定额直接费",
-            feeRate: null,
-            fieldName: 'profit',
-            memo: ''
-        },
-        {
-            ID: 7,
-            code: "4",
-            name: "风险费用",
-            dispExpr: "F1",
-            statement: "定额直接费",
-            feeRate: null,
-            fieldName: 'risk',
-            memo: ''
-        },
-        {
-            ID: 8,
-            code: "5",
-            name: "综合单价",
-            dispExpr: "F1+F5+F6+F7",
-            statement: "定额直接费+企业管理费+利润+风险费用",
-            feeRate: null,
-            fieldName: 'common',
-            memo: ''
-        }
-    ]
-};*/
 let calcTools = {
     getNodeByFlag: function (flag) {
         let bill = cbTools.findBill(flag);
@@ -284,7 +198,7 @@ let calcTools = {
         if (!feeObj) return;
         if (feeObj.fieldName == '') return;
 
-        // 初始化前先拦截末定义的情况
+        // 初始化前先拦截属性末定义、又要给该属性赋0的情况
         if (!treeNode.data.feesIndex || !treeNode.data.feesIndex[feeObj.fieldName]){
             if (feeObj.unitFee == 0 && feeObj.totalFee == 0 && feeObj.tenderUnitFee == 0 && feeObj.tenderTotalFee == 0) return;
         }
@@ -1126,12 +1040,14 @@ let calcTools = {
         }
         return totalFee > maxPrice;
     },
+    
     getTenderCalcType: function () {
         let tenderSetting = projectObj.project.property.tenderSetting;
         let ct = tenderSetting && tenderSetting.calcPriceOption? tenderSetting.calcPriceOption : "coeBase";
         if (ct == 'priceBase') ct = 'priceBase_RCJ';   // 兼容旧项目
         return ct;
     }
+
 };
 
 let rationCalcBases = {
@@ -1372,8 +1288,8 @@ let analyzer = {
             for (let base of arrBase){
                 let baseName = base.slice(1, -1);
                 if (!rationCalcBases[baseName]){
-                    analyzer.error = `定额基数${hintBox.font('[' +baseName + ']')}定义!`;
-                    // hintBox.infoBox('错误提示', `定额基数${hintBox.font('[' +baseName + ']')}定义!`, 1);
+                    analyzer.error = `定额基数${hintBox.font('[' +baseName + ']')}定义!`;
+                    // hintBox.infoBox('错误提示', `定额基数${hintBox.font('[' +baseName + ']')}定义!`, 1);
                     return false;
                 }
             };
@@ -1587,7 +1503,7 @@ let executeObj = {
             return calcTools.marketPriceToBase(me.treeNode, baseName, isTender)
         else{
             if (!rationCalcBases[baseName]){
-                hintBox.infoBox('系统提示', '定额基数“' + baseName + '”定义,计算错误。 (模板 ' + me.template.ID + ',规则 ' + me.tempCalcItem.ID +')', 1);
+                hintBox.infoBox('系统提示', '定额基数“' + baseName + '”定义,计算错误。 (模板 ' + me.template.ID + ',规则 ' + me.tempCalcItem.ID +')', 1);
                 return 0;
             }
             else
@@ -1641,7 +1557,7 @@ class CalcProgram {
         me.compiledFeeTypeMaps = {};
         me.compiledFeeTypeNames = [];
         me.compiledCalcBases = {};
-        // me.saveForReports = [];
+
 
         me.feeRates = this.project.FeeRate.datas.rates;
         me.labourCoes = this.project.labourCoe.datas.coes;
@@ -1656,19 +1572,6 @@ class CalcProgram {
         for (let t of me.templates){
             me.compileTemplate(t);
         };
-
-
-        // 存储费率临时数据,报表用。
-        // if (isInit && 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(){
@@ -1805,275 +1708,276 @@ class CalcProgram {
         };
     };
 
+    // 删掉多余的费用。例如:①切换取费类别 ②从其它计算方式(有很多费)切换到公式计算方式(只需要common费),多出来的费要删除。
+    // 如果指定了保留字段,则按用户指定的来。如果没指定保留字段,则按默认的来:总造价清单只留common, estimate两个费用类别。其它公式清单只留common。
+    deleteUselessFees(treeNode, fieldNameArr){
+        if (!(treeNode.data.fees && treeNode.data.fees.length > 0)) return;
+        let keeps = fieldNameArr ? fieldNameArr : [];
+        // 这两个默认是要保留的
+        if (!keeps.includes('common')) keeps.push('common');
+        if (!keeps.includes('estimate')) keeps.push('estimate');
+
+        for (let i = 0; i < treeNode.data.fees.length; i++) {
+            if (!keeps.includes(treeNode.data.fees[i].fieldName)) {
+                delete treeNode.data.feesIndex[treeNode.data.fees[i].fieldName];
+                treeNode.data.fees.splice(i, 1);
+                treeNode.changed = true;
+            }
+        };
+    };
+
+    // 不能直接删除该属性,否则无法冲掉库中已存储的值。下同。
+    deleteProperties (treeNode, propNamesArr){
+        for (let pn of propNamesArr){
+            if (treeNode.data[pn]){
+                treeNode.data[pn] = null;
+                treeNode.changed = true;
+            }
+        };
+    };
+
     // 只计算treeNode自身。changedArr: 外部传来的一个数组,专门存储发生变动的节点。
     innerCalc(treeNode, changedArr, tenderType){
+        if (treeNode.sourceType === ModuleNames.ration_glj) return;             // 仅用作树节点显示的工料机不能参与计算。
+
         let me = this;
-        // 仅用作树节点显示的工料机不能参与计算。
-        if (treeNode.sourceType === ModuleNames.ration_glj) return;
         //设置定额工料机映射表
         me.setRationMap();
+
         treeNode.calcType = calcTools.getCalcType(treeNode);
-        let nQ = calcTools.uiNodeQty(treeNode);
-        let nTQ = calcTools.uiNodeTenderQty(treeNode);
 
-        function isBaseFeeType(type){
-            return ['labour', 'material', 'machine', 'mainMaterial', 'equipment'].indexOf(type) > -1;
-        };
+        // 父清单汇总子清单的费用类别
+        if (treeNode.calcType == treeNodeCalcType.ctGatherBillsFees)
+            me.innerCalcBill(treeNode, 2)
+        // 叶子清单汇总定额的费用类别
+        else if (treeNode.calcType == treeNodeCalcType.ctGatherRationsFees)
+            me.innerCalcBill(treeNode, 1)
+        // 叶子清单:公式计算
+        else if (treeNode.calcType == treeNodeCalcType.ctCalcBaseValue)
+            me.innerCalcBillExpr(treeNode)
+        // 叶子清单:手工修改单价或金额(无定额、无公式计算,什么都没有时)。
+        else if (treeNode.calcType == treeNodeCalcType.ctNull)
+            me.innerCalcBillCustom(treeNode)
+        // 定额:计算程序
+        else
+            me.innerCalcRation(treeNode, tender);
 
-        // 不能直接删除该属性,否则无法冲掉库中已存储的值。下同。
-        function deleteProperties (treeNode, propNamesArr){
-            for (let pn of propNamesArr){
-                if (treeNode.data[pn]){
-                    treeNode.data[pn] = null;
-                    treeNode.changed = true;
-                }
-            };
-        };
+        if (!calcTools.isTotalCostBill(treeNode))  // 已在上面的分支中计算过
+            calcTools.estimateFee(treeNode);
 
-        // 删掉多余的费用。例如:①切换取费类别 ②从其它计算方式(有很多费)切换到公式计算方式(只需要common费),多出来的费要删除。
-        // 如果指定了保留字段,则按用户指定的来。如果没指定保留字段,则按默认的来:总造价清单只留common, estimate两个费用类别。其它公式清单只留common。
-        function deleteUselessFees(treeNode, fieldNameArr){
-            if (!(treeNode.data.fees && treeNode.data.fees.length > 0)) return;
-            let keeps = fieldNameArr ? fieldNameArr : [];
-            // 这两个默认是要保留的
-            if (!keeps.includes('common')) keeps.push('common');
-            if (!keeps.includes('estimate')) keeps.push('estimate');
+        if (treeNode.changed && !changedArr.includes(treeNode)) changedArr.push(treeNode);
+    };
 
-            for (let i = 0; i < treeNode.data.fees.length; i++) {
-                if (!keeps.includes(treeNode.data.fees[i].fieldName)) {
-                    delete treeNode.data.feesIndex[treeNode.data.fees[i].fieldName];
-                    treeNode.data.fees.splice(i, 1);
-                    treeNode.changed = true;
-                }
-            };
+    // 清单部分抽取出来,供分摊清单公用。commonCalcType:1 叶子清单汇总定额的费用类别; 2 父清单汇总子清单的费用类别。3: 分摊:叶子清单汇总定额的费用类别。
+    innerCalcBill(treeNode, commonCalcType, tender = tenderTypes.ttCalc){
+        let me = this;
+        treeNode.data.programID = null;
+        calcTools.initFees(treeNode);
+
+        let nodes = [];
+        if (commonCalcType == 1){
+            calcTools.getGLJList(treeNode, true);
+            nodes = me.project.Ration.getRationNodes(treeNode);
+        }
+        else if (commonCalcType == 2)
+            nodes = treeNode.children
+        else if (commonCalcType == 3)
+            nodes = treeNode.children;
+
+        function isBaseFeeType(type){
+            return ['labour', 'material', 'machine', 'mainMaterial', 'equipment'].indexOf(type) > -1;
         };
 
-        // 叶子清单汇总定额、父清单汇总子清单的费用类别
-        if (treeNode.calcType == treeNodeCalcType.ctGatherBillsFees || treeNode.calcType == treeNodeCalcType.ctGatherRationsFees){
-            treeNode.data.programID = null;
-            calcTools.initFees(treeNode);
+        let rst = [];
+        for (let ft of cpFeeTypes) {
+            let ftObj = {};
+            ftObj.fieldName = ft.type;
+            ftObj.name = ft.name;
+            let buf = 0, btf = 0, btuf = 0, bttf = 0;
 
-            let nodes = [];
-            if (treeNode.calcType == treeNodeCalcType.ctGatherRationsFees){
-                calcTools.getGLJList(treeNode, true);
-                nodes = me.project.Ration.getRationNodes(treeNode);
-            } else { //固定清单材料(工程设备)暂估价比较特殊,不进行父项汇总(需求)
-                nodes = me.project.Bills.getGatherNodes(treeNode);
+            let nQ = calcTools.uiNodeQty(treeNode);
+            let nTQ = calcTools.uiNodeTenderQty(treeNode);
+            let bq = nQ ? nQ : 1;
+            let btq = nTQ ? nTQ : 1;
+
+            if (commonCalcType == 2){
+                for (let node of nodes) {
+                    if (node.data.feesIndex && node.data.feesIndex[ft.type]) {
+                        btf = (btf + parseFloatPlus(node.data.feesIndex[ft.type].totalFee)).toDecimal(decimalObj.process);
+                        bttf = (bttf + parseFloatPlus(node.data.feesIndex[ft.type].tenderTotalFee)).toDecimal(decimalObj.process);
+                    };
+                };
             }
-            //else nodes = treeNode.children;
-
-            let rst = [];
-            for (let ft of cpFeeTypes) {
-                let ftObj = {};
-                ftObj.fieldName = ft.type;
-                ftObj.name = ft.name;
-                let buf = 0, btf = 0, btuf = 0, bttf = 0;
-                let bq = nQ ? nQ : 1;
-                let btq = nTQ ? nTQ : 1;
-
-                if (treeNode.calcType == treeNodeCalcType.ctGatherBillsFees){
-                    for (let node of nodes) {
-                        if (node.data.feesIndex && node.data.feesIndex[ft.type]) { // 父清单不要汇总综合单价。
-                            btf = (btf + parseFloatPlus(node.data.feesIndex[ft.type].totalFee)).toDecimal(decimalObj.process);
-                            bttf = (bttf + parseFloatPlus(node.data.feesIndex[ft.type].tenderTotalFee)).toDecimal(decimalObj.process);
-                        };
+            else if ((commonCalcType == 1) || (commonCalcType == 3)){
+                let sum_rtf = 0, sum_rttf = 0;
+                for (let node of nodes) {
+                    let ruf = 0, rtuf = 0, rtf = 0, rttf = 0;
+                    if (node.data.feesIndex && node.data.feesIndex[ft.type]) {
+                        ruf = parseFloatPlus(node.data.feesIndex[ft.type].unitFee).toDecimal(decimalObj.bills.unitPrice);
+                        rtuf = parseFloatPlus(node.data.feesIndex[ft.type].tenderUnitFee).toDecimal(decimalObj.bills.unitPrice);
+                        rtf = parseFloatPlus(node.data.feesIndex[ft.type].totalFee).toDecimal(decimalObj.bills.totalPrice);
+                        rttf = parseFloatPlus(node.data.feesIndex[ft.type].tenderTotalFee).toDecimal(decimalObj.bills.totalPrice);
                     };
-                }
-                else if (treeNode.calcType == treeNodeCalcType.ctGatherRationsFees){     // 这里的算法要配合冷姐姐的神图才能看懂^_^
-                    let sum_rtf = 0, sum_rttf = 0;
-                    for (let node of nodes) {
-                        let ruf = 0, rtuf = 0, rtf = 0, rttf = 0;
-                        if (node.data.feesIndex && node.data.feesIndex[ft.type]) {
-                            ruf = parseFloatPlus(node.data.feesIndex[ft.type].unitFee).toDecimal(decimalObj.bills.unitPrice);
-                            rtuf = parseFloatPlus(node.data.feesIndex[ft.type].tenderUnitFee).toDecimal(decimalObj.bills.unitPrice);
-                            rtf = parseFloatPlus(node.data.feesIndex[ft.type].totalFee).toDecimal(decimalObj.bills.totalPrice);
-                            rttf = parseFloatPlus(node.data.feesIndex[ft.type].tenderTotalFee).toDecimal(decimalObj.bills.totalPrice);
-                        };
-                        // 取费方式为子目含量,清单行/列的XX单价应 =ROUND( ∑ROUND(定额XX单价*含量,清单单价精度),清单单价精度)
-                        if (me.project.property.billsCalcMode === leafBillGetFeeType.rationContent) {
-                            buf = (buf + (ruf * parseFloatPlus(node.data.contain)).toDecimal(decimalObj.bills.unitPrice)).toDecimal(decimalObj.process);
-                            node.data.tenderContaion = (node.data.tenderQuantity / bq).toDecimal(decimalObj.process);
-                            btuf = (btuf + (rtuf * parseFloatPlus(node.data.tenderContaion)).toDecimal(decimalObj.bills.unitPrice)).toDecimal(decimalObj.process);
-                        };
-                        sum_rtf = (sum_rtf + rtf).toDecimal(decimalObj.process);
-                        sum_rttf = (sum_rttf + rttf).toDecimal(decimalObj.process);
+                    // 取费方式为子目含量,清单行/列的XX单价应 =ROUND( ∑ROUND(定额XX单价*含量,清单单价精度),清单单价精度)
+                    if (me.project.property.billsCalcMode === leafBillGetFeeType.rationContent) {
+                        buf = (buf + (ruf * parseFloatPlus(node.data.contain)).toDecimal(decimalObj.bills.unitPrice)).toDecimal(decimalObj.process);
+                        node.data.tenderContaion = (node.data.tenderQuantity / bq).toDecimal(decimalObj.process);
+                        btuf = (btuf + (rtuf * parseFloatPlus(node.data.tenderContaion)).toDecimal(decimalObj.bills.unitPrice)).toDecimal(decimalObj.process);
                     };
+                    sum_rtf = (sum_rtf + rtf).toDecimal(decimalObj.process);
+                    sum_rttf = (sum_rttf + rttf).toDecimal(decimalObj.process);
+                };
 
-                    if (me.project.property.billsCalcMode == leafBillGetFeeType.rationPriceConverse ||
-                        me.project.property.billsCalcMode == leafBillGetFeeType.rationPrice) {
-                        buf = (sum_rtf / bq).toDecimal(decimalObj.process);
-                        btuf = (sum_rttf / btq).toDecimal(decimalObj.process);
-                    };
-                    if (isBaseFeeType(ft.type) ||
-                        (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice && ft.type == "common")){
-                        btf = sum_rtf;
-                        bttf = sum_rttf;
-                    }
-                    else{
-                        btf = (buf.toDecimal(decimalObj.bills.unitPrice) * bq).toDecimal(decimalObj.process);
-                        bttf = (btuf.toDecimal(decimalObj.bills.unitPrice) * btq).toDecimal(decimalObj.process);
-                    };
+                if (me.project.property.billsCalcMode == leafBillGetFeeType.rationPriceConverse ||
+                    me.project.property.billsCalcMode == leafBillGetFeeType.rationPrice) {
+                    buf = (sum_rtf / bq).toDecimal(decimalObj.process);
+                    btuf = (sum_rttf / btq).toDecimal(decimalObj.process);
                 };
+                if (isBaseFeeType(ft.type) ||
+                    (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice && ft.type == "common")){
+                    btf = sum_rtf;
+                    bttf = sum_rttf;
+                }
+                else{
+                    btf = (buf.toDecimal(decimalObj.bills.unitPrice) * bq).toDecimal(decimalObj.process);
+                    bttf = (btuf.toDecimal(decimalObj.bills.unitPrice) * btq).toDecimal(decimalObj.process);
+                };
+            };
 
-                ftObj.totalFee = btf.toDecimal(decimalObj.bills.totalPrice);
-                ftObj.tenderTotalFee = bttf.toDecimal(decimalObj.bills.totalPrice);
-                ftObj.unitFee = buf.toDecimal(decimalObj.bills.unitPrice);
-                ftObj.tenderUnitFee = btuf.toDecimal(decimalObj.bills.unitPrice);
+            ftObj.totalFee = btf.toDecimal(decimalObj.bills.totalPrice);
+            ftObj.tenderTotalFee = bttf.toDecimal(decimalObj.bills.totalPrice);
+            ftObj.unitFee = buf.toDecimal(decimalObj.bills.unitPrice);
+            ftObj.tenderUnitFee = btuf.toDecimal(decimalObj.bills.unitPrice);
 
-                calcTools.checkFeeField(treeNode, ftObj);
+            calcTools.checkFeeField(treeNode, ftObj);
 
-                rst.push(ftObj);
-            };
-            treeNode.data.calcTemplate = {"calcItems": rst};
+            rst.push(ftObj);
+        };
+        treeNode.data.calcTemplate = {"calcItems": rst};
+    };
+
+    innerCalcBillExpr(treeNode){
+        delete treeNode.data.gljList;
+        let me = this;
+        me.deleteProperties(treeNode, ['programID']);
+        me.deleteUselessFees(treeNode, ['common', 'rationCommon']);
+
+        let nQ = calcTools.uiNodeQty(treeNode);
+        let nTQ = calcTools.uiNodeTenderQty(treeNode);
+        let f = calcTools.getFeeRateByNode(treeNode);
+        let b = treeNode.data.calcBaseValue ? treeNode.data.calcBaseValue : 0;
+        let tb = treeNode.data.tenderCalcBaseValue ? treeNode.data.tenderCalcBaseValue : 0;
+        let q = nQ ? nQ : 1;
+        let tq = nTQ ? nTQ : 1;
+        let uf = (b * f * 0.01 / q).toDecimal(decimalObj.bills.unitPrice);
+        let tuf = (tb * f * 0.01 / tq).toDecimal(decimalObj.bills.unitPrice);
+        let tf = (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) ? (b * f / 100) : (uf * q);
+        tf = tf.toDecimal(decimalObj.bills.totalPrice);
+        let ttf = (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) ? (tb * f / 100) : (tuf * tq);
+        ttf = ttf.toDecimal(decimalObj.bills.totalPrice);
+
+        calcTools.checkFeeField(treeNode, {'fieldName': 'common', 'unitFee': uf, 'totalFee': tf, 'tenderUnitFee': tuf, 'tenderTotalFee': ttf});
+
+        // 总造价清单还要做单项工程、建设项目的四大项金额汇总
+        if (calcTools.isTotalCostBill(treeNode)){
+            // 公式叶子清单没有暂估费,但总造价清单除外。
+            calcTools.estimateFee(treeNode);
+            calcTools.initSummaryFee(treeNode);
+            treeNode.data.summaryFees.totalFee = tf;
+            treeNode.data.summaryFees.estimateFee = calcTools.getFee(treeNode, 'estimate.totalFee');
+            treeNode.data.summaryFees.safetyFee = calcTools.getFee(calcTools.getNodeByFlag(fixedFlag.SAFETY_CONSTRUCTION), 'common.totalFee');
+            treeNode.data.summaryFees.chargeFee = calcTools.getFee(calcTools.getNodeByFlag(fixedFlag.CHARGE), 'common.totalFee');
         }
-        // 叶子清单无子结点、无公式计算(啥都没有时)
-        else if (treeNode.calcType == treeNodeCalcType.ctNull){
-            delete treeNode.data.gljList;
-            deleteProperties(treeNode, ['calcBase', 'calcBaseValue', 'tenderCalcBaseValue', 'programID']);
-            deleteUselessFees(treeNode, ['rationCommon']);
-            // 不能直接删除该属性,否则无法冲掉库中已存储的值。下同。
-/*            if (treeNode.data.calcBase){
-                treeNode.data.calcBase = null;
-                treeNode.changed = true;
-            }
 
-            if (treeNode.data.calcBaseValue){
-                treeNode.data.calcBaseValue = null;
-                treeNode.changed = true;
-            }
+        treeNode.data.calcTemplate = {"calcItems": []};
+    };
 
-            if (treeNode.data.tenderCalcBaseValue){
-                treeNode.data.tenderCalcBaseValue = null;
+    innerCalcBillCustom(treeNode){
+        let me = this;
+        delete treeNode.data.gljList;
+        me.deleteProperties(treeNode, ['calcBase', 'calcBaseValue', 'tenderCalcBaseValue', 'programID']);
+        me.deleteUselessFees(treeNode, ['rationCommon']);
+
+        // 2017-09-27 需求改了,除了第 1 、 2.2部分以外,都可以手工修改综合单价、综合合价并参与计算
+        // 在没有公式的情况下可以手工修改综合单价并参与计算
+        if(calcTools.canCalcToTalFeeByOwn(treeNode)){
+            if (treeNode.data.feesIndex && treeNode.data.feesIndex.common){
+                let ftObj = {fieldName: 'common'};
+                let nQ = calcTools.uiNodeQty(treeNode);
+                let nTQ = calcTools.uiNodeTenderQty(treeNode);
+                ftObj.unitFee = parseFloatPlus(treeNode.data.feesIndex.common.unitFee);
+                ftObj.totalFee = (ftObj.unitFee * nQ).toDecimal(decimalObj.bills.totalPrice);
+                ftObj.tenderUnitFee = ftObj.unitFee;
+                ftObj.tenderTotalFee = (ftObj.tenderUnitFee * nTQ).toDecimal(decimalObj.bills.totalPrice);
+                calcTools.checkFeeField(treeNode, ftObj);
+            }
+        } else{
+            if (treeNode.data.fees && treeNode.data.fees.length > 0){
+                treeNode.data.fees = null;
+                treeNode.data.feesIndex = null;
                 treeNode.changed = true;
             }
+        };
 
-            if (treeNode.data.programID) {
-                treeNode.data.programID = null;
-                treeNode.changed = true;
-            };*/
-
-            // 第1、2部分以外的叶子清单在没有公式的情况下可以手工修改综合单价并参与计算。
-            // 2017-09-27 需求改了,除了第 1 、 2.2部分以外,都可以手工修改综合单价、综合合价并参与计算
-            //if(!calcTools.isFBFX(treeNode) && !calcTools.isTechMeasure(treeNode)){ // if(!calcTools.isInheritFrom(treeNode, [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE]))
-            // 在没有公式的情况下可以手工修改综合单价并参与计算
-            if(calcTools.canCalcToTalFeeByOwn(treeNode)){
-                if (treeNode.data.feesIndex && treeNode.data.feesIndex.common){
-                    let ftObj = {};
-                    ftObj.fieldName = 'common';
-                    ftObj.unitFee = parseFloatPlus(treeNode.data.feesIndex.common.unitFee);
-                    ftObj.totalFee = (ftObj.unitFee * nQ).toDecimal(decimalObj.bills.totalPrice);
-                    ftObj.tenderUnitFee = ftObj.unitFee;
-                    ftObj.tenderTotalFee = (ftObj.tenderUnitFee * nTQ).toDecimal(decimalObj.bills.totalPrice);
-                    calcTools.checkFeeField(treeNode, ftObj);
-                }
-            } else{
-                if (treeNode.data.fees && treeNode.data.fees.length > 0){
-                    treeNode.data.fees = null;
-                    treeNode.data.feesIndex = null;
+        treeNode.data.calcTemplate = {"calcItems": []};
+    };
+
+    // 定额部分抽取出来,供分摊定额公用。
+    innerCalcRation(treeNode, tenderType = tenderTypes.ttCalc){
+        let me = this;
+        let fnArr = [];
+        calcTools.getGLJList(treeNode, true);
+
+        if (treeNode.calcType == treeNodeCalcType.ctRationCalcProgram) {
+            // 量价、工料机类型的定额要求"市场合价"
+            if (calcTools.isVP_or_GLJR(treeNode)){
+                let u = treeNode.data.marketUnitFee ? treeNode.data.marketUnitFee : 0;
+                let nQ = calcTools.uiNodeQty(treeNode);
+                let t = (u * nQ).toDecimal(decimalObj.ration.totalPrice);
+                if (treeNode.data.marketTotalFee != t){
+                    treeNode.data.marketTotalFee = t;
                     treeNode.changed = true;
-                }
+                } ;
             };
+        };
 
-            treeNode.data.calcTemplate = {"calcItems": []};
-        }
-        // 叶子清单公式计算
-        else if (treeNode.calcType == treeNodeCalcType.ctCalcBaseValue){
-            delete treeNode.data.gljList;
+        let template = me.compiledTemplates[treeNode.data.programID];
+        treeNode.data.calcTemplate = template;
 
-            if (treeNode.data.programID) {
-                treeNode.data.programID = null;
-                treeNode.changed = true;
-            }
-            let f = calcTools.getFeeRateByNode(treeNode);
-            let b = treeNode.data.calcBaseValue ? treeNode.data.calcBaseValue : 0;
-            let tb = treeNode.data.tenderCalcBaseValue ? treeNode.data.tenderCalcBaseValue : 0;
-            let q = nQ ? nQ : 1;
-            let tq = nTQ ? nTQ : 1;
-            let uf = (b * f * 0.01 / q).toDecimal(decimalObj.bills.unitPrice);
-            let tuf = (tb * f * 0.01 / tq).toDecimal(decimalObj.bills.unitPrice);
-            let tf = (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) ? (b * f / 100) : (uf * q);
-            tf = tf.toDecimal(decimalObj.bills.totalPrice);
-            let ttf = (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) ? (tb * f / 100) : (tuf * tq);
-            ttf = ttf.toDecimal(decimalObj.bills.totalPrice);
-            deleteUselessFees(treeNode);
-            calcTools.checkFeeField(treeNode, {'fieldName': 'common', 'unitFee': uf, 'totalFee': tf, 'tenderUnitFee': tuf, 'tenderTotalFee': ttf});
-
-            // 总造价清单还要做单项工程、建设项目的四大项金额汇总
-            if (calcTools.isTotalCostBill(treeNode)){
-                // 公式叶子清单没有暂估费,但总造价清单除外。
-                calcTools.estimateFee(treeNode);
-                calcTools.initSummaryFee(treeNode);
-                treeNode.data.summaryFees.totalFee = tf;
-                treeNode.data.summaryFees.estimateFee = calcTools.getFee(treeNode, 'estimate.totalFee');
-                treeNode.data.summaryFees.safetyFee = calcTools.getFee(calcTools.getNodeByFlag(fixedFlag.SAFETY_CONSTRUCTION), 'common.totalFee');
-                treeNode.data.summaryFees.chargeFee = calcTools.getFee(calcTools.getNodeByFlag(fixedFlag.CHARGE), 'common.totalFee');
-            }
+        if (treeNode && template && template.hasCompiled) {//2018-08-27 空行的时候,取费专业为空,template也为空,加入template非空判断
+            let $CE = executeObj;
+            $CE.treeNode = treeNode;
+            $CE.template = template;
 
-            treeNode.data.calcTemplate = {"calcItems": []};
-        }
-        // 定额或叶子清单自己的计算程序计算
-        else{
-            let fnArr = [];
-            calcTools.getGLJList(treeNode, true);
+            calcTools.initFees(treeNode);
 
-            if (treeNode.calcType == treeNodeCalcType.ctRationCalcProgram) {
-                // 量价、工料机类型的定额要求市场合价
-                if (calcTools.isVP_or_GLJR(treeNode)){
-                    let muf = treeNode.data.marketUnitFee ? treeNode.data.marketUnitFee : 0;
-                    let mtf = (muf * nQ).toDecimal(decimalObj.ration.totalPrice);
-                    if (treeNode.data.marketTotalFee != mtf){
-                        treeNode.data.marketTotalFee = mtf;
-                        treeNode.changed = true;
-                    };
+            for (let idx of template.compiledSeq) {
+                let calcItem = template.calcItems[idx];
+                $CE.tempCalcItem = calcItem;
+                let feeRate = 100;  // 100%
+                if (calcItem.feeRate != undefined)
+                    feeRate = parseFloat(calcItem.feeRate).toDecimal(decimalObj.feeRate);
+
+                calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
+                calcItem.totalFee = (calcItem.unitFee * calcTools.uiNodeQty(treeNode)).toDecimal(decimalObj.decimal('totalPrice', treeNode));
+
+                let tExpr = analyzer.getCompiledTenderExpr(calcItem.compiledExpr);
+                calcItem.tenderUnitFee = (eval(tExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
+                calcItem.tenderTotalFee = (calcItem.tenderUnitFee * treeNode.data.tenderQuantity).toDecimal(decimalObj.decimal('totalPrice', treeNode));
+
+                if (calcItem.fieldName) {
+                    fnArr.push(calcItem.fieldName);
+                    calcTools.checkFeeField(treeNode, calcItem);
                 };
             };
-            // 2018-08-27   zhang  插入空定额的时候,取费专业也为空
-           // if (treeNode.data.programID == undefined) treeNode.data.programID = projectObj.project.projectInfo.property.engineering;
-            let template = me.compiledTemplates[treeNode.data.programID];
-            treeNode.data.calcTemplate = template;
-
-            if (treeNode && template && template.hasCompiled) {//2018-08-27 空行的时候,取费专业为空,template也为空,加入template非空判断
-                let $CE = executeObj;
-                $CE.treeNode = treeNode;
-                $CE.template = template;
-
-                calcTools.initFees(treeNode);
-
-                for (let idx of template.compiledSeq) {
-                    let calcItem = template.calcItems[idx];
-                    $CE.tempCalcItem = calcItem;
-                    let feeRate = 100;  // 100%
-                    if (calcItem.feeRate != undefined)
-                        feeRate = parseFloat(calcItem.feeRate).toDecimal(decimalObj.feeRate);
-                    // console.log(`[${calcItem.ID}]: ${calcItem.compiledExpr}`);   // for test.
-
-                    calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
-                    calcItem.totalFee = (calcItem.unitFee * calcTools.uiNodeQty(treeNode)).toDecimal(decimalObj.decimal('totalPrice', treeNode));
-
-                    // if (tenderType == tenderTypes.ttCalc) {
-                        let tExpr = analyzer.getCompiledTenderExpr(calcItem.compiledExpr);
-                        calcItem.tenderUnitFee = (eval(tExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
-                        calcItem.tenderTotalFee = (calcItem.tenderUnitFee * treeNode.data.tenderQuantity).toDecimal(decimalObj.decimal('totalPrice', treeNode));
-                    // };
-
-                    if (calcItem.fieldName) {
-                        fnArr.push(calcItem.fieldName);
-                        calcTools.checkFeeField(treeNode, calcItem);
-                    };
-                };
 
-                if (tenderType == tenderTypes.ttReverseRation || tenderType == tenderTypes.ttReverseGLJ)
-                    this.calcTenderReverse(treeNode, tenderType);
+            if (tenderType == tenderTypes.ttReverseRation || tenderType == tenderTypes.ttReverseGLJ)
+                this.calcTenderReverse(treeNode, tenderType);
 
-                deleteUselessFees(treeNode, fnArr);
-            };
+            me.deleteUselessFees(treeNode, fnArr);
         };
-
-        if (!calcTools.isTotalCostBill(treeNode))  // 已在上面的分支中计算过
-            calcTools.estimateFee(treeNode);
-
-        if (treeNode.changed && !changedArr.includes(treeNode)) changedArr.push(treeNode);
     };
+
     // 存储、刷新零散的多个结点。
     saveNodes(treeNodes, callback){
         if (treeNodes.length < 1) {
@@ -2100,8 +2004,6 @@ class CalcProgram {
         $.bootstrapLoading.start();
         let startTime = +new Date();
         me.project.updateNodes(dataArr, function (data) {
-            let endShowTime = +new Date();
-            console.log(`保存所需时间——${endShowTime - startTime}`);
             me.rationMap = null;
             me.pgljMap = null;
             if(callback){
@@ -2139,10 +2041,11 @@ class CalcProgram {
 
         return changedNodes;
     };
-    // 计算并保存本节点及所有会被影响到的节点。
+    // 计算并保存一个树节点。(修改一个树节点,实际上要计算和保存的是一批树结点:层层父结点、被其它结点(的公式)引用的公式结点)
     calcAndSave(treeNode, callback, tender){
-        let changedNodes = this.calculate(treeNode, true, true, tender);
-        this.saveNodes(changedNodes, callback);
+        this.calcNodesAndSave([treeNode],callback,tender);
+      /*  let changedNodes = this.calculate(treeNode, true, true, tender); 统一调用相同的方法
+        this.saveNodes(changedNodes, callback);*/
     };
 
     /* 计算所有树结点(分3种情况),并返回发生变动的零散的多个树结点。参数取值如下:
@@ -2161,6 +2064,7 @@ class CalcProgram {
                 if (node.children.length > 0) {
                     calcNodes(node.children);
                 };
+
                 if (calcType == calcAllType.catAll || calcType == node.sourceType) {
                     node.calcType = calcTools.getCalcType(node);
                     if (node.calcType != treeNodeCalcType.ctCalcBaseValue)