Pārlūkot izejas kodu

项目管理金额汇总

zhongzewei 6 gadi atpakaļ
vecāks
revīzija
c1c7a042ae

+ 11 - 1
modules/pm/controllers/pm_controller.js

@@ -543,5 +543,15 @@ module.exports = {
         catch (err){
             callback(req, res, 1, err, null);
         }
-    }
+    },
+    getSummaryInfo: async function(req, res){
+        try{
+            let data = JSON.parse(req.body.data);
+            let summaryInfo = await pm_facade.getSummaryInfo(data.projectIDs);
+            callback(req, res, 0, 'success', summaryInfo);
+        }
+        catch (err){
+            callback(req, res, 1, err, null);
+        }
+    },
 };

+ 114 - 1
modules/pm/facade/pm_facade.js

@@ -23,13 +23,22 @@ let rationGLJModel = mongoose.model('ration_glj');
 let rationCoeModel = mongoose.model('ration_coe');
 let rationInstallationModel = mongoose.model('ration_installation');
 let quantityDetailModel = mongoose.model('quantity_detail');
+let scMathUtil = require('../../../public/scMathUtil').getUtil();
 import CounterModel from "../../glj/models/counter_model";
 import moment from 'moment';
+import billsFlags from '../../common/const/bills_fixed';
+const projectType = {
+    folder: 'Folder',
+    tender: 'Tender',
+    project: 'Project',
+    engineering: 'Engineering',
+};
 
 
 module.exports={
     moveProject:moveProject,
-    copyProject:copyProject
+    copyProject:copyProject,
+    getSummaryInfo: getSummaryInfo
 };
 
 async function copyProject(userID, compilationID,data) {
@@ -447,4 +456,108 @@ function setProperty(Obj,updateData) {
     }
 
 
+}
+
+function isDef(v){
+    return typeof v !== 'undefined' && v !== null;
+}
+
+function getCommonTotalFee(bills) {
+    if(!isDef(bills)){
+        return 0;
+    }
+    if(!isDef(bills.fees) || bills.fees.length <= 0){
+        return 0;
+    }
+    for(let fee of bills.fees){
+        if(isDef(fee.fieldName) && fee.fieldName === 'common'){
+            return isDef(fee.totalFee) ? fee.totalFee : 0;
+        }
+    }
+    return 0;
+}
+
+function summarizeToParent(parent, child) {
+    const decimal = -2;
+    parent.engineeringCost = scMathUtil.roundTo(parseFloat(parent.engineeringCost) + parseFloat(child.engineeringCost), decimal);
+    parent.subEngineering = scMathUtil.roundTo(parseFloat(parent.subEngineering) + parseFloat(child.subEngineering), decimal);
+    parent.measure = scMathUtil.roundTo(parseFloat(parent.measure) + parseFloat(child.measure), decimal);
+    parent.safetyConstruction = scMathUtil.roundTo(parseFloat(parent.safetyConstruction) + parseFloat(child.safetyConstruction), decimal);
+    parent.other = scMathUtil.roundTo(parseFloat(parent.other) + parseFloat(child.other), decimal);
+    parent.charge = scMathUtil.roundTo(parseFloat(parent.charge) + parseFloat(child.charge), decimal);
+    parent.tax = scMathUtil.roundTo(parseFloat(parent.tax) + parseFloat(child.tax), decimal);
+}
+
+async function getSummaryInfo(projectIDs){
+    //ID与汇总信息映射
+    let IDMapping = {};
+    //固定清单类别与汇总金额字段映射
+    let flagFieldMapping = {};
+    flagFieldMapping[billsFlags.ENGINEERINGCOST] = 'engineeringCost';
+    flagFieldMapping[billsFlags.SUB_ENGINERRING] = 'subEngineering';
+    flagFieldMapping[billsFlags.MEASURE] = 'measure';
+    flagFieldMapping[billsFlags.SAFETY_CONSTRUCTION] = 'safetyConstruction';
+    flagFieldMapping[billsFlags.OTHER] = 'other';
+    flagFieldMapping[billsFlags.CHARGE] = 'charge';
+    flagFieldMapping[billsFlags.TAX] = 'tax';
+    for(let projectID of projectIDs){
+        IDMapping[projectID] = {engineeringCost: 0, subEngineering: 0, measure: 0, safetyConstruction: 0, other: 0, charge: 0, tax: 0, rate: 0, buildingArea: '', perCost: ''};
+    }
+    //let projects = await projectModel.find({ID: {$in : projectIDs}, projType: projectType.project, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+    //单项工程
+    let engineerings = await projectModel.find({ParentID: {$in : projectIDs}, projType: projectType.engineering, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+    //单位工程
+    let tenders = [];
+    let engIDs = [];
+    for(let eng of engineerings){
+        engIDs.push(eng.ID);
+        IDMapping[eng.ID] = {engineeringCost: 0, subEngineering: 0, measure: 0, safetyConstruction: 0, other: 0, charge: 0, tax: 0, rate: 0, buildingArea: '', perCost: ''};
+    }
+    if(engIDs.length > 0){
+        tenders = await projectModel.find({ParentID: {$in : engIDs}, projType: projectType.tender, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+    }
+    let tenderIDs = [];
+    if(tenders.length > 0){
+        for(let tender of tenders){
+            tenderIDs.push(tender.ID);
+            IDMapping[tender.ID] = {engineeringCost: 0, subEngineering: 0, measure: 0, safetyConstruction: 0, other: 0, charge: 0, tax: 0, rate: 0, buildingArea: '', perCost: ''};
+        }
+        //需要获取的清单固定类别综合合价:工程造价、分部分项、措施项目、安全文明施工专项、规费、其他项目、税金
+        let needFlags = [billsFlags.ENGINEERINGCOST, billsFlags.SUB_ENGINERRING, billsFlags.MEASURE,
+            billsFlags.SAFETY_CONSTRUCTION, billsFlags.CHARGE, billsFlags.OTHER, billsFlags.TAX];
+        //获取单位工程汇总金额需要用到的所有清单
+        let allBills = await billsModel.find({projectID: {$in: tenderIDs}, 'flags.flag': {$in: needFlags}, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]},
+                                            '-_id projectID fees flags');
+        //进行单位工程级别的汇总
+        for(let bills of allBills){
+            let billsFlag = bills.flags[0]['flag'];
+            let costField = flagFieldMapping[billsFlag];
+            IDMapping[bills.projectID][costField] = getCommonTotalFee(bills);
+        }
+        //进行单项工程级别的汇总
+        for(let tender of tenders){
+            summarizeToParent(IDMapping[tender.ParentID], IDMapping[tender.ID]);
+        }
+        //进行建设项目级别的汇总
+        for(let eng of engineerings){
+            summarizeToParent(IDMapping[eng.ParentID], IDMapping[eng.ID]);
+        }
+        //占造价比例
+        const rateDecimal = -2;
+        for(let tender of tenders){
+            let tenderInfo = IDMapping[tender.ID];
+            let engInfo = IDMapping[tender.ParentID];
+            tenderInfo.rate = engInfo.engineeringCost == 0 ? 0 : scMathUtil.roundTo(tenderInfo.engineeringCost * 100 / engInfo.engineeringCost, rateDecimal);
+        }
+        for(let eng of engineerings){
+            let engInfo = IDMapping[eng.ID];
+            let projInfo = IDMapping[eng.ParentID];
+            engInfo.rate = !isDef(projInfo) || projInfo.engineeringCost == 0 ? 0 : scMathUtil.roundTo(engInfo.engineeringCost * 100 / projInfo.engineeringCost, rateDecimal);
+        }
+        for(let projectID of projectIDs){
+            IDMapping[projectID].rate = 100;
+        }
+    }
+    console.log(IDMapping);
+    return IDMapping;
 }

+ 24 - 8
modules/pm/models/project_model.js

@@ -24,6 +24,7 @@ let feeRateFacade = require('../../fee_rates/facade/fee_rates_facade');
 let labourCoeFacade = require('../../main/facade/labour_coe_facade');
 let calcProgramFacade = require('../../main/facade/calc_program_facade');
 let installationFacade = require('../../main/facade/installation_facade');
+let pmFacade = require('../facade/pm_facade');
 let logger = require("../../../logs/log_helper").logger;
 let BillsModel = require("../../main/models/bills").model;
 let _ = require('lodash');
@@ -46,7 +47,7 @@ let ProjectsDAO = function () {
 let G_FILE_VER = '1.0.1';
 
 ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, callback) {
-    try {
+    try {//
         let projects = await Projects.find({
             '$or': [{
                 'userID': userId,
@@ -55,14 +56,29 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
             }, {'userID': userId, 'compilation': compilation, 'deleteInfo.deleted': {'$in': [null, false]}}]
         }, '-_id');
         let projIDs= [];
-        let projIndex = {};
-        for(let proj of projects){
-            projIDs.push(proj.ID);
-            projIndex[proj.ID] = proj;
+        for(let project of projects){
+            if(project.projType === projectType.project){
+                projIDs.push(project.ID);
+            }
         }
-        let bills = await BillsModel.find({projectID: {$in : projIDs}, 'flags.flag': fixedFlag.ENGINEERINGCOST, 'fees.totalFee': {$exists: true}}, 'projectID fees');
-        for(let bill of bills){
-            projIndex[bill.projectID]._doc.engineeringCost = bill.fees[0].totalFee;
+        //test
+        let summaryInfo = await pmFacade.getSummaryInfo(projIDs);
+        //test
+        //设置汇总字段
+        for(let proj of projects){
+            let summaryProj = summaryInfo[proj.ID];
+            if(summaryProj){
+                proj._doc.engineeringCost = summaryProj.engineeringCost;
+                proj._doc.subEngineering = summaryProj.subEngineering;
+                proj._doc.measure = summaryProj.measure;
+                proj._doc.safetyConstruction = summaryProj.safetyConstruction;
+                proj._doc.other = summaryProj.other;
+                proj._doc.charge = summaryProj.charge;
+                proj._doc.tax = summaryProj.tax;
+                proj._doc.rate = summaryProj.rate;
+                proj._doc.buildingArea = summaryProj.buildingArea;
+                proj._doc.perCost = summaryProj.perCost;
+            }
         }
         callback(0, '', projects);
     }

+ 1 - 0
modules/pm/routes/pm_route.js

@@ -25,6 +25,7 @@ module.exports = function (app) {
      req.body = {data: '{user_id}'}
      */
     pmRouter.post('/getProjects', pmController.getProjects);
+    pmRouter.post('/getSummaryInfo', pmController.getSummaryInfo);
 
     /*
      req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}

+ 105 - 16
web/building_saas/pm/js/pm_newMain.js

@@ -29,6 +29,10 @@ let taxTypeMap = {
     2:"简易计税"
 };
 
+function isDef(v) {
+    return typeof v !== 'undefined' && v !== null;
+}
+
 const projTreeObj = {
     tree: null,
     workBook: null,
@@ -43,12 +47,22 @@ const projTreeObj = {
             rootId: -1,
             autoUpdate: false
         },
+        //0.48 0.13 0.15 0.15 0.09
         header: [
-            {name: '工程列表', dataCode: 'name', width: 0.48, vAlign: 'center', hAlign: 'left'},
-            {name: '工程造价', dataCode: 'engineeringCost', width: 0.13, vAlign: 'center', hAlign: 'right'},
-            {name: '单价文件', dataCode: 'unitPriceFile', width: 0.15, vAlign: 'center', hAlign: 'left'},
-            {name: '费率文件', dataCode: 'feeRateFile', width: 0.15, vAlign: 'center', hAlign: 'left'},
-            {name: '创建日期', dataCode: 'createDateTime', width: 0.09, vAlign: 'center', hAlign: 'center'}
+            {name: '工程列表', dataCode: 'name', width: 300, vAlign: 'center', hAlign: 'left'},
+            {name: '工程造价', dataCode: 'engineeringCost', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '分部分项合计', dataCode: 'subEngineering', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '措施项目合计', dataCode: 'measure', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '其他项目合计', dataCode: 'other', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '安全文明施工费', dataCode: 'safetyConstruction', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '规费', dataCode: 'charge', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '税金', dataCode: 'tax', width: 120, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '占造价比例(%)', dataCode: 'rate', width: 100, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '建筑面积', dataCode: 'buildingArea', width: 100, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
+            {name: '单方造价', dataCode: 'perCost', width: 100, vAlign: 'center', hAlign: 'right'},
+            {name: '单价文件', dataCode: 'unitPriceFile', width: 140, vAlign: 'center', hAlign: 'left'},
+            {name: '费率文件', dataCode: 'feeRateFile', width: 140, vAlign: 'center', hAlign: 'left'},
+            {name: '创建日期', dataCode: 'createDateTime', width: 100, vAlign: 'center', hAlign: 'center'}
         ],
         //选中行颜色
         style: {
@@ -118,8 +132,11 @@ const projTreeObj = {
             let workBookWidth = getWorkBookWidth();
             for(let i = 0, len = headers.length; i < len; i++){
                 sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
-                let width = workBookWidth * headers[i].width;
+                let width = headers[i].width;
                 sheet.setColumnWidth(i, width, GC.Spread.Sheets.SheetArea.colHeader);
+                if (headers[i].formatter) {
+                    sheet.setFormatter(-1, i, headers[i].formatter, GC.Spread.Sheets.SheetArea.viewport);
+                }
             }
         };
         me.renderSheetFuc(sheet, fuc);
@@ -307,6 +324,30 @@ const projTreeObj = {
                             });
                         }, 500);
                     }
+                },
+                "refreshSummary": {
+                    name: "刷新当前项目造价",
+                    icon: 'fa-refresh',
+                    disabled: function () {
+                        let selectedItem = projTreeObj.tree.selected;
+                        return !(selectedItem && selectedItem.data.projType !== projectType.folder);
+                    },
+                    callback: function (key, opt) {
+                        //获取当前节点的建设项目ID
+                        let project = projTreeObj.tree.selected;
+                        while (project.data.projType !== projectType.project && project.parent){
+                            project = project.parent;
+                        }
+                        if(project && project.data.ID){
+                            $.bootstrapLoading.start();
+                            CommonAjax.post('/pm/api/getSummaryInfo', {user_id: userID, projectIDs: [project.data.ID]}, function (summaryInfo) {
+                                refreshProjSummary(project, summaryInfo);
+                                $.bootstrapLoading.end();
+                            }, function () {
+                                $.bootstrapLoading.start();
+                            });
+                        }
+                    }
                 }
             }
         });
@@ -819,13 +860,7 @@ const projTreeObj = {
         const {row, col} = cell;
         let dataCode = this.setting.header[col]['dataCode'];
         let value = '';
-        if(dataCode === 'engineeringCost'){
-            if(node.data.projType !== projectType.folder){//显示除了文件夹节点的工程造价结果 -- vincent
-                value =  node.data.engineeringCost ? node.data.engineeringCost : '0.00';
-                value = scMathUtil.roundToString(value,2);
-            }
-        }
-        else if(dataCode === 'unitPriceFile'){
+        if(dataCode === 'unitPriceFile'){
             if(node.data.projType === projectType.tender){
                 value = node.data.property && node.data.property.unitPriceFile && node.data.property.unitPriceFile.name ? node.data.property.unitPriceFile.name : '';
             }
@@ -839,7 +874,7 @@ const projTreeObj = {
             value = node.data.createDateTime ? new Date(node.data.createDateTime).Format('yyyy-MM-dd') : '';
         }
         else {
-            value = node.data[dataCode] ? node.data[dataCode] : '';
+            value = isDef(node.data[dataCode]) ? node.data[dataCode] : '';
         }
         sheet.setValue(row, col, value);
     },
@@ -1028,7 +1063,7 @@ const projTreeObj = {
 $(document).ready(function() {
     //列宽随着屏幕改变
     $(window).resize(function () {
-        autoPmWdith(projTreeObj.workBook, projTreeObj.setting.header);
+        //autoPmWdith(projTreeObj.workBook, projTreeObj.setting.header);
         autoPmWdith(gcTreeObj.workBook, gcTreeObj.setting.header);
         autoPmWdith(pmShare.spreadObj.workBook, pmShare.headers)
 
@@ -1672,12 +1707,30 @@ function getChangedFunc(input, nameInfo){
             nameInfo.show();
         }
         else {
-            nameInfo.text('');N
+            nameInfo.text('');
             nameInfo.hide();
         }
     }
 }
 
+//新增节点,初始化汇总数值
+function setInitSummaryData(data) {
+    if(data.projType === projectType.folder){
+        return;
+    }
+    //just for View
+    data.engineeringCost = 0;
+    data.subEngineering = 0;
+    data.measure = 0;
+    data.safetyConstruction = 0;
+    data.other = 0;
+    data.charge = 0;
+    data.tax = 0;
+    data.rate = data.projType === projectType.project ? 100 : 0;
+    data.buildingArea = '';
+    data.perCost = '';
+}
+
 function AddTenderItems(selected, projName, engName, tenderName, property, callback){
     const addPath = {p_e_t: 'p_e_t', e_t: 'e_t', t: 't'};
     let path, updateDatas = [];
@@ -1727,6 +1780,7 @@ function AddTenderItems(selected, projName, engName, tenderName, property, callb
                 let projData, engData, tenderData;
                 datas.forEach(function (data) {
                     if (data.updateType === 'new') {
+                        setInitSummaryData(data.updateData);
                         if(data.updateData.projType === projectType.project){
                             projData = data.updateData;
                         }
@@ -1767,6 +1821,7 @@ function AddTenderItems(selected, projName, engName, tenderName, property, callb
                 let engData, tenderData;
                 datas.forEach(function (data) {
                     if (data.updateType === 'new') {
+                        setInitSummaryData(data.updateData);
                         if(data.updateData.projType === projectType.engineering){
                             engData = data.updateData;
                         }
@@ -1794,6 +1849,7 @@ function AddTenderItems(selected, projName, engName, tenderName, property, callb
             UpdateProjectData(updateDatas, function (datas) {
                 datas.forEach(function (data) {
                     if(data.updateType === 'new') {
+                        setInitSummaryData(data.updateData);
                         projTreeObj.insert(data.updateData, tempEng, null);
                     }
                 });
@@ -1832,6 +1888,7 @@ function AddChildrenItem(selected, name, property, type, existCallback, sucCallb
             UpdateProjectData(updateData, function(datas){
                 datas.forEach(function (data) {
                     if (data.updateType === 'new') {
+                        setInitSummaryData(data.updateData);
                         projTreeObj.insert(data.updateData, parent, null);
                     }
                 });
@@ -1870,6 +1927,7 @@ function AddSiblingsItem(selected, name, property, type, existCallback, sucCallb
             UpdateProjectData(updateData, function(datas){
                 datas.forEach(function (data) {
                     if (data.updateType === 'new') {
+                        setInitSummaryData(data.updateData);
                         projTreeObj.insert(data.updateData, parent, next);
                     }
                 });
@@ -3097,3 +3155,34 @@ $('#allowCopy').change(function () {
         $('#allowCopyHint').hide();
     }
 });
+
+//刷新建设项目汇总金额信息
+function refreshProjSummary(project, summaryInfo) {
+    let refreshNodes = [];
+    refreshNodes.push(project);
+    getNodes(project);
+    function getNodes(node) {
+        if(node.children.length > 0){
+            refreshNodes = refreshNodes.concat(node.children);
+            for(let cNode of node.children){
+                getNodes(cNode);
+            }
+        }
+    }
+    for(let node of refreshNodes){
+        let nodeInfo = summaryInfo[node.data.ID];
+        if(nodeInfo){
+            node.data.engineeringCost = nodeInfo.engineeringCost;
+            node.data.subEngineering = nodeInfo.subEngineering;
+            node.data.measure = nodeInfo.measure;
+            node.data.safetyConstruction = nodeInfo.safetyConstruction;
+            node.data.other = nodeInfo.other;
+            node.data.charge = nodeInfo.charge;
+            node.data.tax = nodeInfo.tax;
+            node.data.rate = nodeInfo.rate;
+            node.data.buildingArea = nodeInfo.buildingArea;
+            node.data.perCost = nodeInfo.perCost;
+        }
+    }
+    projTreeObj.refreshNodeData(refreshNodes);
+}