Переглянути джерело

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

TonyKang 4 роки тому
батько
коміт
d11b3836ef
45 змінених файлів з 1016 додано та 284 видалено
  1. 25 18
      app/const/audit.js
  2. 1 0
      app/const/report.js
  3. 2 2
      app/const/sms_type.js
  4. 4 3
      app/controller/material_controller.js
  5. 1 1
      app/controller/measure_controller.js
  6. 43 1
      app/controller/profile_controller.js
  7. 8 4
      app/controller/report_controller.js
  8. 2 7
      app/controller/stage_controller.js
  9. 7 2
      app/lib/bills_pos_convert.js
  10. 46 0
      app/lib/rpt_data_analysis.js
  11. 7 2
      app/middleware/material_check.js
  12. BIN
      app/public/images/wechat.png
  13. 1 1
      app/public/js/ledger.js
  14. 1 0
      app/public/js/material_audit.js
  15. 1 1
      app/public/js/measure_compare.js
  16. 4 4
      app/public/js/measure_stage.js
  17. 2 2
      app/public/js/revise.js
  18. 1 1
      app/public/js/revise_history.js
  19. 19 13
      app/public/js/stage.js
  20. 1 1
      app/public/js/stage_compare.js
  21. 4 3
      app/public/js/stage_im.js
  22. 78 2
      app/public/report/js/rpt_custom.js
  23. 10 0
      app/public/report/js/rpt_main.js
  24. 1 0
      app/reports/rpt_component/jpc_value_define.js
  25. 2 0
      app/router.js
  26. 52 12
      app/service/material_audit.js
  27. 10 2
      app/service/project_account.js
  28. 8 2
      app/service/report.js
  29. 8 1
      app/service/rpt_custom_define.js
  30. 254 0
      app/service/rpt_stage_sum_memory.js
  31. 225 149
      app/view/material/audit_modal.ejs
  32. 2 1
      app/view/material/file.ejs
  33. 4 3
      app/view/profile/sms.ejs
  34. 68 0
      app/view/profile/wechat.ejs
  35. 22 0
      app/view/profile/wechat_modal.ejs
  36. 9 0
      app/view/report/index.ejs
  37. 29 0
      app/view/report/rpt_all_popup.ejs
  38. 2 2
      app/view/stage/audit_modal.ejs
  39. 6 2
      app/view/stage/bwtz.ejs
  40. 2 1
      app/view/stage/index.ejs
  41. 1 1
      app/view/stage/report_modal.ejs
  42. 32 39
      builder_report_index_define.js
  43. 2 1
      config/config.default.js
  44. 5 0
      config/menu.js
  45. 4 0
      sql/update.sql

+ 25 - 18
app/const/audit.js

@@ -8,7 +8,7 @@
  * @version
  */
 // 台账审批流程
-const ledger = (function () {
+const ledger = (function() {
     const status = {
         uncheck: 1, // 待上报
         checking: 2, // 待审批|审批中
@@ -41,11 +41,11 @@ const ledger = (function () {
     auditStringClass[status.checking] = 'text-warning';
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
-    return { status, statusString, statusClass, auditString, auditStringClass }
+    return { status, statusString, statusClass, auditString, auditStringClass };
 })();
 
 // 台账修订 审批流程
-const revise = (function () {
+const revise = (function() {
     const status = {
         uncheck: 1, // 待上报
         checking: 2, // 待审批|审批中
@@ -93,7 +93,7 @@ const revise = (function () {
 })();
 
 // 期审批流程
-const stage = (function () {
+const stage = (function() {
     // 流程状态
     const status = {
         uncheck: 1, // 待上报
@@ -184,18 +184,18 @@ const stage = (function () {
         org: 1,
         pre: 2,
     };
-    return { status, statusString, statusClass, statusButton, statusButtonClass, auditString, auditStringClass, auditProgress, auditProgressClass, backType, timesLen: 100 }
+    return { status, statusString, statusClass, statusButton, statusButtonClass, auditString, auditStringClass, auditProgress, auditProgressClass, backType, timesLen: 100 };
 })();
 
 
 // 变更令状态
 const status = {
-    uncheck: 1,     // 待上报
-    checking: 2,    // 审批中
-    checked: 3,     // 审批完成
+    uncheck: 1, // 待上报
+    checking: 2, // 审批中
+    checked: 3, // 审批完成
     // checkNo: 4,     // 审批终止
-    back: 5,        // 重新上报
-    backnew: 6,     // 退回
+    back: 5, // 重新上报
+    backnew: 6, // 退回
 };
 const statusButton = [];
 statusButton[status.uncheck] = '上报';
@@ -233,13 +233,13 @@ statusClass[status.backnew] = 'text-warning';
 
 // 变更令审批人状态
 const auditStatus = {
-    uncheck: 1,     // 待审批
-    checking: 2,    // 审批中或者原报人待上报
-    checked: 3,     // 审批通过或者原报人上报完成
+    uncheck: 1, // 待审批
+    checking: 2, // 审批中或者原报人待上报
+    checked: 3, // 审批通过或者原报人上报完成
     // checkNo: 4,     // 审批终止
-    back: 5,       // 退回到原报人重新上报
-    backnew: 6,    // 退回到上一个审批人
-    checkAgain: 7,    // 重新审批
+    back: 5, // 退回到原报人重新上报
+    backnew: 6, // 退回到上一个审批人
+    checkAgain: 7, // 重新审批
 };
 
 const auditStatusString = [];
@@ -279,12 +279,14 @@ filter.statusString[filter.status.checked] = '已完成';
 // filter.statusString[filter.status.checkNo] = '终止';
 
 // 材料调差审批流程
-const material = (function () {
+const material = (function() {
     const status = {
         uncheck: 1, // 待上报
         checking: 2, // 待审批|审批中
         checked: 3, // 审批通过
-        checkNo: 4, // 审批退回
+        checkNo: 4, // 审批退回原报
+        checkNoPre: 5, // 审批退回上一人
+        checkAgain: 6, // 终审退回  --该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
     };
     // 流程状态提示
     const statusString = [];
@@ -292,12 +294,17 @@ const material = (function () {
     statusString[status.checking] = '审批中';
     statusString[status.checked] = '审批通过';
     statusString[status.checkNo] = '审批退回';
+    statusString[status.checkNoPre] = '审批退回';
+    statusString[status.checkAgain] = '重新审批';
+
     // 流程状态样式
     const statusClass = [];
     statusClass[status.uncheck] = '';
     statusClass[status.checking] = '';
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-warning';
+    statusClass[status.checkNoPre] = 'text-warning';
+    statusClass[status.checkAgain] = 'text-warning';
 
     // 按钮
     const statusButton = [];

+ 1 - 0
app/const/report.js

@@ -16,6 +16,7 @@ rptCustomType[JV.NODE_CUS_COMPARE_SELECT] = 3;
 
 const rptDataType = {};
 rptDataType[JV.NODE_CUS_MATERIAL_SELECT] = 1;
+rptDataType[JV.NODE_CUS_STAGE_SELECT] = 2;
 
 module.exports = {
     rptCustomType,

+ 2 - 2
app/const/sms_type.js

@@ -1,7 +1,7 @@
 'use strict';
 
 /**
- * 短信通知类型
+ * 短信、微信通知类型
  *
  * @author Ellisran
  * @date 2019/8/20
@@ -11,7 +11,7 @@ const smsConst = {
     TZ: 'TZ',
     JL: 'JL',
     BG: 'BG',
-    XD: 'XD'
+    XD: 'XD',
 };
 const judgeConst = {
     approval: 1,

+ 4 - 3
app/controller/material_controller.js

@@ -203,8 +203,8 @@ module.exports = app => {
             const times = ctx.material.status === auditConst.status.checkNo ? ctx.material.times - 1 : ctx.material.times;
             ctx.material.user = await ctx.service.projectAccount.getAccountInfoById(ctx.material.user_id);
             ctx.material.auditHistory = [];
-            if (ctx.material.times >= 1) {
-                for (let i = 1; i <= times; i++) {
+            if (ctx.material.times > 1) {
+                for (let i = 1; i < ctx.material.times; i++) {
                     ctx.material.auditHistory.push(await ctx.service.materialAudit.getAuditors(ctx.material.id, i));
                 }
             }
@@ -304,7 +304,6 @@ module.exports = app => {
                 renderData.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 // 获取所选期数据并合并相加同类清单项
                 renderData.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, ctx.material.stage_id);
-                console.log('curLedgerData:', renderData.curLedgerData);
                 renderData.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, ctx.material.stage_id);
                 await this.layout('material/list.ejs', renderData, 'material/list_modal.ejs');
             } catch (err) {
@@ -326,6 +325,8 @@ module.exports = app => {
                 // const searchsql = { tid: ctx.tender.id };
                 renderData.fileList = await ctx.service.materialFile.getAllMaterialFiles(ctx.tender.id, ctx.material.id);
                 renderData.auditors = ctx.material.auditors.map(audit => audit.aid);
+                // renderData.auditors = ctx.material.auditors2.map(audit => audit.aid);
+                renderData.report_id = ctx.material.user_id;
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.file);
                 await this.layout('material/file.ejs', renderData, 'material/file_modal.ejs');
             } catch (err) {

+ 1 - 1
app/controller/measure_controller.js

@@ -247,7 +247,7 @@ module.exports = app => {
                 if (ctx.request.body.confirm !== undefined && ctx.request.body.confirm !== '确认删除本期') {
                     throw '请输入正确的文本信息';
                 }
-                if (stageInfo && (ctx.session.sessionUser.accountId !== stageInfo.user_id || (ctx.session.sessionUser.is_admin && ctx.request.body.confirm === '确认删除本期')) && stage_highOrder === stageInfo.order) {
+                if (stageInfo && (ctx.session.sessionUser.accountId === stageInfo.user_id || (ctx.session.sessionUser.is_admin && ctx.request.body.confirm === '确认删除本期')) && stage_highOrder === stageInfo.order) {
                     const result = await ctx.service.stage.deleteStage(stage_id);
                     if (!result) {
                         throw '删除计量期失败,请重试';

+ 43 - 1
app/controller/profile_controller.js

@@ -212,7 +212,7 @@ module.exports = app => {
         async smsType(ctx) {
             try {
                 const sessionUser = ctx.session.sessionUser;
-                const result = await ctx.service.projectAccount.smsTypeSet(sessionUser.accountId, ctx.request.body);
+                const result = await ctx.service.projectAccount.noticeTypeSet(sessionUser.accountId, ctx.request.body);
 
                 if (!result) {
                     throw '修改通知类型失败!';
@@ -359,6 +359,48 @@ module.exports = app => {
             };
             await this.layout('profile/safe.ejs', renderData);
         }
+
+        /**
+         * 微信通知
+         *
+         * @param {object} ctx - egg全局变量
+         * @return {void}
+         */
+        async wechat(ctx) {
+            // 获取当前用户数据
+            const sessionUser = ctx.session.sessionUser;
+
+            // 获取账号数据
+            const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId });
+
+            const renderData = {
+                accountData,
+                smsType: smsTypeConst.type,
+            };
+            await this.layout('profile/wechat.ejs', renderData, 'profile/wechat_modal.ejs');
+        }
+
+        /**
+         * 微信解绑
+         *
+         * @param {object} ctx - egg全局变量
+         * @return {void}
+         */
+        async removeWechat(ctx) {
+            try {
+                const sessionUser = ctx.session.sessionUser;
+                const result = await ctx.service.projectAccount.bindWx(sessionUser.accountId, null, null);
+
+                if (!result) {
+                    throw '解绑微信失败!';
+                }
+                this.setMessage('微信解绑成功', this.messageType.SUCCESS);
+            } catch (error) {
+                console.log(error);
+                this.setMessage(error.toString(), this.messageType.ERROR);
+            }
+            ctx.redirect(ctx.request.header.referer);
+        }
     }
 
     return ProfileController;

+ 8 - 4
app/controller/report_controller.js

@@ -76,6 +76,8 @@ module.exports = app => {
                 }
                 customSelects.gather_select = await ctx.service.rptCustomDefine.getCustomSelectByRpt(cid,
                     reportConst.rptCustomType[JV.NODE_CUS_GATHER_SELECT], ctx.tender.id, -1);
+                customSelects.stage_select = await ctx.service.rptCustomDefine.getCustomSelectByRpt(cid,
+                    reportConst.rptCustomType[JV.NODE_CUS_STAGE_SELECT], ctx.tender.id, -1);
                 const dataSelects = {};
                 dataSelects.material_select = await ctx.service.rptCustomDefine.getDataSelectByRpt(cid,
                     reportConst.rptDataType[JV.NODE_CUS_MATERIAL_SELECT]);
@@ -164,6 +166,7 @@ module.exports = app => {
                     customSelects,
                     rptCustomType: reportConst.rptCustomType,
                     materialList,
+                    stages: stageList,
                     dataSelects
                 };
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
@@ -225,10 +228,11 @@ module.exports = app => {
             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 copyCustomSelect = this.ctx.helper.clone(customSelect);
-            if (!params.gather_select && copyCustomSelect) {
-                delete copyCustomSelect.gather_select;
-            }
+            if (!params.gather_select && copyCustomSelect) delete copyCustomSelect.gather_select;
+            if (!params.stage_select && copyCustomSelect) delete copyCustomSelect.stage_select;
+
             const pageRst = await getAllPagesCommon(ctx, rptTpl, params, JV.PAGING_OPTION_NORMAL, JV.OUTPUT_TYPE_NORMAL, this.app.baseDir, copyCustomSelect);
             // console.log(pageRst);
             // const roleRel = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_tpl_id)) : [];
@@ -629,7 +633,7 @@ async function getAllPagesCommon(ctx, rptTpl, params, option, outputType, baseDi
     // console.log(filter.tables);
     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');
+    //await ctx.helper.saveBufferFile(JSON.stringify(rawDataObj, '', '\t'), ctx.app.baseDir + '/mem.json');
     // console.log(rawDataObj);
     try {
         const printCom = JpcEx.createNew();

+ 2 - 7
app/controller/stage_controller.js

@@ -120,6 +120,7 @@ module.exports = app => {
          */
         async _getStageAuditViewData(ctx) {
             const times = ctx.stage.status === auditConst.status.checkNo ? ctx.stage.times - 1 : ctx.stage.times;
+
             ctx.stage.user = await ctx.service.projectAccount.getAccountInfoById(ctx.stage.user_id);
             ctx.stage.auditHistory = [];
             if (ctx.stage.times > 1) {
@@ -1003,7 +1004,6 @@ module.exports = app => {
         async checkAudit(ctx) {
             try {
                 this._checkStageCanModify(ctx);
-
                 if (!this.ctx.stage || (this.ctx.stage.status !== auditConst.status.checking && this.ctx.stage.status !== auditConst.status.checkNoPre)) {
                     throw '当前期数据有误';
                 }
@@ -1206,9 +1206,7 @@ module.exports = app => {
             if (ctx.stage.readOnly) {
                 if (ctx.stage.status === auditConst.status.checked) {
                     // 当前期状态为完成,且提交人是审核列表中的则可再次上传
-                    console.log('当前期审核通过!');
-                    if (ctx.stage.auditors.findIndex(auditor => auditor.aid === ctx.session.sessionUser.accountId) !== -1) {
-                        console.log('当前用户拥有上传权限!');
+                    if (ctx.stage.user_id === ctx.session.sessionUser.accountId || ctx.stage.auditors.findIndex(auditor => auditor.aid === ctx.session.sessionUser.accountId) !== -1) {
                         // 再次上传的图片要给个标识,方便给前端进行编辑操作
                         ctx.reUploadPermission = true;
                         return;
@@ -1273,7 +1271,6 @@ module.exports = app => {
                         filepath: path.join(dirName, fileName),
                     };
                     if (ctx.reUploadPermission) {
-                        console.log('111111111111111');
                         fileData.re_upload = 1;
                     }
                     const result = await ctx.service.stageAtt.save(parts.field, fileData, ctx.session.sessionUser.accountId);
@@ -1285,7 +1282,6 @@ module.exports = app => {
                     files.length !== 0 ? files.unshift(attData) : files.push(attData);
                     ++index;
                 }
-                console.log('files:', files);
                 responseData.data = files;
             } catch (err) {
                 this.log(err);
@@ -1350,7 +1346,6 @@ module.exports = app => {
             if (id) {
                 try {
                     const fileInfo = await ctx.service.stageAtt.getDataById(id);
-                    console.log(fileInfo);
                     if (fileInfo && Object.keys(fileInfo).length) {
                         fileInfo.filepath && (responseData.data = { filepath: fileInfo.filepath.slice(3) });
                     }

+ 7 - 2
app/lib/bills_pos_convert.js

@@ -97,7 +97,7 @@ class BillsPosConvert {
         node.pre_gather_qty = this.ctx.helper.add(node.pre_gather_qty, data.pre_gather_qty);
 
         node.real_qty = this.ctx.helper.add(node.real_qty, data.real_qty);
-        node.estimate_qty = this.ctx.helper.checkZero(node.real_qty)
+        node.estimate_qty = !this.ctx.helper.checkZero(node.real_qty)
             ? this.ctx.helper.sub(this.ctx.helper.sub(node.real_qty, node.quantity), node.end_qc_qty)
             : null;
     }
@@ -189,7 +189,7 @@ class BillsPosConvert {
     }
 
     _calculateChild(child) {
-        //const tpDecimal = this.ctx.tender.info.decimal.tp;
+        const tpDecimal = this.ctx.tender.info.decimal.tp;
 
         child.gather_qty = this.ctx.helper.add(child.contract_qty, child.qc_qty);
         child.pre_gather_qty = this.ctx.helper.add(child.pre_contract_qty, child.pre_qc_qty);
@@ -219,6 +219,9 @@ class BillsPosConvert {
         child.final_tp = this.ctx.helper.add(child.total_price, child.end_qc_tp);
         child.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(child.end_gather_tp, child.final_tp, 4), 100);
 
+        child.real_tp = this.ctx.helper.mul(child.real_qty, child.unit_price, tpDecimal);
+        child.estimate_tp = this.ctx.helper.mul(child.estimate_qty, child.unit_price, tpDecimal);
+
         child.bgl_code = this.ctx.helper._.uniq(this.ctx.helper._.map(child.changes, 'c_code')).join(';');
     }
     _calculateNode(node, children) {
@@ -233,6 +236,8 @@ class BillsPosConvert {
             node.end_contract_tp = this.ctx.helper.add(node.end_contract_tp, child.end_contract_tp);
             node.end_qc_tp = this.ctx.helper.add(node.end_qc_tp, child.end_qc_tp);
             node.end_gather_tp = this.ctx.helper.add(node.end_gather_tp, child.end_gather_tp);
+            node.real_tp = this.ctx.helper.add(node.real_tp, child.real_tp);
+            node.estimate_tp = this.ctx.helper.add(node.estimate_tp, child.estimate_tp);
         }
         node.final_tp = this.ctx.helper.add(node.total_price, node.end_qc_tp);
         node.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(node.end_gather_tp, node.final_tp, 4), 100);

+ 46 - 0
app/lib/rpt_data_analysis.js

@@ -1331,6 +1331,51 @@ const splitXmjCode = {
         }
     }
 };
+const stageSelectConverse = {
+    name: '交叉多期汇总数据',
+    hint: '需搭配 用户交互--单标段多期汇总选择 一起使用',
+    defaultSetting: {
+        table: ['mem_stage_sum_bills'],
+    },
+    _commonConverse: function (helper, data, stages) {
+        const result = [];
+        const reg = new RegExp('^t_[0-9]+_');
+        for (const s of stages) {
+            const curReg = new RegExp('^s_' + s + '_');
+            for (const [i, d] of data.entries()) {
+                const nd = { cross_index: i + 1};
+                for (const prop in d) {
+                    if (reg.test(prop)) {
+                        if (curReg.test(prop)) {
+                            nd[prop.replace(curReg, 's_')] = 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.stage_select;
+        if (!gsDefine || !gsDefine.enable || !gsDefine.setting || gsDefine.setting === '') return;
+        const gsCustom = csRela.cDefine ? csRela.cDefine.stage_select : null;
+
+        for (const t of options.table) {
+            switch (t) {
+                case 'mem_stage_sum_bills':
+                case 'mem_stage_sum_pay':
+                case 'mem_union_data':
+                    data[t] = this._commonConverse(ctx.helper, data[t], gsCustom.stages);
+                    break;
+            }
+        }
+    }
+};
 
 const analysisObj = {
     changeSort,
@@ -1346,6 +1391,7 @@ const analysisObj = {
     auditSelect,
     datetimeFormat,
     gatherSelectConverse,
+    stageSelectConverse,
     sortPos,
     unionPos,
     splitXmjCode,

+ 7 - 2
app/middleware/material_check.js

@@ -46,11 +46,16 @@ module.exports = options => {
 
             // 读取原报、审核人数据
             material.auditors = yield this.service.materialAudit.getAuditors(material.id, material.times);
+            // if (material.status > 1) {
+            //     material.auditors2 === 2 ? material.auditors : (yield this.service.materialAudit.getAuditors(material.id, material.times - 1));
+            // }
             material.curAuditor = yield this.service.materialAudit.getCurAuditor(material.id, material.times);
 
             // 权限相关
             // todo 校验权限 (标段参与人、分享)
-            const accountId = this.session.sessionUser.accountId, auditorIds = _.map(material.auditors, 'aid'), shareIds = [];
+            const accountId = this.session.sessionUser.accountId,
+                auditorIds = _.map(material.auditors, 'aid'),
+                shareIds = [];
             if (accountId === material.user_id) { // 原报
                 // if (material.curAuditor) {
                 //     material.readOnly = material.status === status.checking && material.curAuditor.user_id === accountId;
@@ -75,7 +80,7 @@ module.exports = options => {
                     material.curOrder = _.max(_.map(material.auditors, 'order'));
                 } else if (material.status === status.checkNo) {
                     const audit = this.service.materialAudit.getDataByCondition({
-                        mid: material.id, times: material.times, status: status.checkNo
+                        mid: material.id, times: material.times, status: status.checkNo,
                     });
                     material.curOrder = audit.order;
                 } else {

BIN
app/public/images/wechat.png


+ 1 - 1
app/public/js/ledger.js

@@ -814,6 +814,7 @@ $(document).ready(function() {
         },
         selectionChanged: function (e, info) {
             if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
+                SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
                 posOperationObj.loadCurPosData();
                 posSearch.search($('#pos-keyword').val());
             }
@@ -1525,7 +1526,6 @@ $(document).ready(function() {
         loadCurPosData: function () {
             //spreadJsObj.reinitSheet(posSpread.getActiveSheet());
             const node = treeOperationObj.getSelectNode(ledgerSpread.getActiveSheet());
-            SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
             if (node) {
                 const posData = pos.ledgerPos[itemsPre + node.id] || [];
                 SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), 'data', posData);

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

@@ -163,6 +163,7 @@ function checkAuditorFrom () {
 }
 // texterea换行
 function auditCheck(i) {
+    console.log('11111111111')
     const opinion = $('textarea[name="opinion"]').eq(i).val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
     $('textarea[name="opinion"]').eq(i).val(opinion);
     return true;

+ 1 - 1
app/public/js/measure_compare.js

@@ -160,7 +160,6 @@ $(document).ready(() => {
     }, null, true);
     function loadPosData(iRow) {
         const node = iRow ? billsSheet.zh_tree.nodes[iRow] : SpreadJsObj.getSelectObject(billsSheet);
-        SpreadJsObj.resetTopAndSelect(posSheet);
         if (node) {
             SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, cPos.getLedgerPos(node.id));
         } else {
@@ -174,6 +173,7 @@ $(document).ready(() => {
             if (info.oldSelections) {
                 const iOldRow = info.oldSelections[0].row;
                 if (iNewRow !== iOldRow) {
+                    SpreadJsObj.resetTopAndSelect(posSheet);
                     loadPosData(iNewRow);
                 }
             } else {

+ 4 - 4
app/public/js/measure_stage.js

@@ -39,14 +39,14 @@ $('a[data-target="#sp-list" ]').on('click', function () {
                    righthtml.push('<h5 class="card-title">');
                    righthtml.push('<i class="fa fa-play-circle fa-rotate-90 text-success"></i> '+ stageAuditor.name +' <small class="text-muted">'+ stageAuditor.role +'</small><span class="pull-right">原报</span></h5>');
                    righthtml.push('<div class="ml-3">');
-                   righthtml.push('<span class="text-success"><small>' + (ah[iA].begin_time ? moment(ah[iA].begin_time).format('YYYY-MM-DD') : '') + '</small> '+ (auditHistory.indexOf(ah) > 0 ? '重新' : '') + '上报</span></div></li>');
+                   righthtml.push('<span class="text-success"><small>' + (ah[iA].begin_time ? moment(ah[iA].begin_time).format('YYYY-MM-DD HH:mm:ss') : '') + '</small> '+ (auditHistory.indexOf(ah) > 0 ? '重新' : '') + '上报</span></div></li>');
                    righthtml.push('<li class="list-group-item">');
                    righthtml.push('<h5 class="card-title"><i class="fa '+ (iA === ah.length - 1 ? 'fa-stop-circle ' : 'fa-chevron-circle-down ') + auditConst.statusClass[ah[iA].status] +'"></i> '+ ah[iA].name +' <small class="text-muted">'+ ah[iA].role +'</small><span class="pull-right">' + (ah[iA].sort === ah[iA].max_sort ? '终' : transFormToChinese(ah[iA].sort)) + '审</span></h5>');
                    righthtml.push('<div class="ml-3">');
                    if (ah[iA].status !== auditConst.status.uncheck) {
                        let timeHtml = '';
                        if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre) {
-                           timeHtml = '<small>'+ (ah[iA].end_time ? moment(ah[iA].end_time).format('YYYY-MM-DD') : '') +'</small> ';
+                           timeHtml = '<small>'+ (ah[iA].end_time ? moment(ah[iA].end_time).format('YYYY-MM-DD HH:mm:ss') : '') +'</small> ';
                        }
                        righthtml.push('<span class="' + auditConst.statusClass[ah[iA].status] +'">'+ timeHtml + auditConst.statusString[ah[iA].status] + (ah[iA].status === auditConst.status.checkNo ? ' ' + stageAuditor.name : '') + '</span>');
                    }
@@ -59,7 +59,7 @@ $('a[data-target="#sp-list" ]').on('click', function () {
                    if (ah[iA].status !== auditConst.status.uncheck) {
                        let timeHtml = '';
                        if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre) {
-                           timeHtml = '<small>'+ (ah[iA].end_time ? moment(ah[iA].end_time).format('YYYY-MM-DD') : '') +'</small> ';
+                           timeHtml = '<small>'+ (ah[iA].end_time ? moment(ah[iA].end_time).format('YYYY-MM-DD HH:mm:ss') : '') +'</small> ';
                        }
                        righthtml.push('<span class="' + auditConst.statusClass[ah[iA].status] +'">' + timeHtml + auditConst.statusString[ah[iA].status] + (ah[iA].status === auditConst.status.checkNo ? ' ' + stageAuditor.name : '') + '</span>');
                    }
@@ -72,7 +72,7 @@ $('a[data-target="#sp-list" ]').on('click', function () {
                    if (ah[iA].status !== auditConst.status.uncheck) {
                        let timeHtml = '';
                        if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre) {
-                           timeHtml = '<small>'+ (ah[iA].end_time ? moment(ah[iA].end_time).format('YYYY-MM-DD') : '') +'</small> ';
+                           timeHtml = '<small>'+ (ah[iA].end_time ? moment(ah[iA].end_time).format('YYYY-MM-DD HH:mm:ss') : '') +'</small> ';
                        }
                        righthtml.push('<span class="' + auditConst.statusClass[ah[iA].status] +'">'+ timeHtml + auditConst.statusString[ah[iA].status] + (ah[iA].status === auditConst.status.checkNo ? ' ' + stageAuditor.name : '') + '</span>');
                    }

+ 2 - 2
app/public/js/revise.js

@@ -40,7 +40,7 @@ $(document).ready(() => {
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
     const posSheet = posSpread.getActiveSheet();
     sjsSettingObj.setGridSelectStyle(posSpreadSetting);
-    if (thousandth) sjsSettingObj.setTpThousandthFormat(SpreadSetting);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(posSheet, posSpreadSetting);
 
     const posSearch = $.posSearch({selector: '#pos-search', searchSpread: posSpread});
@@ -262,6 +262,7 @@ $(document).ready(() => {
             if (info.newSelections) {
                 if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
                     billsTreeSpreadObj.refreshOperationValid(info.sheet);
+                    SpreadJsObj.resetTopAndSelect(posSheet);
                     posSpreadObj.loadCurPosData();
                     SpreadJsObj.saveTopAndSelect(billsSheet, ckBillsSpread);
                     posSearch.search($('#pos-keyword').val());
@@ -1166,7 +1167,6 @@ $(document).ready(() => {
          */
         loadCurPosData: function () {
             const node = SpreadJsObj.getSelectObject(billsSheet);
-            SpreadJsObj.resetTopAndSelect(posSheet);
             if (node) {
                 const posData = pos.getLedgerPos(node.id) || [];
                 SpreadJsObj.loadSheetData(posSheet, 'data', posData);

+ 1 - 1
app/public/js/revise_history.js

@@ -124,6 +124,7 @@ $(document).ready(() => {
                 if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
                     SpreadJsObj.saveTopAndSelect(billsSheet, ckBillsSpread);
                     if (isTz) {
+                        SpreadJsObj.resetTopAndSelect(posSheet);
                         posSpreadObj.loadCurPosData();
                         posSearch.search($('#pos-keyword').val());
                     }
@@ -143,7 +144,6 @@ $(document).ready(() => {
          */
         loadCurPosData: function () {
             const node = SpreadJsObj.getSelectObject(billsSheet);
-            SpreadJsObj.resetTopAndSelect(posSheet);
             if (node) {
                 const posData = pos.getLedgerPos(node.id) || [];
                 SpreadJsObj.loadSheetData(posSheet, 'data', posData);

+ 19 - 13
app/public/js/stage.js

@@ -661,8 +661,8 @@ $(document).ready(() => {
         },
         selectionChanged: function (e, info) {
             if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
-                stagePosSpreadObj.loadCurPosData();
                 SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
+                stagePosSpreadObj.loadCurPosData();
                 if (posSearch) {
                     posSearch.search();
                 }
@@ -1117,7 +1117,6 @@ $(document).ready(() => {
          */
         loadCurPosData: function () {
             const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
-            SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
             if (node) {
                 const posData = stagePos.ledgerPos[itemsPre + node.id] || [];
                 SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', posData);
@@ -1254,9 +1253,9 @@ $(document).ready(() => {
             if (info.sheet.zh_setting) {
                 const sortData = info.sheet.zh_data;
                 const range = info.cellRange;
-                const validField = ['contract_qty', 'qc_qty', 'postil'];
+                const validField = ['contract_qty', 'qc_qty', 'postil', 'real_qty'];
                 if (!checkTzMeasureType()) {
-                    validField.push('name', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'real_qty', 'position', 'drawing_code');
+                    validField.push('name', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'position', 'drawing_code');
                 }
                 for (let iCol = range.col; iCol < range.col + range.colCount; iCol++) {
                     const col = info.sheet.zh_setting.cols[iCol];
@@ -2604,6 +2603,7 @@ $(document).ready(() => {
                     $('#view-calc-img').attr('src', updateData.img);
                     $('#show-calc-img').attr('src', updateData.img);
                     $('#view-calc-remark').text(img_remark);
+
                     $('#edit-img').modal('hide');
                     // updateImageData = updateData;
                     // $('#edit-img').modal('hide');
@@ -2740,7 +2740,7 @@ $(document).ready(() => {
             $('#view-calc-remark').text(calcImgRemark);
             $("#view-calc-remark").attr('readonly', true);
             // 处理 编辑 -> 添加草图中textarea多余的空格
-            $('#text-edit').val(calcImgRemark)
+            $('#text-edit').text(calcImgRemark)
         }
     }
     class CheckedChange {
@@ -3050,15 +3050,21 @@ $(document).ready(() => {
             $('#show-att tr').eq(3).children('td').eq(0).text(att.username);
             $('#show-att tr').eq(3).children('td').eq(1).text(att.in_time);
             $('#show-att tr').eq(4).children('td').text(att.remark);
-            if (parseInt(userID) === att.uid) {
+            // 附件uid等于当前用户id, 附件上传本人
+            if (parseInt(cur_uid) === att.uid) {
                 $('#btn-att').show();
-                if (stage.status === auditConst.status.checked) {
-                    if (parseInt(att.re_upload) === 1) {
-                        $('#btn-att a').eq(3).show();
-                    } else {
-                        $('#btn-att a').eq(3).hide();
-                    }
-                }
+                let showDel = true;
+
+                // 审核完成后再上传的,上传者可删除附件
+                if (stage.status === auditConst.status.checked && !parseInt(att.re_upload)) showDel = false;
+
+                // 审核被退回,原报上传的附件可以删除
+                if (stage.status === auditConst.status.checkNo && parseInt(cur_uid) !== stage.user_id) showDel = false;
+
+                // 台账未上报、当前用户为原报, 可以删除附件
+                if (stage.status === auditConst.status.uncheck && stage.user_id !== parseInt(cur_uid)) showDel = false;
+
+                if (showDel) $('#btn-att a').eq(3).show();
                 // $('#btn-att a').eq(3).show();
                 $('#btn-att a').eq(2).hide();
                 $('#btn-att a').eq(4).hide();

+ 1 - 1
app/public/js/stage_compare.js

@@ -169,7 +169,6 @@ $(document).ready(function () {
     // 获取部位明细数据
     function loadPosData(iRow) {
         const node = ledgerSpread.getActiveSheet().zh_tree.nodes[iRow];
-        SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
         if (node) {
             SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), SpreadJsObj.DataType.Data, scPos.getLedgerPos(node.id) || []);
         } else {
@@ -184,6 +183,7 @@ $(document).ready(function () {
             if (info.oldSelections) {
                 const iOldRow = info.oldSelections[0].row;
                 if (iNewRow !== iOldRow) {
+                    SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
                     loadPosData(iNewRow);
                 }
             } else {

+ 4 - 3
app/public/js/stage_im.js

@@ -460,10 +460,11 @@ const stageIm = (function () {
                 if (p.children && p.children.length > 0) continue;
                 if (checkUsed(p)) return [p, getFirstUsedPos(p)]
             }
+            return [null, null];
         } else if (checkUsed(node)) {
             return [node, getFirstUsedPos(node)];
         } else {
-            return [null, null]
+            return [null, null];
         }
     }
 
@@ -935,14 +936,14 @@ const stageIm = (function () {
             if (parent.check) {
                 return parent;
             } else {
-                parent = this.getParent(parent);
+                parent = gsTree.getParent(parent);
             }
         }
         return null;
     }
     function getRelaXmj(node) {
         if (checkUsed(node)) {
-            if (stage.im_gather) {
+            if (stage.im_gather && (stage.im_type !== imType.bw.value && stage.im_type !== imType.bb.value)) {
                 const checkedParent = getParentCheckNode(node);
                 return checkedParent ? checkedParent : gsTree.getLeafXmjParent(node);
             } else {

+ 78 - 2
app/public/report/js/rpt_custom.js

@@ -25,6 +25,8 @@ const rptCustomObj = (function () {
 
         orgSelect: null,
     };
+    // 期选择
+    const sStageSelect = 'stage_select';
     const grSpreadSetting = {
         baseCols: [
             {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
@@ -242,6 +244,25 @@ const rptCustomObj = (function () {
         $('#gather-select-ok').unbind('click');
         $('#gather-select-ok').bind('click', () => {rptCustomObj.resetGatherSelect(resolve);});
     };
+    const initStageSelect = function (gsSetting, gsSelect, rptName, resolve = null) {
+        const setting = JSON.parse(gsSetting);
+        $('#stage-select-count').html(gsSelect && gsSelect.stages ? gsSelect.stages.length : 0);
+        $('#stage-select-title').html(setting.title + (rptName ? '-' + rptName : ''));
+        // 初始化选择结果
+        $('#stage-select-hint').attr('min-select', setting.min).attr('max-select', setting.max).hide();
+        for (const sc of $('[name=stage-select-check]')) {
+            sc.checked = false;
+        }
+        if (gsSelect && gsSelect.stages) {
+            for (const s of gsSelect.stages) {
+                $('#stage-select-' + s)[0].checked = true;
+            }
+        }
+        $("#stage-select").modal('show');
+
+        $('#stage-select-ok').unbind('click');
+        $('#stage-select-ok').bind('click', () => {rptCustomObj.resetStageSelect(resolve);});
+    };
     const init = function (cDefine, sfData, cSelect, rptName, resolve = null) {
         stageFlow = sfData;
         if (cDefine && cDefine[sAuditSelect] && cDefine[sAuditSelect].enable && cDefine[sAuditSelect].setting) {
@@ -256,6 +277,12 @@ const rptCustomObj = (function () {
         } else {
             $('#pnl_gather_select').hide();
         }
+        if (cDefine && cDefine[sStageSelect] && cDefine[sStageSelect].enable && cDefine[sStageSelect].setting) {
+            $('#pnl_stage_select').show();
+            initStageSelect(cDefine[sStageSelect].setting, cSelect ? cSelect[sStageSelect] : null, rptName, resolve);
+        } else {
+            $('#pnl_stage_select').hide();
+        }
     };
 
     const reloadReportData = function (result) {
@@ -415,6 +442,40 @@ const rptCustomObj = (function () {
         }
     };
 
+    const resetStageSelect = function (resolve = null) {
+        const data = {}, hintObj = $('#stage-select-hint');
+        if (!resolve) getCommonParams(data);
+        data[sStageSelect] = {
+            stages: [],
+        };
+        for (const sc of $('[name=stage-select-check]:checked')) {
+            data[sStageSelect].stages.push(parseInt($(sc).attr('stage-order')));
+        }
+        if (data[sStageSelect].stages.length <= parseInt(hintObj.attr('min-select'))) {
+            hintObj.html('请至少选择' + hintObj.attr('min-select') + '期数据').show();
+            return;
+        } else if (data[sStageSelect].stages.length >= parseInt(hintObj.attr('max-select'))) {
+            hintObj.html('最多只能选择' + hintObj.attr('max-select') + '期数据').show();
+            return;
+        }
+        hintObj.hide();
+        if (resolve) {
+            resolve(data);
+        } else {
+            postData('/report/cDefine', data, function (result) {
+                reloadReportData(result);
+                const stage_select = customSelects.stage_select.find(function (x) {
+                    return x.id === zTreeOprObj.currentNode.refId;
+                });
+                if (stage_select) {
+                    stage_select.gather_select = data[sStageSelect];
+                }
+                $('#stage-select-count').html(data[sStageSelect].stages.length);
+                $('#stage-select').modal('hide');
+            });
+        }
+    };
+
     const initTenderTree = function (tenders, category) {
         const gsSpread = SpreadJsObj.createNewSpread($('#gather-source-spread')[0]);
         gsObj.gsSheet = gsSpread.getActiveSheet();
@@ -482,8 +543,23 @@ const rptCustomObj = (function () {
             } else {
                 params.customSelect.push(null);
             }
+
+            const stage_select = customSelects.stage_select.find(function (x) {
+                return x.id === rptId;
+            });
+            if (stage_select && stage_select.custom_define && stage_select.custom_define[sStageSelect].enable) {
+                if (rptId === currentRptId) {
+                    params.customSelect.push(stage_select[sStageSelect]);
+                } else {
+                    const chkNode = chkNodes.find(function (x) { return x.refId === rptId});
+                    params.customSelect.push(await comfirmSelectPromise(chkNode ? chkNode.name : '', stage_select));
+                }
+            } else {
+                params.customSelect.push(null);
+            }
         }
         $('#gather-select').modal('hide');
+        $('#stage-select').modal('hide');
     };
 
     const showMaterialSelect = function () {
@@ -513,9 +589,9 @@ const rptCustomObj = (function () {
 
     return {
         init,
-        resetAuditSelect, resetGatherSelect,
+        resetAuditSelect, resetGatherSelect, resetStageSelect,
         initTenderTree,
         getCustomSelect,
-        showMaterialSelect, changeMaterial
+        showMaterialSelect, changeMaterial,
     };
 })();

+ 10 - 0
app/public/report/js/rpt_main.js

@@ -223,6 +223,7 @@ let zTreeOprObj = {
     onCheck: function(event, treeId, treeNode) {
         zTreeOprObj.countChkedRptTpl();
         rptCustomObj.showMaterialSelect();
+        rptCustomObj.showStageSelect();
         if (treeNode.isParent) {
             zTreeOprObj.treeObj.expandNode(treeNode, true, true, false);
         }
@@ -252,6 +253,15 @@ let zTreeOprObj = {
                 return;
             }
 
+            const stage_select = customSelects.stage_select.find(function (x) {
+                return x.id === treeNode.refId;
+            });
+            if (stage_select) {
+                rptCustomObj.init(stage_select.custom_define, customSelects.stageFlow, stage_select);
+                return;
+            }
+
+
             me.requestNormalReport(params);
             me.countChkedRptTpl();
             rptCustomObj.showMaterialSelect();

+ 1 - 0
app/reports/rpt_component/jpc_value_define.js

@@ -54,6 +54,7 @@ module.exports = {
     NODE_CUS_GATHER_SELECT: 'gather_select',
     NODE_CUS_COMPARE_SELECT: 'compare_select',
     NODE_CUS_MATERIAL_SELECT: 'material_select',
+    NODE_CUS_STAGE_SELECT: 'stage_select',
 
     NODE_MAP_DATA_HANDLE_INFO: '映射数据预处理',
     PROP_DATA_KEY: '映射数据对象',

+ 2 - 0
app/router.js

@@ -312,6 +312,8 @@ module.exports = app => {
     app.post('/profile/code', sessionAuth, 'profileController.getCode');
     app.post('/profile/bind', sessionAuth, 'profileController.bindMobile');
     app.get('/profile/qrCode', sessionAuth, 'profileController.qrCode');
+    app.get('/profile/wechat', sessionAuth, 'profileController.wechat');
+    app.post('/profile/wechat/remove', sessionAuth, 'profileController.removeWechat');
 
     // 标准库相关
     app.post('/std-lib/get-data', sessionAuth, 'standardLibController.getData');

+ 52 - 12
app/service/material_audit.js

@@ -32,7 +32,7 @@ module.exports = app => {
          * @param {Number} materialId - 材料调差期id
          * @param {Number} auditorId - 审核人id
          * @param {Number} times - 第几次审批
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getAuditor(materialId, auditorId, times = 1) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
@@ -48,7 +48,7 @@ module.exports = app => {
          *
          * @param {Number} materialId - 材料调差期id
          * @param {Number} times - 第几次审批
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getAuditors(materialId, times = 1) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +
@@ -70,7 +70,7 @@ module.exports = app => {
          *
          * @param {Number} materialId - 材料调差期id
          * @param {Number} times - 第几次审批
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getCurAuditor(materialId, times = 1) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
@@ -86,7 +86,7 @@ module.exports = app => {
          *
          * @param {Number} materialId - 材料调差期id
          * @param {Number} times - 第几次审批
-         * @returns {Promise<number>}
+         * @return {Promise<number>}
          */
         async getNewOrder(materialId, times = 1) {
             const sql = 'SELECT Max(??) As max_order FROM ?? Where `mid` = ? and `times` = ?';
@@ -123,7 +123,7 @@ module.exports = app => {
          * @param {Number} materialId - 材料调差期id
          * @param {Number} auditorId - 审核人id
          * @param {Number} times - 第几次审批
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          * @private
          */
         async _syncOrderByDelete(transaction, materialId, order, times) {
@@ -406,17 +406,54 @@ module.exports = app => {
             }
         }
 
+        async _checkNoPre(materialId, checkData, times) {
+            const time = new Date();
+            // 整理当前流程审核人状态更新
+            const audit = await this.getDataByCondition({ mid: materialId, times, status: auditConst.status.checking });
+            console.log('audit', audit);
+            if (!audit || audit.order <= 1) {
+                throw '审核数据错误';
+            }
+            // 添加重新审批后,不能用order-1,取groupby值里的上一个才对
+            const auditors2 = await this.getAuditGroupByList(materialId, times);
+            const auditorIndex = await auditors2.findIndex(function(item) {
+                return item.aid === audit.aid;
+            });
+            const preAuditor = auditors2[auditorIndex - 1];
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: audit.tid, mid: audit.mid, aid: preAuditor.aid,
+                    times: audit.times, order: audit.order + 1, status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                newAuditors.push({
+                    tid: audit.tid, mid: audit.mid, aid: audit.aid,
+                    times: audit.times, order: audit.order + 2, status: auditConst.status.uncheck,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                await transaction.commit();
+            } catch (error) {
+                await transaction.rollback();
+                throw error;
+            }
+        }
+
         /**
          * 审批
          * @param {Number} materialId - 材料调差期id
          * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果
          * @param {Number} times - 第几次审批
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async check(materialId, checkData, times = 1) {
-            if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo) {
+            if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo && checkData.checkType !== auditConst.status.checkNoPre) {
                 throw '提交数据错误';
             }
+            console.log('checkData:', checkData);
+            console.log('times', times);
             switch (checkData.checkType) {
                 case auditConst.status.checked:
                     await this._checked(materialId, checkData, times);
@@ -424,6 +461,9 @@ module.exports = app => {
                 case auditConst.status.checkNo:
                     await this._checkNo(materialId, checkData, times);
                     break;
+                case auditConst.status.checkNoPre:
+                    await this._checkNoPre(materialId, checkData, times);
+                    break;
                 default:
                     throw '无效审批操作';
             }
@@ -433,7 +473,7 @@ module.exports = app => {
          * 审批
          * @param {Number} materialId - 材料调差期id
          * @param {Number} times - 第几次审批
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async checkAgain(materialId, times = 1) {
             const time = new Date();
@@ -494,7 +534,7 @@ module.exports = app => {
          * 获取审核人需要审核的期列表
          *
          * @param auditorId
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getAuditMaterial(auditorId) {
             const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`mid`,' +
@@ -512,7 +552,7 @@ module.exports = app => {
          * @param {Number} pid - 查询标段
          * @param {Number} uid - 查询人
          * @param {Date} time - 查询时间
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getNoticeMaterial(pid, uid, time) {
             const sql = 'SELECT * FROM (SELECT t.`name`, t.`project_id`, t.`type`, t.`user_id`, ' +
@@ -535,7 +575,7 @@ module.exports = app => {
          * 获取审核人流程列表
          *
          * @param auditorId
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async getAuditGroupByList(materialId, times) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`mid`, la.`aid`, la.`order` ' +
@@ -551,7 +591,7 @@ module.exports = app => {
          * @param transaction - 新增一期的事务
          * @param {Object} preMaterial - 上一期
          * @param {Object} newaMaterial - 最新一期
-         * @returns {Promise<*>}
+         * @return {Promise<*>}
          */
         async copyPreMaterialAuditors(transaction, preMaterial, newMaterial) {
             const auditors = await this.getAuditGroupByList(preMaterial.id, preMaterial.times);

+ 10 - 2
app/service/project_account.js

@@ -620,14 +620,21 @@ module.exports = app => {
          * @param {Number} data - 通知类型
          * @return {Boolean} - 返回修改结果
          */
-        async smsTypeSet(id, data) {
+        async noticeTypeSet(id, data) {
             if (data._csrf !== undefined) {
                 delete data._csrf;
             }
+            const type = parseInt(data.type) === 1 ? 1 : 0; // 对应微信通知和短信通知设置
+            delete data.type;
             const updateData = {
                 id,
-                sms_type: JSON.stringify(data),
             };
+            if (type === 1) {
+                updateData.sms_type = JSON.stringify(data);
+            } else {
+                updateData.wx_type = JSON.stringify(data);
+            }
+            console.log(updateData);
 
             const operate = await this.db.update(this.tableName, updateData);
 
@@ -697,6 +704,7 @@ module.exports = app => {
                 id,
                 wx_openid: openid,
                 wx_name: nickname,
+                wx_type: null,
             };
 
             const operate = await this.db.update(this.tableName, updateData);

+ 8 - 2
app/service/report.js

@@ -148,8 +148,14 @@ module.exports = app => {
                             runnableRst.push(service.rptGatherMemory.getMaterialGl(params.tender_id, params.material_order, memFieldKeys[filter]));
                             runnableKey.push(filter);
                             break;
-                        case 'mem_sum_stage_bills':
-                            runnableRst.push(service.rptGatherMemory.getSumStageBillsData(params.tender_id, memFieldKeys[filter]));
+                        case 'mem_stage_sum_bills':
+                            runnableRst.push(service.rptStageSumMemory.getStageSumBills(params.tender_id, memFieldKeys[filter],
+                                customDefine.stage_select, customSelect ? customSelect.stage_select : null));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_stage_sum_pay':
+                            runnableRst.push(service.rptStageSumMemory.getStageSumPay(params.tender_id, memFieldKeys[filter],
+                                customDefine.stage_select, customSelect ? customSelect.stage_select : null));
                             runnableKey.push(filter);
                             break;
                         default:

+ 8 - 1
app/service/rpt_custom_define.js

@@ -30,6 +30,9 @@ module.exports = app => {
             if (data && data.gather_select) {
                 data.gather_select = JSON.parse(data.gather_select);
             }
+            if (data && data.stage_select) {
+                data.stage_select = JSON.parse(data.stage_select);
+            }
             return data;
         }
 
@@ -53,12 +56,13 @@ module.exports = app => {
         }
 
         async saveCustomSelect(data) {
-            const sid = data.gather_select ? -1 : data.stage_id;
+            const sid = data.gather_select || data.stage_select ? -1 : data.stage_id;
             const filter = {tid: data.tender_id, sid: sid, rid: data.rpt_tpl_id};
             const count = await this.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 (data.stage_select) updateData.stage_select = JSON.stringify(data.stage_select);
             if (count > 0) {
                 await this.update(updateData, filter);
             } else {
@@ -91,6 +95,9 @@ module.exports = app => {
                 if (cs.gather_select) {
                     r.gather_select = JSON.parse(cs.gather_select);
                 }
+                if (cs.stage_select) {
+                    r.stage_select = JSON.parse(cs.stage_select);
+                }
             }
             return result;
         }

+ 254 - 0
app/service/rpt_stage_sum_memory.js

@@ -0,0 +1,254 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Ledger = require('../lib/ledger');
+const PayCalculator = require('../lib/pay_calc');
+
+const auditConst = require('../const/audit');
+const payConst = require('../const/deal_pay');
+
+const moment = require('moment');
+
+const indexPre = 'id_';
+
+
+const gatherUtils = {
+    completeStageSumData: function (datas, completeDatas) {
+        for (const data of datas) {
+            for (const cd of completeDatas) {
+                data[cd.prefix + 'order'] = cd.order;
+            }
+        }
+    },
+    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 + "dgn_qty1"] = helper.add(gatherNode[prefix + "dgn_qty1"], sourceNode.dgn_qty1);
+        gatherNode[prefix + "dgn_qty2"] = helper.add(gatherNode[prefix + "dgn_qty2"], sourceNode.dgn_qty2);
+
+        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[prefix + "deal_dgn_qty1"] = helper.add(gatherNode[prefix + "deal_dgn_qty1"], sourceNode.deal_dgn_qty1);
+        gatherNode[prefix + "deal_dgn_qty2"] = helper.add(gatherNode[prefix + "deal_dgn_qty2"], sourceNode.deal_dgn_qty2);
+        gatherNode[prefix + "c_dgn_qty1"] = helper.add(gatherNode[prefix + "c_dgn_qty1"], sourceNode.c_dgn_qty1);
+        gatherNode[prefix + "c_dgn_qty2"] = helper.add(gatherNode[prefix + "c_dgn_qty2"], sourceNode.c_dgn_qty2);
+
+        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 + "dgn_qty1"] = helper.add(gatherNode[prefix + "dgn_qty1"], sourceNode.dgn_qty1);
+        gatherNode[prefix + "dgn_qty2"] = helper.add(gatherNode[prefix + "dgn_qty2"], sourceNode.dgn_qty2);
+
+        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 + "deal_dgn_qty1"] = helper.add(gatherNode[prefix + "deal_dgn_qty1"], sourceNode.deal_dgn_qty1);
+        gatherNode[prefix + "deal_dgn_qty2"] = helper.add(gatherNode[prefix + "deal_dgn_qty2"], sourceNode.deal_dgn_qty2);
+        gatherNode[prefix + "c_dgn_qty1"] = helper.add(gatherNode[prefix + "c_dgn_qty1"], sourceNode.c_dgn_qty1);
+        gatherNode[prefix + "c_dgn_qty2"] = helper.add(gatherNode[prefix + "c_dgn_qty2"], sourceNode.c_dgn_qty2);
+
+        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);
+    },
+    gatherLedger: 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 + "dgn_qty1"] = helper.add(gatherNode[prefix + "dgn_qty1"], sourceNode.dgn_qty1);
+        gatherNode[prefix + "dgn_qty2"] = helper.add(gatherNode[prefix + "dgn_qty2"], sourceNode.dgn_qty2);
+
+        gatherNode['s_' + "qty"] = helper.add(gatherNode['s_' + "qty"], sourceNode.quantity);
+        gatherNode['s_' + "tp"] = helper.add(gatherNode['s_' + "tp"], sourceNode.total_price);
+    },
+    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);
+
+        gatherNode[prefix + "dgn_qty1"] = helper.add(gatherNode[prefix + "dgn_qty1"], sourceNode.dgn_qty1);
+        gatherNode[prefix + "dgn_qty2"] = helper.add(gatherNode[prefix + "dgn_qty2"], sourceNode.dgn_qty2);
+    },
+};
+
+module.exports = app => {
+    class RptStageSumMemory extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局context
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+        }
+
+        async getStageSumBills(tid, memFieldKeys, gsDefine, gsCustom) {
+            const self = this;
+            if (!gsDefine || !gsDefine.enable) return [];
+            if (!gsCustom || !gsCustom.stages || gsCustom.stages.length === 0) return [];
+
+            await this.ctx.service.tender.checkTender(tid);
+            const billsData = await this.ctx.service.ledger.getData(this.ctx.tender.id);
+            const calcFields = ['deal_tp', 'total_price'], calcPrefix = [];
+
+            const gsSetting = JSON.parse(gsDefine.setting);
+            for (const s of gsCustom.stages) {
+                const stage = await this.db.get(this.ctx.service.stage.tableName, { tid: this.ctx.tender.id, order: s });
+                if (!stage) continue;
+                await this.ctx.service.stage.doCheckStage(stage);
+
+                const prefix = 's_' + stage.order + '_';
+                calcFields.push(prefix + 'contract_tp');
+                calcFields.push(prefix + 'qc_tp');
+                calcFields.push(prefix + 'gather_tp');
+                calcPrefix.push(prefix);
+
+                const curStage = stage.readOnly
+                    ? await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, stage.id);
+                this.ctx.helper.assignRelaData(billsData, [
+                    {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: prefix, relaId: 'lid'}
+                ]);
+            }
+
+            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: calcFields,
+                calc: function (node) {
+                    for (const prefix of calcPrefix) {
+                        if (node.children && node.children.length === 0) {
+                            node[prefix + 'gather_qty'] = self.ctx.helper.add(node[prefix + 'contract_qty'], node[prefix + 'qc_qty']);
+                        }
+                        node[prefix + 'gather_tp'] = self.ctx.helper.add(node[prefix + 'contract_tp'], node[prefix + 'qc_tp']);
+                    }
+                }
+            });
+            billsTree.loadDatas(billsData);
+            billsTree.calculateAll();
+            return billsTree.getDefaultDatas();
+        }
+
+        /**
+         * 合同支付
+         */
+
+        async _checkStagePayCalc(tender, stage, dealPay) {
+            if (!stage.readOnly) {
+                // 计算 本期金额
+                const payCalculator = new PayCalculator(this.ctx, stage, tender.info);
+                await payCalculator.calculateAll(dealPay);
+            }
+        }
+
+        async _gatherStagePay(tender, stage, payData) {
+            if (stage) {
+                await this.ctx.service.stage.doCheckStage(stage);
+
+                const dealPay = await this.ctx.service.stagePay.getStagePays(stage);
+                await this._checkStagePayCalc(tender, stage, dealPay);
+                this.ctx.helper.assignRelaData(billsData, [
+                    {data: dealPay, fields: ['tp'], prefix: 's_' + stage.order + '_', relaId: 'pid'}
+                ]);
+            }
+        }
+
+        async getStageSumPay(tid, memFieldKeys, gsDefine, gsCustom) {
+            if (!gsDefine || !gsDefine.enable) return [];
+            if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return [];
+
+            await this.ctx.service.tender.checkTender(tid);
+            const payData = await this.ctx.service.pay.getAllDataByCondition({
+                where: {tid: tid},
+                order: [['order', 'ASC']],
+            });
+
+            const gsSetting = JSON.parse(gsDefine.setting);
+            for (const s of gsCustom.stages) {
+                const stage = this.getSelectStage(gsSetting, s);
+                await this.ctx.service.stage.doCheckStage(stage);
+                await this._gatherStagePay(this.ctx.tender, stage, payData);
+
+            }
+        }
+    }
+
+    return RptStageSumMemory;
+};

Різницю між файлами не показано, бо вона завелика
+ 225 - 149
app/view/material/audit_modal.ejs


+ 2 - 1
app/view/material/file.ejs

@@ -3,7 +3,7 @@
   <div class="panel-title">
     <div class="title-main d-flex justify-content-between">
       <div class="d-flex justify-content-start align-items-center">
-        <% if(auditors.includes(ctx.session.sessionUser.accountId)) { %>
+        <% if(report_id === ctx.session.sessionUser.accountId || auditors.includes(ctx.session.sessionUser.accountId)) { %>
           <a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>
         <% } %>
           <span class="d-flex align-items-center" style="margin-left: 5px;">
@@ -48,4 +48,5 @@
   const curMaterialStatus = '<%- material.status %>';
   const fileList = '<%- JSON.stringify(fileList) %>';
   const auditors = JSON.parse('<%- JSON.stringify(auditors) %>');
+  const report_id = '<%- JSON.stringify(report_id) %>'
 </script>

+ 4 - 3
app/view/profile/sms.ejs

@@ -46,7 +46,7 @@
                     <div class="mt-5">
                         <h4>通知类型</h4>
                         <p class="text-muted">勾选您需要接收的短信类型。</p>
-                        <form id="sms-form" method="post" action="/profile/sms/type">
+                        <form id="sms-form" method="post" action="/profile/sms/type?csrf=<%- ctx.csrf %>">
                             <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                             <% const user_smsType = accountData.sms_type !== '' ? JSON.parse(accountData.sms_type) : null; %>
                             <% for (const s in smsType) { %>
@@ -57,13 +57,14 @@
                                 <div class="col-5">
                                     <% for (const c of smsType[s].children) { %>
                                     <div class="form-check ">
-                                        <input class="form-check-input" type="checkbox" name="<%= s %>[]" value="<%= c.value %>" <% if (user_smsType !== null && user_smsType[s] !== undefined && user_smsType[s].indexOf(c.value.toString()) !== -1) { %>checked<% } %>>
-                                        <label class="form-check-label" for="<%= s %>"><%= c.title %></label>
+                                        <input class="form-check-input" id="<%= s %>_<%- c.value %>" type="checkbox" name="<%= s %>[]" value="<%= c.value %>" <% if (user_smsType !== null && user_smsType[s] !== undefined && user_smsType[s].indexOf(c.value.toString()) !== -1) { %>checked<% } %>>
+                                        <label class="form-check-label" for="<%= s %>_<%- c.value %>"><%= c.title %></label>
                                     </div>
                                     <% } %>
                                 </div>
                             </div>
                             <% } %>
+                            <input name="type" value="1" type="hidden">
                             <button type="submit" class="btn btn-primary btn-sm">确认修改</button>
                         </form>
                     </div>

+ 68 - 0
app/view/profile/wechat.ejs

@@ -0,0 +1,68 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content" id="app">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>微信通知</h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+            <div class="row m-0">
+                <div class="col-5 my-3">
+                    <% if (accountData.wx_openid !== null && accountData.wx_openid !== '') { %>
+                    <!--已绑定手机-->
+                    <div class="form-group">
+                        <label>微信账号</label>
+                        <input class="form-control-plaintext" readonly="" value="<%= accountData.wx_name %>">
+                        <a href="#remove-wechat" class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#remove-wechat">解绑</a>
+                    </div>
+                    <% } else { %>
+                    <div class="form-group">
+                        <label>微信账号</label>
+                        <input class="form-control-plaintext" readonly="" value="未绑定">
+                    </div>
+                    <% } %>
+                    <!--二维码-->
+                    <div class="form-group">
+                        <label>扫码或搜索 关注服务号</label>
+                        <div><img class="w-50" src="/public/images/wechat.png"></div>
+                    </div>
+                    <% if (accountData.wx_openid !== null && accountData.wx_openid !== '') { %>
+                    <!--短信通知开关(已有认证手机后显示)-->
+                    <div class="mt-5">
+                        <h4>通知类型</h4>
+                        <p class="text-muted">勾选您需要接收的微信类型。</p>
+                        <form id="sms-form" method="post" action="/profile/sms/type">
+                            <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
+                            <% const user_wxType = accountData.wx_type !== '' ? JSON.parse(accountData.wx_type) : null; %>
+                            <% for (const s in smsType) { %>
+                            <div class="form-group row">
+                                <label class="col-auto col-form-label"><%= smsType[s].name %>
+                                    <!--<a href="#sms-view" data-toggle="modal" data-target="#sms-view" class="ml-2"><i class="fa fa-info-circle"></i></a>-->
+                                </label>
+                                <div class="col-5">
+                                    <% for (const c of smsType[s].children) { %>
+                                    <div class="form-check ">
+                                        <input class="form-check-input" id="<%= s %>_<%- c.value %>" type="checkbox" name="<%= s %>[]" value="<%= c.value %>" <% if (user_wxType !== null && user_wxType[s] !== undefined && user_wxType[s].indexOf(c.value.toString()) !== -1) { %>checked<% } %>>
+                                        <label class="form-check-label" for="<%= s %>_<%- c.value %>"><%= c.title %></label>
+                                    </div>
+                                    <% } %>
+                                </div>
+                            </div>
+                            <% } %>
+                            <input name="type" value="0" type="hidden">
+                            <button type="submit" class="btn btn-primary btn-sm">确认修改</button>
+                        </form>
+                    </div>
+                    <% } %>
+                </div>
+            </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script type="text/javascript">
+    const csrf = '<%= ctx.csrf %>';
+</script>
+<script type="text/javascript" src="/public/js/profile.js"></script>

+ 22 - 0
app/view/profile/wechat_modal.ejs

@@ -0,0 +1,22 @@
+<!--短信图示-->
+<div class="modal fade" id="remove-wechat" >
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" method="post" action="/profile/wechat/remove">
+            <div class="modal-header">
+                <h5 class="modal-title">解绑微信账号</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">×</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <h6>确认解绑微信号 <%- accountData.wx_name %>?</h6>
+                <h6>解绑后无法在微信接收通知。</h6>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="submit" class="btn btn-sm btn-primary">确定解绑</button>
+            </div>
+        </form>
+    </div>
+</div>

+ 9 - 0
app/view/report/index.ejs

@@ -174,6 +174,15 @@
                                     </div>
                                 </div>
                             </div>
+                            <div class="panel" id="pnl_stage_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="#stage-select">
+                                            选择期<br><span class="badge badge-light" id="stage-select-count">5</span>
+                                        </button>
+                                    </div>
+                                </div>
+                            </div>
                         </div>
                     </div>
                     <div class="sjs-height-4">

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

@@ -317,6 +317,35 @@
         </div>
     </div>
 </div>
+<div class="modal fade" id="stage-select" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="stage-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">
+                    <% for (const s of stages) { %>
+                    <div class="col-3">
+                        <div class="custom-control custom-checkbox custom-control-inline">
+                            <input type="checkbox" id="stage-select-<%- s.order %>" name="stage-select-check" stage-order="<%- s.order %>" class="custom-control-input">
+                            <label class="custom-control-label" for="stage-select-<%- s.order %>">第<%- s.order %>期</label>
+                        </div>
+                    </div>
+                    <% } %>
+                </div>
+                <div class="alert alert-danger my-2 p-2" id="stage-select-hint">我是提示呀</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="stage-select-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
 <!--管理通用报表-->
 <div class="modal fade" id="man-c" data-backdrop="static">
     <div class="modal-dialog" role="document">

+ 2 - 2
app/view/stage/audit_modal.ejs

@@ -340,12 +340,12 @@
                                                         <p class="card-text"><%- auditors[iA].opinion %></p>
                                                     </div>
                                                 <% } else if (auditors[iA].status == auditConst.status.checking) { %>
-                                                    <span class="pull-right">审批中</span>
+                                                    <span class="pull-right" style="line-height: 1.2;">-审批中</span>
                                                     <h5 class="card-title">
                                                         <i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down' : 'fa fa-stop-circle') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small><span class="pull-right"><%= auditors[iA].sort === auditors[iA].max_sort ? '终' : ctx.helper.transFormToChinese(auditors[iA].sort) %>审</span>
                                                     </h5>
                                                     <div class="ml-3">
-                                                        <span>审批中</span>
+                                                        <!-- <span>审批中</span> -->
                                                         <p class="card-text"><%- auditors[iA].opinion %></p>
                                                     </div>
                                                 <% } else if (auditors[iA].status === auditConst.status.checkNoPre) { %>

+ 6 - 2
app/view/stage/bwtz.ejs

@@ -128,8 +128,12 @@
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number'},
-            {title: '现场实际数量', colSpan: '1', rowSpan: '2', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'},
-            {title: '预计变更数量', colSpan: '1', rowSpan: '2', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            <% if (ctx.tender.info.display.stage.realComplete) { %>
+            {title: '现场实际|数量', colSpan: '2|1', rowSpan: '1|1', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'real_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '预计变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'estimate_tp', hAlign: 2, width: 60, type: 'Number'},
+            <% } %>
             {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},

+ 2 - 1
app/view/stage/index.ejs

@@ -264,7 +264,7 @@
                                 <li class="nav-item ml-auto">
                                     <!--所有附件 翻页-->
                                     <span id="showPage" style="display: none"><a href="javascript:void(0);" class="page-select" content="pre"><i class="fa fa-chevron-left"></i></a> <span id="currentPage">1</span>/<span id="totalPage">10</span> <a href="javascript:void(0);" class="page-select" content="next"><i class="fa fa-chevron-right"></i></a></span>
-                                    <a href="#upload" data-toggle="modal" data-target="#upload"  class="btn btn-sm btn-outline-primary ml-3">上传</a>
+                                        <a href="#upload" data-toggle="modal" data-target="#upload"  class="btn btn-sm btn-outline-primary ml-3">上传</a>
                                 </li>
                             </ul>
                         </div>
@@ -586,6 +586,7 @@
     let attData = JSON.parse('<%- JSON.stringify(attData) %>');
     const ckColSetting = 'stage-col-visible-1.0.3-<%- tender.id %>';
     const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
+    const cur_uid = '<%- ctx.session.sessionUser.accountId %>';
 </script>
 <% if (ctx.stage.status === auditConst.status.uncheck && ctx.session.sessionUser.accountId === ctx.stage.user_id) {%>
 <script>

+ 1 - 1
app/view/stage/report_modal.ejs

@@ -187,4 +187,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 32 - 39
builder_report_index_define.js

@@ -1026,12 +1026,12 @@ const materialGl = {
     ],
 };
 
-const sum_stage_bills = {
-    name: '期汇总-清单数据表(mem_sum_stage_bills)',
+const stage_sum_bills = {
+    name: '期汇总-清单-交叉数据表(mem_stage_sum_bills)',
     remark: '',
     id: 42,
-    key: 'mem_sum_stage_bills',
-    prefix: '期汇总-清单数据',
+    key: 'mem_stage_sum_bills',
+    prefix: '期汇总-清单-交叉数据',
     cols: [
         { name: '台账ID', field: 'id', type: dataType.int },
         { name: '标段ID', field: 'tender_id', type: dataType.int },
@@ -1068,40 +1068,33 @@ const sum_stage_bills = {
         { name: '节点类型', field: 'node_type', type: dataType.int },
         { name: '总额计量', field: 'is_tp', type: dataType.int },
 
-        { name: '第1期-合同-数量', field: 's1_contract_qty', type: dataType.currency },
-        { name: '第1期-合同-金额', field: 's1_contract_tp', type: dataType.currency },
-        { name: '第1期-变更-数量', field: 's1_qc_qty', type: dataType.currency },
-        { name: '第1期-变更-金额', field: 's1_qc_tp', type: dataType.currency },
-        { name: '第1期-完成-数量', field: 's1_gather_qty', type: dataType.currency },
-        { name: '第1期-完成-金额', field: 's1_gather_tp', type: dataType.currency },
-
-        { name: '第2期-合同-数量', field: 's2_contract_qty', type: dataType.currency },
-        { name: '第2期-合同-金额', field: 's2_contract_tp', type: dataType.currency },
-        { name: '第2期-变更-数量', field: 's2_qc_qty', type: dataType.currency },
-        { name: '第2期-变更-金额', field: 's2_qc_tp', type: dataType.currency },
-        { name: '第2期-完成-数量', field: 's2_gather_qty', type: dataType.currency },
-        { name: '第2期-完成-金额', field: 's2_gather_tp', type: dataType.currency },
-
-        { name: '第3期-合同-数量', field: 's3_contract_qty', type: dataType.currency },
-        { name: '第3期-合同-金额', field: 's3_contract_tp', type: dataType.currency },
-        { name: '第3期-变更-数量', field: 's3_qc_qty', type: dataType.currency },
-        { name: '第3期-变更-金额', field: 's3_qc_tp', type: dataType.currency },
-        { name: '第3期-完成-数量', field: 's3_gather_qty', type: dataType.currency },
-        { name: '第3期-完成-金额', field: 's3_gather_tp', type: dataType.currency },
-
-        { name: '第4期-合同-数量', field: 's4_contract_qty', type: dataType.currency },
-        { name: '第4期-合同-金额', field: 's4_contract_tp', type: dataType.currency },
-        { name: '第4期-变更-数量', field: 's4_qc_qty', type: dataType.currency },
-        { name: '第4期-变更-金额', field: 's4_qc_tp', type: dataType.currency },
-        { name: '第4期-完成-数量', field: 's4_gather_qty', type: dataType.currency },
-        { name: '第4期-完成-金额', field: 's4_gather_tp', type: dataType.currency },
-
-        { name: '第5期-合同-数量', field: 's5_contract_qty', type: dataType.currency },
-        { name: '第5期-合同-金额', field: 's5_contract_tp', type: dataType.currency },
-        { name: '第5期-变更-数量', field: 's5_qc_qty', type: dataType.currency },
-        { name: '第5期-变更-金额', field: 's5_qc_tp', type: dataType.currency },
-        { name: '第5期-完成-数量', field: 's5_gather_qty', type: dataType.currency },
-        { name: '第5期-完成-金额', field: 's5_gather_tp', type: dataType.currency },
+        { name: '(期)-第几期', field: 's_order', type: dataType.int },
+        { name: '(期)-合同-数量', field: 's1_contract_qty', type: dataType.currency },
+        { name: '(期)-合同-金额', field: 's1_contract_tp', type: dataType.currency },
+        { name: '(期)-变更-数量', field: 's1_qc_qty', type: dataType.currency },
+        { name: '(期)-变更-金额', field: 's1_qc_tp', type: dataType.currency },
+        { name: '(期)-完成-数量', field: 's1_gather_qty', type: dataType.currency },
+        { name: '(期)-完成-金额', field: 's1_gather_tp', type: dataType.currency },
+
+        { name: '交叉排序', field: 'cross_index', type: dataType.int },
+    ],
+};
+const stage_sum_pay = {
+    name: '期汇总-合同支付 数据表(mem_stage_sum_pay)',
+    remark: '',
+    id: 43,
+    key: 'mem_stage_sum_pay',
+    prefix: '期汇总-合同支付',
+    cols: [
+        { name: '名称', field: 'name', type: dataType.str },
+        { name: '类型', field: 'ptype', type: dataType.int },
+        { name: '是否扣款项', field: 'minus', type: dataType.int },
+        { name: '是否参与本期应付计算', field: 'is_yf', type: dataType.int },
+
+        { name: '(期)第几期', field: 's_order', type: dataType.int },
+        { name: '(期)本期-金额', field: 't_tp', type: dataType.currency },
+
+        { name: '交叉排序', field: 'cross_index', type: dataType.int },
     ],
 };
 
@@ -1206,7 +1199,7 @@ const defines = [
     stage_im_zl, stage_im_tz, stage_im_tz_bills,
     gather_stage_bills, gather_tender_info, gather_stage_pay, gather_deal_bills,
     material, materialGl,
-    sum_stage_bills
+    stage_sum_bills, stage_sum_pay
 ];
 for (const d of defines) {
     exportTableDefine(d);

+ 2 - 1
config/config.default.js

@@ -68,9 +68,10 @@ module.exports = appInfo => {
     // session配置
     config.session = {
         key: 'ZHC_SESS',
-        maxAge: 3600 * 1000, // 1小时
+        maxAge: 3600 * 1000 * 24, // 1小时
         httpOnly: true,
         encrypt: true,
+        rolling: true, // 每次都更新session有效期
     };
 
     // session使用redis

+ 5 - 0
config/menu.js

@@ -286,6 +286,11 @@ const profileMenu = {
         display: false,
         url: '/profile/sms',
     },
+    wechat: {
+        name: '微信通知',
+        display: false,
+        url: '/profile/wechat',
+    },
     sign: {
         name: '电子签名',
         display: false,

+ 4 - 0
sql/update.sql

@@ -95,3 +95,7 @@ ADD COLUMN `re_upload` INT NOT NULL DEFAULT 0 COMMENT '是否为审核通过后
 
 ALTER TABLE `zh_project_account` ADD `wx_openid` VARCHAR(50) NULL DEFAULT NULL COMMENT '微信绑定openid' AFTER `bind`;
 ALTER TABLE `zh_project_account` ADD `wx_name` VARCHAR(255) NULL DEFAULT NULL COMMENT '微信昵称' AFTER `wx_openid`;
+ALTER TABLE `zh_project_account` ADD `wx_type` TEXT NULL DEFAULT NULL COMMENT '微信通知类型' AFTER `sms_type`;
+
+ALTER TABLE `zh_rpt_custom_define`
+ADD COLUMN `stage_select`  text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '用户定制信息 - 多期汇总表' AFTER `gather_select`;