Sfoglia il codice sorgente

Merge branch 'budget' of http://192.168.1.41:3000/SmartCost/ConstructionCost into budget

zhangweicheng 4 anni fa
parent
commit
df4b6d5ca8

+ 12 - 1
modules/equipment_purchase/facade/equipment_purchase_facade.js

@@ -5,7 +5,8 @@ module.exports={
     deleteEquipment:deleteEquipment,
     updateEquipments:updateEquipments,
     getEquipmentTotalCost:getEquipmentTotalCost,
-    getEquipmentSummary:getEquipmentSummary
+    getEquipmentSummary:getEquipmentSummary,
+    getSortedEquipmentData,
 };
 
 let mongoose = require('mongoose');
@@ -57,6 +58,16 @@ function sortEquipments(equipments) {
     }
 }
 
+// 获取单位工程设备购置数据(按树结构排好序的)
+async function getSortedEquipmentData(unitID) {
+    const equipment = await equipmentPurchaseModel.findOne({ projectID: unitID }).lean();
+    if (!equipment) {
+        return [];
+    }
+    const items = sortEquipments(equipment.equipments || []);
+    items.forEach(item => item.feeType = 'equipment');
+    return items;
+}
 
 async function getEquipmentSummary(user_id, projectIDs) {
      let data = [];

+ 47 - 9
modules/main/facade/bill_facade.js

@@ -19,7 +19,7 @@ let ration_template_Model = mongoose.model('ration_template');
 let bill_Model = require('../models/bills').model;
 let billsLibDao = require("../../bills_lib/models/bills_lib_interfaces");
 const pmFacade = require('../../pm/facade/pm_facade');
-const { getSortedTreeData, getEngineeringFeeType } = require('../../../public/common_util');
+const { getSortedTreeData, getSortedSeqTreeData, getEngineeringFeeType } = require('../../../public/common_util');
 const { billType, constructionFeeNodeID, constructionEquipmentFeeNodeID, BudgetArea, fixedFlag, BudgetType } = require('../../../public/common_constants');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
 const uuidV1 = require('uuid/v1');
@@ -183,20 +183,58 @@ module.exports={
         });
         return map;
     },
-    // 获取概算汇总单项工程级别数据(拍好序的,报表接口用)
+    // 获取单位工程分部数据(按树结构排好序的)
+    getFBData: async function (unitID) {
+        const bills = await bill_Model.find({ projectID: unitID }).lean();
+        const sortedData = getSortedTreeData('-1', bills);
+        return sortedData.filter(item => item.type === billType.FB);
+    },
+    // 获取概算汇总单项工程级别数据(排好序的,报表接口用)
     getSinglesBudgetSummary: async function (constructionID) {
         const items = await this.getBudgetSummary(constructionID, true);
         // 去除建设项目层级
         items.shift();
-        items.forEach(item => {
+        const [budgetTotalItem] = items.splice(items.length - 1, 1);
+        const budgetTotalFeeItem = budgetTotalItem.fees && budgetTotalItem.fees.find(f => f.fieldName === 'common');
+        const budgetTotalFee = budgetTotalFeeItem ? +budgetTotalFeeItem.totalFee : 0;
+        const rst = [];
+        for (const project of items) {
+            rst.push(project);
             // 方便报表取值处理
-            if (item.projType === 'Engineering') {
-                item.engineeringName = item.name || '';
-            } else if (item.projType === 'Tender') {
-                item.tenderName = item.name || '';
+            if (project.projType === 'Engineering') {
+                project.engineeringName = project.name || '';
+            } else if (project.projType === 'Tender') {
+                project.tenderName = project.name || '';
+                // 追加分部和设备购置数据
+                const feeType = getEngineeringFeeType(project.property && project.property.engineeringName || '');
+                const fbs = await this.getFBData(project.orgProjectID);
+                const equipments = await equipmentFacade.getSortedEquipmentData(project.orgProjectID);
+                const detailData = [...fbs, ...equipments].map(item => {
+                    let totalFee = 0;
+                    if (item.feeType === 'equipment') {
+                        totalFee = item.totalPrice || 0;
+                    } else {
+                        const totalFeeItem = item.fees && item.fees.find(f => f.fieldName === 'common');
+                        totalFee = totalFeeItem ? +totalFeeItem.totalFee : 0;
+                    }
+                    // 计算占总投资比例
+                    const rate = (budgetTotalFee ?  scMathUtil.roundForObj(totalFee / budgetTotalFee, 4) : 0) * 100;
+                    return {
+                        code: item.code || '',
+                        name: item.name || '',
+                        buildingFee: item.feeType !== 'equipment' && feeType === 'building' ? totalFee : 0,
+                        installationFee: item.feeType !== 'equipment' && feeType === 'installation' ? totalFee : 0,
+                        equipmentFee: item.feeType === 'equipment' ? totalFee : 0,
+                        otherFee: 0,
+                        totalFee,
+                        rate,
+                    }
+                });
+                rst.push(...detailData);
+                
             }
-        });
-        return items;
+        }
+        return rst;
     },
 
     // 获取概算汇总数据(拍好序的)

+ 9 - 9
modules/reports/controllers/rpt_controller.js

@@ -212,14 +212,14 @@ async function getAllPagesCommon(construct_id, user_id, prj_id, prj_ids, rpt_id,
             let promiseArr = [null, null, null];
             if (summaryRst.length > 0) {
                 if (summaryRst.indexOf(`Budget`) >= 0 || summaryRst.indexOf(`BudgetDetail`) >= 0) {
-                    promiseArr[0] = bill_facade.getBudgetSummary(construct_id); //与其他汇总类的请求互斥,不能同时出现!
-                    // if (flag.budgetSumType && flag.budgetSumType === 'budget_construct') {
-                    //     //建设项目level
-                    //     promiseArr[0] = bill_facade.getBudgetSummary(construct_id); //与其他汇总类的请求互斥,不能同时出现!
-                    // } else {
-                    //     //单项工程level
-                    //     promiseArr[0] = bill_facade.getSinglesBudgetSummary(construct_id); //与其他汇总类的请求互斥,不能同时出现!
-                    // }
+                    // promiseArr[0] = bill_facade.getBudgetSummary(construct_id, false); //与其他汇总类的请求互斥,不能同时出现!
+                    if (flag.budgetSumType && flag.budgetSumType === 'budget_single') {
+                        //单项工程level
+                        promiseArr[0] = bill_facade.getSinglesBudgetSummary(construct_id); //与其他汇总类的请求互斥,不能同时出现!
+                    } else {
+                        //建设项目level
+                        promiseArr[0] = bill_facade.getBudgetSummary(construct_id); //与其他汇总类的请求互斥,不能同时出现!
+                    }
                 }
                 if (summaryRst.indexOf(`Construct`) >= 0 || summaryRst.indexOf(`ConstructDetail`) >= 0) {
                     promiseArr[0] = pm_facade.getSummaryInfoByTender(prj_id, pm_facade.projectType.project);
@@ -281,7 +281,7 @@ async function getAllPagesCommon(construct_id, user_id, prj_id, prj_ids, rpt_id,
                     cb('Exception occurs while on going...', null);
                 }
             };
-        if (flag !== undefined && flag !== null && (flag.constructSumType === 'constructSum' || flag.auditType === 'audit_compare' || flag.budgetSumType === 'budget_single')) {
+        if (flag !== undefined && flag !== null && (flag.constructSumType === 'constructSum' || flag.auditType === 'audit_compare' || (flag.budgetSumType === 'budget_single' && summaryRst.indexOf(`ConstructDetail`) < 0))) {
                 //备注:原先这个功能是为了合并建设项目下所有的单位工程的数据,
                 //     现在发现就是通用型,审核对比也可以用这个逻辑把数据合并在一起再处理(审核对比的处理逻辑放在模板计算式里)
                 try {

+ 25 - 2
public/common_util.js

@@ -72,13 +72,13 @@ function deleteEmptyObject(arr) {
         return sortSameDedth(rootID, items).reverse();
 
         function sortSameDedth(parentID, items) {
-            const sameDepthItems = items.filter(item => item.ParentID === parentID);
+            const sameDepthItems = items.filter(item => item.ParentID == parentID);
             if (!sameDepthItems.length) {
                 return [];
             }
             const NextIDMapping = {};
             sameDepthItems.forEach(item => NextIDMapping[item.NextSiblingID] = item);
-            let curItem = sameDepthItems.length > 1 ? sameDepthItems.find(item => +item.NextSiblingID === -1) : sameDepthItems[0];
+            let curItem = sameDepthItems.length > 1 ? sameDepthItems.find(item => +item.NextSiblingID == -1) : sameDepthItems[0];
             const sorted = [];
             while (curItem) {
                 sorted.push(...sortSameDedth(curItem.ID, items));
@@ -89,6 +89,29 @@ function deleteEmptyObject(arr) {
         }
     }
 
+    function getSortedSeqTreeData(rootID, items) {
+        const parentMap = {};
+        items.forEach(item => {
+            (parentMap[item.ParentID] || (parentMap[item.ParentID] = [])).push(item);
+        });
+        Object.values(parentMap).forEach(data => data.sort((a, b) => a.seq - b.seq));
+        const rst = [];
+        function extractItems(data) {
+            if (data && data.length) {
+                for (const item of data) {
+                    rst.push(item);
+                    const children = parentMap[item.ID];
+                    if (children && children.length) {
+                        extractItems(children);
+                    }
+                }
+            }
+            
+        }
+        extractItems(parentMap[rootID]);
+        return rst;
+    }
+
     // 控制全屏(浏览器有限制)
     // Element.requestFullscreen的全屏和“F11”的全屏是不一样的。前者是将相关Element变成全屏显示。后者是将浏览器导航、标签等隐藏。
     // Fullscreen API对于全屏的判断和监听都是基于Element.requestFullscreen的,比如Document.fullscreenElement。通过F11触发的全屏Document.fullscreenElement返回null,无法正确返回全屏状态。

+ 5 - 5
web/building_saas/main/html/main.html

@@ -80,7 +80,7 @@
         <li class="nav-item"><a data-toggle="tab" href="#calc_program_manage" id="tab_calc_program_manage" role="tab"
             style="display:none">总计算程序</a></li>
         <li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab" style="display:none">调价</a></li>
-     
+
         <li class="nav-item"><a data-toggle="tab" href="#reports" role="tab" id="tab_report"
             onclick="rptTplObj.iniPage();">报表</a></li>
         <li class="nav-item"><a data-toggle="tab" href="#index" id="tab_index" role="tab" style="display:none">指标信息</a>
@@ -174,7 +174,7 @@
                  <!--  <a id="menu_index_info" href="javascript:void(0);" style="display: none" class="dropdown-item"><i
                       class="fa fa-database" aria-hidden="true"></i> 指标信息</a> -->
                 </div>
-              </div> 
+              </div>
              <!--  <a href="javascript:void(0)" class="btn btn-light btn-sm" id="locate-sub">分项</a>
               <a href="javascript:void(0)" class="btn btn-light btn-sm" id="locate-measure">措施</a>
               <a href="javascript:void(0)" class="btn btn-light btn-sm" id="locate-other">其他</a> -->
@@ -281,9 +281,9 @@
                       <a class="nav-link sub-item" id="linkGCLMX" data-toggle="tab" href="#subSpread"
                         role="tab">工程量明细</a>
                     </li>
-                    <!--<li class="nav-item">-->
-                      <!--<a class="nav-link sub-item" id="linkJSCX" data-toggle="tab" href="#subSpread" role="tab">计算程序</a>-->
-                    <!--</li>-->
+                    <li class="nav-item">
+                      <a class="nav-link sub-item" id="linkJSCX" data-toggle="tab" href="#subSpread" role="tab">计算程序</a>
+                    </li>
                     <!-- <li class="nav-item">   2018-11-08  新需求,隐藏说明信息
                                       <a class="nav-link" data-toggle="tab" href="#comments" role="tab" id="linkComments">说明信息</a>
                                   </li>-->

+ 51 - 12
web/building_saas/main/js/models/calc_program.js

@@ -51,6 +51,15 @@ let calcTools = {
     isNullBill: function (treeNode) {
         return this.isLeafBill(treeNode) && (treeNode.children.length === 0) && (!treeNode.data.calcBase);
     },
+    // 无效的、影响正常计算的行(无意义的空行、没有金额的行等)
+    isInvalidNode: function (treeNode) {
+        return !(
+            treeNode.data &&
+            treeNode.data.feesIndex &&
+            treeNode.data.feesIndex.common &&
+            treeNode.data.feesIndex.common.totalFee
+        );
+    },
     isCalcBaseBill: function(treeNode){
         return this.isLeafBill(treeNode) && (treeNode.children.length === 0) && (treeNode.data.calcBase);
     },
@@ -1823,7 +1832,7 @@ class CalcProgram {
             me.innerCalcBillCustom(treeNode)
         // 定额:计算程序
         else
-            me.innerCalcRation2(treeNode, tenderType);
+            me.innerCalcRation(treeNode, tenderType);
 
         if (!calcTools.isTotalCostBill(treeNode))  // 已在上面的分支中计算过
             calcTools.estimateFee(treeNode);
@@ -2072,10 +2081,22 @@ class CalcProgram {
             if (calcTools.isVP_or_GLJR(treeNode)){
                 let u = treeNode.data.marketUnitFee ? treeNode.data.marketUnitFee : 0;
                 let t = (u * nQ).toDecimal(decimalObj.ration.totalPrice);
-                if (treeNode.data.marketTotalFee != t){
+                if ((treeNode.data.marketTotalFee != t) ||
+                    (!treeNode.data.feesIndex || !treeNode.data.feesIndex.common || (treeNode.data.feesIndex.common.totalFee != t))
+                ){
+
                     treeNode.data.marketTotalFee = t;
+                    let obj = {};
+                    obj.fieldName = 'common';
+                    obj.unitFee = u;
+                    obj.totalFee = t;
+                    obj.tenderUnitFee = obj.unitFee;
+                    obj.tenderTotalFee = obj.totalFee;
+                    calcTools.checkFeeField(treeNode, obj);
+                    me.deleteUselessFees(treeNode, budgetFields);
                     treeNode.changed = true;
                 } ;
+                return;
             };
         };
 
@@ -2089,6 +2110,33 @@ class CalcProgram {
 
             calcTools.initFees(treeNode);
 
+            // 只有人、材、机、主、设。
+            let sum = 0;
+            for (let i = 0; i < 5; i++) {
+                if (!rationCalcBases[budgetBaseNames[i]]){
+                    hintBox.infoBox('错误提示', `定额基数${hintBox.font('[' +funcNames[i] + ']')}未定义!`, 1);
+                    return;
+                }
+                let obj = {};
+                obj.fieldName = budgetFields[i];
+                obj.unitFee = (rationCalcBases[budgetBaseNames[i]](treeNode)).toDecimal(decimalObj.decimal('unitPrice', treeNode));
+                obj.totalFee = (obj.unitFee * nQ).toDecimal(decimalObj.decimal('totalPrice', treeNode));
+                obj.tenderUnitFee = obj.unitFee;
+                obj.tenderTotalFee = obj.totalFee;
+                sum = sum + obj.unitFee;
+                calcTools.checkFeeField(treeNode, obj);
+            };
+
+            let obj = {};
+            obj.fieldName = 'common';
+            obj.unitFee = obj.unitFee = sum.toDecimal(decimalObj.decimal('unitPrice', treeNode));
+            obj.totalFee = (obj.unitFee * nQ).toDecimal(decimalObj.decimal('totalPrice', treeNode));
+            obj.tenderUnitFee = obj.unitFee;
+            obj.tenderTotalFee = obj.totalFee;
+            calcTools.checkFeeField(treeNode, obj);
+
+            me.deleteUselessFees(treeNode, budgetFields);
+
             for (let idx of template.compiledSeq) {
                 let calcItem = template.calcItems[idx];
                 $CE.tempCalcItem = calcItem;
@@ -2102,17 +2150,7 @@ class CalcProgram {
                 let tExpr = analyzer.getCompiledTenderExpr(calcItem.compiledExpr);
                 calcItem.tenderUnitFee = (eval(tExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                 calcItem.tenderTotalFee = (calcItem.tenderUnitFee * nTQ).toDecimal(decimalObj.decimal('totalPrice', treeNode));
-
-                if (calcItem.fieldName) {
-                    fnArr.push(calcItem.fieldName);
-                    calcTools.checkFeeField(treeNode, calcItem);
-                };
             };
-
-            if (tenderType == tenderTypes.ttReverseRation || tenderType == tenderTypes.ttReverseGLJ)
-              this.reverseTenderCalc(treeNode, tenderType);
-
-            me.deleteUselessFees(treeNode, fnArr);
         };
     };
 
@@ -2613,6 +2651,7 @@ class CalcProgram {
             let arr = [];
             for (let i = 0; i < tender_obj.tenderTree.items.length; i++) {
                 let node = tender_obj.tenderTree.items[i];
+                if (calcTools.isInvalidNode(node)) continue;
                 // 量价还是要参与,因为它贡献了金额,如果它的金额比重很大,它退出了,会导致其它结点过调。
                 // if (calcTools.isRationCategory(node) && (!calcTools.isVP_or_GLJR(node))){
                 if (calcTools.isRationCategory(node)){