Selaa lähdekoodia

控制目标相关

MaiXinRong 1 päivä sitten
vanhempi
commit
b3e44571b5

+ 12 - 3
app/base/base_budget_service.js

@@ -38,8 +38,7 @@ class BaseBudget extends TreeService {
         this.tableName = tablename;
     }
 
-    async initByTemplate(conn, budgetId, templateId){
-        if (!conn) throw '内部错误';
+    async initByTemplate(conn, budgetId, templateId) {
         if (budgetId <= 0) throw '概算项目id错误';
         const data = await this.ctx.service.tenderNodeTemplate.getData(templateId);
         if (!data.length) throw '模板数据有误';
@@ -62,10 +61,20 @@ class BaseBudget extends TreeService {
                 node_type: tmp.node_type,
             });
         }
-        const operate = await conn.insert(this.tableName, insertData);
+        const operate = conn ? await conn.insert(this.tableName, insertData) : await this.db.insert(this.tableName, insertData);
         return operate.affectedRows === data.length;
     }
 
+    async reInitByTemplate(budgetId, templateId) {
+        const conn = await this.db.beginTransaction();
+        try {
+            await conn.delete(this.tableName, { bid: budgetId });
+            await this.initByTemplate(conn, budgetId, templateId);
+        } catch {
+            await conn.rollback();
+        }
+    }
+
     async addChild(budgetId, selectId, data) {
         if ((budgetId <= 0) || (selectId <= 0)) return [];
         const selectData = await this.getDataByKid(budgetId, selectId);

+ 19 - 1
app/controller/budget_controller.js

@@ -21,6 +21,7 @@ const ValidDskProject = {
     gai: [5],
     yu: [1, 18],
     zb: [4, 18, 19],
+    ctrl: [4, 18, 19],
 };
 const DSK = require('../lib/dsk');
 const sendToWormhole = require('stream-wormhole');
@@ -50,6 +51,7 @@ module.exports = app => {
                     bl.gai_tp = await ctx.service.budgetGai.getSumTp(bl.budget_id);
                     bl.yu_tp = await ctx.service.budgetYu.getSumTp(bl.budget_id);
                     bl.zb_tp = await ctx.service.budgetZb.getSumTp(bl.budget_id);
+                    bl.ctrl_tp = await ctx.service.budgetCtrl.getSumTp(bl.budget_id);
                 }
                 renderData.tenderList = await ctx.service.tender.getList4Select('stage');
                 renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
@@ -121,6 +123,7 @@ module.exports = app => {
                     data.gai = await ctx.service.budgetGai.getData(ctx.budget.id);
                     data.yu = await ctx.service.budgetYu.getData(ctx.budget.id);
                     data.zb = await ctx.service.budgetZb.getData(ctx.budget.id);
+                    data.ctrl = await ctx.service.budgetCtrl.getData(ctx.budget.id);
                 }
                 ctx.body = { err: 0, msg: '', data };
             } catch (err) {
@@ -187,13 +190,14 @@ module.exports = app => {
                 case 'gai': return this.ctx.service.budgetGai;
                 case 'yu': return this.ctx.service.budgetYu;
                 case 'zb': return this.ctx.service.budgetZb;
+                case 'ctrl': return this.ctx.service.budgetCtrl;
                 default: return null;
             }
         }
         async _getNeedGcl() {
             if (!this.ctx.params.btype) throw '参数错误';
             const funRela = this.ctx.subProject.fun_rela;
-            return ['yu', 'zb'].indexOf(this.ctx.params.btype) >= 0 && !!funRela.needGcl;
+            return ['yu', 'zb', 'ctrl'].indexOf(this.ctx.params.btype) >= 0 && !!funRela.needGcl;
         }
 
         async detail(ctx) {
@@ -396,6 +400,20 @@ module.exports = app => {
             }
         }
 
+        async init(ctx) {
+            const relaService = this._getRelaService(ctx.params.btype);
+            try {
+                const data = await relaService.getData(ctx.budget.id);
+                if (data.length > 0) throw '已有数据,请勿重复初始化';
+                const budgetStd = await this.ctx.service.budgetStd.getDataById(this.ctx.budget.std_id);
+                await relaService.initByTemplate(null, this.ctx.budget.id, budgetStd[ctx.params.btype + '_template_id']);
+            } catch(err) {
+                ctx.log(err);
+                ctx.postError(err, '初始化数据错误');
+            }
+            ctx.redirect(ctx.request.header.referer);
+        }
+
         async decimal(ctx) {
             try {
                 if (!ctx.budget) throw '项目数据错误';

+ 2 - 0
app/controller/sub_proj_controller.js

@@ -236,6 +236,8 @@ module.exports = app => {
                 if (info.sgt_tp_unit === '万元') info.sgt_tp = this.ctx.helper.div(info.sgt_tp, 10000, 6);
                 info.zbys_tp = await this.ctx.service.budgetZb.getSumTp(ctx.subProject.budget_id);
                 if (info.zbys_tp_unit === '万元') info.zbys_tp = this.ctx.helper.div(info.zbys_tp, 10000, 6);
+                info.kzmb_tp = await this.ctx.service.budgetCtrl.getSumTp(ctx.subProject.budget_id);
+                if (info.kzmb_tp_unit === '万元') info.kzmb_tp = this.ctx.helper.div(info.kzmb_tp, 10000, 6);
                 const renderData = {
                     info,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.subProject.info),

+ 20 - 0
app/lib/budget_final.js

@@ -155,6 +155,20 @@ class BudgetFinal {
         });
     }
 
+    async _loadCtrl(budget) {
+        const helper = this.ctx.helper;
+        const ctrl = await this.ctx.service.budgetCtrl.getData(budget.id);
+        const ctrlTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        ctrlTree.loadDatas(ctrl);
+        ctrlTree.calculateAll();
+        this.finalTree.loadTree(ctrlTree, function (cur, source) {
+            cur.base = true;
+            cur.ctrl_dgn_qty1 = helper.add(cur.ctrl_dgn_qty1, source.dgn_qty1);
+            cur.ctrl_dgn_qty2 = helper.add(cur.ctrl_dgn_qty2, source.dgn_qty2);
+            cur.ctrl_tp = helper.add(cur.ctrl_tp, source.total_price);
+        });
+    }
+
     async _loadTender(id) {
         const helper = this.ctx.helper;
         const bills = await this.ctx.service.ledger.getFinalData(id, ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
@@ -252,6 +266,10 @@ class BudgetFinal {
             node.zb_dgn_qty = node.zb_dgn_qty1
                 ? (node.zb_dgn_qty2 ? node.zb_dgn_qty1 + '/' + node.zb_dgn_qty2 : node.zb_dgn_qty1 + '')
                 : (node.zb_dgn_qty2 ? '/' + node.zb_dgn_qty2 : '');
+            node.ctrl_dgn_price = helper.div(node.ctrl_tp, node.ctrl_dgn_qty1, 2);
+            node.ctrl_dgn_qty = node.ctrl_dgn_qty1
+                ? (node.ctrl_dgn_qty2 ? node.ctrl_dgn_qty1 + '/' + node.ctrl_dgn_qty2 : node.ctrl_dgn_qty1 + '')
+                : (node.ctrl_dgn_qty2 ? '/' + node.ctrl_dgn_qty2 : '');
 
             node.final_dgn_price = helper.div(node.final_tp, node.final_dgn_qty1, 2);
             node.final_dgn_qty = node.final_dgn_qty1
@@ -293,6 +311,7 @@ class BudgetFinal {
                 gai_dgn_qty1: x.gai_dgn_qty1 || 0, gai_dgn_qty2: x.gai_dgn_qty2 || 0, gai_dgn_qty: x.gai_dgn_qty || '', gai_dgn_price: x.gai_dgn_price || 0, gai_tp: x.gai_tp || 0,
                 yu_dgn_qty1: x.yu_dgn_qty1 || 0, yu_dgn_qty2: x.yu_dgn_qty2 || 0, yu_dgn_qty: x.yu_dgn_qty || '', yu_dgn_price: x.yu_dgn_price || 0, yu_tp: x.yu_tp || 0,
                 zb_dgn_qty1: x.zb_dgn_qty1 || 0, zb_dgn_qty2: x.zb_dgn_qty2 || 0, zb_dgn_qty: x.zb_dgn_qty || '', zb_dgn_price: x.zb_dgn_price || 0, zb_tp: x.zb_tp || 0,
+                ctrl_dgn_qty1: x.ctrl_dgn_qty1 || 0, ctrl_dgn_qty2: x.ctrl_dgn_qty2 || 0, ctrl_dgn_qty: x.ctrl_dgn_qty || '', ctrl_dgn_price: x.ctrl_dgn_price || 0, ctrl_tp: x.ctrl_tp || 0,
 
                 dgn_qty1: x.dgn_qty1 || 0, dgn_qty2: x.dgn_qty2 || 0, total_price: x.total_price || 0,
                 dgn_price: x.dgn_price || 0, dgn_qty: x.dgn_qty || '',
@@ -315,6 +334,7 @@ class BudgetFinal {
         for (const t of final.tender) {
             await this._loadTender(t);
         }
+        await this._loadCtrl(budget);
         await this._loadZb(budget);
         await this._loadYu(budget);
         await this._loadGai(budget);

+ 12 - 0
app/lib/rm/budget.js

@@ -84,6 +84,14 @@ class rptMemPaymentSafe extends RptMemBase {
         // return showLevel ? tree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : tree.getDefaultDatas();
         return tree.getDefaultDatas();
     }
+    async budgetCtrl(bid, showLevel = false) {
+        const ctrl = await this.ctx.service.budgetCtrl.getAllDataByCondition({ where: { bid } });
+        const tree = new ledger.billsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        tree.loadDatas(ctrl);
+        tree.calculateAll();
+        // return showLevel ? tree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : tree.getDefaultDatas();
+        return tree.getDefaultDatas();
+    }
     async budgetFinalInfo(bid) {
         const budget = this.ctx.budget && this.ctx.budget.id === bid
             ? this.ctx.budget
@@ -118,6 +126,8 @@ class rptMemPaymentSafe extends RptMemBase {
                 return this.budgetYu(this.ctx.budget.id);
             case 'mem_budget_zb':
                 return this.budgetZb(this.ctx.budget.id);
+            case 'mem_budget_ctrl':
+                return this.budgetCtrl(this.ctx.budget.id);
             case 'mem_budget_final':
                 return this.budgetFinal(this.ctx.budget.id);
             case 'mem_budget_gu_filter':
@@ -128,6 +138,8 @@ class rptMemPaymentSafe extends RptMemBase {
                 return this.budgetYu(this.ctx.budget.id, true);
             case 'mem_budget_zb_filter':
                 return this.budgetZb(this.ctx.budget.id, true);
+            case 'mem_budget_ctrl_filter':
+                return this.budgetCtrl(this.ctx.budget.id, true);
             case 'mem_budget_final_filter':
                 return this.budgetFinal(this.ctx.budget.id, true);
             default:

+ 4 - 0
app/lib/rm/tender.js

@@ -187,6 +187,8 @@ class rptMemPaymentSafe extends RptMemBase {
                 return params.budget_id ? budgetSource.budgetYu(params.budget_id) : budgetSource.tenderYu(params.tender_id);
             case 'mem_budget_zb':
                 return params.budget_id ? budgetSource.budgetZb(params.budget_id) : budgetSource.tenderZb(params.tender_id);
+            case 'mem_budget_ctrl':
+                return params.budget_id ? budgetSource.budgetCtrl(params.budget_id) : budgetSource.tenderCtrl(params.tender_id);
             case 'mem_budget_final':
                 return params.budget_id ? budgetSource.budgetFinal(params.budget_id) : budgetSource.tenderFinal(params.tender_id);
             case 'mem_budget_gu_filter':
@@ -197,6 +199,8 @@ class rptMemPaymentSafe extends RptMemBase {
                 return params.budget_id ? budgetSource.budgetYu(params.budget_id, true) : budgetSource.tenderYu(params.tender_id, true);
             case 'mem_budget_zb_filter':
                 return params.budget_id ? budgetSource.budgetZb(params.budget_id, true) : budgetSource.tenderZb(params.tender_id, true);
+            case 'mem_budget_ctrl_filter':
+                return params.budget_id ? budgetSource.budgetCtrl(params.budget_id, true) : budgetSource.tenderCtrl(params.tender_id, true);
             case 'mem_budget_final_filter':
                 return params.budget_id ? budgetSource.budgetFinal(params.budget_id, true) : budgetSource.tenderFinal(params.tender_id, true);
             case 'mem_pm_deal_pay':

+ 12 - 0
app/lib/rm/tender_budget.js

@@ -50,6 +50,13 @@ class reportMemoryBudget {
         tree.calculateAll();
         return showLevel ? tree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : tree.getDefaultDatas();
     }
+    async budgetCtrl(bid, showLevel = false) {
+        const ctrl = await this.ctx.service.budgetCtrl.getAllDataByCondition({ where: { bid } });
+        const tree = new ledger.billsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        tree.loadDatas(ctrl);
+        tree.calculateAll();
+        return showLevel ? tree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : tree.getDefaultDatas();
+    }
     async budgetFinal(bid, showLevel = false) {
         const budget = this.ctx.budget && this.ctx.budget.id === bid
             ? this.ctx.budget
@@ -100,6 +107,11 @@ class reportMemoryBudget {
         return this.budget ? await this.budgetZb(this.budget.id, filter) : [];
     }
 
+    async tenderCtrl(tid, filter = false) {
+        await this._getTenderBudget(tid);
+        return this.budget ? await this.budgetCtrl(this.budget.id, filter) : [];
+    }
+
     async tenderFinal(tid, filter = false) {
         await this._getTenderBudget(tid);
         console.log(this.budget);

+ 19 - 0
app/public/js/budget_compare.js

@@ -9,6 +9,7 @@ function customColDisplay () {
         { title: '设计概算', fields: ['gai_dgn_qty', 'gai_dgn_price', 'gai_tp'], visible: true },
         { title: '施工图预算', fields: ['yu_dgn_qty', 'yu_dgn_price', 'yu_tp'], visible: true },
         { title: '招标预算', fields: ['zb_dgn_qty', 'zb_dgn_price', 'zb_tp'], visible: true },
+        { title: '控制目标', fields: ['ctrl_dgn_qty', 'ctrl_dgn_price', 'ctrl_tp'], visible: false },
         { title: '台账', fields: ['dgn_qty', 'dgn_price', 'total_price'], visible: true },
         { title: '预估决算', fields: ['final_dgn_qty', 'final_dgn_price', 'final_tp'], visible: true },
     ];
@@ -55,6 +56,7 @@ $(document).ready(() => {
         { key: 'gai', name: '设计概算', dgn1: 'gai_dgn_qty1', dgn2: 'gai_dgn_qty2', tp: 'gai_tp'},
         { key: 'yu', name: '施工图预算', dgn1: 'yu_dgn_qty1', dgn2: 'yu_dgn_qty2', tp: 'yu_tp'},
         { key: 'zb', name: '招标预算', dgn1: 'zb_dgn_qty1', dgn2: 'zb_dgn_qty2', tp: 'zb_tp'},
+        { key: 'ctrl', name: '控制目标', dgn1: 'ctrl_dgn_qty1', dgn2: 'ctrl_dgn_qty2', tp: 'ctrl_tp'},
         { key: 'tz', name: '台账', dgn1: 'dgn_qty1', dgn2: 'dgn_qty2', tp: 'total_price'},
         { key: 'final', name: '预估决算', dgn1: 'final_dgn_qty1', dgn2: 'final_dgn_qty2', tp: 'final_tp'},
     ];
@@ -75,6 +77,9 @@ $(document).ready(() => {
             {title: '招标预算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'zb_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'zb_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'zb_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
+            {title: '控制目标|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'ctrl_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
+            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'ctrl_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'ctrl_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
             {title: '台账|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
@@ -213,6 +218,16 @@ $(document).ready(() => {
             });
             const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
 
+            const ctrlTree = createNewPathTree('ledger', setting);
+            ctrlTree.loadDatas(result.ctrl);
+            treeCalc.calculateAll(ctrlTree);
+            compareTree.loadTree(ctrlTree, function (cur, source) {
+                cur.base = true;
+                cur.ctrl_dgn_qty1 = ZhCalc.add(cur.ctrl_dgn_qty1, source.dgn_qty1);
+                cur.ctrl_dgn_qty2 = ZhCalc.add(cur.ctrl_dgn_qty2, source.dgn_qty2);
+                cur.ctrl_tp = ZhCalc.add(cur.ctrl_tp, source.total_price);
+            });
+
             const zbTree = createNewPathTree('ledger', setting);
             zbTree.loadDatas(result.zb);
             treeCalc.calculateAll(zbTree);
@@ -270,6 +285,10 @@ $(document).ready(() => {
                 node.zb_dgn_qty = node.zb_dgn_qty1
                     ? (node.zb_dgn_qty2 ? node.zb_dgn_qty1 + '/' + node.zb_dgn_qty2 : node.zb_dgn_qty1)
                     : (node.zb_dgn_qty2 ? '/' + node.zb_dgn_qty2 : '');
+                node.ctrl_dgn_price = ZhCalc.div(node.ctrl_tp, node.ctrl_dgn_qty1, 2);
+                node.ctrl_dgn_qty = node.ctrl_dgn_qty1
+                    ? (node.ctrl_dgn_qty2 ? node.ctrl_dgn_qty1 + '/' + node.ctrl_dgn_qty2 : node.ctrl_dgn_qty1)
+                    : (node.ctrl_dgn_qty2 ? '/' + node.ctrl_dgn_qty2 : '');
             });
             compareTree.resortChildrenByCustom(function (x, y) {
                 const iCode = compareCode(x.code, y.code);

+ 5 - 2
app/public/js/budget_info.js

@@ -298,7 +298,7 @@ $(document).ready(() => {
             const huishouInfo = _.find(tree.children, function (item) {
                 return item.name.indexOf('回收金额') !== -1 && (item.gu_tp || item.gai_tp || (item.final_tp !== undefined && item.final_tp) || item.yu_tp || (item.total_price !== undefined && item.total_price))
             });
-            let total_rate = 0, total_gai_tp = 0, total_gu_tp = 0, total_yu_tp = 0, total_zb_tp = 0, total_price = 0, total_final_tp = 0;
+            let total_rate = 0, total_gai_tp = 0, total_gu_tp = 0, total_yu_tp = 0, total_zb_tp = 0, total_ctrl_tp = 0, total_price = 0, total_final_tp = 0;
             if (level1List.length > 0) {
                 if (huishouInfo) {
                     total_gai_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'gai_tp')), huishouInfo.gai_tp);
@@ -306,6 +306,7 @@ $(document).ready(() => {
                     total_gu_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'gu_tp')), huishouInfo.gu_tp);
                     total_yu_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'yu_tp')), huishouInfo.yu_tp);
                     total_zb_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'zb_tp')), huishouInfo.zb_tp);
+                    total_ctrl_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'ctrl_tp')), huishouInfo.ctrl_tp);
                     total_price = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'total_price')), huishouInfo.total_price);
                     total_rate = ZhCalc.div(ZhCalc.sub(total_final_tp, total_gai_tp), total_gai_tp);
                     $('#total_gai_tp').text(total_gai_tp ? ZhCalc.div(total_gai_tp, 10000) : 0);
@@ -313,6 +314,7 @@ $(document).ready(() => {
                     $('#total_gu_tp').text(total_gu_tp ? ZhCalc.div(total_gu_tp, 10000) : 0);
                     $('#total_yu_tp').text(total_yu_tp ? ZhCalc.div(total_yu_tp, 10000) : 0);
                     $('#total_zb_tp').text(total_zb_tp ? ZhCalc.div(total_zb_tp, 10000) : 0);
+                    $('#total_ctrl_tp').text(total_ctrl_tp ? ZhCalc.div(total_ctrl_tp, 10000) : 0);
                     // level1List.push(huishouInfo);
                 } else {
                     total_gai_tp = ZhCalc.sum(_.map(level1List, 'gai_tp'));
@@ -320,6 +322,7 @@ $(document).ready(() => {
                     total_gu_tp = ZhCalc.sum(_.map(level1List, 'gu_tp'));
                     total_yu_tp = ZhCalc.sum(_.map(level1List, 'yu_tp'));
                     total_zb_tp = ZhCalc.sum(_.map(level1List, 'zb_tp'));
+                    total_ctrl_tp = ZhCalc.sum(_.map(level1List, 'ctrl_tp'));
                     total_price = ZhCalc.sum(_.map(level1List, 'total_price'));
                     total_rate = ZhCalc.div(ZhCalc.sub(total_final_tp, total_gai_tp), total_gai_tp);
                     $('#total_gai_tp').text(total_gai_tp ? ZhCalc.div(total_gai_tp, 10000) : 0);
@@ -327,6 +330,7 @@ $(document).ready(() => {
                     $('#total_gu_tp').text(total_gu_tp ? ZhCalc.div(total_gu_tp, 10000) : 0);
                     $('#total_yu_tp').text(total_yu_tp ? ZhCalc.div(total_yu_tp, 10000) : 0);
                     $('#total_zb_tp').text(total_zb_tp ? ZhCalc.div(total_zb_tp, 10000) : 0);
+                    $('#total_ctrl_tp').text(total_ctrl_tp ? ZhCalc.div(total_ctrl_tp, 10000) : 0);
                 }
             }
             $('#total_rate').text((total_rate ? ZhCalc.round(ZhCalc.mul(total_rate,100), 2) : 0) + '%');
@@ -337,7 +341,6 @@ $(document).ready(() => {
             } else if (total_rate === 0) {
                 $('#total_rate').parents('.canyu-band').removeClass('text-success').removeClass('text-danger');
             }
-            console.log(level1List);
             option.series[0].data = [
                 ZhCalc.div(total_gu_tp, 10000),
                 ZhCalc.div(total_gai_tp, 10000),

+ 1 - 0
app/router.js

@@ -409,6 +409,7 @@ module.exports = app => {
     app.post('/sp/:id/budget/compare/load', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.compareLoad');
     app.post('/sp/:id/budget/compare/final', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.compareFinal');
     app.get('/sp/:id/budget/:btype', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detail');
+    app.get('/sp/:id/budget/:btype/init', sessionAuth, subProjectCheck, budgetCheck, projectManagerCheck, 'budgetController.init');
     app.post('/sp/:id/budget/:btype/load', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detailLoad');
     app.post('/sp/:id/budget/:btype/update', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detailUpdate');
     app.post('/sp/:id/budget/:btype/upload-excel/:ueType', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detailUploadExcel');

+ 1 - 0
app/service/budget.js

@@ -102,6 +102,7 @@ module.exports = app => {
             await this.ctx.service.budgetGai.initByTemplate(transaction, operate.insertId, budgetStd.gai_template_id);
             await this.ctx.service.budgetYu.initByTemplate(transaction, operate.insertId, budgetStd.yu_template_id);
             await this.ctx.service.budgetZb.initByTemplate(transaction, operate.insertId, budgetStd.zb_template_id);
+            await this.ctx.service.budgetCtrl.initByTemplate(transaction, operate.insertId, budgetStd.ctrl_template_id);
             return operate.insertId;
         }
 

+ 28 - 0
app/service/budget_ctrl.js

@@ -0,0 +1,28 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+    class BudgetCtrl extends app.BaseBudgetService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @param {String} tableName - 表名
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx, { keyPre: 'budget_ctrl_maxLid:' });
+            this.tableName = 'budget_ctrl';
+        }
+    }
+
+    return BudgetCtrl;
+};

+ 8 - 0
app/service/report.js

@@ -386,6 +386,10 @@ module.exports = app => {
                             runnableRst.push(params.budget_id > 0 ? budgetSource.budgetZb(params.budget_id) : budgetSource.tenderZb(params.tender_id));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_budget_ctrl':
+                            runnableRst.push(params.budget_id > 0 ? budgetSource.budgetCtrl(params.budget_id) : budgetSource.tenderCtrl(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
                         case 'mem_budget_final':
                             runnableRst.push(params.budget_id > 0 ? budgetSource.budgetFinal(params.budget_id) : budgetSource.tenderFinal(params.tender_id));
                             runnableKey.push(filter);
@@ -406,6 +410,10 @@ module.exports = app => {
                             runnableRst.push(params.budget_id > 0 ? budgetSource.budgetZb(params.budget_id, true) : budgetSource.tenderZb(params.tender_id, true));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_budget_ctrl_filter':
+                            runnableRst.push(params.budget_id > 0 ? budgetSource.budgetCtrl(params.budget_id, true) : budgetSource.tenderCtrl(params.tender_id, true));
+                            runnableKey.push(filter);
+                            break;
                         case 'mem_budget_final_filter':
                             runnableRst.push(params.budget_id > 0 ? budgetSource.budgetFinal(params.budget_id, true) : budgetSource.tenderFinal(params.tender_id, true));
                             runnableKey.push(filter);

+ 5 - 1
app/service/sub_project.js

@@ -18,6 +18,8 @@ const defaultFunRela = {
     showMinusCol: true,
     imType: imType.zl.value,
     needGcl: false,
+    budgetZb: true,
+    budgetCtrl: true,
 };
 const funSet = require('../const/fun_set');
 const defaultFunSet = funSet.defaultInfo;
@@ -721,7 +723,9 @@ module.exports = app => {
             const result = await this.db.update(this.tableName, {
                 id: id, fun_rela: JSON.stringify({
                     banOver: data.banOver, hintOver: data.hintOver, banMinusChangeBills: data.banMinusChangeBills,
-                    imType: data.imType, needGcl: data.needGcl, minusNoValue: data.minusNoValue,
+                    imType: data.imType,
+                    needGcl: data.needGcl, budgetCtrl: data.budgetCtrl, budgetZb: data.budgetZb,
+                    minusNoValue: data.minusNoValue,
                     lockPayExpr: data.lockPayExpr, showMinusCol: data.showMinusCol, ledgerAss: data.ledgerAss,
                 }),
             });

+ 3 - 0
app/view/budget/detail.ejs

@@ -32,6 +32,9 @@
                     <a class="btn btn-sm btn-primary" href="javascript: void(0);" id="budget-import">导入</a>
                     <a href="#import-dsk" data-toggle="modal" data-target="#import-dsk" class="btn btn-sm btn-primary">导入计价文件</a>
                     <% } %>
+                    <% if (ctx.session.sessionUser.is_admin) { %>
+                    <a class="btn btn-sm btn-primary" href="<%- ctx.params.btype %>/init" id="budget-import">初始化</a>
+                    <% } %>
                 </div>
             </div>
         </div>

+ 6 - 3
app/view/budget/info.ejs

@@ -78,15 +78,18 @@
                                     <h1 id="total_rate" class="">0%</h1>
                                     <h3 class="text-muted">增减幅度</h3>
                                     <div class="row my-4 mx-0">
-                                        <div class="col-4 text-center text-muted p-0">
+                                        <div class="col-3 text-center text-muted p-0">
                                             <h5><b id="total_gu_tp"></b></h5> <h6>投资估算</h6>
                                         </div>
-                                        <div class="col-4 text-center text-muted p-0">
+                                        <div class="col-3 text-center text-muted p-0">
                                             <h5><b id="total_yu_tp"></b></h5> <h6>施工图预算</h6>
                                         </div>
-                                        <div class="col-4 text-center text-muted p-0">
+                                        <div class="col-3 text-center text-muted p-0">
                                             <h5><b id="total_zb_tp"></b></h5> <h6>招标预算</h6>
                                         </div>
+                                        <div class="col-3 text-center text-muted p-0">
+                                            <h5><b id="total_ctrl_tp"></b></h5> <h6>控制目标</h6>
+                                        </div>
                                     </div>
                                 </div>
                             </div>

+ 6 - 1
app/view/budget/sub_menu_list.ejs

@@ -3,8 +3,13 @@
 <nav-menu title="投资估算" url="/sp/<%= ctx.subProject.id %>/budget/gu" ml="3" active="<%= ctx.url.indexOf('/gu') %>"></nav-menu>
 <nav-menu title="设计概算" url="/sp/<%= ctx.subProject.id %>/budget/gai%>" ml="3" active="<%= ctx.url.indexOf('/gai') %>"></nav-menu>
 <nav-menu title="施工图预算" url="/sp/<%= ctx.subProject.id %>/budget/yu" ml="3" active="<%= ctx.url.indexOf('/yu') %>"></nav-menu>
+<% if (ctx.subProject.page_show.budgetZb) { %>
 <nav-menu title="招标预算" url="/sp/<%= ctx.subProject.id %>/budget/zb" ml="3" active="<%= ctx.url.indexOf('/zb') %>"></nav-menu>
+<% } %>
+<% if (ctx.subProject.page_show.budgetCtrl) { %>
+<nav-menu title="控制目标" url="/sp/<%= ctx.subProject.id %>/budget/ctrl" ml="3" active="<%= ctx.url.indexOf('/ctrl') %>"></nav-menu>
+<% } %>
 <!--<nav-menu title="输出报表" url="/sp/<%= ctx.subProject.id %>/budget/report" ml="3" active="<%= ctx.url.indexOf('/report') %>"></nav-menu>-->
-<% if (!ctx.budget.readOnly && (ctx.url.indexOf('/gu') > 0 || ctx.url.indexOf('/gai') > 0 || ctx.url.indexOf('/yu') > 0 || ctx.url.indexOf('/zb') > 0)) { %>
+<% if (!ctx.budget.readOnly && (ctx.url.indexOf('/gu') > 0 || ctx.url.indexOf('/gai') > 0 || ctx.url.indexOf('/yu') > 0 || ctx.url.indexOf('/zb') > 0 || ctx.url.indexOf('/ctrl') > 0)) { %>
 <div class="contarl-box"><button class="btn btn-primary btn-sm btn-block" data-toggle="modal" data-target="#budget-set">设置</button></div>
 <% } %>

+ 14 - 0
app/view/sp_setting/fun.ejs

@@ -363,6 +363,18 @@
                                 <div class="form-group mb-4">
                                     <div>
                                         <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="budget_zb" name="budget_zb" <% if (funRela.budgetZb) { %>checked<% } %> onchange="updateSetting();">
+                                            <label class="form-check-label" for="budget_zb">招标预算</label>
+                                        </div>
+                                    </div>
+                                    <div>
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="budget_ctrl" name="budget_ctrl" <% if (funRela.budgetCtrl) { %>checked<% } %> onchange="updateSetting();">
+                                            <label class="form-check-label" for="budget_ctrl">控制目标</label>
+                                        </div>
+                                    </div>
+                                    <div>
+                                        <div class="form-check form-check-inline">
                                             <input class="form-check-input" type="checkbox" id="need_gcl" name="need_gcl" <% if (funRela.needGcl) { %>checked<% } %> onchange="updateSetting();">
                                             <label class="form-check-label" for="need_gcl">显示清单信息</label>
                                         </div>
@@ -642,6 +654,8 @@
             lockPayExpr: $('#lockPayExpr')[0].checked,
             showMinusCol: $('#showMinusCol')[0].checked,
             needGcl: $('#need_gcl')[0].checked,
+            budgetZb: $('#budget_zb')[0].checked,
+            budgetCtrl: $('#budget_ctrl')[0].checked,
             openChangeProject: $('#openChangeProject')[0].checked,
             openChangeApply: $('#openChangeApply')[0].checked,
             openChangePlan: $('#openChangePlan')[0].checked,

+ 42 - 0
sql/update.sql

@@ -398,6 +398,48 @@ ADD COLUMN `folder_id` varchar(36) NOT NULL DEFAULT '' COMMENT '分类id(zh_calc
 ALTER TABLE `calculation`.`zh_project`
 ADD COLUMN `calc_tmpl_set` json NULL COMMENT '计算模板配置' AFTER `common_json`;
 
+CREATE TABLE `zh_budget_ctrl`  (
+  `id` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '自增id',
+  `bid` int(10) NOT NULL COMMENT '概算投资项目id',
+  `tree_id` int(10) NOT NULL COMMENT '节点id',
+  `tree_pid` int(10) NOT NULL COMMENT '父节点id',
+  `level` tinyint(4) NOT NULL COMMENT '层级',
+  `order` mediumint(4) NOT NULL DEFAULT 0 COMMENT '同级排序',
+  `full_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '层级定位辅助字段parent.full_path.ledger_id',
+  `is_leaf` tinyint(1) NOT NULL COMMENT '是否叶子节点,界面显示辅助字段',
+  `code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '节点编号',
+  `b_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
+  `unit` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '单位',
+  `unit_price` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '单价',
+  `quantity` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '数量',
+  `total_price` decimal(24, 8) NULL DEFAULT 0.00000000 COMMENT '金额',
+  `dgn_qty1` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '设计数量1',
+  `dgn_qty2` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '设计数量2',
+  `drawing_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图册号',
+  `memo` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注',
+  `node_type` int(4) UNSIGNED NULL DEFAULT 0 COMMENT '节点类别',
+  `source` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '添加源',
+  `remark` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注',
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `idx_tender_id`(`bid`) USING BTREE,
+  INDEX `idx_template_pid`(`tree_pid`) USING BTREE,
+  INDEX `idx_level`(`level`) USING BTREE,
+  INDEX `idx_full_path`(`bid`, `full_path`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '控制目标数据' ROW_FORMAT = Dynamic;
+
+ALTER TABLE `zh_budget_final`
+ADD COLUMN `ctrl_dgn_qty1` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '控制目标-设计数量1' AFTER `zb_tp`,
+ADD COLUMN `ctrl_dgn_qty2` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '控制目标-设计数量1' AFTER `ctrl_dgn_qty1`,
+ADD COLUMN `ctrl_dgn_qty` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '控制目标-设计数量1/设计数量2' AFTER `ctrl_dgn_qty2`,
+ADD COLUMN `ctrl_dgn_price` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '控制目标-经济指标' AFTER `ctrl_dgn_qty`,
+ADD COLUMN `ctrl_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '控制目标-金额' AFTER `ctrl_dgn_price`;
+
+ALTER TABLE `zh_budget_std`
+ADD COLUMN `ctrl_chapter_id` varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT '控制目标-项目节-id列表(\',\'分隔)' AFTER `zb_bills_id`,
+ADD COLUMN `ctrl_template_id` varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT '控制目标-新建模板-id列表(\',\'分隔)' AFTER `ctrl_chapter_id`,
+ADD COLUMN `ctrl_bills_id` varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT '控制目标-工程量清单-id列表(\',\'分隔)' AFTER `ctrl_template_id`;
+
 ------------------------------------
 -- 表数据
 ------------------------------------