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

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

TonyKang 4 лет назад
Родитель
Сommit
6942431fcb

+ 29 - 1
modules/all_models/calc_program_model.js

@@ -44,5 +44,33 @@ let projectCalcPrograms = new Schema({
     templates: [templateSchema]
 },{versionKey:false});
 
+let gatherCalcItemSchema = new Schema({
+  ID: Number,
+  code: String,
+  dispExprUser: String,
+  displayFieldName: String,
+  fieldName: String,
+  name: String,
+  statement: String,
+  totalFee: String,
+  unitFee: String,
+  feeRate: String,
+  memo: String
+},{versionKey:false, _id: false});
+
+let gatherCalcProgramSchema = new Schema({
+  ID: Number,
+  name: String,
+  totalFee: String,
+  calcItems: [gatherCalcItemSchema],
+},{versionKey:false, _id: false});
+
+let gatherCalcPrograms = new Schema({
+  projectID: {type: Number, index: true},
+  totalFee: String,
+  calcPrograms: [gatherCalcProgramSchema]
+},{versionKey:false});
+
 mongoose.model('std_calc_programs', stdCalcPrograms, 'std_calc_programs');
-mongoose.model('calc_programs', projectCalcPrograms, 'calc_programs');
+mongoose.model('calc_programs', projectCalcPrograms, 'calc_programs');
+mongoose.model('gather_calc_programs', gatherCalcPrograms, 'gather_calc_programs');

+ 2 - 2
modules/all_models/equipment_purchase.js

@@ -15,7 +15,7 @@ let equipment = {
     freight:Number,//设备运杂费
     sparePartCost:Number,//备品备件费
     unitPrice:Number,//单价
-    totalPrice:Number,//金额
+    totalPrice:Number,//合价
     remark:String
 }
 
@@ -25,7 +25,7 @@ let equipment_purchase = new Schema({
         type: Number,
         index: true
     },
-    total:Number,//合计
+    total:Number,//设备购置费合计
     equipments :[equipment]
     },{versionKey:false})
 mongoose.model('equipment_purchase', equipment_purchase,'equipment_purchase');

+ 4 - 3
modules/equipment_purchase/facade/equipment_purchase_facade.js

@@ -15,19 +15,20 @@ let decimal_facade = require('../../main/facade/decimal_facade');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
 let _ = require('lodash')
 
+//返回 ID - total 映射如: {2566:60}
 async function getEquipmentTotalCost(projectIDs) {
-    let sum = 0;
     let decimal =2;
+    let totalMap ={};
     if(projectIDs.length > 0){
         let decimalObject =await decimal_facade.getProjectDecimal(projectIDs[0]);
          if(decimalObject&&decimalObject.glj&&decimalObject.glj.unitPrice)decimal = decimalObject.glj.unitPrice;
         let equipments = await equipmentPurchaseModel.find({projectID:{$in: projectIDs}}).lean();
         for(let e of equipments){
             let total =e.total?scMathUtil.roundForObj(e.total,decimal):0;
-            sum = scMathUtil.roundForObj(sum + total,6);
+            totalMap[e.projectID] = total;
         }
     }
-    return scMathUtil.roundForObj(sum,decimal);
+    return totalMap;
 }
 
 function sortEquipments(equipments) {

+ 10 - 0
modules/main/controllers/bills_controller.js

@@ -269,6 +269,16 @@ module.exports = {
             callback(req, res, 1, err, null);
         }
     },
+    initialBudgetSummary: async function (req, res) {
+        const data = JSON.parse(req.body.data);
+        try {
+            const bills = await bill_facade.initialBudgetSummary(data.constructionID);
+            callback(req, res, 0, 'success', bills);
+        } catch (err) {
+            console.log(err);
+            callback(req, res, 1, err, null);
+        }
+    },
     getBudgetSummary: async function (req, res) {
         const data = JSON.parse(req.body.data);
         try {

+ 20 - 2
modules/main/controllers/calc_program_controller.js

@@ -13,7 +13,8 @@ module.exports = {
     updateTemplate: updateTemplate,
     updateTemplateFile: updateTemplateFile,
     addTemplate: addTemplate,
-    deleteTemplate: deleteTemplate
+    deleteTemplate: deleteTemplate,
+    saveGatherCalcPrograms: saveGatherCalcPrograms
 };
 
 async function getProjectCalcProgram(req, res) {
@@ -147,4 +148,21 @@ async function deleteTemplate(req, res) {
         res.json(result);
     });
 
-};
+};
+
+async function saveGatherCalcPrograms(req, res) {
+  let result = {};
+  calcProgramFacade.saveGatherCalcPrograms(req.body.data, function (err, msg) {
+    if (err) {
+      result.error = 1;
+      result.data = 0;
+    }
+    else{
+      result.error = 0;
+      result.data = 1;
+    }
+    result.message = msg;
+    res.json(result);
+  });
+
+};

+ 66 - 18
modules/main/facade/bill_facade.js

@@ -23,6 +23,8 @@ const { getSortedTreeData, getEngineeringFeeType } = require('../../../public/co
 const { billType, constructionFeeNodeID, constructionEquipmentFeeNodeID, BudgetArea, fixedFlag } = require('../../../public/common_constants');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
 const uuidV1 = require('uuid/v1');
+const equipmentFacade = require('../../equipment_purchase/facade/equipment_purchase_facade');
+const gatherModel = mongoose.model('gather_calc_programs');
 
 const GLJController = require("../../glj/controllers/glj_controller");
 
@@ -129,17 +131,40 @@ module.exports={
             await bill_Model.bulkWrite(bulks);
         }
     },
-    // 获取单位工程ID 工程费用 映射表
+    // 获取单位工程ID 工程费用(费用汇总里的totalFee) 映射表
     getUnitsBudgetMap: async function (unitIDs) {
         const rst = {};
         unitIDs.forEach(unitID => {
             rst[unitID] = 1000000;
         });
+        /* const gatherData = await gatherModel.find({ projectID: { $in: unitIDs } }, '-_id projectID totalFee').lean();
+        const gatherMap = {};
+        gatherData.forEach(item => gatherMap[item.projectID] = item.totalFee || 0);
+        unitIDs.forEach(unitID => {
+            rst[unitID] = gatherMap[unitID] || 0;
+        }); */
         return rst;
     },
     // 获取设备购置费
-    getEquipmentFee: async function (unitIDs) {
-        return 5000.15;
+    getUnitsEquipmentMap: async function (unitIDs) {
+        //return await equipmentFacade.getEquipmentTotalCost(unitIDs);
+        const rst = {};
+        unitIDs.forEach(unitID => {
+            rst[unitID] = 5000;
+        });
+        return rst;
+    },
+    // 获取概算汇总初始化数据
+    initialBudgetSummary: async function (constructionID) {
+        const treeData = await this.getBudgetSummary(constructionID);
+        const construction = await projectModel.findOne({ ID: constructionID }, { 'property.costGrowthRate': 1, 'property.growthPeriod': 1 }).lean();
+        const costGrowthRate = construction && construction.property && construction.property.costGrowthRate || 0;
+        const growthPeriod = construction && construction.property && construction.property.growthPeriod || 0;
+        return {
+            treeData,
+            costGrowthRate,
+            growthPeriod,
+        }
     },
     // 获取概算汇总数据(拍好序的)
     getBudgetSummary: async function (constructionID) {
@@ -152,6 +177,12 @@ module.exports={
         // 汇算工程费用
         await this.summarizeData(constructionFeeBills);
         const rst = [...constructionFeeBills, ...sortedOtherFeeBills];
+        const totalItem = rst.find(item => item.flags && item.flags[0] && item.flags[0].flag === fixedFlag.CONSTRUCTION_BUDGET);
+        let totalFee = 0;
+        if (totalItem) {
+            const totalFeeItem = totalItem.fees && totalItem.fees.find(f => f.fieldName === 'common');
+            totalFee = totalFeeItem ? +totalFeeItem.totalFee : 0;
+        }
         // 方便报表取数据,规范数据
         rst.forEach(item => {
             item.code = item.code || '';
@@ -168,7 +199,9 @@ module.exports={
             item.equipmentFee = equipmentFeeItem ? equipmentFeeItem.totalFee : 0;
             item.otherFee = otherFeeItem ? otherFeeItem.totalFee : 0;
             item.totalFee = totalFeeItem ? totalFeeItem.totalFee : 0;
-            item.rate = '0';
+            // 计算占总投资比例
+            const rate = totalFee ?  scMathUtil.roundForObj(item.totalFee / totalFee, 4) : 0; 
+            item.rate = rate * 100; // 转换为百分比
         });
         return rst;
     },
@@ -193,7 +226,7 @@ module.exports={
         let curSingleNo = 0;
         let curSingleID;
         let curUnitNo = 0;
-        let latestSingleNode;
+        // let latestSingleNode;
         items.forEach((item, index) => {
             item.area = BudgetArea.CONSTRUCTION_FEE;
             item.ID = IDMap[item.ID];
@@ -209,7 +242,7 @@ module.exports={
             } else {
                 item.type = billType.BILL;
                 if (item.ParentID === curConstructionID) {
-                    latestSingleNode = item;
+                    // latestSingleNode = item;
                     curSingleNo += 1;
                     curUnitNo = 0;
                     curSingleID = item.ID;
@@ -220,7 +253,7 @@ module.exports={
                 }
             }
         });
-        const constructionFeeNode = items[0];
+        /* const constructionFeeNode = items[0];
         // 设备及工器具购置费
         const constructionEquipmentNode = {
             type: billType.BILL,
@@ -233,7 +266,7 @@ module.exports={
             name: '设备及工器具购置费',
         };
         latestSingleNode.NextSiblingID = constructionEquipmentNode.ID;
-        items.push(constructionEquipmentNode);
+        items.push(constructionEquipmentNode); */
         return items;
     },
     // 汇算数据
@@ -252,18 +285,23 @@ module.exports={
         // 获取第一个单位工程的小数位数(清单合价)
         const theUnit = await projectModel.findOne({ ID: unitProjectIDs[0] }, { _id: 0, 'property.decimal': 1 }).lean();
         const decimal = theUnit && theUnit.property && theUnit.property.decimal && theUnit.property.decimal.bills && theUnit.property.decimal.bills.totalPrice || 2;
-        // 获取单位工程 工程费用映射表
+        // 获取单位工程 -工程费用映射表
         const unitBudgetMap = await this.getUnitsBudgetMap(unitProjectIDs);
+        // 获取单位工程 - 设备购置费映射表
+        const unitEquipmentMap = await this.getUnitsEquipmentMap(unitProjectIDs);
         // 汇算
-        const constructionFeeObj = { total: 0, building: 0, installation: 0 };
+        const constructionFeeObj = { total: 0, building: 0, installation: 0, equipment: 0 };
         for (const single of singles) {
-            const singleFeeObj = { total: 0, building: 0, installation: 0 };
+            const singleFeeObj = { total: 0, building: 0, installation: 0, equipment: 0 };
             const refUnits = units.filter(unit => unit.ParentID === single.ID);
             for (const unit of refUnits) {
-                const unitFee = unitBudgetMap[unit.orgProjectID];
+                const unitFee = unitBudgetMap[unit.orgProjectID]; // 费用汇总算出来的值
+                const unitEquipmentFee = unitEquipmentMap[unit.orgProjectID]; // 设备购置窗口的值
+                const unitTotalFee = scMathUtil.roundForObj(unitFee + unitEquipmentFee, decimal); // 费用汇总算出来的值 + 设备购置值
                 // 汇算到单项工程
-                singleFeeObj.total = scMathUtil.roundForObj(singleFeeObj.total + unitFee, processDecimal);
-                const unitFeeObj = { total: unitFee || 0, building: 0, installation: 0 };
+                singleFeeObj.total = scMathUtil.roundForObj(singleFeeObj.total + unitTotalFee, processDecimal);
+                singleFeeObj.equipment = scMathUtil.roundForObj(singleFeeObj.equipment + unitEquipmentFee, processDecimal);
+                const unitFeeObj = { total: unitTotalFee || 0, building: 0, installation: 0, equipment: unitEquipmentFee || 0 };
                 // 建筑工程费、安装工程费
                 const feeType = getEngineeringFeeType(unit.property && unit.property.engineeringName || '');
                 if (feeType) {
@@ -277,27 +315,30 @@ module.exports={
             constructionFeeObj.total = scMathUtil.roundForObj(constructionFeeObj.total + singleFeeObj.total, processDecimal);
             constructionFeeObj.building = scMathUtil.roundForObj(constructionFeeObj.building + singleFeeObj.building, processDecimal);
             constructionFeeObj.installation = scMathUtil.roundForObj(constructionFeeObj.installation + singleFeeObj.installation, processDecimal);
+            constructionFeeObj.equipment = scMathUtil.roundForObj(constructionFeeObj.equipment + singleFeeObj.equipment, processDecimal);
         }
         construction.fees = feeObj2Fees(constructionFeeObj);
-        // 获取设备购置费
-        const equipmentFee = await this.getEquipmentFee(unitProjectIDs);
+        // 获取设备购置费(旧)
+        /* const equipmentFee = await this.getEquipmentFee(unitProjectIDs);
         const equipmentItem = items.find(item => item.flags && item.flags[0] && item.flags[0].flag === fixedFlag.CONSTRUCTION_EQUIPMENT_FEE);
         const equipmentFeeObj = { fieldName: 'equipment', totalFee: equipmentFee };
         if (equipmentItem) {
             equipmentItem.fees = [equipmentFeeObj];
         }
-        construction.fees.push(equipmentFeeObj);
+        construction.fees.push(equipmentFeeObj); */
 
         function feeObj2Fees(feeObj) {
             return [
                 { fieldName: 'common', totalFee: scMathUtil.roundForObj(feeObj.total, decimal) },
                 { fieldName: 'building', totalFee: scMathUtil.roundForObj(feeObj.building, decimal) },
                 { fieldName: 'installation', totalFee: scMathUtil.roundForObj(feeObj.installation, decimal) },
+                { fieldName: 'equipment', totalFee: scMathUtil.roundForObj(feeObj.equipment, decimal) },
             ];
         }
     },
     bulkOperation: async function (bulkData) {
         const bulks = [];
+        const projectBulks = [];
         bulkData.forEach(item => {
             if (item.type === 'new') {
                 bulks.push({
@@ -307,6 +348,10 @@ module.exports={
                 bulks.push({
                     updateOne: { filter: { ID: item.data.ID }, update: { $set: item.data } }
                 });
+            } else if (item.type === 'updateProject') {
+                projectBulks.push({
+                    updateOne: { filter: { ID: item.data.ID }, update: { $set: item.data } }
+                });
             } else {
                 bulks.push({
                     deleteOne: { filter: { ID: item.data.ID } }
@@ -314,7 +359,10 @@ module.exports={
             }
         });
         if (bulks.length) {
-            await bill_Model.bulkWrite(bulks);
+            await bill_Model.bulkWrite(bulks);  
+        }
+        if (projectBulks.length) {
+            await projectModel.bulkWrite(projectBulks);
         }
     }
 };

+ 37 - 1
modules/main/facade/calc_program_facade.js

@@ -7,6 +7,7 @@ let logger = require("../../../logs/log_helper").logger;
 let mongoose = require('mongoose');
 let stdCalcProgramsModel = mongoose.model('std_calc_programs');
 let projectCalcProgramsModel = mongoose.model('calc_programs');
+let gatherCalcProgramsModel = mongoose.model('gather_calc_programs');
 let EngineeringLibModel = require("../../users/models/engineering_lib_model");
 let _=require("lodash");
 let consts = require('../models/project_consts');
@@ -23,7 +24,8 @@ module.exports = {
     updateTemplate: updateTemplate,
     updateTemplateFile: updateTemplateFile,
     addTemplate: addTemplate,
-    deleteTemplate: deleteTemplate
+    deleteTemplate: deleteTemplate,
+    saveGatherCalcPrograms: saveGatherCalcPrograms
 };
 
 async function newProjectCalcProgramFile(data) {
@@ -260,8 +262,42 @@ function deleteTemplate(dataObj, callback) {
     );
 };
 
+function saveGatherCalcPrograms(dataObj, callback) {
+  dataObj = JSON.parse(dataObj);
+  gatherCalcProgramsModel.findOne({projectID: dataObj.projectID}, function (err, data) {
+    if(data == null){ // 第一次保存,找不到
+      let doc={
+        projectID: dataObj.projectID,
+        totalFee: dataObj.totalFee,
+        calcPrograms: dataObj.calcPrograms
+      };
+      gatherCalcProgramsModel.create(doc, function (err) {
+        if (err) {
+          callback(1, '汇总计算程序创建失败' + err, null);
+        } else {
+          callback(0, '成功', doc.projectID);
+        }
+      });
+    }
+    else {
+      data.totalFee = dataObj.totalFee;
+      data.calcPrograms = dataObj.calcPrograms;
+      data.save(function (err) {
+        if (err) {
+          callback(1, '汇总计算程序保存失败' + err, null);
+        } else {
+          callback(0, '成功', data.projectID);
+        }
+      });
+    }
+  })
+};
+
+
 // for test
 // let s = '{"projectID":2164,"ID":23,"name":"建筑工程7","custom":true,"calcItems":[]}';
 // addTemplate(s, function(data){console.log('add successfull!')});
 // let s = '{"projectID":2164,"ID":15}';
 // deleteTemplate(s, function(data){console.log('delete successfull!')});
+// let s = '{"projectID":2164, "totalFee": "23", "calcPrograms":[]}';
+// saveGatherCalcPrograms(s, function(data){console.log(data)});

+ 1 - 0
modules/main/routes/bills_route.js

@@ -22,6 +22,7 @@ module.exports = function (app) {
     billsRouter.post('/import', billsController.import);
     billsRouter.get('/downloadExamp', billsController.downloadExample);
     billsRouter.post('/insertBills', billsController.insertBills);
+    billsRouter.post('/initialBudgetSummary', billsController.initialBudgetSummary);
     billsRouter.post('/getBudgetSummary', billsController.getBudgetSummary);
     billsRouter.post('/bulkOperation', billsController.bulkOperation);
     app.use('/bills', billsRouter);

+ 1 - 0
modules/main/routes/calc_program_route.js

@@ -17,6 +17,7 @@ module.exports = function (app) {
     cpRouter.post('/updateTemplateFile', cpController.updateTemplateFile);
     cpRouter.post('/addTemplate', cpController.addTemplate);
     cpRouter.post('/deleteTemplate', cpController.deleteTemplate);
+    cpRouter.post('/saveGatherCalcPrograms', cpController.saveGatherCalcPrograms);
 
     app.use('/calcProgram',cpRouter);
 }

+ 3 - 3
modules/main/templates/constructionBillsTemplate.js

@@ -30,7 +30,7 @@ const buildingTemplate = [
   { ID: 2281, ParentID: 228, NextSiblingID: 2282, code: '2.2.8.1', name: '勘察费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
   { ID: 2282, ParentID: 228, NextSiblingID: -1, code: '2.2.8.2', name: '设计费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.DESIGN_FEE }], type: billType.BILL },
   { ID: 229, ParentID: 22, NextSiblingID: 2210, code: '2.2.9', name: '咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
-  { ID: 2291, ParentID: 229, NextSiblingID: 2292, code: '2.2.9.1', name: '设计咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2291, ParentID: 229, NextSiblingID: 2292, code: '2.2.9.1', name: '设计咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.DESIGN_COUNSEL_FEE }], type: billType.BILL },
   { ID: 2292, ParentID: 229, NextSiblingID: 2293, code: '2.2.9.2', name: '工程造价咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
   { ID: 2293, ParentID: 229, NextSiblingID: -1, code: '2.2.9.3', name: '施工图审查费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
   { ID: 2210, ParentID: 22, NextSiblingID: 2211, code: '2.2.10', name: '全过程工程咨询服务费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
@@ -56,7 +56,7 @@ const buildingTemplate = [
   { ID: 41, ParentID: 4, NextSiblingID: 42, code: '4.1', name: '车辆购置费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
   { ID: 42, ParentID: 4, NextSiblingID: 43, code: '4.2', name: '建设期贷款利息', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.LOAN_INTEREST }], type: billType.BILL },
   { ID: 43, ParentID: 4, NextSiblingID: -1, code: '4.3', name: '铺底流动资金', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
-  { ID: 5, ParentID: -1, NextSiblingID: -1, code: '5', name: '建设项目总概算', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_BUDGET }], type: billType.DXFY },
+  { ID: 5, ParentID: -1, NextSiblingID: -1, code: '5', name: '建设项目总概算', unit: '', calcBase: '{工程费用}+{工程建设其他费用}+{预备费}+{专项费用}', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_BUDGET }], type: billType.DXFY },
 ];
 
 /* 概算汇总,城市轨道交通工程,建设其他费清单模板 */
@@ -120,7 +120,7 @@ const railTemplate = [
   { ID: 41, ParentID: 4, NextSiblingID: 42, code: '4.1', name: '车辆购置费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
   { ID: 42, ParentID: 4, NextSiblingID: 43, code: '4.2', name: '建设期贷款利息', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.LOAN_INTEREST }], type: billType.BILL },
   { ID: 43, ParentID: 4, NextSiblingID: -1, code: '4.3', name: '铺底流动资金', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
-  { ID: 5, ParentID: -1, NextSiblingID: -1, code: '5', name: '建设项目总概算', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_BUDGET }], type: billType.DXFY },
+  { ID: 5, ParentID: -1, NextSiblingID: -1, code: '5', name: '建设项目总概算', unit: '', calcBase: '{工程费用}+{工程建设其他费用}+{预备费}+{专项费用}', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_BUDGET }], type: billType.DXFY },
 ];
 
 module.exports = {

+ 3 - 0
modules/pm/controllers/pm_controller.js

@@ -36,6 +36,7 @@ const multiparty = require("multiparty");
 let logger = require("../../../logs/log_helper").logger;
 let rp = require('request-promise');
 const commonUtil = require('../../../public/common_util');
+const { BudgetType } = require('../../../public/common_constants');
 //统一回调函数
 let callback = function (req, res, err, message, data) {
     res.json({ error: err, message: message, data: data });
@@ -254,6 +255,8 @@ module.exports = {
             projInfo.property.basicInformation = constructionProperty && constructionProperty.basicInformation ? constructionProperty.basicInformation : [];
             //编制说明
             projInfo.property.compilationIllustrationProject = constructionProperty && constructionProperty.compilationIllustration ? constructionProperty.compilationIllustration : '';
+            // 概算类型
+            projInfo.property.budgetType = constructionProperty && constructionProperty.budgetType || BudgetType.BUILDING;
             //获取单位工程完整目录结构
             let fullPath = await pm_facade.getFullPath(projectID);
             projInfo.fullPath = fullPath;

+ 8 - 6
public/common_constants.js

@@ -95,18 +95,20 @@
         CONSTRUCTION_OTHER_FEE: 1003,
         // 设计费
         DESIGN_FEE: 1004,
+        // 设计咨询费
+        DESIGN_COUNSEL_FEE: 1005,
         // 预备费
-        BUDGET_RESERVE: 1005,
+        BUDGET_RESERVE: 1006,
         // 基本预备费
-        BASIC_BUDGET_RESERVE: 1006,
+        BASIC_BUDGET_RESERVE: 1007,
         // 价差预备费
-        DIFF_BUDGET_RESERVE: 1007,
+        DIFF_BUDGET_RESERVE: 1008,
         // 专项费用
-        CONSTRUCTION_SPECIAL_FEE: 1008,
+        CONSTRUCTION_SPECIAL_FEE: 1009,
         // 建设期贷款利息
-        LOAN_INTEREST: 1009,
+        LOAN_INTEREST: 1010,
         // 建设项目总概算
-        CONSTRUCTION_BUDGET: 1010,
+        CONSTRUCTION_BUDGET: 1011,
 
     };
     // 清单类型

+ 8 - 2
public/common_util.js

@@ -133,7 +133,7 @@ function deleteEmptyObject(arr) {
 
     // 判断单位工程的工程专业的金额所属是“建筑工程费”还是“安装工程费”
     const getEngineeringFeeType = (engineeringName) => {
-        if (['土建工程', '装饰工程', '市政工程', '城市轨道交通工程', '装配式建筑工程', '城市地下综合管廊工程', '轨道工程'].includes(engineeringName)) {
+        if (['土建工程', '装饰工程', '市政工程', '城市轨道交通工程', '装配式建筑工程', '城市地下综合管廊工程'].includes(engineeringName)) {
             return 'building';
         }
         if (['安装工程', '城市轨道交通安装']) {
@@ -142,6 +142,11 @@ function deleteEmptyObject(arr) {
         return null;
     };
 
+    // 获取造价书树或概算汇总树
+    const getActiveTree = () => {
+        return $('#tab-budget-summary').hasClass('active') ? budgetSummaryObj.getTree() : projectObj.project.mainTree;
+    }
+
     return {
         isDef,
         isEmptyVal,
@@ -151,6 +156,7 @@ function deleteEmptyObject(arr) {
         getSortedTreeData,
         isNotEmptyObject,
         handleFullscreen,
-        getEngineeringFeeType
+        getEngineeringFeeType,
+        getActiveTree,
     };
 });

+ 47 - 0
web/building_saas/budget-summary/html/budget-summary.html

@@ -1,6 +1,10 @@
 <div class="budget-summary">
   <div class="toolsbar px-1 d-flex justify-content-between">
     <div class="tools-btn btn-group align-top">
+      <!-- <span id="openConstructionSet" class="btn btn-light btn-sm" data-toggle="tooltip" data-original-title="建设项目设置"
+                data-placement="bottom">
+        <a href="javascript:void(0);"><i class="fa fa-cog"></i></a>
+      </span> -->
       <a href="javascript:void(0)" class="btn btn-light btn-sm" id="budget-upLevel" data-toggle="tooltip"
         data-placement="bottom" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
       <a href="javascript:void(0)" class="btn btn-light btn-sm" id="budget-downLevel" data-toggle="tooltip"
@@ -13,4 +17,47 @@
     </div>
   </div>
   <div class="sheet-wrapper" id="budget-summary-sheet"></div>
+</div>
+<div class="modal fade" id="constructionSet" data-backdrop="static">
+  <div class="modal-dialog modal-lg" role="document" style="max-width: 800px;">
+      <div class="modal-content ui-draggable">
+          <div class="modal-header ui-draggable-handle">
+              <h5 class="modal-title">建设项目设置</h5>
+              <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                  <span aria-hidden="true">×</span>
+              </button>
+          </div>
+          <div class="modal-body">
+            <div class="row">
+                <div class="col-3">
+                    <ul class="nav flex-column nav-pills" role="tablist">
+                        <li class="nav-item"><a class="nav-link active show" data-toggle="pill" href="#construction-setting" id="about-construction-calc" role="tab">关于计算</a></li>
+                    </ul>
+                </div>
+                <div class="col-9">
+                    <div class="tab-content">
+                        <!--关于计算-->
+                        <div class="tab-pane fade active show" id="construction-setting" role="tabpanel">
+                            <div class="modal-auto-height">
+                                <fieldset class="form-group" id="jcybf">
+                                    <h5>价差预备费</h5>
+                                    <div class="mt-1">
+                                        年造价增涨率<input id="costGrowthRate" class="form-control form-control-sm"  value="0" type="text" style="display: inline-block; width: 90px;">%
+                                    </div>
+                                    <div class="mt-1">
+                                        增涨计费年限<input id="growthPeriod" class="form-control form-control-sm"  value="0" type="text" style="display: inline-block; width: 90px;">
+                                    </div>
+                                </fieldset>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+          <div class="modal-footer">
+            <a href="javascript:void(0);" class="btn btn-primary" id="construction-set-ok" data-dismiss="modal">确定</a>
+            <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+          </div>
+      </div>
+  </div>
 </div>

+ 1 - 1
web/building_saas/budget-summary/js/budgetSummarySetting.js

@@ -91,7 +91,7 @@ const budgetSummaryTreeSetting = {
         width: 100,
         readOnly: true,
         head: {
-            titleNames: ["金额"],
+            titleNames: ["合价"],
             spanCols: [1],
             spanRows: [1],
             vAlign: [1],

+ 287 - 88
web/building_saas/budget-summary/js/budgetSummarySheet.js

@@ -6,10 +6,17 @@ const budgetSummaryObj = (() => {
 
   // 原始数据
   let rawData = [];
+  // ID与原始数据映射表(主要是恢复用)
+  const orgMap = {};
   // 建设其他费表格对象
   let spread = null;
   // 建设其他费树
   let tree = null;
+  // 计算相关
+  const calcSetting = {
+    costGrowthRate: 0,
+    growthPeriod: 0,
+  };
 
   // 单位设置下拉框
   const setUnitCombo = (sheet, data) => {
@@ -51,43 +58,90 @@ const budgetSummaryObj = (() => {
     return validator[item.data.type || 'text'];
   }
 
+  // 单元格文本转换处理
+  const textFactory = {
+    calcBase(node) {
+      if (node.data.calcBase && node.data.calcBase !== "") {
+        return cbParser.toFExpr(node.data.calcBase);
+      }
+    },
+    'feesIndex.common.unitFee': (node) => {
+      return _.get(node, 'data.feesIndex.common.unitFee', '') || '';
+    },
+    'feesIndex.common.totalFee': (node) => {
+      return _.get(node, 'data.feesIndex.common.totalFee', '') || '';
+    },
+    'feesIndex.building.totalFee': (node) => {
+      return _.get(node, 'data.feesIndex.building.totalFee', '') || '';
+    },
+    'feesIndex.installation.totalFee': (node) => {
+      return _.get(node, 'data.feesIndex.installation.totalFee', '') || '';
+    },
+    'feesIndex.equipment.totalFee': (node) => {
+      return _.get(node, 'data.feesIndex.equipment.totalFee', '') || '';
+    },
+    'feesIndex.other.totalFee': (node) => {
+      return _.get(node, 'data.feesIndex.other.totalFee', '') || '';
+    },
+  };
+
   /* 表格事件相关 */
-  // 恢复数据
-  const recover = (sheet, changedCells) => {
+  // 根据节点数据刷新表格数据
+  const refreshData = (sheet, changedCells) => {
     if (!tree) {
       return;
     }
-    changedCells.forEach(({ row, col }) => {
-      const node = tree.items[row];
-      const field = getFieldByCol(col);
-      if (!field || !node) {
-        return;
+    TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
+      changedCells.forEach(({ row, col }) => {
+        const node = tree.items[row];
+        const field = getFieldByCol(col);
+        if (!field || !node) {
+          return;
+        }
+        const textFunc = textFactory[field];
+        const val = textFunc ? textFunc(node) : node.data[field];
+        sheet.setValue(row, col, val);
+      });
+    });
+  }
+
+  // 刷新整个表格
+  const refreshAll = (sheet) => {
+    const changedCells = [];
+    const colCount = budgetSummaryTreeSetting.cols.length;
+    for (let row = 0; row < tree.items.length; row++) {
+      for (let col = 0; col < colCount; col++) {
+        changedCells.push({ row, col })
       }
-      const orgVal = node.data[field];
-      sheet.setValue(row, col, orgVal);
-    })
+    }
+    refreshData(sheet, changedCells);
   }
 
   // 更新数据
   const bulkOperation = async (bulkData) => {
-    await ajaxPost('/bills/bulkOperation', { bulkData });
+    if (bulkData.length) {
+      await ajaxPost('/bills/bulkOperation', { bulkData });
+    }
   };
 
   // 编辑相关
-  const edit = async (sheet, changedCells) => {
-    if (!changedCells.length) {
+  const edit = async (sheet, changedCells, calcNodes = null) => {
+    if (!changedCells.length && !calcNodes) {
       return;
     }
-    // 单元格值验证
-    const isValid = changedCells.every(({ row, col }) => {
-      const val = sheet.getValue(row, col);
-      return getValidator(col)(val);
-    });
-    // 验证不通过,恢复
-    if (!isValid) {
-      recover(sheet, changedCells);
+    if (changedCells.length) {
+      // 单元格值验证
+      const isValid = changedCells.every(({ row, col }) => {
+        const val = sheet.getValue(row, col);
+        return getValidator(col)(val);
+      });
+      // 验证不通过,恢复
+      if (!isValid) {
+        refreshData(sheet, changedCells);
+      }
     }
-    console.log(changedCells);
+    let needCalc = false;
+    const nodes = [];
     try {
       $.bootstrapLoading.start();
       const IDMap = {};
@@ -97,21 +151,108 @@ const budgetSummaryObj = (() => {
         if (!node) {
           return;
         }
+        if (!nodes.find(n => n.data.ID !== node.data.ID)) {
+          nodes.push(node);
+        }
         const field = getFieldByCol(col);
+        const value = sheet.getValue(row, col) || '';
         const data = (IDMap[node.data.ID] || (IDMap[node.data.ID] = {}));
-        data[field] = sheet.getValue(row, col);
+        if (['feesIndex.common.unitFee'].includes(field)) {
+          const fees = node.data.fees || [];
+          const feeItem = fees.find(item => item.fieldName === 'common');
+          if (feeItem) {
+            feeItem.unitFee = value;
+          } else {
+            fees.push({ fieldName: 'common', totalFee: 0, unitFee: +value });
+          }
+          data[field] = fees;
+          node.data[field] = fees;
+          node.data.feesIndex = getFeeIndex(node.data.fees);
+        } else {
+          data[field] = value;
+          node.data[field] = value;
+        }
+        if (field === 'calcBase') {
+          node.data.userCalcBase = value;
+          projectObj.project.calcBase.calculate(node, null, false);
+          if (!projectObj.project.calcBase.success) {
+            throw projectObj.project.calcBase.errMsg;
+          } else if (isEmptyVal(value)) {
+            // 删除清单基数,单价要清空
+            calcTools.setFieldValue(node, 'feesIndex.common.unitFee', 0);
+          }
+          data.calcBase = node.data.calcBase;
+          data.calcBaseValue = node.data.calcBaseValue;
+          data.tenderCalcBaseValue = node.data.tenderCalcBaseValue;
+        }
+        if (['quantity', 'feesIndex.common.unitFee', 'calcBase', 'feeRate'].includes(field)) {
+          needCalc = true;
+        }
+      });
+      calcNodes = calcNodes ? calcNodes : nodes;
+      // 重算节点
+      const dataArr = [];
+      if ((needCalc && nodes.length) || (calcNodes && calcNodes.length)) {
+        const changedNodes = projectObj.project.calcProgram.calcNodes(calcNodes, false, tree);
+        for (const node of changedNodes) {
+          nodes.push(node);
+          if (node.changed) {
+            const data = calcTools.cutNodeForSave(node);
+            dataArr.push(data);
+          }
+        }
+      }
+      dataArr.forEach(item => {
+        delete item.projectID;
+        const data = IDMap[item.ID];
+        if (data) {
+          Object.assign(data, item);
+        } else {
+          IDMap[item.ID] = item;
+        }
       });
+      // 保存节点
       Object
         .entries(IDMap)
         .forEach(([ID, data]) => {
+          const node = tree.findNode(ID);
+          if (!node) {
+            return;
+          }
+          // 处理其他费用
+          if (node.isBelongToFlags([fixedFlag.CONSTRUCTION_OTHER_FEE])) {
+            const fees = data.fees || [];
+            const commonFeeItem = fees.find(item => item.fieldName === 'common');
+            const otherFeeItem = fees.find(item => item.fieldName === 'other');
+            if (otherFeeItem) {
+              otherFeeItem.totalFee = commonFeeItem && commonFeeItem.totalFee || 0;
+            } else {
+              fees.push({ fieldName: 'other', totalFee: commonFeeItem && commonFeeItem.totalFee || 0 });
+            }
+          }
           bulkData.push({ type: 'update', data: { ID, ...data } });
         });
       await bulkOperation(bulkData);
-      bulkData.forEach(item => {
-        const node = tree.findNode(item.data.ID);
-        Object.assign(node.data, item.data);
-      });
+      Object
+        .entries(IDMap)
+        .forEach(([ID, data]) => {
+          const node = tree.findNode(ID);
+          if (node) {
+            Object.assign(node.data, data);
+            node.data.feesIndex = getFeeIndex(node.data.fees);
+            orgMap[ID] = _.cloneDeep(node.data);
+          }
+        });
+      refreshAll(sheet);
     } catch (err) {
+      console.log(err);
+      nodes.forEach(node => {
+        const orgItem = orgMap[node.data.ID];
+        if (orgItem) {
+          Object.assign(node.data, orgItem);
+        }
+      });
+      refreshData(sheet, changedCells);
       alert(err);
     } finally {
       $.bootstrapLoading.end();
@@ -217,39 +358,62 @@ const budgetSummaryObj = (() => {
     const lock = () => {
       if (projectReadOnly) {
         sheet.getRange(0, 0, nodes.length, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true);
-        return;
+        return 0;
       }
       // 工程费用区域,只读
       const equipmentNode = nodes.find(node => node.getFlag() === fixedFlag.CONSTRUCTION_EQUIPMENT_FEE);
       if (!equipmentNode) {
-        return;
+        return 0;
       }
       sheet.getRange(0, 0, equipmentNode.serialNo() + 1, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true);
-      // 剩下的区域,根据单元格锁定方法,按字段进行锁定判断
-      const startIndex = equipmentNode.serialNo() + 1;
-      for (let row = startIndex; row < nodes.length; row ++) {
-        const node = nodes[row];
-        budgetSummaryTreeSetting.cols.forEach((item, col) => {
-          const field = item.data.field;
-          const lockFunc = lockFactory[field];
-          if (!lockFunc) {
-            return;
-          }
-          const isLocked = lockFunc(node);
-          sheet.getCell(row, col).locked(isLocked);
-        });
-      }
-
+      return equipmentNode.serialNo() + 1;
     }
     if (isMass) {
       TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
-        lock();
+        return lock();
       });
     } else {
-      lock();
+      return lock();
+    }
+  }
+
+  /* 单元格类型 */
+  const calcBaseButtonCallback = (hitInfo) => {
+    calcBaseView.onCalcBaseButtonClick(hitInfo, 'budget');
+  };
+  const cellTypeFactory = {
+    calcBase(node) {
+      const locked = lockFactory.calcBase(node);
+      return sheetCommonObj.getCusButtonCellType(calcBaseButtonCallback, locked);
     }
   }
 
+  /* 设置可编辑区域节点的只读性和单元格类型 */
+  const setCells = (sheet, startRow, nodes) => {
+    for (let row = startRow; row < nodes.length; row++) {
+      const node = nodes[row];
+      budgetSummaryTreeSetting.cols.forEach((item, col) => {
+        const field = item.data.field;
+        // 锁定单元格
+        const lockFunc = lockFactory[field];
+        if (lockFunc) {
+          const isLocked = lockFunc(node);
+          sheet.getCell(row, col).locked(isLocked);
+        }
+        // 设置单元格类型
+        const cellTypeFunc = cellTypeFactory[field];
+        if (cellTypeFunc) {
+          const cellType = cellTypeFunc(node);
+          sheet.getCell(row, col).cellType(cellType);
+        }
+        // 单元格文本转换
+        const textFunc = textFactory[field];
+        if (textFunc) {
+          sheet.setValue(row, col, textFunc(node));
+        }
+      });
+    }
+  }
   /* 初始化表格 */
   const initSpread = () => {
     if (!spread) {
@@ -282,7 +446,8 @@ const budgetSummaryObj = (() => {
     sheet.setRowCount(data.length);
     setUnitCombo(sheet, data);
     TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
-      lockData(sheet, tree.items, false);
+      const startRow = lockData(sheet, tree.items, false);
+      setCells(sheet, startRow, tree.items);
       // 表格格式化
       budgetSummaryTreeSetting.cols.forEach((item, index) => {
         sheet.setFormatter(-1, index, item.formatter || '@', GC.Spread.Sheets.SheetArea.viewport);
@@ -297,13 +462,18 @@ const budgetSummaryObj = (() => {
     // 更新数据
     updateData.forEach(item => {
       if (item.type === 'new') {
+        orgMap[item.data.ID] = _.cloneDeep(item.data);
         rawData.push(item.data)
       } else if (item.type === 'update') {
+        if (orgMap[item.data.ID]) {
+          Object.assign(orgMap[item.data.ID], item.data);
+        }
         const node = tree.findNode(item.data.ID);
         if (node) {
           Object.assign(node.data, item.data);
         }
       } else {
+        delete orgMap[item.data.ID];
         const removeIndex = rawData.findIndex(d => d.ID === item.data.ID);
         if (removeIndex > -1) {
           rawData.splice(removeIndex, 1);
@@ -499,46 +669,6 @@ const budgetSummaryObj = (() => {
             remove(sheet, curNode);
           }
         },
-        /*       upLevel: {
-                name: '升级',
-                icon: 'fa-arrow-left',
-                disabled() {
-                  return !curNode || !curNode.canUpLevel() || isConstructionFeeArea(curNode);
-                },
-                callback() {
-                  upLevel(sheet, curNode);
-                }
-              },
-              downLevel: {
-                name: '降级',
-                icon: 'fa-arrow-right',
-                disabled() {
-                  return !curNode || !curNode.canDownLevel() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.preSibling);
-                },
-                callback() {
-                  downLevel(sheet, curNode);
-                }
-              },
-              upMove: {
-                name: '上移',
-                icon: 'fa-arrow-up',
-                disabled() {
-                  return !curNode || !curNode.canUpMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.preSibling);
-                },
-                callback() {
-                  upMove(sheet, curNode);
-                }
-              },
-              downMove: {
-                name: '下移',
-                icon: 'fa-arrow-down',
-                disabled() {
-                  return !curNode || !curNode.canDownMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.nextSibling);
-                },
-                callback() {
-                  downMove(sheet, curNode);
-                }
-              }, */
         refresh: {
           name: '刷新数据',
           icon: 'fa-refresh',
@@ -554,7 +684,12 @@ const budgetSummaryObj = (() => {
   const init = async (constructionID) => {
     try {
       $.bootstrapLoading.start();
-      rawData = await ajaxPost('/bills/getBudgetSummary', { constructionID });
+      const { treeData, costGrowthRate, growthPeriod } = await ajaxPost('/bills/initialBudgetSummary', { constructionID });
+      calcSetting.costGrowthRate = costGrowthRate;
+      calcSetting.growthPeriod = growthPeriod;
+      $('#costGrowthRate').val(costGrowthRate);
+      $('#growthPeriod').val(growthPeriod);
+      rawData = treeData;
       rawData.forEach((item) => {
         if (item.quantity) {
           item.quantity = parseFloat(item.quantity);
@@ -569,7 +704,13 @@ const budgetSummaryObj = (() => {
       });
       const spread = initSpread();
       const sheet = spread.getSheet(0);
+      rawData.forEach(item => {
+        orgMap[item.ID] = _.cloneDeep(item);
+      });
       initTree(rawData, sheet, budgetSummaryTreeSetting);
+      calcBase.initBudget();
+      // 造价计算
+      await edit(sheet, [], tree.roots.slice(1));
     } catch (err) {
       console.log(err);
       alert(err);
@@ -597,9 +738,67 @@ const budgetSummaryObj = (() => {
     downMove();
   });
 
+  /* 建设项目设置 */
+  $('#openConstructionSet').click(() => {
+    $('#constructionSet').modal('show');
+  });
+  function isKeyNumber(keyCode) {
+    // 数字
+    if (keyCode >= 48 && keyCode <= 57) {
+      return true;
+    } else if (keyCode >= 96 && keyCode <= 105) { //小键盘数字
+      return true;
+    } else if (keyCode == 8 || keyCode == 46 || keyCode == 37 || keyCode == 39 || keyCode == 108 || keyCode == 110) {  // Backspace, del, 左右方向键
+      return true;
+    } else if (keyCode >= 112 && keyCode <= 123) { //F1 -F12
+      return true;
+    }
+    return false;
+  }
+
+  //年造价增涨率
+  $('#costGrowthRate').keydown(function (e) {
+    return isKeyNumber(e.keyCode);
+  });
+  //计费年限
+  $("#growthPeriod").keydown(function (e) {
+    return isKeyNumber(e.keyCode);
+  });
+  // 确认设置
+  $('#construction-set-ok').click(async () => {
+    try {
+      $.bootstrapLoading.start();
+      const curCostGrowthRate = $('#costGrowthRate').val();
+      const curGrowthPeriod = $('#growthPeriod').val();
+      const bulkData = [
+        {
+          type: 'updateProject',
+          data: {
+            ID: projectObj.project.property.rootProjectID,
+            'property.costGrowthRate': curCostGrowthRate,
+            'property.growthPeriod': curGrowthPeriod,
+          }
+        }
+      ];
+      await bulkOperation(bulkData);
+      calcSetting.costGrowthRate = curCostGrowthRate;
+      calcSetting.growthPeriod = curGrowthPeriod;
+    } catch (err) {
+      alert(err);
+      $('#costGrowthRate').val(calcSetting.costGrowthRate);
+      $('#growthPeriod').val(calcSetting.growthPeriod);
+    } finally {
+      $.bootstrapLoading.end();
+    }
+  });
+
+
   // 对外暴露
   return {
     getTree: () => tree,
+    getSheet: () => spread.getSheet(0),
+    calcSetting,
+    edit,
   };
 
 })();

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

@@ -2788,6 +2788,7 @@
 
 
 
+
   <%include ../../../common/components/share/index.html %>
 
   <img src="/web/dest/css/img/folder_open.png" id="folder_open_pic" style="display: none">

Разница между файлами не показана из-за своего большого размера
+ 581 - 340
web/building_saas/main/js/models/calc_base.js


+ 30 - 10
web/building_saas/main/js/models/calc_program.js

@@ -1026,7 +1026,7 @@ let calcTools = {
             let r = projectObj.project.FeeRate.getFeeRateByID(node.data.feeRateID);
             if (r) return scMathUtil.roundForObj(r.rate, decimal);
         };
-        if (node.data.feeRate || node.data.feeRate == 0)
+        if (node.data.feeRate || node.data.feeRate === 0 || node.data.feeRate === '0')
             return scMathUtil.roundForObj(node.data.feeRate,decimal);
 
         return 100;
@@ -1094,6 +1094,7 @@ let calcTools = {
             if (!arr.includes(tpl))
               arr.push(tpl);
         };
+        arr.sort(function (x, y) {return x.ID - y.ID});
         return arr;
     },
     getRationsByProgramID: function(programID){
@@ -2230,7 +2231,7 @@ class CalcProgram {
     // 计算零散的、混杂的树节点:清单、定额混合等(如:用到某一计算程序的定额和清单)。
     // 计算多条零散的定额,并计算他们所属的清单、父清单、引用清单。如:批量替换工料机后受影响的定额。
     // 计算多条零散的清单,并计算他们的父清单、引用清单。如:花选删除树结点(如花选清单、定额等,不区分树结点类型)。
-    calcNodes(nodes, tender){
+    calcNodes(nodes, tender, tree){
         let me = this, rationNodes = [], billNodes = [], leafBills = [], allChangedNodes = [];
         for (let node of nodes) {
             if (node.sourceType == ModuleNames.ration)
@@ -2254,7 +2255,7 @@ class CalcProgram {
             allChangedNodes.merge(changeBills);
         };
 
-        me.calcFormulaNodes(allChangedNodes, tender);
+        me.calcFormulaNodes(allChangedNodes, tender, tree);
         return allChangedNodes;
     };
 
@@ -2265,9 +2266,9 @@ class CalcProgram {
     };
 
     // 计算全部公式项。 (changedArr:将通过本方法后发生改变的节点存入changedArr中)
-    calcFormulaNodes(changedArr, tender){
+    calcFormulaNodes(changedArr, tender, tree = projectObj.project.mainTree){
         let me = this;
-        let formulaNodes = cbTools.getFormulaNodes(true);
+        let formulaNodes = cbTools.getFormulaNodes(true, tree);
         if (formulaNodes.length == 0) return;
         for (let formulaNode of formulaNodes){
             formulaNode.data.userCalcBase = formulaNode.data.calcBase;    // 这句不该出现,projectObj.project.calcBase中要改进。
@@ -2881,23 +2882,42 @@ class CalcProgram {
                 rst = calcItem.totalFee;
             }
         };
-        return rst;
+
+        let tobj = {
+            ID: template.ID,
+            name: template.name,
+            totalFee: template.totalFee,
+            calcItems: template.calcItems
+        };
+        // let tobj = JSON.parse(JSON.stringify(template));
+        return {value: rst, template: tobj};
     };
 
     //报表用取费类别汇总。调用:let obj = projectObj.project.calcProgram.getGatherFeeData();
     getGatherFeeData(){
       // $.bootstrapLoading.start();
       // setTimeout(()=>{
-        let rst = {};
+        let rst = {projectID: projectObj.project.ID(), totalFee: 0, calcPrograms: []};
         let tpls = calcTools.getUsedTemplates();
 
         let temp = 0;
         for (const t of tpls) {
-            temp = temp + this.gatherFee2(t);
+            let obj = this.gatherFee2(t);
+            rst.calcPrograms.push(obj.template);
+            temp = temp + obj.value;
         }
-        rst.calcPrograms = tpls;
         rst.totalFee = temp.toDecimal(decimalObj.bills.totalPrice);
-        projectObj.project.mainTree.items[0].data.totalFee = rst.totalFee;
+        let oldValue = projectObj.project.mainTree.items[0].data.totalFee;
+        if (oldValue != rst.totalFee){
+          projectObj.project.mainTree.items[0].data.totalFee = rst.totalFee;
+          $.bootstrapLoading.start();
+          CommonAjax.post("/calcProgram/saveGatherCalcPrograms", rst, function (data) {
+            // if(callback){
+            //   callback(data);
+            $.bootstrapLoading.end();
+            // }
+          });
+        }
         console.log(rst);
         return rst;
       // });

+ 96 - 56
web/building_saas/main/js/views/calc_base_view.js

@@ -8,26 +8,26 @@ let calcBaseView = {
     //可用计算基数的清单固定列映射(与fixedFlag)
     inputExpr: $('#calcBaseExp'),
     confirmBtn: $('#calcBaseFeeRateConf'),//org:calcBaseConf
-    type: {bills: 'bills', ration: 'ration'},
-    billsCBClass:{ALL: [], FBFX: [], CSXM: [], QTXM: [], FBF: [], RCJ: [], GF: [], SJ: [], SQGCZJ: []},
+    type: { bills: 'bills', ration: 'ration' },
+    billsCBClass: { ALL: [], FBFX: [], CSXM: [], QTXM: [], FBF: [], RCJ: [], GF: [], SJ: [], SQGCZJ: [] },
     curType: null,
     editingCell: null,
     workBook: null,
-    setting:{
+    setting: {
         billsHeader: [
-            {name: '计算基础名称', dataCode: 'base', width: 220, vAlign: 'center', hAlign: 'left'},
-            {name: '金额', dataCode: 'price', width: 100, vAlign: 'center', hAlign: 'right'}
+            { name: '计算基础名称', dataCode: 'base', width: 220, vAlign: 'center', hAlign: 'left' },
+            { name: '金额', dataCode: 'price', width: 100, vAlign: 'center', hAlign: 'right' }
         ],
         rationHeader: [
-            {name: '定额计算程序基数名称', dataCode: 'base', width: 400, vAlign: 'center', hAlign: 'left'}
+            { name: '定额计算程序基数名称', dataCode: 'base', width: 400, vAlign: 'center', hAlign: 'left' }
         ],
         options: {
-            tabStripVisible:  false,
-            allowCopyPasteExcelStyle : false,
+            tabStripVisible: false,
+            allowCopyPasteExcelStyle: false,
             allowExtendPasteRange: false,
-            allowUserDragDrop : false,
+            allowUserDragDrop: false,
             allowUserDragFill: false,
-            scrollbarMaxAlign : true
+            scrollbarMaxAlign: true
         },
         locked: {
             rows: [],
@@ -44,7 +44,7 @@ let calcBaseView = {
     },
 
     setOptions: function (workbook, opts) {
-        for(let opt in opts){
+        for (let opt in opts) {
             workbook.options[opt] = opts[opt];
         }
     },
@@ -60,7 +60,7 @@ let calcBaseView = {
             sheet.setColumnCount(headers.length);
             sheet.setRowHeight(0, 20, GC.Spread.Sheets.SheetArea.colHeader);
             sheet.setColumnWidth(0, 20, GC.Spread.Sheets.SheetArea.rowHeader);
-            for(let i = 0, len = headers.length; i < len; i++){
+            for (let i = 0, len = headers.length; i < len; i++) {
                 sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
                 sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
             }
@@ -69,12 +69,12 @@ let calcBaseView = {
     },
 
     buildSheet: function () {
-        if(!this.workBook){
-            this.workBook = new GC.Spread.Sheets.Workbook($('#billsBaseSpread')[0], {sheetCount: 1});
+        if (!this.workBook) {
+            this.workBook = new GC.Spread.Sheets.Workbook($('#billsBaseSpread')[0], { sheetCount: 1 });
             sheetCommonObj.spreadDefaultStyle(this.workBook);
             this.setOptions(this.workBook, this.setting.options);
             //bills
-            if(this.curType === this.type.bills){
+            if (this.curType === this.type.bills || this.curType === 'budget') {
                 this.setting.header = this.setting.billsHeader;
             }
             //ration
@@ -92,7 +92,7 @@ let calcBaseView = {
         sheet.bind(_events.CellDoubleClick, this.onCellDoubleClick);
     },
 
-    showData(datas){
+    showData(datas) {
         let me = calcBaseView;
         let sheet = this.workBook.getActiveSheet();
         let cols = this.setting.header;
@@ -101,10 +101,10 @@ let calcBaseView = {
             let style = new GC.Spread.Sheets.Style();
             //style.formatter = MainTreeCol.getNumberFormatter(decimalObj.bills.totalPrice, true);
             sheet.setStyle(-1, 1, style);
-            for(let col = 0, cLen = cols.length; col < cLen; col++){
+            for (let col = 0, cLen = cols.length; col < cLen; col++) {
                 sheet.getRange(-1, col, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[cols[col]['hAlign']]);
                 sheet.getRange(-1, col, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[cols[col]['vAlign']]);
-                for(let row = 0, rLen = datas.length; row < rLen; row++){
+                for (let row = 0, rLen = datas.length; row < rLen; row++) {
                     sheet.setValue(row, col, datas[row][cols[col]['dataCode']]);
                 }
             }
@@ -114,17 +114,17 @@ let calcBaseView = {
 
     onCellDoubleClick: function (sender, args) {
         let me = calcBaseView;
-        if(args.col === 0){
+        if (args.col === 0) {
             let v = args.sheet.getValue(args.row, args.col);
-            if(!me.isDef(v)){
+            if (!me.isDef(v)) {
                 return;
             }
             let baseFigure = '';
-            if(me.curType == me.type.bills)
+            if (me.curType == me.type.bills || me.curType === 'budget')
                 baseFigure = `{${v}}`
             else if (me.curType == me.type.ration)
                 baseFigure = `[${v}]`;
-            if(baseFigure.trim() !== ''){
+            if (baseFigure.trim() !== '') {
                 //在光标后面插入
                 let insertStr = me.insertStr(baseFigure);
                 me.inputExpr.val(insertStr);
@@ -138,16 +138,16 @@ let calcBaseView = {
     },
     //processMouseDown触发时间比SelectionChanged早,所以直接取selected是上一个节点的,需要传row取当前选中节点
     ifEdit: function (type, row) {
-        if (type == 'ration'){
+        if (type == 'ration') {
             return true;
-        } else{
+        } else {
             let selected = projectObj.project.mainTree.items[row];
-            return selected && MainTreeCol.readOnly.forCalcBase(selected)?false:true;
+            return selected && MainTreeCol.readOnly.forCalcBase(selected) ? false : true;
         }
     },
     bindClassBtn: function () {
         let me = this;
-        for(let clas in me.billsCBClass){
+        for (let clas in me.billsCBClass) {
             let jqS = `#cb_${clas}`;
             $(jqS).click(function () {
                 sheetCommonObj.cleanData(me.workBook.getSheet(0), me.setting, -1);
@@ -158,28 +158,40 @@ let calcBaseView = {
         }
     },
     //计算基数转换为显示数据Obj to Array, 给清单基数分类赋值
+    /*     toViewData: function (obj) {
+            let rst = [];
+            for(let figure in obj){
+                let figureObj = Object.create(null);
+                figureObj.base = figure;
+                figureObj.price = projectObj.project.calcBase.getBase(figure);
+                rst.push(figureObj);
+                //set class datas
+                this.billsCBClass.ALL.push(figureObj);
+                this.billsCBClass[obj[figure]['class']].push(figureObj);
+            }
+            return this.billsCBClass.ALL;
+        }, */
     toViewData: function (obj) {
         let rst = [];
-        for(let figure in obj){
+        for (let figure in obj) {
             let figureObj = Object.create(null);
             figureObj.base = figure;
             figureObj.price = projectObj.project.calcBase.getBase(figure);
             rst.push(figureObj);
             //set class datas
             this.billsCBClass.ALL.push(figureObj);
-            this.billsCBClass[obj[figure]['class']].push(figureObj);
         }
         return this.billsCBClass.ALL;
     },
 
-    initCalctor: function (type,field) {//type = bills、ration
+    initCalctor: function (type, field) {//type = bills、ration
         let me = calcBaseView;
         let showDatas;
         me.inputExpr = $('#calcBaseExp');
         me.curType = type;
         if (type === me.type.bills) {
             //锁定的清单不显示
-            if(field && MainTreeCol.lockBillChecking(projectObj.project.mainTree.selected,field)){
+            if (field && MainTreeCol.lockBillChecking(projectObj.project.mainTree.selected, field)) {
                 return;
             }
             //显示清单基数分类
@@ -196,8 +208,25 @@ let calcBaseView = {
             showDatas = me.toViewData(baseObj);
             $('#cbClassList li .btn ').removeClass('btn btn-outline-secondary btn-sm active');
             $('#cb_ALL').addClass('btn btn-outline-secondary btn-sm active');
-        }
-        else if (type == me.type.ration) {
+        } else if (type === 'budget') {
+            try {
+                //去除清单基数分类
+                $('#cbClassList').hide();
+                $('#cbRowDiv').removeClass('row');
+                $('#billsBaseSpread').removeClass('col-9');
+                const node = budgetSummaryObj.getTree().selected;
+                //输入框显示原本的
+                if (me.isDef(node.data.calcBase)) {
+                    me.inputExpr.val(cbParser.toFExpr(node.data.calcBase));
+                }
+                let baseObj = projectObj.project.calcBase.getBudgetBaseByClass(node);
+                showDatas = me.toViewData(baseObj);
+                $('#cbClassList li .btn ').removeClass('btn btn-outline-secondary btn-sm active');
+                $('#cb_ALL').addClass('btn btn-outline-secondary btn-sm active');
+            } catch (err) {
+                alert(err);
+            }
+        } else if (type == me.type.ration) {
             //去除清单基数分类
             $('#cbClassList').hide();
             //$('#qd-jsjs .modal-content').css('width', '');
@@ -206,7 +235,7 @@ let calcBaseView = {
             let calcItem = calcProgramManage.getSelectionInfo().calcItem;
             if (calcItem.dispExprUser) {
                 me.inputExpr.val(calcItem.dispExpr);
-                if (calcItem.dispExpr == '0'){
+                if (calcItem.dispExpr == '0') {
                     me.inputExpr.focus();
                     me.inputExpr.select();
                 }
@@ -215,7 +244,7 @@ let calcBaseView = {
             // bnArr.sort();
             let baseArr = [];
             for (let bn of bnArr) {
-                baseArr.push({base: bn})
+                baseArr.push({ base: bn })
             };
             showDatas = baseArr;
         }
@@ -244,7 +273,7 @@ let calcBaseView = {
     //运算符点击显示到运算窗口
     clickOpr: function (operators) {
         let me = calcBaseView;
-        for(let i = 0, len = operators.length; i < len; i++){
+        for (let i = 0, len = operators.length; i < len; i++) {
             operators[i].bind('click', function () {
                 let v = $(this)[0].textContent;
                 let insertStr = me.insertStr(v);
@@ -272,9 +301,9 @@ let calcBaseView = {
     //输入窗口控制
     inputControl: function () {
         let me = calcBaseView;
-        me.inputExpr.on('input',function () {
-            if(!me.arithmeticLegal(me.inputExpr.val())){
-                if(me.preInputExpr){
+        me.inputExpr.on('input', function () {
+            if (!me.arithmeticLegal(me.inputExpr.val())) {
+                if (me.preInputExpr) {
                     me.inputExpr.val(me.preInputExpr);
                 }
             }
@@ -288,18 +317,29 @@ let calcBaseView = {
         me.confirmBtn.bind('click', function () {
             //通过什么触发:计算基数、综合合价
             let toggle = me.confirmBtn.attr('toggle');
-            if(!toggle || toggle !== 'calcBase'){
+            if (!toggle || toggle !== 'calcBase') {
                 return;
             }
             debugger;
             //bills
-            if(me.curType === me.type.bills){
+            if (me.curType === me.type.bills) {
                 let selected = projectObj.project.mainTree.selected;
-                projectObj.updateCellValue(selected, me.getInputExpr(), {data: {field: 'calcBase'}});
-                if(projectObj.project.calcBase.success || selected.data.calcBase === me.getInputExpr()){
+                projectObj.updateCellValue(selected, me.getInputExpr(), { data: { field: 'calcBase' } });
+                if (projectObj.project.calcBase.success || selected.data.calcBase === me.getInputExpr()) {
                     //$('#qd-jsjs').modal('hide');
                     $('#calcBaseFeeRate').modal('hide');
                 }
+            } else if (me.curType === 'budget') {
+                const budgetSheet = budgetSummaryObj.getSheet();
+                const budgetTree = budgetSummaryObj.getTree();
+                const row = budgetTree.selected.serialNo();
+                const col = budgetSummaryTreeSetting.cols.findIndex(item => item.data.field === 'calcBase')
+                const changedCells = [{ row, col }];
+                budgetSheet.setValue(row, col, me.getInputExpr());
+                budgetSummaryObj.edit(budgetSheet, changedCells);
+                if (projectObj.project.calcBase.success || selected.data.calcBase === me.getInputExpr()) {
+                    $('#calcBaseFeeRate').modal('hide');
+                }
             }
             else if (me.curType === me.type.ration) {
                 let expr = me.inputExpr.val();
@@ -309,8 +349,8 @@ let calcBaseView = {
                 let template = calcProgramManage.getSelectionInfo().template;
                 let calcItem = calcProgramManage.getSelectionInfo().calcItem;
 
-                if (calcItem.dispExpr != expr){
-                    if (analyzer.isLegal(expr, calcItem.ID, template)){
+                if (calcItem.dispExpr != expr) {
+                    if (analyzer.isLegal(expr, calcItem.ID, template)) {
                         let cp = projectObj.project.calcProgram;
                         let lc = analyzer.calcItemLabourCoe(calcItem);
                         calcItem.dispExpr = expr;
@@ -325,7 +365,7 @@ let calcBaseView = {
                             'calcItem': calcItem
                         };
                         calcProgramManage.saveCalcItem(data, function (rst) {
-                            if (rst){
+                            if (rst) {
                                 cp.compileTemplate(template);
                                 let relationNodes = calcTools.getNodesByProgramID(template.ID);
                                 cp.calcNodesAndSave(relationNodes);
@@ -336,21 +376,21 @@ let calcBaseView = {
                             }
                         });
                     }
-                    else{
+                    else {
                         $('#errorInfo').html(analyzer.error);
                     };
                 }
-                else{
+                else {
                     //$('#qd-jsjs').modal('hide');
                     $('#calcBaseFeeRate').modal('hide');
                 }
             }
         });
     },
-    onCalcBaseButtonClick:function (hitinfo,type='bills') {
+    onCalcBaseButtonClick: function (hitinfo, type = 'bills') {
         let me = calcBaseView;
         hitinfo.sheet.setActiveCell(hitinfo.row, hitinfo.col);
-        if(hitinfo.sheet.getParent() === projectObj.mainSpread){
+        if (hitinfo.sheet.getParent() === projectObj.mainSpread) {
             let node = projectObj.project.mainTree.items[hitinfo.row] ? projectObj.project.mainTree.items[hitinfo.row] : null;
             projectObj.mainController.setTreeSelected(node);
         }
@@ -358,17 +398,17 @@ let calcBaseView = {
         changeCalcBaseFeeRate('calcBase');
         $('#tabCalcBase').tab('show');
         let field = projectObj.mainController.setting.cols[hitinfo.col].data.field;
-        calcBaseView.initCalctor(type,field);
+        calcBaseView.initCalctor(type, field);
     }
 };
 
 $(document).ready(function () {
- /*   $('#qd-jsjs').on('shown.bs.modal', function () {
-        calcBaseView.workBook.refresh();
-    });*/
+    /*   $('#qd-jsjs').on('shown.bs.modal', function () {
+           calcBaseView.workBook.refresh();
+       });*/
     $('#calcBaseFeeRate').on('shown.bs.modal', function () {
         let toggle = calcBaseView.confirmBtn.attr('toggle');
-        if(toggle === 'calcBase'){
+        if (toggle === 'calcBase') {
             $('#tabCalcBase').tab('show');
         }
         sheetCommonObj.refreshWorkbookDelDefer(calcBaseView.workBook, 100);
@@ -395,12 +435,12 @@ $(document).ready(function () {
         $('#errorInfo').text('');
         //清空输入框
         calcBaseView.inputExpr.val('');
-        if(calcBaseView.workBook){
+        if (calcBaseView.workBook) {
             calcBaseView.workBook.destroy();
             calcBaseView.workBook = null;
         }
         //清空清单分类数据
-        for(let attr in calcBaseView.billsCBClass){
+        for (let attr in calcBaseView.billsCBClass) {
             calcBaseView.billsCBClass[attr] = [];
         }
     });

+ 6 - 10
web/building_saas/main/js/views/gather_fees_view.js

@@ -68,7 +68,7 @@ let gatherFeesView = {
         me.detailSpread = sheetCommonObj.buildSheet($('#gfDetailSpread')[0], me.detailSetting, me.datas[0].calcItems.length);
         me.detailSheet = me.detailSpread.getSheet(0);
         sheetCommonObj.spreadDefaultStyle(me.detailSpread);
-        let arr = projectObj.project.calcProgram.compiledFeeTypeNames.slice();
+        // let arr = projectObj.project.calcProgram.compiledFeeTypeNames.slice();
         // arr.delete('暂估费');
         // let fieldName = new GC.Spread.Sheets.CellTypes.ComboBox();
         // fieldName.items(arr);
@@ -83,7 +83,7 @@ let gatherFeesView = {
         me.mainSheet.setRowCount(me.datas.length);
 
         me.detailSheet.name('gatherfee_detail');
-        feeRateObject.setFeeRateCellCol(me.detailSheet, _.findIndex(me.detailSetting.header,{'dataCode':'feeRate'}));
+        // feeRateObject.setFeeRateCellCol(me.detailSheet, _.findIndex(me.detailSetting.header,{'dataCode':'feeRate'}));
         sheetCommonObj.showData(me.detailSheet, me.detailSetting, me.datas[0].calcItems);
         me.getfeeRateColor(me.datas[0].calcItems);
         customRowHeader(me.detailSheet, me.datas[0].calcItems.length);
@@ -551,10 +551,11 @@ $(document).ready(function(){
     $('#tab_gather_fees').on('shown.bs.tab', function (e) {
         sessionStorage.setItem('mainTab', '#tab_gather_fees');
         $(e.relatedTarget.hash).removeClass('active');
-        if (!gatherFeesView.mainSpread)
+        // 每次进来都要重新计算一次
+        // if (!gatherFeesView.mainSpread)
             gatherFeesView.buildSheet()
-        else
-            gatherFeesView.mainSpread.refresh();
+        // else
+        //     gatherFeesView.mainSpread.refresh();
 
         let count = gatherFeesView.datas[gatherFeesView.mainSheet.getActiveRowIndex()].calcItems.length;
         gatherFeesView.detailSheet.setRowCount(count, GC.Spread.Sheets.SheetArea.viewport);
@@ -581,11 +582,6 @@ $(document).ready(function(){
         });
     });
 
-    // $("#btnGatherFee").bind('click',function () {
-    //   gatherFeesView.buildSheet();
-    //   gatherFeesView.refreshDetailSheet();
-    // });
-
     $("#lblGatherFee").bind('click',function () {
         gatherFeesView.buildSheet();
         gatherFeesView.refreshDetailSheet();

+ 1 - 1
web/building_saas/pm/html/project-management.html

@@ -517,7 +517,7 @@
                             </select>
                         </div>
                     </div>-->
-                    <div class="form-group row">
+                    <div class="form-group row" style="display: none;">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">计算程序</label>
                         <div class="col">
                             <select class="form-control  form-control-sm" id="tender-calcProgram">