Explorar o código

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

TonyKang %!s(int64=5) %!d(string=hai) anos
pai
achega
abb52940e3
Modificáronse 39 ficheiros con 1468 adicións e 109 borrados
  1. 20 2
      app/const/tender_info.js
  2. 9 4
      app/controller/dashboard_controller.js
  3. 2 1
      app/controller/ledger_controller.js
  4. 37 6
      app/controller/report_controller.js
  5. 2 2
      app/controller/setting_controller.js
  6. 13 2
      app/controller/tender_controller.js
  7. 2 1
      app/extend/helper.js
  8. 70 0
      app/lib/ledger.js
  9. 3 1
      app/lib/pay_calc.js
  10. 67 8
      app/lib/rpt_data_analysis.js
  11. 19 1
      app/middleware/tender_check.js
  12. 11 0
      app/public/js/change_detail.js
  13. 1 0
      app/public/js/se_bonus.js
  14. 1 0
      app/public/js/se_other.js
  15. 95 0
      app/public/js/shares/tenders2tree.js
  16. 6 0
      app/public/js/stage_pay.js
  17. 282 5
      app/public/report/js/rpt_custom.js
  18. 6 7
      app/service/ledger_audit.js
  19. 6 2
      app/service/report.js
  20. 3 0
      app/service/rpt_custom_define.js
  21. 355 0
      app/service/rpt_gather_memory.js
  22. 63 57
      app/service/stage.js
  23. 9 0
      app/service/stage_audit.js
  24. 2 1
      app/service/stage_bonus.js
  25. 2 0
      app/service/stage_other.js
  26. 7 3
      app/service/tender.js
  27. 2 1
      app/view/change/info.ejs
  28. 1 1
      app/view/dashboard/index.ejs
  29. 7 1
      app/view/layout/page.ejs
  30. 1 1
      app/view/material/info.ejs
  31. 18 1
      app/view/report/index.ejs
  32. 48 0
      app/view/report/rpt_all_popup.ejs
  33. 1 0
      app/view/tender/detail.ejs
  34. 114 1
      app/view/tender/detail_modal.ejs
  35. 78 0
      builder_report_index_define.js
  36. 17 0
      config/web.js
  37. 9 0
      sql/update.sql
  38. 15 0
      test/app/lib/rpt_data_analysis.test.js
  39. 64 0
      test/app/service/rpt_gather_memory.test.js

+ 20 - 2
app/const/tender_info.js

@@ -8,7 +8,7 @@
  * @version
  */
 
-const parseInfo = ['deal_info', 'construction_unit', 'tech_param', 'decimal', 'precision', 'deal_param', 'display'];
+const parseInfo = ['deal_info', 'construction_unit', 'tech_param', 'decimal', 'precision', 'deal_param', 'display', 'pay_account'];
 const arrayInfo = ['chapter'];
 const defaultInfo = {
     // 合同信息
@@ -107,7 +107,25 @@ const defaultInfo = {
         {code: '1100', name: '通信系统'},
         {code: '1200', name: '消防系统'},
         {code: '1300', name: '供配电及照明系统'},
-    ]
+    ],
+    pay_account: {
+        project: {
+            name: '',
+            bank: '',
+            account: '',
+            rate: '',
+            contact: '',
+            phone: '',
+        },
+        worker: {
+            name: '',
+            bank: '',
+            account: '',
+            rate: '',
+            contact: '',
+            phone: '',
+        },
+    },
 };
 
 module.exports = {

+ 9 - 4
app/controller/dashboard_controller.js

@@ -31,10 +31,15 @@ module.exports = app => {
             const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
             const lastNotice = pa.last_notice ? pa.last_notice : (pa.last_notice === 0 ? new Date() : new Date(pa.last_login * 1000));
             const noticeLedger = await ctx.service.ledgerAudit.getNoticeTender(ctx.session.sessionProject.id, pa.id, lastNotice);
-            const noticeStage = await ctx.service.stageAudit.getNoticeStage(ctx.session.sessionProject.id, pa.id, lastNotice);
-            const noticeChange = await ctx.service.changeAudit.getNoticeChange(ctx.session.sessionProject.id, pa.id, lastNotice);
-            const noticeRevise = await ctx.service.reviseAudit.getNoticeRevise(ctx.session.sessionProject.id, pa.id, lastNotice);
-            const noticeMaterial = await ctx.service.materialAudit.getNoticeMaterial(ctx.session.sessionProject.id, pa.id, lastNotice);
+            // const noticeStage = await ctx.service.stageAudit.getNoticeStage(ctx.session.sessionProject.id, pa.id, lastNotice);
+            // const noticeChange = await ctx.service.changeAudit.getNoticeChange(ctx.session.sessionProject.id, pa.id, lastNotice);
+            // const noticeRevise = await ctx.service.reviseAudit.getNoticeRevise(ctx.session.sessionProject.id, pa.id, lastNotice);
+            // const noticeMaterial = await ctx.service.materialAudit.getNoticeMaterial(ctx.session.sessionProject.id, pa.id, lastNotice);
+            // const noticeLedger = [];
+            const noticeStage = [];
+            const noticeChange = [];
+            const noticeRevise = [];
+            const noticeMaterial = [];
             const projectData = await ctx.service.project.getDataById(ctx.session.sessionProject.id);
             // 获取销售人员数据
             const salesmanData = await ctx.service.manager.getDataById(projectData.manager_id);

+ 2 - 1
app/controller/ledger_controller.js

@@ -52,7 +52,8 @@ module.exports = app => {
          */
         _ledgerReadOnly() {
             const tender = this.ctx.tender.data;
-            return tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked;
+            return tender.user_id !== this.ctx.session.sessionUser.accountId ||
+                (tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked);
         }
 
         /**

+ 37 - 6
app/controller/report_controller.js

@@ -6,6 +6,7 @@
 
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
+const auditConst = require('../const/audit');
 const accountGroup = require('../const/account_group').group;
 const JpcEx = require('../reports/rpt_component/jpc_ex');
 const JV = require('../reports/rpt_component/jpc_value_define');
@@ -42,6 +43,22 @@ module.exports = app => {
                 const roleList = await ctx.service.signatureRole.getSignatureRolesByTenderId(tender.id);
                 const usedList = await ctx.service.signatureUsed.getSignatureUsedByTenderId(tender.id);
 
+                // 分类列表
+                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                // 获取用户权限
+                const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
+                const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
+                // 获取用户可查看的标段
+                const tenderList = await this.ctx.service.tender.getList('', userPermission);
+                for (const t of tenderList) {
+                    if (t.ledger_status === auditConst.ledger.status.checked) {
+                        t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
+                        if (t.lastStage) {
+                            await this.ctx.service.stage.checkStageGatherData(t.lastStage);
+                        }
+                    }
+                }
+
                 // const allTpls = await ctx.service.rptTpl.getAllTplByIds(tmpRptIds);
                 // for (const tpl of allTpls) {
                 //     const fName = tpl.id + '_' + tpl.rpt_tpl_name + '.js';
@@ -110,7 +127,10 @@ module.exports = app => {
                     tenderMenu,
                     preUrl: '/tender/' + tender.id,
                     measureType,
-                    // jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.ledger.explode),
+                    categoryData,
+                    tenderList,
+                    auditConst,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.report.main),
                 };
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
                 // await this.layout('report/index.ejs', renderData);
@@ -167,7 +187,13 @@ module.exports = app => {
             }
             rptTpl = JSON.parse(rptTpl[0].rpt_content);
             // console.log('get the template!');
-            const customSelect = await ctx.service.rptCustomDefine.getCustomDefine(params.tender_id, params.stage_id, params.rpt_tpl_id);
+            const customSelect = rptTpl[JV.NODE_CUSTOM_DEFINE] && rptTpl[JV.NODE_CUSTOM_DEFINE][JV.NODE_CUS_AUDIT_SELECT].enable
+                ? await ctx.service.rptCustomDefine.getCustomDefine(params.tender_id, params.stage_id, params.rpt_tpl_id)
+                : await ctx.service.rptCustomDefine.getCustomDefine(params.tender_id, -1, params.rpt_tpl_id);
+            const gather_select = customSelect.gather_select;
+            if (!params.gather_select && customSelect) {
+                delete customSelect.gather_select;
+            }
             const pageRst = await getAllPagesCommon(ctx, rptTpl, params, JV.PAGING_OPTION_NORMAL, JV.OUTPUT_TYPE_NORMAL, this.app.baseDir, customSelect);
             // console.log(pageRst);
             // const roleRel = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_tpl_id)) : [];
@@ -179,6 +205,9 @@ module.exports = app => {
             await encodeSignatureDataUri(roleRel, this.app.baseDir);
             await encodeDummySignatureDataUri(pageRst, this.app.baseDir);
             const stageFlow = await ctx.service.stageAudit.getAuditGroupByListWithOwner(params.stage_id, params.stage_times);
+            if (!params.gather_select && customSelect) {
+                customSelect.gather_select = gather_select;
+            }
 
             // console.log('encodeSignatureDataUri!');
             return {
@@ -391,16 +420,17 @@ module.exports = app => {
         async setCustomDefine(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
-                console.log(data);
-                const filter = {tid: data.tender_id, sid: data.stage_id, rid: data.rpt_tpl_id};
+                const sid = data.gather_select ? -1 : data.stage_id;
+                const filter = {tid: data.tender_id, sid: sid, rid: data.rpt_tpl_id};
                 const count = await ctx.service.rptCustomDefine.count(filter);
                 const updateData = {};
                 if (data.audit_select) updateData.audit_select = JSON.stringify(data.audit_select);
+                if (data.gather_select) updateData.gather_select = JSON.stringify(data.gather_select);
                 if (count > 0) {
                     await ctx.service.rptCustomDefine.update(updateData, filter);
                 } else {
                     updateData.tid = data.tender_id;
-                    updateData.sid = data.stage_id;
+                    updateData.sid = sid;
                     updateData.rid = data.rpt_tpl_id;
                     await ctx.service.rptCustomDefine.db.insert(ctx.service.rptCustomDefine.tableName, updateData);
                 }
@@ -533,7 +563,8 @@ async function getAllPagesCommon(ctx, rptTpl, params, option, outputType, baseDi
     rptDataUtil.initialize(rptTpl);
     const filter = rptDataUtil.getDataRequestFilter();
     // console.log(filter.tables);
-    const rawDataObj = await ctx.service.report.getReportData(params, filter.tables, filter.memFieldKeys);
+    const rawDataObj = await ctx.service.report.getReportData(params, filter.tables, filter.memFieldKeys,
+        rptTpl[JV.NODE_CUSTOM_DEFINE], customSelect);
     await ctx.helper.saveBufferFile(JSON.stringify(rawDataObj, "", "\t"), ctx.app.baseDir + '/mem.json');
     // console.log(rawDataObj);
     try {

+ 2 - 2
app/controller/setting_controller.js

@@ -272,7 +272,7 @@ module.exports = app => {
                 }
 
                 this.setMessage('保存账号数据成功', this.messageType.SUCCESS);
-                ctx.redirect('/' + ctx.controllerName + '/user');
+                ctx.redirect(ctx.request.header.referer);
             } catch (error) {
                 console.log(error);
                 this.setMessage(error.toString(), this.messageType.ERROR);
@@ -339,7 +339,7 @@ module.exports = app => {
                 }
 
                 this.setMessage('保存账号数据成功', this.messageType.SUCCESS);
-                ctx.redirect('/' + ctx.controllerName + '/user/permission/set');
+                ctx.redirect(ctx.request.header.referer);
             } catch (error) {
                 console.log(error);
                 this.setMessage(error.toString(), this.messageType.ERROR);

+ 13 - 2
app/controller/tender_controller.js

@@ -363,12 +363,23 @@ module.exports = app => {
                 if (!data) {
                     throw '提交数据错误';
                 }
+                // 针对查阅所有标段者但非原报和审批人提示
+                const times = ctx.tender.data.ledger_status === auditConst.ledger.status.checkNo ? ctx.tender.data.ledger_times - 1 : ctx.tender.data.ledger_times;
+                const auditors = await this.service.ledgerAudit.getAuditors(ctx.tender.id, times);
+                const auditorsId = ctx.helper._.map(auditors, 'audit_id');
+                const stageAuditors = await this.service.stageAudit.getAllAuditors(ctx.tender.id);
+                const stageAUditorsId = ctx.helper._.map(stageAuditors, 'aid');
+                const accountId = ctx.session.sessionUser.accountId;
+                if (auditorsId.indexOf(accountId) === -1 && ctx.tender.data.user_id !== accountId &&
+                    stageAUditorsId.indexOf(accountId) === -1) {
+                    throw '您无权修改标段设置内容';
+                }
+
                 if (ctx.tender.data.ledger_status === auditConst.ledger.status.checked) {
                     if (data.deal_param) {
                         const lastStage = await this.ctx.service.stage.getLastestStage(ctx.tender.id, true);
                         if (lastStage) {
-                            if (lastStage.order > 1 || (lastStage.status === auditConst.stage.status.checked || lastStage.status === auditConst.stage.status.checking))
-                                throw '第一期上报后不可修改合同参数';
+                            if (lastStage.order > 1 || (lastStage.status === auditConst.stage.status.checked || lastStage.status === auditConst.stage.status.checking)) throw '第一期上报后不可修改合同参数';
                             if (lastStage.user_id !== ctx.session.sessionUser.accountId) throw '仅原报可修改合同参数';
                         }
                     }

+ 2 - 1
app/extend/helper.js

@@ -764,7 +764,7 @@ module.exports = {
     },
 
     /**
-     * 合并相关数据
+     * 合并 相关数据
      * @param {Array} main - 主数据
      * @param {Array[]}rela - 相关数据 {data, fields, prefix, relaId}
      */
@@ -790,6 +790,7 @@ module.exports = {
             loadFields(r.data, r.fields, r.prefix, r.relaId);
         }
     },
+
     whereSql (where, as) {
         if (!where) {
             return '';

+ 70 - 0
app/lib/ledger.js

@@ -412,6 +412,75 @@ class filterGatherTree extends baseTree {
     }
 }
 
+class gatherTree extends baseTree {
+
+    constructor(ctx, setting) {
+        super(ctx, setting);
+        this._newId = 1;
+    }
+
+    get newId() {
+        return this._newId++;
+    }
+
+    loadGatherNode(node, parent, loadFun) {
+        const siblings = parent ? parent.children : this.children;
+        let cur = siblings.find(function (x) {
+            return node.b_code
+                ? x.b_code === node.b_code && x.name === node.name && x.unit === node.unit && x.unit_price === node.unit_price
+                : x.code === node.code && x.name === node.name;
+        });
+        if (!cur) {
+            const id = this.newId;
+            cur = {
+                id: id,
+                pid: parent ? parent.id : this.setting.rootId,
+                full_path: parent ? parent.full_path + '-' + id : '' + id,
+                level: parent ? parent.level + 1 : 1,
+                order: siblings.length + 1,
+                children: [],
+                code: node.code, b_code: node.b_code, name: node.name,
+                unit: node.unit, unit_price: node.unit_price,
+            };
+            siblings.push(cur);
+            this.datas.push(cur);
+        }
+        loadFun(cur, node);
+        for (const c of node.children) {
+            this.loadGatherNode(c, cur, loadFun);
+        }
+    }
+
+    generateSortNodes() {
+        const self = this;
+        const addSortNode = function (node) {
+            self.nodes.push(node);
+            for (const c of node.children) {
+                addSortNode(c);
+            }
+        };
+        this.nodes = [];
+        for (const n of this.children) {
+            addSortNode(n);
+        }
+    }
+
+    loadGatherTree(sourceTree,  loadFun) {
+        for (const c of sourceTree.children) {
+            this.loadGatherNode(c, null, loadFun);
+        }
+        // todo load Pos Data;
+    }
+
+    calculateSum() {
+        if (this.setting.calcSum) {
+            for (const d of this.datas) {
+                this.setting.calcSum(d, this.count);
+            }
+        }
+    }
+}
+
 class pos {
     /**
      * 构造函数
@@ -485,4 +554,5 @@ module.exports = {
     pos,
     filterTree,
     filterGatherTree,
+    gatherTree,
 };

+ 3 - 1
app/lib/pay_calc.js

@@ -215,8 +215,10 @@ class PayCalculate {
             p.end_tp = this.ctx.helper.round(this.ctx.helper.add(p.tp, p.pre_tp), this.decimal);
         }
         this.yf.end_tp = this.ctx.helper.add(this.yf.tp, this.yf.pre_tp);
-        if (this.sf.tp === null) {
+        if (this.sf.expr === null || this.sf.expr === '') {
             this.sf.tp = this.yf.tp;
+        } else {
+            this.sf.tp = this.ctx.helper._.toNumber(this.sf.expr);
         }
         this.sf.end_tp = this.ctx.helper.add(this.sf.tp, this.sf.pre_tp);
     }

+ 67 - 8
app/lib/rpt_data_analysis.js

@@ -585,10 +585,11 @@ const filter = {
     defaultSetting: {
         "table": "pay",
         "condition": [
-            {"field": "minus", "type": "bool", "value": "false"},
-            {"field": "ptype", "type": "num", "operate": "=", "value": 1},
-            {"field": "name", "type": "str", "operate": "=", "value": "扣回"}
+            {"field": "minus", "type": "bool", "value": "false", },
+            {"field": "ptype", "type": "num", "operate": "=", "value": 1, "rela": "and"},
+            {"field": "name", "type": "str", "operate": "=", "value": "扣回", "rela": "or"}
         ],
+        "f_type": "or'",
     },
     _typeFun: {
         bool: '_checkBoolean',
@@ -645,13 +646,26 @@ const filter = {
             c.fun = this._typeFun[c.type];
         }
 
-        const result = fData.filter(function (d) {
-            for (const c of options.condition) {
-                if (!self[c.fun](ctx, d[c.field], c)) return false;
+        data[options.table] = fData.filter(function (d) {
+            if (options.condition.length > 0) {
+                let con = options.condition[0];
+                let result = self[con.fun](ctx, d[con.field], con);
+                for (let i = 1, iLen = options.condition.length; i < iLen; i++) {
+                    con = options.condition[i];
+                    result = (con.rela && con.rela === 'or')
+                        ? (result || self[con.fun](ctx, d[con.field], con))
+                        : (result && self[con.fun](ctx, d[con.field], con));
+                }
+                return result;
+            } else {
+                return true;
             }
-            return true;
+            // const result;
+            // for (const c of options.condition) {
+            //     if (!self[c.fun](ctx, d[c.field], c)) return false;
+            // }
+            // return true;
         });
-        data[options.table] = result;
     }
 };
 const gatherStagePay = {
@@ -990,6 +1004,50 @@ const datetimeFormat = {
         }
     }
 };
+const gatherSelectConverse = {
+    name: '交叉汇总数据',
+    hint: '需搭配 用户交互--汇总标段选择 一起使用',
+    defaultSetting: {
+        table: ['mem_gather_stage_bills'],
+    },
+    _stageBills: function (helper, data, count) {
+        const result = [];
+        const reg = new RegExp('^t_[0-9]+_');
+        for (let i = 0; i < count; i++) {
+            const curReg = new RegExp('^t_' + i + '_');
+            for (const d of data) {
+                const nd = {};
+                for (const prop in d) {
+                    if (reg.test(prop)) {
+                        if (curReg.test(prop)) {
+                            nd[prop.replace(curReg, 't_')] = d[prop];
+                        }
+                    } else {
+                        nd[prop] = d[prop];
+                    }
+                }
+                result.push(nd);
+            }
+        }
+        return result;
+    },
+    fun: function (ctx, data, fieldsKey, options, csRela) {
+        if (!csRela.tplDefine) return;
+
+        const gsDefine = csRela.tplDefine.gather_select;
+        if (!gsDefine || !gsDefine.enable || !gsDefine.setting || gsDefine.setting === '') return;
+        const gsCustom = csRela.cDefine ? csRela.cDefine.gather_select : null;
+        const count = gsCustom.tenders.length - gsDefine.setting.special.length;
+
+        for (const t of options.table) {
+            switch (t) {
+                case 'mem_gather_stage_bills':
+                    data[t] = this._stageBills(ctx.helper, data[t], count);
+                    break;
+            }
+        }
+    }
+};
 
 const analysisObj = {
     changeSort,
@@ -1004,6 +1062,7 @@ const analysisObj = {
     addSumChapter,
     auditSelect,
     datetimeFormat,
+    gatherSelectConverse,
 };
 const analysisDefine = (function (obj) {
     const result = [];

+ 19 - 1
app/middleware/tender_check.js

@@ -42,9 +42,27 @@ module.exports = options => {
             if (!tender.data.ledger_times) {
                 tender.data.ledger_times = 1;
             }
-            // todo 校验权限 (标段参与人、分享)
             if (tender.data.project_id !== this.session.sessionProject.id) {
                 throw '您无权查看该项目';
+            } else {
+                const accountId = this.session.sessionUser.accountId;
+                if (tender.data.ledger_status === auditConst.status.uncheck) {
+                    if (tender.data.user_id !== accountId) {
+                        throw '您无权查看该项目';
+                    }
+                } else {
+                    const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
+                    const auditors = yield this.service.ledgerAudit.getAuditors(tender.id, times);
+                    const auditorsId = this.helper._.map(auditors, 'audit_id');
+                    const stageAuditors = yield this.service.stageAudit.getAllAuditors(tender.id);
+                    const stageAUditorsId = this.helper._.map(stageAuditors, 'aid');
+                    const tenderPermission = this.session.sessionUser.permission ? this.session.sessionUser.permission.tender : null;
+                    if (auditorsId.indexOf(accountId) === -1 && tender.data.user_id !== accountId &&
+                        (tenderPermission === null || tenderPermission === undefined || tenderPermission.indexOf('2') === -1) &&
+                        stageAUditorsId.indexOf(accountId) === -1) {
+                        throw '您无权查看该项目';
+                    }
+                }
             }
             tender.ledgerReadOnly = this.session.sessionUser.accountId !== tender.data.user_id ||
                 tender.data.ledger_status === auditConst.status.checking || tender.data.ledger_status === auditConst.status.checked;

+ 11 - 0
app/public/js/change_detail.js

@@ -42,6 +42,10 @@ $(document).ready(() => {
                 table.destroy();
             }
             table = $('.table-list').removeAttr('width').DataTable(billsTable);
+            if (!$('.change-detail-checkbox').is(':checked')) {
+                const column = table.column(3);
+                column.visible(!column.visible());
+            }
             $('#bills').removeClass('first-bill-pane');
         }
     });
@@ -109,12 +113,19 @@ $(document).ready(() => {
         });
     });
 
+    //
+    const cca = getLocalCache('change-checkbox-account-' + accountId);
+    if (cca !== null && cca !== undefined) {
+        $('#customCheck1').prop('checked', cca !== 'false');
+    }
     // 变更详情展示和隐藏
     $('.change-detail-checkbox').on('click', function (e) {
         if($(e.target).is('label')){
             return;
         }
         let column = table.column(3);
+        // 设置用户项目本地记录展示和隐藏情况
+        setLocalCache('change-checkbox-account-'+ accountId, $(this).is(':checked'));
         column.visible(!column.visible());
     })
 });

+ 1 - 0
app/public/js/se_bonus.js

@@ -43,6 +43,7 @@ $(document).ready(() => {
     const spreadSetting = {
         cols: [
             {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 235, formatter: '@', readOnly: isPre, },
+            {title: '类型', colSpan: '1', rowSpan: '1', field: 'b_type', hAlign: 0, width: 40, formatter: '@', readOnly: isPre, },
             {title: '金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number', readOnly: isPre, },
             {
                 title: '时间', colSpan: '1', rowSpan: '1', field: 'real_time', hAlign: 2, width: 150, readOnly: true,

+ 1 - 0
app/public/js/se_other.js

@@ -16,6 +16,7 @@ $(document).ready(() => {
     const otherSpreadSetting = {
         cols: [
             {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 235, formatter: '@'},
+            {title: '类型', colSpan: '1', rowSpan: '1', field: 'o_type', hAlign: 0, width: 40, formatter: '@'},
             {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number'},
             {title: '本期金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number'},
             {title: '截止本期金额', colSpan: '1', rowSpan: '1', field: 'end_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},

+ 95 - 0
app/public/js/shares/tenders2tree.js

@@ -0,0 +1,95 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Tender2Tree = (function () {
+    const treeSetting = {
+        id: 'tmt_id',
+        pid: 'tmt_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        fullPath: 'full_path',
+    };
+    const tenderTree = createNewPathTree('gather', treeSetting);
+
+    // 查询方法
+    function findNode (key, value, arr) {
+        for (const a of arr) {
+            if (a[key] && a[key] === value) {
+                return a;
+            }
+        }
+    }
+
+    function findCategoryTreeNode(cid, value, array) {
+        for (const a of array) {
+            if (a.cid === cid && a.vid === value) {
+                return a;
+            }
+        }
+    }
+
+    function getCategoryTreeNode (category, value, parent) {
+        const array = parent ?  parent.children : tenderTree.children;
+        let cate = findCategoryTreeNode(category.id, value, array);
+        if (!cate) {
+            const cateValue = findNode('id', value, category.value);
+            if (!cateValue) return null;
+
+            cate = tenderTree.addNode({
+                cid: category.id,
+                vid: value,
+                name: cateValue.value,
+            }, parent);
+        }
+        return cate;
+    }
+
+    function loadCategoryTreeNode ( tender, levelCate) {
+        let tenderCategory = null;
+        for (const [i, lc] of levelCate.entries()) {
+            const tenderCate = findNode('cid', lc.id, tender.category);
+
+            if (tenderCate) {
+                tenderCategory = getCategoryTreeNode(lc, tenderCate.value, tenderCategory);
+            } else {
+                if (i === 0 && tender.category) {
+                    for (const [j, c] of tender.category.entries()) {
+                        const cate = findNode('id', c.cid, category);
+                        tenderCategory = getCategoryTreeNode(cate, c.value, tenderCategory);
+                    }
+                }
+                return tenderCategory;
+            }
+        }
+        return tenderCategory;
+    }
+
+    function convert (category, tenders) {
+        tenderTree.clearDatas();
+
+        const levelCategory = category.filter(function (c) {
+            return c.level && c.level > 0;
+        });
+
+        for (const t of tenders) {
+            const parent = (t.category && levelCategory.length > 0) ? loadCategoryTreeNode(t, levelCategory) : null;
+            tenderTree.addNode({
+                tid: t.id,
+                name: t.name,
+                phase: t.lastStage ? '第' + t.lastStage.order + '期' : '台账',
+                status: t.lastStage ? auditConst.stage.statusString[t.lastStage.status] : auditConst.ledger.statusString[t.ledger_status]
+            }, parent);
+        }
+        tenderTree.sortTreeNode(false);
+        return tenderTree;
+    }
+    return { convert }
+})();

+ 6 - 0
app/public/js/stage_pay.js

@@ -618,6 +618,9 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
+                    if (payBase.isSF(select)) {
+                        data.updateData.expr = data.updateData.tp;
+                    }
                 } else if (col.field === 'name') {
                     data.updateData.pid = select.pid;
                     data.updateData.name = validText;
@@ -784,6 +787,9 @@ $(document).ready(() => {
                                 SpreadJsObj.reLoadSheetData(paySpread.getActiveSheet());
                                 return;
                             }
+                            if (payBase.isSF(node)) {
+                                data.updateData.expr = data.updateData.tp;
+                            }
                         } else if (col.field === 'name') {
                             updateData.pid = node.pid;
                             updateData.name = validText;

+ 282 - 5
app/public/report/js/rpt_custom.js

@@ -9,8 +9,132 @@
  */
 
 const rptCustomObj = (function () {
+    // 审批人选择
     const sAuditSelect = 'audit_select';
     let stageFlow = [];
+    // 汇总表
+    const sGatherSelect = 'gather_select';
+    let gsObj = {
+        setting: null,
+
+        gsSheet: null,
+        grSheet: null,
+
+        tenderSourceTree: null,
+        grArray: [],
+
+        orgSelect: null,
+    };
+    const grSpreadSetting = {
+        baseCols: [
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+        ],
+        extraCols: [
+            {title: '%s', colSpan: '1', rowSpan: '1', field: '%s', hAlign: 1, vAlign: '1', width: 60, cellType: 'checkbox', readOnly: true},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        headColWidth: []
+    };
+    const gatherSelectSpreadObj = {
+        _addTender: function (tender) {
+            const gr = gsObj.grArray.find(function (x) {
+                return x.tid === tender.tid;
+            });
+            const t = {tid: tender.tid, name: tender.name}
+            if (!gr) gsObj.grArray.push(t);
+            return t;
+        },
+        _removeTender: function (tender) {
+            const gri = gsObj.grArray.findIndex(function (x, i, arr) {
+                return x.tid === tender.tid;
+            });
+            if (gri >= 0) gsObj.grArray.splice(gri, 1);
+        },
+        reloadResultData: function () {
+            SpreadJsObj.reLoadSheetData(gsObj.grSheet);
+        },
+        gsButtonClicked: function (e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field !== 'selected') return;
+
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            node.selected = !node.selected;
+            if (node.children && node.children.length > 0) {
+                const posterity = gsObj.tenderSourceTree.getPosterity(node);
+                for (const p of posterity) {
+                    p.selected = node.selected;
+                    if (!p.children || p.children.length === 0){
+                        if (p.selected) {
+                            gatherSelectSpreadObj._addTender(p);
+                        } else {
+                            gatherSelectSpreadObj._removeTender(p);
+                        }
+                    }
+                }
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, posterity.length + 1);
+            } else {
+                if (node.selected) {
+                    gatherSelectSpreadObj._addTender(node);
+                } else {
+                    gatherSelectSpreadObj._removeTender(node);
+                }
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+            }
+            gatherSelectSpreadObj.reloadResultData();
+        },
+        grButtonClicked: function (e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field === 'name') return;
+
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            const refreshRows = [info.row];
+            node[col.field] = !node[col.field];
+            for (const rCol of info.sheet.zh_setting.cols) {
+                if (rCol.field !== 'name' && rCol.field !== col.field) {
+                    node[rCol.field] = false;
+                }
+            }
+            if (node[col.field]) {
+                for (const [i, gra] of gsObj.grArray.entries()) {
+                    if (gra[col.field] && gra.tid !== node.tid) {
+                        gra[col.field] = false;
+                        refreshRows.push(i);
+                    }
+                }
+            }
+            SpreadJsObj.reLoadRowsData(info.sheet, refreshRows);
+        },
+        initSelectTenders: function (tenders) {
+            if (!tenders || tenders.length === 0) return;
+
+            const specCol = gsObj.setting.special ? gsObj.setting.special : [];
+            const select = [];
+            for (const node of gsObj.tenderSourceTree.nodes) {
+                node.selected = false;
+            }
+            for (const t of tenders) {
+                const tender = gsObj.tenderSourceTree.nodes.find(function (x) { return x.tid === t.tid});
+                tender.selected = true;
+                select.push(tender);
+                const st = this._addTender(tender);
+                for (const sc of specCol) {
+                    st[sc.key] = t[sc.key];
+                }
+            }
+            SpreadJsObj.reLoadNodesData(gsObj.gsSheet, select);
+            if (select.length > 0) SpreadJsObj.locateTreeNode(gsObj.gsSheet, select.tmt_id);
+            this.reloadResultData();
+        }
+    };
 
     const getStageFlowSelectHtml = function (select, id) {
         const html = [];
@@ -60,14 +184,70 @@ const rptCustomObj = (function () {
         }
     };
 
+    const initGrSpreadSetting = function (gsSetting) {
+        grSpreadSetting.cols = [];
+        for (const bc of grSpreadSetting.baseCols) {
+            grSpreadSetting.cols.push(bc);
+        }
+        for (const s of gsSetting.special) {
+            for (const ec of grSpreadSetting.extraCols) {
+                const c = {};
+                c.title = ec.title.replace('%s', s.title);
+                c.colSpan = ec.colSpan;
+                c.field = ec.field.replace('%s', s.key);
+                c.hAlign = ec.hAlign;
+                c.width = s.width ? s.width : ec.width;
+                c.cellType = ec.cellType;
+                c.readOnly = ec.readOnly;
+                grSpreadSetting.cols.push(c);
+            }
+        }
+    };
+
+    const initGatherSelect = function (gsSetting, gsSelect) {
+        gsObj.setting = JSON.parse(gsSetting);
+        gsObj.orgSelect = gsSelect;
+        $('#gather-select-count').html(gsSelect ? gsSelect.tenders.length : 0);
+        $('#gather-select-title').html(gsObj.setting.title);
+        initGrSpreadSetting(gsObj.setting);
+        SpreadJsObj.initSheet(gsObj.grSheet, grSpreadSetting);
+        if (gsObj.setting.type === 'month') {
+            $('#gather-by-month').show();
+            $('#gather-by-zone').hide();
+        } else if (gsObj.setting.type === 'zone') {
+            $('#gather-by-month').hide();
+            $('#gather-by-zone').show();
+        } else {
+            $('#gather-by-month').hide();
+            $('#gather-by-zone').hide();
+        }
+        SpreadJsObj.loadSheetData(gsObj.grSheet, SpreadJsObj.DataType.Data, gsObj.grArray);
+        // 初始化选择结果
+        if (gsSelect) {
+            if (gsSelect.zone) {
+                $('#gather-zone').val(gsSelect.zone ? gsSelect.zone : '');
+            } else if (gsSelect.month) {
+                $('#gather-month').val(gsSelect.month ? gsSelect.month: '');
+            }
+            gatherSelectSpreadObj.initSelectTenders(gsSelect.tenders);
+        }
+        // 初始化
+        $("#gather-select").modal('show');
+    };
     const init = function (cDefine, sfData, cSelect) {
         stageFlow = sfData;
         if (cDefine && cDefine[sAuditSelect] && cDefine[sAuditSelect].enable && cDefine[sAuditSelect].setting) {
             $('#pnl_audit_select').show();
-            initAuditSelect(cDefine[sAuditSelect].setting, cSelect ? cSelect.audit_select : []);
+            initAuditSelect(cDefine[sAuditSelect].setting, cSelect ? cSelect[sAuditSelect] : []);
         } else {
             $('#pnl_audit_select').hide();
         }
+        if (cDefine && cDefine[sGatherSelect] && cDefine[sGatherSelect].enable && cDefine[sGatherSelect].setting) {
+            $('#pnl_gather_select').show();
+            initGatherSelect(cDefine[sGatherSelect].setting, cSelect ? cSelect[sGatherSelect] : null);
+        } else {
+            $('#pnl_gather_select').hide();
+        }
     };
 
     const reloadReportData = function (result) {
@@ -128,9 +308,7 @@ const rptCustomObj = (function () {
         }
     };
 
-    const resetAuditSelect = function () {
-        const selObj = $('select', '#audit-select-list');
-        const data = { audit_select: [] };
+    const getCommonParams = function (data) {
         data.pageSize = rptControlObj.getCurrentPageSize();
         data.orientation = rptControlObj.getCurrentOrientation();
         data.rpt_tpl_id = zTreeOprObj.currentNode.refId;
@@ -141,6 +319,12 @@ const rptCustomObj = (function () {
         data.stage_status = getStageStatus();
         data.stage_order = getStageOrder();
         data.stage_times = getStageTimes();
+    };
+
+    const resetAuditSelect = function () {
+        const selObj = $('select', '#audit-select-list');
+        const data = { audit_select: [] };
+        getCommonParams(data);
         for (const s of selObj) {
             const sf = stageFlow[s.selectedIndex];
             if (!sf) {
@@ -156,5 +340,98 @@ const rptCustomObj = (function () {
         });
     };
 
-    return {init, resetAuditSelect};
+    const resetGatherSelect = function () {
+        const data = {}, hintObj = $('#gather-hint');
+        getCommonParams(data);
+        data[sGatherSelect] = {
+            tenders: [],
+            type: gsObj.setting.type,
+        };
+        const specCol = gsObj.setting.special ? gsObj.setting.special : [];
+        for (const gra of gsObj.grArray) {
+            const ra = {tid: gra.tid};
+            for (const sc of specCol) {
+                if (gra[sc.key]) {
+                    ra[sc.key] = true;
+                    sc.sCount += 1;
+                }
+            }
+            data[sGatherSelect].tenders.push(ra);
+        }
+        for (const sc of specCol) {
+            if (sc.sCount === 0) {
+                hintObj.html('请选择 ' + sc.title).show();
+                return;
+            }
+        }
+        if (data[sGatherSelect].tenders.length <= specCol.length) {
+            hintObj.html('请至少选择1个普通汇总项目').show();
+            return;
+        }
+        if (gsObj.setting.type === 'month') {
+            data[sGatherSelect].month = $('#gather-month').val();
+            if (data[sGatherSelect].month === '') {
+                hintObj.html('请选择 汇总年月').show();
+                return;
+            }
+        } else if (gsObj.setting.type === 'zone') {
+            data[sGatherSelect].zone = $('#gather-zone').val();
+            if (data[sGatherSelect].zone === '') {
+                hintObj.html('请选择 汇总周期').show();
+                return;
+            } else if(data[sGatherSelect].zone.indexOf(' - ') < 0) {
+                hintObj.html('请选择 完整汇总周期').show();
+                return;
+            }
+        }
+        hintObj.hide();
+        postData('/report/cDefine', data, function (result) {
+            reloadReportData(result);
+            $('#gather-select-count').html(data[sGatherSelect].tenders.length);
+            $('#gather-select').modal('hide');
+        });
+
+    };
+
+    const initTenderTree = function (tenders, category) {
+        const gsSpread = SpreadJsObj.createNewSpread($('#gather-source-spread')[0]);
+        gsObj.gsSheet = gsSpread.getActiveSheet();
+        const spreadSetting = {
+            cols: [
+                {title: '选择', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox', readOnly: true},
+                {title: '名称', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true, cellType: 'tree'},
+                {title: '期数', field: 'phase', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+                {title: '审批状态', field: 'status', hAlign: 1, width: 60, formatter: '@', readOnly: true}
+            ],
+            emptyRows: 0,
+            headRows: 1,
+            headRowHeight: [32],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+            headColWidth: [0],
+            selectedBackColor: '#fffacd',
+        };
+        SpreadJsObj.initSheet(gsObj.gsSheet, spreadSetting);
+        gsObj.tenderSourceTree = Tender2Tree.convert(category, tenders);
+        SpreadJsObj.loadSheetData(gsObj.gsSheet, SpreadJsObj.DataType.Tree, gsObj.tenderSourceTree);
+        gsSpread.bind(spreadNS.Events.ButtonClicked, gatherSelectSpreadObj.gsButtonClicked);
+
+        const grSpread = SpreadJsObj.createNewSpread($('#gather-result-spread')[0]);
+        gsObj.grSheet = grSpread.getActiveSheet();
+        grSpread.bind(spreadNS.Events.ButtonClicked, gatherSelectSpreadObj.grButtonClicked);
+
+        $('#gather-hint').hide();
+
+        $('#gather-select').bind('shown.bs.modal', function () {
+            if (gsSpread) gsSpread.refresh();
+            if (grSpread) grSpread.refresh();
+        });
+
+        $('.datepicker-here').datepicker({
+            autoClose: true,
+        });
+    };
+
+    return {init, resetAuditSelect, resetGatherSelect, initTenderTree};
 })();

+ 6 - 7
app/service/ledger_audit.js

@@ -376,16 +376,15 @@ module.exports = app => {
          * @returns {Promise<*>}
          */
         async getNoticeTender(projectId, auditorId, noticeTime) {
-            const sql = 'SELECT la.`audit_id`, la.`times`, la.`audit_order`, la.`end_time`, la.`status`, t.`id`, t.`name`, t.`project_id`, t.`type`, t.`user_id`, ' +
+            const sql = 'SELECT * FROM (SELECT la.`audit_id`, la.`times`, la.`audit_order`, la.`end_time`, la.`status`, t.`id`, t.`name`, t.`project_id`, t.`type`, t.`user_id`, ' +
                         '    pa.name As `lu_name`, pa.role As `lu_role`, pa.company As `lu_company`' +
-                        '  FROM ?? As t ' +
+                        '  FROM (SELECT * FROM ?? WHERE `user_id` = ? OR `id` in (SELECT `tender_id` FROM ?? WHERE `audit_id` = ? GROUP BY `tender_id`)) As t ' +
                         '  LEFT JOIN ?? As la ON la.`tender_id` = t.`id`' +
                         '  LEFT JOIN ?? As pa ON la.`audit_id` = pa.`id`' +
-                        '  WHERE la.`audit_id` <> ? and la.`end_time` > ? and t.`project_id` = ?' +
-                        '  GROUP By t.`id`' +
-                        '  ORDER By la.`end_time`';
-            const sqlParam = [this.ctx.service.tender.tableName, this.tableName, this.ctx.service.projectAccount.tableName,
-                auditorId, noticeTime, projectId];
+                        '  WHERE la.`end_time` > ? and t.`project_id` = ?' +
+                        '  ORDER By la.`end_time` DESC LIMIT 1000) as new_t GROUP BY new_t.`id` ORDER BY new_t.`end_time`';
+            const sqlParam = [this.ctx.service.tender.tableName, auditorId, this.tableName, auditorId, this.tableName, this.ctx.service.projectAccount.tableName,
+                noticeTime, projectId];
             return await this.db.query(sql, sqlParam);
         }
     }

+ 6 - 2
app/service/report.js

@@ -30,7 +30,7 @@ module.exports = app => {
             }
         }
 
-        async getReportData(params, filters, memFieldKeys) {
+        async getReportData(params, filters, memFieldKeys, customDefine, customSelect) {
             const service = this.ctx.service;
             const rst = {};
             const runnableRst = [];
@@ -117,10 +117,14 @@ module.exports = app => {
                             runnableKey.push(filter);
                             break;
                         case 'mem_stage_other':
-                        case 'mem_stage_other':
                             runnableRst.push(service.reportMemory.getStageOther(params.tender_id, params.stage_id, memFieldKeys[filter]));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_gather_stage_bills':
+                            runnableRst.push(service.rptGatherMemory.getGatherStageBills(memFieldKeys[filter],
+                                customDefine.gather_select.setting, customSelect.gather_select));
+                            runnableKey.push(filter);
+                            break;
                         default:
                             break;
                     }

+ 3 - 0
app/service/rpt_custom_define.js

@@ -26,6 +26,9 @@ module.exports = app => {
             if (data && data.audit_select) {
                 data.audit_select = JSON.parse(data.audit_select);
             }
+            if (data && data.gather_select) {
+                data.gather_select = JSON.parse(data.gather_select);
+            }
             return data;
         }
 

+ 355 - 0
app/service/rpt_gather_memory.js

@@ -0,0 +1,355 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Ledger = require('../lib/ledger');
+const auditConst = require('../const/audit');
+const moment = require('moment');
+const indexPre = 'id_';
+
+const gatherUtils = {
+    gatherStage: function (tender, gatherNode, sourceNode, prefix, helper) {
+        gatherNode[prefix + 'id'] = tender.id;
+        gatherNode[prefix + 'name'] = tender.name;
+
+        gatherNode[prefix + "qty"] = helper.add(gatherNode[prefix + "qty"], sourceNode.quantity);
+        gatherNode[prefix + "tp"] = helper.add(gatherNode[prefix + "tp"], sourceNode.total_price);
+
+        gatherNode[prefix + "contract_qty"] = helper.add(gatherNode[prefix + "contract_qty"], sourceNode.contract_qty);
+        gatherNode[prefix + "contract_tp"] = helper.add(gatherNode[prefix + "contract_tp"], sourceNode.contract_tp);
+        gatherNode[prefix + "qc_qty"] = helper.add(gatherNode[prefix + "qc_qty"], sourceNode.qc_qty);
+        gatherNode[prefix + "qc_tp"] = helper.add(gatherNode[prefix + "qc_tp"], sourceNode.qc_tp);
+        gatherNode[prefix + "gather_qty"] = helper.add(gatherNode[prefix + "gather_qty"], sourceNode.gather_qty);
+        gatherNode[prefix + "gather_tp"] = helper.add(gatherNode[prefix + "gather_tp"], sourceNode.gather_tp);
+
+        gatherNode[prefix + "pre_contract_qty"] = helper.add(gatherNode[prefix + "pre_contract_qty"], sourceNode.pre_contract_qty);
+        gatherNode[prefix + "pre_contract_tp"] = helper.add(gatherNode[prefix + "pre_contract_tp"], sourceNode.pre_contract_tp);
+        gatherNode[prefix + "pre_qc_qty"] = helper.add(gatherNode[prefix + "pre_qc_qty"], sourceNode.pre_qc_qty);
+        gatherNode[prefix + "pre_qc_tp"] = helper.add(gatherNode[prefix + "pre_qc_tp"], sourceNode.pre_qc_tp);
+        gatherNode[prefix + "pre_gather_qty"] = helper.add(gatherNode[prefix + "pre_gather_qty"], sourceNode.pre_gather_qty);
+        gatherNode[prefix + "pre_gather_tp"] = helper.add(gatherNode[prefix + "pre_gather_tp"], sourceNode.pre_gather_tp);
+
+        gatherNode[prefix + "end_contract_qty"] = helper.add(gatherNode[prefix + "end_contract_qty"], sourceNode.end_contract_qty);
+        gatherNode[prefix + "end_contract_tp"] = helper.add(gatherNode[prefix + "end_contract_tp"], sourceNode.end_contract_tp);
+        gatherNode[prefix + "end_qc_qty"] = helper.add(gatherNode[prefix + "end_qc_qty"], sourceNode.end_qc_qty);
+        gatherNode[prefix + "end_qc_tp"] = helper.add(gatherNode[prefix + "end_qc_tp"], sourceNode.end_qc_tp);
+        gatherNode[prefix + "end_gather_qty"] = helper.add(gatherNode[prefix + "end_gather_qty"], sourceNode.end_gather_qty);
+        gatherNode[prefix + "end_gather_tp"] = helper.add(gatherNode[prefix + "end_gather_tp"], sourceNode.end_gather_tp);
+
+        gatherNode['s_' + "qty"] = helper.add(gatherNode['s_' + "qty"], sourceNode.quantity);
+        gatherNode['s_' + "tp"] = helper.add(gatherNode['s_' + "tp"], sourceNode.total_price);
+
+        gatherNode['s_' + "contract_qty"] = helper.add(gatherNode['s_' + "contract_qty"], sourceNode.contract_qty);
+        gatherNode['s_' + "contract_tp"] = helper.add(gatherNode['s_' + "contract_tp"], sourceNode.contract_tp);
+        gatherNode['s_' + "qc_qty"] = helper.add(gatherNode['s_' + "qc_qty"], sourceNode.qc_qty);
+        gatherNode['s_' + "qc_tp"] = helper.add(gatherNode['s_' + "qc_tp"], sourceNode.qc_tp);
+        gatherNode['s_' + "gather_qty"] = helper.add(gatherNode['s_' + "gather_qty"], sourceNode.gather_qty);
+        gatherNode['s_' + "gather_tp"] = helper.add(gatherNode['s_' + "gather_tp"], sourceNode.gather_tp);
+
+        gatherNode['s_' + "pre_contract_qty"] = helper.add(gatherNode['s_' + "pre_contract_qty"], sourceNode.pre_contract_qty);
+        gatherNode['s_' + "pre_contract_tp"] = helper.add(gatherNode['s_' + "pre_contract_tp"], sourceNode.pre_contract_tp);
+        gatherNode['s_' + "pre_qc_qty"] = helper.add(gatherNode['s_' + "pre_qc_qty"], sourceNode.pre_qc_qty);
+        gatherNode['s_' + "pre_qc_tp"] = helper.add(gatherNode['s_' + "pre_qc_tp"], sourceNode.pre_qc_tp);
+        gatherNode['s_' + "pre_gather_qty"] = helper.add(gatherNode['s_' + "pre_gather_qty"], sourceNode.pre_gather_qty);
+        gatherNode['s_' + "pre_gather_tp"] = helper.add(gatherNode['s_' + "pre_gather_tp"], sourceNode.pre_gather_tp);
+
+        gatherNode['s_' + "end_contract_qty"] = helper.add(gatherNode['s_' + "end_contract_qty"], sourceNode.end_contract_qty);
+        gatherNode['s_' + "end_contract_tp"] = helper.add(gatherNode['s_' + "end_contract_tp"], sourceNode.end_contract_tp);
+        gatherNode['s_' + "end_qc_qty"] = helper.add(gatherNode['s_' + "end_qc_qty"], sourceNode.end_qc_qty);
+        gatherNode['s_' + "end_qc_tp"] = helper.add(gatherNode['s_' + "end_qc_tp"], sourceNode.end_qc_tp);
+        gatherNode['s_' + "end_gather_qty"] = helper.add(gatherNode['s_' + "end_gather_qty"], sourceNode.end_gather_qty);
+        gatherNode['s_' + "end_gather_tp"] = helper.add(gatherNode['s_' + "end_gather_tp"], sourceNode.end_gather_tp);
+    },
+    gatherZone: function (tender, gatherNode, sourceNode, prefix, helper) {
+        gatherNode[prefix + 'id'] = tender.id;
+        gatherNode[prefix + 'name'] = tender.name;
+
+        gatherNode[prefix + "qty"] = helper.add(gatherNode[prefix + "qty"], sourceNode.quantity);
+        gatherNode[prefix + "tp"] = helper.add(gatherNode[prefix + "tp"], sourceNode.total_price);
+
+        gatherNode[prefix + "contract_qty"] = helper.add(gatherNode[prefix + "contract_qty"], sourceNode.contract_qty);
+        gatherNode[prefix + "contract_tp"] = helper.add(gatherNode[prefix + "contract_tp"], sourceNode.contract_tp);
+        gatherNode[prefix + "qc_qty"] = helper.add(gatherNode[prefix + "qc_qty"], sourceNode.qc_qty);
+        gatherNode[prefix + "qc_tp"] = helper.add(gatherNode[prefix + "qc_tp"], sourceNode.qc_tp);
+        gatherNode[prefix + "gather_qty"] = helper.add(gatherNode[prefix + "gather_qty"], sourceNode.gather_qty);
+        gatherNode[prefix + "gather_tp"] = helper.add(gatherNode[prefix + "gather_tp"], sourceNode.gather_tp);
+
+        gatherNode['s_' + "qty"] = helper.add(gatherNode['s_' + "qty"], sourceNode.quantity);
+        gatherNode['s_' + "tp"] = helper.add(gatherNode['s_' + "tp"], sourceNode.total_price);
+
+        gatherNode['s_' + "contract_qty"] = helper.add(gatherNode['s_' + "contract_qty"], sourceNode.contract_qty);
+        gatherNode['s_' + "contract_tp"] = helper.add(gatherNode['s_' + "contract_tp"], sourceNode.contract_tp);
+        gatherNode['s_' + "qc_qty"] = helper.add(gatherNode['s_' + "qc_qty"], sourceNode.qc_qty);
+        gatherNode['s_' + "qc_tp"] = helper.add(gatherNode['s_' + "qc_tp"], sourceNode.qc_tp);
+        gatherNode['s_' + "gather_qty"] = helper.add(gatherNode['s_' + "gather_qty"], sourceNode.gather_qty);
+        gatherNode['s_' + "gather_tp"] = helper.add(gatherNode['s_' + "gather_tp"], sourceNode.gather_tp);
+    },
+    gatherSpecial: function (gatherNode, sourceNode, prefix, helper) {
+        gatherNode[prefix + "qty"] = helper.add(gatherNode[prefix + "qty"], sourceNode.quantity);
+        gatherNode[prefix + "tp"] = helper.add(gatherNode[prefix + "tp"], sourceNode.total_price);
+    },
+};
+
+module.exports = app => {
+    class RptGatherMemory extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局context
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.resultTree = new Ledger.gatherTree(ctx, {
+                id: 'id',
+                pid: 'pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1
+            });
+        }
+
+        _checkSpecialTender(tender, special) {
+            for (const spec of special) {
+                if (tender[spec.key] === true) return spec.key;
+            }
+            return '';
+        }
+
+        async _gatherStageData(index, tender, stage, hasPre) {
+            const helper = this.ctx.helper;
+            const billsTree = new Ledger.billsTree(this.ctx, {
+                id: 'ledger_id',
+                pid: 'ledger_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+                keys: ['id', 'tender_id', 'ledger_id'],
+                stageId: 'id',
+                calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp'],
+                calc: function (node) {
+                    if (node.children && node.children.length === 0) {
+                        node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
+                        node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
+                        node.end_contract_qty = helper.add(node.pre_contract_qty, node.contract_qty);
+                        node.end_qc_qty = helper.add(node.pre_qc_qty, node.qc_qty);
+                        node.end_gather_qty = helper.add(node.pre_gather_qty, node.gather_qty);
+                    }
+                    node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
+                    node.gather_tp = helper.add(node.contract_tp, node.qc_tp);
+                    node.end_contract_tp = helper.add(node.pre_contract_tp, node.contract_tp);
+                    node.end_qc_tp = helper.add(node.pre_qc_tp, node.qc_tp);
+                    node.end_gather_tp = helper.add(node.pre_gather_tp, node.gather_tp);
+                }
+            });
+            const billsData = await this.ctx.service.ledger.getData(tender.id);
+            if (stage) {
+                await this.ctx.service.stage.doCheckStage(stage);
+                if (stage.readOnly) {
+                    const curStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id,
+                        stage.id, stage.curTimes, stage.curOrder);
+                    this.ctx.helper.assignRelaData(billsData, [
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                    ]);
+                } else {
+                    const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+                    this.ctx.helper.assignRelaData(billsData, [
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                    ]);
+                }
+                if (hasPre) {
+                    const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
+                    this.ctx.helper.assignRelaData(posData, [
+                        {data: preStage, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid'}
+                    ]);
+                }
+            }
+            billsTree.loadDatas(billsData);
+            billsTree.calculateAll();
+            this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
+                gatherUtils.gatherStage(tender, gatherNode, sourceNode, 't_' + index + '_', helper);
+            });
+        }
+
+        async _gatherMonthData(sTender, index, month, hasPre) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.id, s_time: month});
+            await this._gatherStageData(index, tender, stage, hasPre);
+        }
+
+        async _gatherZoneData(sTender, index, zone) {
+            const helper = this.ctx.helper;
+            /**
+             * 汇总并合并 相关数据
+             * @param {Array} index - 主数据
+             * @param {Array[]}rela - 相关数据 {data, fields, prefix, relaId}
+             */
+            const sumAssignRelaData = function (index, rela) {
+                const loadFields = function (datas, fields, prefix, relaId) {
+                    for (const d of datas) {
+                        const key = indexPre + d[relaId];
+                        const m = index[key];
+                        if (m) {
+                            for (const f of fields) {
+                                if (d[f] !== undefined) {
+                                    m[prefix + f] = helper.add(m[prefix + f], d[f]);
+                                }
+                            }
+                        }
+                    }
+                };
+                for (const r of rela) {
+                    loadFields(r.data, r.fields, r.prefix, r.relaId);
+                }
+            };
+
+            const billsTree = new Ledger.billsTree(this.ctx, {
+                id: 'ledger_id',
+                pid: 'ledger_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+                keys: ['id', 'tender_id', 'ledger_id'],
+                stageId: 'id',
+                calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp'],
+                calc: function (node) {
+                    if (node.children && node.children.length === 0) {
+                        node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
+                    }
+                    node.gather_tp = helper.add(node.contract_tp, node.qc_tp);
+                }
+            });
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const billsData = await this.ctx.service.ledger.getData(tender.id);
+
+            let billsIndexData = {};
+            for (const bd of billsData) {
+                billsIndexData[indexPre + bd.id] = bd;
+            }
+
+            const times = zone.split(' - ');
+            if (times.length !== 2) throw '选择的汇总周期无效';
+            const beginTime = moment(times[0], 'YYYY-MM').date();
+            const endTime = moment(times[1], 'YYYY-MM').date();
+
+
+            const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: tender.id } });
+            for (const stage of stages) {
+                const sTime = moment(stage.s_time, 'YYYY-MM').date();
+                if (sTime >= beginTime && sTime <= endTime) {
+                    await this.ctx.service.stage.doCheckStage(stage);
+                    if (stage.readOnly) {
+                        const curStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id,
+                            stage.id, stage.curTimes, stage.curOrder);
+                        sumAssignRelaData(billsIndexData, [
+                            {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        ]);
+                    } else {
+                        const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+                        sumAssignRelaData(billsIndexData, [
+                            {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        ]);
+                    }
+                }
+            }
+            billsTree.loadDatas(billsData);
+            billsTree.calculateAll();
+            this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
+                gatherUtils.gatherZone(gatherNode, sourceNode, 't_' + index + '_', helper);
+            });
+        }
+
+        async _gatherFinalData(sTender, index, hasPre) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this.db.select(this.tableName, {
+                where: { tid: tender.id },
+                orders: [['order', 'desc']],
+            });
+            if (stages.length !== 0) {
+                const lastStage = stages[stages.length - 1];
+                if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
+                    stages.splice(stages.length - 1, 1);
+                }
+            }
+            await this._gatherStageData(index, tender, stages[0], hasPre);
+        }
+
+        async _gatherCheckedFinalData(sTender, index, hasPre) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this.db.select(this.tableName, {
+                where: { tid: tender.id },
+                orders: [['order', 'desc']],
+            });
+            if (stages.length !== 0) {
+                const lastStage = stages[stages.length - 1];
+                if (lastStage.status !== auditConst.stage.status.checked) {
+                    stages.splice(stages.length - 1, 1);
+                }
+            }
+            await this._gatherStageData(index, tender, stages[0], hasPre);
+        }
+
+        async _gatherSpecialData(sTender, sKey) {
+            const helper = this.ctx.helper;
+            const billsTree = new Ledger.billsTree(this.ctx, {
+                id: 'ledger_id',
+                pid: 'ledger_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+                keys: ['id', 'tender_id', 'ledger_id'],
+                stageId: 'id',
+                calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp'],
+            });
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const billsData = await this.ctx.service.ledger.getData(tender.id);
+            billsTree.loadDatas(billsData);
+            billsTree.calculateAll();
+            this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
+                gatherUtils.gatherSpecial(gatherNode, sourceNode, 'ts_' + sKey + '_', helper);
+            })
+        }
+
+        async getGatherStageBills(memFieldKeys, gsDefine, gsCustom) {
+            if (!gsDefine || !gsDefine.enable) return [];
+            if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return [];
+
+            let commonIndex = 0;
+            for (const tender of gsCustom.tenders) {
+                const specialKey = this._checkSpecialTender(tender, gsDefine.setting.special);
+                if (specialKey === '') {
+                    switch (gsDefine.setting.type) {
+                        case 'month':
+                            await this._gatherMonthData(tender, commonIndex, gsCustom.month, gsDefine.hasPre);
+                            break;
+                        case 'zone':
+                            await this._gatherZoneData(tender, commonIndex, gsCustom.zone);
+                            break;
+                        case 'final':
+                            await this._gatherFinalData(tender, commonIndex, gsDefine.hasPre);
+                            break;
+                        case 'checked-final':
+                            await this._gatherCheckedFinalData(tender, commonIndex, gsDefine.hasPre);
+                            break;
+                    }
+                    commonIndex++;
+                } else {
+                    await this._gatherSpecialData(tender, specialKey);
+                }
+            }
+
+            this.resultTree.generateSortNodes();
+            return this.resultTree.getDefaultDatas();
+        }
+
+    }
+
+    return RptGatherMemory;
+};

+ 63 - 57
app/service/stage.js

@@ -28,67 +28,73 @@ module.exports = app => {
             this.tableName = 'stage';
         }
 
-        async checkStage(sid) {
-            if (!this.ctx.stage) {
-                const status = auditConst.status;
-                const stage = await this.ctx.service.stage.getDataById(sid);
-                stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
-                stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
+        async doCheckStage(stage) {
+            const status = auditConst.status;
+            stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
+            stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
 
-                const accountId = this.ctx.session.sessionUser.accountId, auditorIds = this._.map(stage.auditors, 'aid'), shareIds = [];
-                const permission = this.ctx.session.sessionUser.permission;
-                if (accountId === stage.user_id) { // 原报
-                    if (stage.curAuditor) {
-                        stage.readOnly = stage.curAuditor.aid !== accountId;
-                    } else {
-                        stage.readOnly = stage.status !== status.uncheck && stage.status !== status.checkNo;
-                    }
-                    stage.curTimes = stage.times;
-                    if (stage.status === status.uncheck || stage.status === status.checkNo) {
-                        stage.curOrder = 0;
-                    } else if (stage.status === status.checked) {
-                        stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
-                    } else {
-                        stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
-                    }
-                } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
-                    if (stage.status === status.uncheck) {
-                        throw '您无权查看该数据';
-                    }
-                    stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
-                    if (stage.status === status.checked) {
-                        stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
-                    } else if (stage.status === status.checkNo) {
-                        const audit = await this.service.stageAudit.getDataByCondition({
-                            sid: stage.id, times: stage.times - 1, status: status.checkNo
-                        });
-                        stage.curOrder = audit.order;
-                    } else {
-                        stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
-                    }
-                    stage.readOnly = (stage.status !== status.checking && stage.status !== status.checkNoPre) || accountId !== stage.curAuditor.aid;
-                } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
-                    if (stage.status === status.uncheck) {
-                        throw '您无权查看该数据';
-                    }
-                    stage.readOnly = true;
-                    stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
-                    if (stage.status === status.checkNo) {
-                        const audit = await this.service.stageAudit.getDataByCondition({
-                            sid: stage.id, times: stage.times - 1, status: status.checkNo,
-                        });
-                        stage.curOrder = audit.order;
-                    } else {
-                        stage.curOrder = stage.status === status.checked ? this._.max(this._.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
-                    }
+            const accountId = this.ctx.session.sessionUser.accountId, auditorIds = this._.map(stage.auditors, 'aid'), shareIds = [];
+            const permission = this.ctx.session.sessionUser.permission;
+            if (accountId === stage.user_id) { // 原报
+                if (stage.curAuditor) {
+                    stage.readOnly = stage.curAuditor.aid !== accountId;
+                } else {
+                    stage.readOnly = stage.status !== status.uncheck && stage.status !== status.checkNo;
+                }
+                stage.curTimes = stage.times;
+                if (stage.status === status.uncheck || stage.status === status.checkNo) {
+                    stage.curOrder = 0;
+                } else if (stage.status === status.checked) {
+                    stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
+                } else {
+                    stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
+                }
+            } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
+                if (stage.status === status.uncheck) {
+                    throw '您无权查看该数据';
                 }
+                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
+                if (stage.status === status.checked) {
+                    stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
+                } else if (stage.status === status.checkNo) {
+                    const audit = await this.service.stageAudit.getDataByCondition({
+                        sid: stage.id, times: stage.times - 1, status: status.checkNo
+                    });
+                    stage.curOrder = audit.order;
+                } else {
+                    stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
+                }
+                stage.readOnly = (stage.status !== status.checking && stage.status !== status.checkNoPre) || accountId !== stage.curAuditor.aid;
+            } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
+                if (stage.status === status.uncheck) {
+                    throw '您无权查看该数据';
+                }
+                stage.readOnly = true;
+                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
+                if (stage.status === status.checkNo) {
+                    const audit = await this.service.stageAudit.getDataByCondition({
+                        sid: stage.id, times: stage.times - 1, status: status.checkNo,
+                    });
+                    stage.curOrder = audit.order;
+                } else {
+                    stage.curOrder = stage.status === status.checked ? this._.max(this._.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
+                }
+            }
+
+            let time = stage.readOnly ? stage.cache_time_r : stage.cache_time_l;
+            if (!time) {
+                time = stage.in_time ? stage.in_time : new Date();
+            }
+            stage.cacheTime = time.getTime();//this.ctx.stage.readOnly ? (this.ctx.stage.cache_time_r).getTime(): (this.ctx.stage.cache_time_l).getTime();
 
+            return stage;
+        }
+
+        async checkStage(sid) {
+            if (!this.ctx.stage) {
+                const stage = await this.ctx.service.stage.getDataById(sid);
+                await this.doCheckStage(stage);
                 this.ctx.stage = stage;
-                let time = this.ctx.stage.readOnly ? this.ctx.stage.cache_time_r : this.ctx.stage.cache_time_l;
-                if (!time) {
-                    time = this.ctx.stage.in_time ? this.ctx.stage.in_time : new Date();
-                }
-                this.ctx.stage.cacheTime = time.getTime();//this.ctx.stage.readOnly ? (this.ctx.stage.cache_time_r).getTime(): (this.ctx.stage.cache_time_l).getTime();
             }
         }
 

+ 9 - 0
app/service/stage_audit.js

@@ -64,6 +64,15 @@ module.exports = app => {
             return result;
         }
 
+        async getAllAuditors(tenderId) {
+            const sql = 'SELECT sa.aid, sa.tid FROM ' + this.tableName + ' sa' +
+                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On sa.tid = t.id' +
+                '  WHERE t.id = ?' +
+                '  GROUP BY  sa.aid';
+            const sqlParam = [tenderId];
+            return this.db.query(sql, sqlParam);
+        }
+
         /**
          * 获取标段审核人最后一位的名称
          *

+ 2 - 1
app/service/stage_bonus.js

@@ -65,6 +65,7 @@ module.exports = app => {
                     name: d.name,
                     order: d.order,
                 };
+                nd.b_type = d.b_type ? d.b_type : null;
                 nd.tp = d.tp ? this.ctx.helper.round(d.to, this.ctx.tender.info.decimal.tp) : 0;
                 nd.code = d.code ? d.code: null;
                 nd.proof = d.proof ? d.proof : null;
@@ -100,6 +101,7 @@ module.exports = app => {
 
                 const nd = {id: od.id};
                 if (d.name !== undefined) nd.name = d.name;
+                if (d.b_type !== undefined) nd.b_type = d.b_type;
                 if (d.tp !== undefined) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
                 if (d.code !== undefined) nd.code = d.code;
                 if (d.proof !== undefined) nd.proof = d.proof;
@@ -108,7 +110,6 @@ module.exports = app => {
                 if (d.order !== undefined) nd.order = d.order;
                 if (d.doc_co !== undefined) nd.doc_co = d.doc_co;
                 uDatas.push(nd);
-                console.log(nd);
             }
             if (uDatas.length > 0) {
                 await this.db.updateRows(this.tableName, uDatas);

+ 2 - 0
app/service/stage_other.js

@@ -67,6 +67,7 @@ module.exports = app => {
                 nd.name = d.name;
                 nd.order = d.order;
 
+                if (d.o_type) nd.o_type = d.o_type;
                 if (d.total_price) nd.total_price = this.ctx.helper.round(d.total_price, this.ctx.tender.info.decimal.tp);
                 if (d.tp) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
                 if (d.real_time) nd.real_time = d.real_time;
@@ -104,6 +105,7 @@ module.exports = app => {
                     nd.name = d.name;
                 }
                 if (d.order !== undefined) nd.order = d.order;
+                if (d.o_type !== undefined) nd.o_type = d.o_type;
                 if (d.total_price !== undefined) {
                     if (od.pre_used) throw '往期已使用,不可修改金额';
                     nd.total_price = this.ctx.helper.round(d.total_price, this.ctx.tender.info.decimal.tp);

+ 7 - 3
app/service/tender.js

@@ -348,11 +348,15 @@ module.exports = app => {
             return result;
         }
 
-        async checkTender(tid) {
-            if (this.ctx.tender) return;
+        async getCheckTender(tid) {
             const tender = await this.ctx.service.tender.getTender(tid);
             tender.info = await this.ctx.service.tenderInfo.getTenderInfo(tid);
-            this.ctx.tender = tender;
+            return tender;
+        }
+
+        async checkTender(tid) {
+            if (this.ctx.tender) return;
+            this.ctx.tender = this.getCheckTender(tid);
         }
 
         async setTenderType(tender, type) {

+ 2 - 1
app/view/change/info.ejs

@@ -134,7 +134,7 @@
                     <% } %>
                     <div class="d-inline-block">
                         <div class="custom-control custom-checkbox" style="line-height: normal;">
-                            <input type="checkbox" class="custom-control-input change-detail-checkbox" id="customCheck1" checked>
+                            <input type="checkbox" class="custom-control-input change-detail-checkbox" id="customCheck1">
                             <label class="custom-control-label" for="customCheck1">变更详情</label>
                         </div>
                     </div>
@@ -735,6 +735,7 @@
     let table = '';
     const totalPriceUnit = '<%- tpUnit %>';
     const unitPriceUnit = '<%- upUnit %>';
+    const accountId = '<%- uid %>';
     autoFlashHeight();
 </script>
 <script src="/public/js/datatable/jquery.dataTables.min.js"></script>

+ 1 - 1
app/view/dashboard/index.ejs

@@ -168,7 +168,7 @@
                                                         <a href="/tender/<%- nl.id %>"><%- nl.name %></a> 台帐<%- acLedger.statusString[nl.status]%>
                                                     </div>
                                                 </div>
-                                                <p class="mt-1 mb-0"><%- nl.lu_name %><small class="ml-1 text-muted"><%- (nl.lu_role ? '- ' + nl.role : '') %></small>
+                                                <p class="mt-1 mb-0"><%- nl.lu_name %><small class="ml-1 text-muted"><%- (nl.lu_role ? '- ' + nl.lu_role : '') %></small>
                                                     <span class="pull-right text-muted"><%- nl.end_time.toLocaleString() %></span>
                                                 </p>
                                             </div>

+ 7 - 1
app/view/layout/page.ejs

@@ -1,3 +1,9 @@
+<style>
+    ul.pagination {
+        text-align: center!important;
+        justify-content: center!important;
+    }
+</style>
 <nav aria-label="Page navigation example">
     <ul class="pagination pagination-sm"></ul>
 </nav>
@@ -97,4 +103,4 @@
     } else {
         $(".pagination").hide();
     }
-</script>
+</script>

+ 1 - 1
app/view/material/info.ejs

@@ -31,7 +31,7 @@
                     <div class="bc-bar mb-1">
                         <div class="input-group input-group-sm ">
                             <div class="input-group-prepend">
-                                <span class="input-group-text" id="basic-addon1">增税税率</span>
+                                <span class="input-group-text" id="basic-addon1">增税税率</span>
                             </div>
                             <select class="form-control form-control-sm col-1" id="changeRate">
                                 <% if (!material.readOnly) { %>

+ 18 - 1
app/view/report/index.ejs

@@ -133,6 +133,15 @@
                                     </button>
                                 </div>
                             </div>
+                            <div class="panel" id="pnl_gather_select" style="display: none;">
+                                <div class="panel-body">
+                                    <div class="btn-group" role="group">
+                                        <button class="btn btn-primary btn-sm" type="button" data-toggle="modal" data-target="#gather-select">
+                                            选择标段<br><span class="badge badge-light" id="gather-select-count">5</span>
+                                        </button>
+                                    </div>
+                                </div>
+                            </div>
                         </div>
                     </div>
                     <div class="sjs-height-4">
@@ -174,6 +183,14 @@
         }
     });
 </script>
+<script>
+    const tenders = JSON.parse('<%- JSON.stringify(tenderList) %>');
+    const category = JSON.parse('<%- JSON.stringify(categoryData) %>');
+    const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
+    $(document).ready(() => {
+        rptCustomObj.initTenderTree(tenders, category);
+    });
+</script>
 
 <script type="text/javascript">  autoFlashHeight();</script>
 <script type="text/javascript" src="/public/jspdf/jspdf.min.js"></script>
@@ -199,9 +216,9 @@
 <script type="text/javascript" src="/public/report/js/jpc_output.js"></script>
 <script type="text/javascript" src="/public/report/js/rpt_print.js"></script>
 <script type="text/javascript" src="/public/report/js/rpt_signature.js"></script>
-<script type="text/javascript" src="/public/report/js/rpt_custom.js"></script>
 <script type="text/javascript" src="/public/report/js/rpt_jspdf.js"></script>
 
+
 <script type="text/javascript">
     const TOP_TREE_NODES = <%- rpt_tpl_data %>;
     let CUST_CFG = <%- cust_cfg %>;

+ 48 - 0
app/view/report/rpt_all_popup.ejs

@@ -269,6 +269,54 @@
         </div>
     </div>
 </div>
+<!--选择标段-->
+<div class="modal fade" id="gather-select" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="gather-select-title">选择标段</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-6">
+                        <h5>可选标段 </h5>
+                        <div class="modal-height-300" id="gather-source-spread" style="height: 330px">
+                        </div>
+                    </div>
+                    <div class="col-6">
+                        <h5>已选标段 </h5>
+                        <div class="modal-height-300" id="gather-result-spread">
+                        </div>
+                        <div class="mt-1" id="gather-by-month" style="width: 60%">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">汇总年月</span>
+                                </div>
+                                <input id="gather-month" class="datepicker-here form-control form-control-sm" auto-close="true" autocomplete="off" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" autocomplete="off">
+                            </div>
+                        </div>
+                        <div id="gather-by-zone">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">汇总周期</span>
+                                </div>
+                                <input id="gather-zone" class="datepicker-here form-control mt-0" placeholder="点击选择周期" data-range="true" data-multiple-dates-separator=" - "  data-min-view="months" data-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" autocomplete="off">
+                            </div>
+                        </div>
+                    </div>
+                    <div class="text-danger text-center ml-3" id="gather-hint">我是提示呀</div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button class="btn btn-sm btn-primary" id="gather-select-ok" onclick="rptCustomObj.resetGatherSelect(this)">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
 
 <script>
     zTreeOprObj.getCustomerCfg();

+ 1 - 0
app/view/tender/detail.ejs

@@ -99,6 +99,7 @@
                                 <a href="#bd-set-4" data-toggle="modal" data-target="#bd-set-4" class="btn btn-sm btn-outline-primary">合同参数</a>
                                 <a href="#bd-set-5" data-toggle="modal" data-target="#bd-set-5" class="btn btn-sm btn-outline-primary">显示设置</a>
                                 <a href="#bd-set-6" data-toggle="modal" data-target="#bd-set-6" class="btn btn-sm btn-outline-primary">章节设置</a>
+                                <a href="#bd-set-7" data-toggle="modal" data-target="#bd-set-7" class="btn btn-sm btn-outline-primary">付款账号</a>
                             </div>
                         </div>
                     </div>

+ 114 - 1
app/view/tender/detail_modal.ejs

@@ -532,6 +532,40 @@
         </div>
     </div>
 </div>
+<!--标段设置-付款账号-->
+<div class="modal fade" id="bd-set-7" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">付款账号</h5>
+            </div>
+            <div class="modal-body">
+                <div class="sjs-option-height">
+                    <table class="table table-bordered">
+                        <tr><th colspan="2" class="text-center">工程款账户</th></tr>
+                        <tr><td>开户名称</td><td><input type="text" id="project-name" class="form-control form-control-sm"></td></tr>
+                        <tr><td>开户银行</td><td><input type="text" id="project-bank" class="form-control form-control-sm"></td></tr>
+                        <tr><td>开户账号</td><td><input type="text" id="project-account" class="form-control form-control-sm"></td></tr>
+                        <tr><td>分账划拨比例(%)</td><td><input type="text" id="project-rate" class="form-control form-control-sm"></td></tr>
+                        <tr><td>联系人</td><td><input type="text" id="project-contact" class="form-control form-control-sm"></td></tr>
+                        <tr><td>联系电话</td><td><input type="text" id="project-phone" class="form-control form-control-sm"></td></tr>
+                        <tr><th colspan="2" class="text-center">农民工工资专用账户</th></tr>
+                        <tr><td>开户名称</td><td><input type="text" id="worker-name" class="form-control form-control-sm"></td></tr>
+                        <tr><td>开户银行</td><td><input type="text" id="worker-bank" class="form-control form-control-sm"></td></tr>
+                        <tr><td>开户账号</td><td><input type="text" id="worker-account" class="form-control form-control-sm"></td></tr>
+                        <tr><td>分账划拨比例(%)</td><td><input type="text" id="worker-rate" class="form-control form-control-sm"></td></tr>
+                        <tr><td>联系人</td><td><input type="text" id="worker-contact" class="form-control form-control-sm"></td></tr>
+                        <tr><td>联系电话</td><td><input type="text" id="worker-phone" class="form-control form-control-sm"></td></tr>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" onclick="post7()" >确认修改</button>
+            </div>
+        </div>
+    </div>
+</div>
 <script>
     let property = JSON.parse('<%- JSON.stringify(tenderInfo) %>');
     let ledgerChecked = <%- tender.ldeger_status === audit.ledger.status.checked %>;
@@ -612,6 +646,7 @@
         $('#start-date').val(property.tech_param.startDate);
         $('#plan-end-date').val(property.tech_param.planEndDate);
     }
+
     $('#bd-set-1').on('show.bs.modal', function () {
         loadCommonProperty();
     })
@@ -1144,4 +1179,82 @@
             $('#bd-set-6').modal('hide');
         });
     }
-</script>
+
+    // 属性
+    function loadPayProperty () {
+        // 付款账号
+        $('#project-name').val(property.pay_account.project.name);
+        $('#project-bank').val(property.pay_account.project.bank);
+        $('#project-account').val(property.pay_account.project.account);
+        $('#project-rate').val(property.pay_account.project.rate);
+        $('#project-contact').val(property.pay_account.project.contact);
+        $('#project-phone').val(property.pay_account.project.phone);
+
+        $('#worker-name').val(property.pay_account.worker.name);
+        $('#worker-bank').val(property.pay_account.worker.bank);
+        $('#worker-account').val(property.pay_account.worker.account);
+        $('#worker-rate').val(property.pay_account.worker.rate);
+        $('#worker-contact').val(property.pay_account.worker.contact);
+        $('#worker-phone').val(property.pay_account.worker.phone);
+    }
+    $('#bd-set-7').on('show.bs.modal', function () {
+        loadPayProperty();
+    })
+    function post7 () {
+        let flag = false;
+        const rate_reg = /^(([1-9][0-9]*)|(([0]\.\d{1,2}|[1-9][0-9]*\.\d{1,2}))|)$/;
+        const phone_reg = /^((0\d{2,3}-\d{7,8})|(1[3456789]\d{9}))$/;
+
+        const project_rate = $('#project-rate').val();
+        if (!rate_reg.test(project_rate) && project_rate !== '') {
+            toastr.error('工程款账户分账划拨比例数字格式有误且最多保留2位小数');
+            flag = true;
+        }
+
+        const project_phone = $('#project-phone').val();
+        if (!phone_reg.test(project_phone) && project_phone !== '') {
+            toastr.error('工程款账户联系电话格式有误');
+            flag = true;
+        }
+
+        const worker_rate = $('#worker-rate').val();
+        if (!rate_reg.test(worker_rate) && worker_rate !== '') {
+            toastr.error('农民工工资专用账户分账划拨比例数字格式有误且最多保留2位小数');
+            flag = true;
+        }
+
+        const worker_phone = $('#worker-phone').val();
+        if (!phone_reg.test(worker_phone) && worker_phone !== '') {
+            toastr.error('农民工工资专用账户联系电话格式有误');
+            flag = true;
+        }
+        if (flag) {
+            return false;
+        }
+        const prop = {
+            pay_account: {
+                project: {
+                    name: $('#project-name').val(),
+                    bank: $('#project-bank').val(),
+                    account: $('#project-account').val(),
+                    rate: $('#project-rate').val(),
+                    contact: $('#project-contact').val(),
+                    phone: $('#project-phone').val(),
+                },
+                worker: {
+                    name: $('#worker-name').val(),
+                    bank: $('#worker-bank').val(),
+                    account: $('#worker-account').val(),
+                    rate: $('#worker-rate').val(),
+                    contact: $('#worker-contact').val(),
+                    phone: $('#worker-phone').val(),
+                },
+            },
+        };
+        const tenderId = window.location.pathname.split('/')[2];
+        postData('/tender/' + tenderId + '/save', prop, function (data) {
+            property.pay_account = data.pay_account;
+            $('#bd-set-7').modal('hide');
+        });
+    }
+</script>

+ 78 - 0
builder_report_index_define.js

@@ -76,6 +76,7 @@ const stage_bonus = {
         { name: '排序', field: 'order', type: dataType.int },
         { name: '编号', field: 'code', type: dataType.str},
         { name: '发文单位', field: 'doc_co', type: dataType.str},
+        { name: '类型', field: 'b_type', type: dataType.str },
     ],
 };
 const stage_other = {
@@ -102,6 +103,7 @@ const stage_other = {
         { name: '排序', field: 'order', type: dataType.int },
         { name: '往期是否已用', field: 'pre_used', type: dataType.int },
         { name: '截止上期-金额', field: 'pre_tp', type: dataType.currency, tag: { type: 'tp' } },
+        { name: '类型', field: 'o_type', type: dataType.str },
     ],
 };
 // 变更令
@@ -424,6 +426,81 @@ const stage_im_tz_bills = {
     ],
 };
 
+const gather_stage_bills = {
+    name: '汇总-清单数据表(mem_gather_stage_bills)',
+    remark: '',
+    id: 33,
+    key: 'mem_gather_stage_bills',
+    prefix: '汇总-清单数据',
+    cols: [
+        { name: '项目节编号', field: 'code', type: dataType.str },
+        { name: '清单编号', field: 'b_code', type: dataType.str },
+        { name: '名称', field: 'name', type: dataType.str },
+        { name: '单位', field: 'unit', type: dataType.str },
+        { name: '单价', field: 'unit_price', type: dataType.currency },
+
+        { name: '标段id', field: 't_id', type: dataType.int },
+        { name: '标段-名称', field: 't_name', type: dataType.int },
+
+        { name: '(标段)台账-数量', field: 't_qty', type: dataType.currency },
+        { name: '(标段)台账-金额', field: 't_tp', type: dataType.currency },
+
+        { name: '(标段)本期-合同-数量', field: 't_contract_qty', type: dataType.currency },
+        { name: '(标段)本期-合同-金额', field: 't_contract_tp', type: dataType.currency },
+        { name: '(标段)本期-变更-数量', field: 't_qc_qty', type: dataType.currency },
+        { name: '(标段)本期-变更-金额', field: 't_qc_tp', type: dataType.currency },
+        { name: '(标段)本期-完成-数量', field: 't_gather_qty', type: dataType.currency },
+        { name: '(标段)本期-完成-金额', field: 't_gather_tp', type: dataType.currency },
+
+        { name: '(标段)截止上期-合同-数量', field: 't_pre_contract_qty', type: dataType.currency },
+        { name: '(标段)截止上期-合同-金额', field: 't_pre_contract_tp', type: dataType.currency },
+        { name: '(标段)截止上期-变更-数量', field: 't_pre_qc_qty', type: dataType.currency },
+        { name: '(标段)截止上期-变更-金额', field: 't_pre_qc_tp', type: dataType.currency },
+        { name: '(标段)截止上期-完成-数量', field: 't_pre_gather_qty', type: dataType.currency },
+        { name: '(标段)截止上期-完成-金额', field: 't_pre_gather_tp', type: dataType.currency },
+
+        { name: '(标段)截止本期-合同-数量', field: 't_end_contract_qty', type: dataType.currency },
+        { name: '(标段)截止本期-合同-金额', field: 't_end_contract_tp', type: dataType.currency },
+        { name: '(标段)截止本期-变更-数量', field: 't_end_qc_qty', type: dataType.currency },
+        { name: '(标段)截止本期-变更-金额', field: 't_end_qc_tp', type: dataType.currency },
+        { name: '(标段)截止本期-完成-数量', field: 't_end_gather_qty', type: dataType.currency },
+        { name: '(标段)截止本期-完成-金额', field: 't_end_gather_tp', type: dataType.currency },
+
+        { name: '(合计)台账-数量', field: 's_ty', type: dataType.currency },
+        { name: '(合计)台账-金额', field: 's_tp', type: dataType.currency },
+
+        { name: '(合计)本期-合同-数量', field: 's_contract_qty', type: dataType.currency },
+        { name: '(合计)本期-合同-金额', field: 's_contract_tp', type: dataType.currency },
+        { name: '(合计)本期-变更-数量', field: 's_qc_qty', type: dataType.currency },
+        { name: '(合计)本期-变更-金额', field: 's_qc_tp', type: dataType.currency },
+        { name: '(合计)本期-完成-数量', field: 's_gather_qty', type: dataType.currency },
+        { name: '(合计)本期-完成-金额', field: 's_gather_tp', type: dataType.currency },
+
+        { name: '(合计)截止上期-合同-数量', field: 's_pre_contract_qty', type: dataType.currency },
+        { name: '(合计)截止上期-合同-金额', field: 's_pre_contract_tp', type: dataType.currency },
+        { name: '(合计)截止上期-变更-数量', field: 's_pre_qc_qty', type: dataType.currency },
+        { name: '(合计)截止上期-变更-金额', field: 's_pre_qc_tp', type: dataType.currency },
+        { name: '(合计)截止上期-完成-数量', field: 's_pre_gather_qty', type: dataType.currency },
+        { name: '(合计)截止上期-完成-金额', field: 's_pre_gather_tp', type: dataType.currency },
+
+        { name: '(合计)截止本期-合同-数量', field: 's_end_contract_qty', type: dataType.currency },
+        { name: '(合计)截止本期-合同-金额', field: 's_end_contract_tp', type: dataType.currency },
+        { name: '(合计)截止本期-变更-数量', field: 's_end_qc_qty', type: dataType.currency },
+        { name: '(合计)截止本期-变更-金额', field: 's_end_qc_tp', type: dataType.currency },
+        { name: '(合计)截止本期-完成-数量', field: 's_end_gather_qty', type: dataType.currency },
+        { name: '(合计)截止本期-完成-金额', field: 's_end_gather_tp', type: dataType.currency },
+
+        { name: '(特殊1-需替换key1)台账-数量', field: 'ts_key1_qty', type: dataType.currency },
+        { name: '(特殊1-需替换key1)台账-金额', field: 'ts_key1_tp', type: dataType.currency },
+
+        { name: '(特殊2-需替换key2)台账-数量', field: 'ts_key2_qty', type: dataType.currency },
+        { name: '(特殊2-需替换key2)台账-金额', field: 'ts_key2_tp', type: dataType.currency },
+
+        { name: '(特殊3-需替换key3)台账-数量', field: 'ts_key3_qty', type: dataType.currency },
+        { name: '(特殊3-需替换key3)台账-金额', field: 'ts_key3_tp', type: dataType.currency },
+    ],
+};
+
 const recursiveMkdirSync = async function(pathName) {
     if (!fs.existsSync(pathName)) {
         const upperPath = path.dirname(pathName);
@@ -519,6 +596,7 @@ const defines = [stage_jgcl, stage_bonus, stage_other,
     stage_pos, stage_pos_compare,
     stage_pay,
     stage_im_zl, stage_im_tz, stage_im_tz_bills,
+    gather_stage_bills,
 ];
 for (const d of defines) {
     exportTableDefine(d);

+ 17 - 0
config/web.js

@@ -549,6 +549,23 @@ const JsFiles = {
                 ],
                 mergeFile: 'gather_stage',
             },
+        },
+        report: {
+            main: {
+                files: [
+                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/decimal.min.js",
+                    "/public/js/moment/moment.min.js",
+                ],
+                mergeFiles: [
+                    "/public/js/zh_calc.js",
+                    "/public/js/path_tree.js",
+                    "/public/js/spreadjs_rela/spreadjs_zh.js",
+                    "/public/js/shares/tenders2tree.js",
+                    "/public/report/js/rpt_custom.js",
+                ],
+                mergeFile: 'report_main',
+            }
         }
     }
 

+ 9 - 0
sql/update.sql

@@ -0,0 +1,9 @@
+ALTER TABLE `zh_tender_info` ADD `pay_account` VARCHAR(5000) NULL DEFAULT NULL COMMENT '付款账号' AFTER `chapter`;
+
+ALTER TABLE `zh_stage_bonus`
+ADD COLUMN `b_type`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '类型' AFTER `name`;
+
+ALTER TABLE `zh_stage_other`
+ADD COLUMN `o_type`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '类型' AFTER `name`;
+
+

+ 15 - 0
test/app/lib/rpt_data_analysis.test.js

@@ -395,6 +395,21 @@ describe('test/app/service/report_memory.test.js', () => {
         reportDataAnalysis.analysisObj.filter.fun(ctx, data, [], m1);
         result = ctx.helper._.map(data.test, 'order');
         assert(ctx.helper._.isEqual(result, [8]));
+
+        // mix2
+
+        const m2 = {
+            table: 'test',
+            condition: [
+                { field: "c", type: "bool", value: 'true'},
+                { field: "b", type: "num", operate: 'non-zero', rela: "or"},
+                { field: "a", type: "str", operate: 'enum', value: ['aaa', 'ddd'], rela: "and"},
+            ],
+        };
+        data = JSON.parse(JSON.stringify(orgData));
+        reportDataAnalysis.analysisObj.filter.fun(ctx, data, [], m2, true);
+        result = ctx.helper._.map(data.test, 'order');
+        assert(ctx.helper._.isEqual(result, [1, 2, 3, 8]));
     });
     // it('test gatherChapter custom', function* () {
     //     const ctx = app.mockContext(mockData);

+ 64 - 0
test/app/service/rpt_gather_memory.test.js

@@ -0,0 +1,64 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2020/4/17
+ * @version
+ */
+
+const { app, assert } = require('egg-mock/bootstrap');
+const path = require('path');
+let savePath;
+const mockData = {};
+
+describe('test/app/service/rpt_gather_memory.test.js', () => {
+    // 准备测试数据
+    before(function* () {
+        const ctx = app.mockContext();
+        savePath = path.join(ctx.app.baseDir,'report_temp');
+        const postData = {
+            account: '734406061@qq.com',
+            project: 'T201711273363',
+            project_password: 'mai654321',
+        };
+        ctx.session = {};
+        const loginResult = yield ctx.service.projectAccount.accountLogin(postData, 2);
+        assert(loginResult);
+        mockData.session = ctx.session;
+    });
+    // 期部位明细数据
+    it('test getGatherStageBills - month', function* () {
+        const ctx = app.mockContext(mockData);
+
+        const select = {
+            tenders: [{tid: 2256}, {tid: 2257}, {tid: 2258, gs: true}],
+            type: 'month',
+            month: '2020-01',
+        };
+        const define = {
+            enable: true,
+            setting: {
+                title: '请选择汇总的标段',
+                type: 'month',
+                special: [
+                    {"title": "批复概算", "key": "gs"}
+                ]
+            }
+        };
+
+        const mem_gather_stage_bills = yield ctx.service.rptGatherMemory.getGatherStageBills([], define, select);
+        yield ctx.helper.saveBufferFile(JSON.stringify(mem_gather_stage_bills, "", "\t"), path.join(savePath, 'mem_gather_stage_bills.json'));
+
+        const reportDataAnalysis = require('../../../app/lib/rpt_data_analysis');
+        // 配合部位明细
+        const reportData = {mem_gather_stage_bills: mem_gather_stage_bills};
+        reportDataAnalysis.analysisObj.gatherSelectConverse.fun(ctx, reportData, [], {table: ["mem_gather_stage_bills"]}, {
+            cDefine: { gather_select: select },
+            tplDefine: {gather_select: define },
+        });
+        yield ctx.helper.saveBufferFile(JSON.stringify(reportData, "", "\t"), path.join(savePath, 'mem_gather_stage_bills_coverse.json'));
+        assert(reportData.mem_gather_stage_bills.length === mem_gather_stage_bills.length * 2);
+    });
+});