Browse Source

台账审批,重新审批

MaiXinRong 1 year ago
parent
commit
8cf6c4b51b

+ 7 - 0
app/const/audit.js

@@ -30,6 +30,7 @@ const ledger = (function() {
         checking: 2, // 待审批|审批中
         checked: 3, // 审批通过
         checkNo: 4, // 审批退回
+        checkAgain: 6, // 重新审批 // 该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
     };
 
     const statusString = [];
@@ -37,12 +38,14 @@ const ledger = (function() {
     statusString[status.checking] = '审批中';
     statusString[status.checked] = '审批完成';
     statusString[status.checkNo] = '审批退回';
+    statusString[status.checkAgain] = '重新审批';
 
     const statusClass = [];
     statusClass[status.uncheck] = '';
     statusClass[status.checking] = '';
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-warning';
+    statusClass[status.checkAgain] = 'text-warning';
 
     // 标段概况页
     // 描述文本
@@ -51,12 +54,14 @@ const ledger = (function() {
     auditString[status.checking] = '审批中';
     auditString[status.checked] = '审批通过';
     auditString[status.checkNo] = '审批退回';
+    auditString[status.checkAgain] = '重新审批';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
     auditStringClass[status.checking] = 'text-warning';
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
+    auditStringClass[status.checkAgain] = 'text-warning';
 
     // 金额概况
 
@@ -65,11 +70,13 @@ const ledger = (function() {
     tiStatusString[status.checking] = '审批中';
     tiStatusString[status.checked] = '审批通过';
     tiStatusString[status.checkNo] = '审批退回';
+    tiStatusString[status.checkAgain] = '审批中';
     const tiStatusStringClass = [];
     tiStatusStringClass[status.uncheck] = '';
     tiStatusStringClass[status.checking] = 'text-warning';
     tiStatusStringClass[status.checked] = 'text-success';
     tiStatusStringClass[status.checkNo] = 'text-warning';
+    tiStatusStringClass[status.checkAgain] = 'text-warning';
     return { status, statusString, statusClass, auditString, auditStringClass, tiStatusString, tiStatusStringClass };
 })();
 

+ 27 - 0
app/controller/ledger_audit_controller.js

@@ -161,6 +161,33 @@ module.exports = app => {
                 ctx.redirect(ctx.request.header.referer);
             }
         }
+
+        /**
+         * 重新审批(post)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkAgain(ctx) {
+            try {
+                const tender = ctx.tender;
+                if (!tender.data || tender.data.ledger_status !== auditConst.status.checked) {
+                    throw '当前标段数据有误';
+                }
+                const revise = await this.ctx.service.ledgerRevise.getLastestRevise(tender.id);
+                if (revise) throw '当前标段存在台账修订,不可重新审批';
+                const stage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id });
+                if (stage) throw '当前标段已开始计量,不可重新审批';
+
+                await ctx.service.ledgerAudit.checkAgain(tender.id, tender.data.ledger_times);
+
+                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+            } catch (err) {
+                this.log(err);
+                ctx.session.postError = err.toString();
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
     }
 
     return LedgerAuditController;

+ 7 - 0
app/controller/ledger_controller.js

@@ -131,6 +131,12 @@ module.exports = app => {
                 const whiteList = this.ctx.app.config.multipart.whitelist;
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const syncLedger = await this.ctx.service.specPull.syncLedger(this.ctx.session.sessionProject.id);
+                // 是否已验证手机短信
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const revise = await this.ctx.service.ledgerRevise.getLastestRevise(tender.id);
+                tender.data.hasRevise = !!revise;
+                const stage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id });
+                tender.data.hasStage = !!stage;
                 const renderData = {
                     tender: tender.data,
                     tenderInfo: tender.info,
@@ -154,6 +160,7 @@ module.exports = app => {
                     categoryData,
                     syncLedgerUrl: syncLedger ? `${ctx.app.config.url3f}/${syncLedger.pull_class}/sync-tz?pCode=${tender.id}` : '',
                     nodeType: stdConst.nodeType,
+                    authMobile: pa.auth_mobile,
                 };
                 if ((tender.data.ledger_status === auditConst.status.uncheck || tender.data.ledger_status === auditConst.status.checkNo) && tender.data.user_id === ctx.session.sessionUser.accountId) {
                     // renderData.accountGroup = accountGroup;

+ 1 - 0
app/router.js

@@ -227,6 +227,7 @@ module.exports = app => {
     app.post('/tender/:id/ledger/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.remove');
     app.post('/tender/:id/ledger/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, ledgerAuditCheck, tenderBuildCheck, 'ledgerAuditController.start');
     app.post('/tender/:id/ledger/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'ledgerAuditController.check');
+    app.post('/tender/:id/ledger/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'ledgerAuditController.checkAgain');
 
     // 部位台账
     app.get('/tender/:id/ledger/bwtz', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.bwtz');

+ 60 - 22
app/service/ledger_audit.js

@@ -436,6 +436,66 @@ module.exports = app => {
             }
         }
 
+
+        /**
+         * 重新审批
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkAgain(tenderId, times = 1) {
+            const time = new Date();
+            // 整理当前流程审核人状态更新
+            const auditors = await this.getAllDataByCondition({
+                where: { tender_id: tenderId, times },
+                orders: [['audit_order', 'desc']],
+            });
+            if (auditors.length <= 0) throw '台账审核数据错误';
+            const selfAudit = auditors[0];
+            if (selfAudit.audit_id !== this.ctx.session.sessionUser.accountId) throw '当前台账您无权重审';
+            const tender = this.ctx.service.tender.getDataById(tenderId);
+            if (!tender) throw '标段数据错误';
+
+            let otherAuditIds = [tender.user_id, ...auditors.map(x => { return x.audit_id })];
+            otherAuditIds = this._.uniq(otherAuditIds).filter(x => { return x !== selfAudit.audit_id; });
+
+            const checkAgainAuditor = {
+                tender_id: tenderId, times, audit_order: selfAudit.audit_order + 1, audit_id: selfAudit.audit_id,
+                begin_time: time, end_time: time, opinion: '', status: auditConst.status.checkAgain,
+            };
+            const checkingAuditor = {
+                tender_id: tenderId, times, audit_order: selfAudit.audit_order + 2, audit_id: selfAudit.audit_id,
+                begin_time: time, end_time: null, opinion: '', status: auditConst.status.checking,
+            };
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 当前审批人2次添加至流程中
+                await transaction.insert(this.tableName, checkAgainAuditor);
+                await transaction.insert(this.tableName, checkingAuditor);
+                // 同步标段信息
+                await transaction.update(this.ctx.service.tender.tableName, {
+                    id: tenderId,
+                    ledger_status: auditConst.status.checking,
+                });
+                await this.ctx.service.tenderCache.updateLedgerCache(transaction, tenderId, auditConst.status.checking, auditConst.status.checkAgain, checkingAuditor);
+
+                if (otherAuditIds.length > 0) {
+                    await this.ctx.helper.sendAliSms(otherAuditIds, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), SmsAliConst.template.ledger_check);
+                    // 微信模板通知
+                    const wechatData = {
+                        status: wxConst.status.check,
+                        tips: wxConst.tips.check,
+                        begin_time: Date.parse(time),
+                    };
+                    await this.ctx.helper.sendWechat(otherAuditIds, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), wxConst.template.ledger, wechatData);
+                }
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
         /**
          * 获取审核人需要审核的标段列表
          *
@@ -485,28 +545,6 @@ module.exports = app => {
         }
 
         /**
-         * 获取 某时间后 审批进度 更新的台账
-         * @param {Integer} pid - 项目id
-         * @param {Integer} uid - 查询人id
-         * @param {Date} noticeTime - 查询事件
-         * @return {Promise<*>}
-         */
-        async getNoticeTender(pid, uid, noticeTime) {
-            let notice = await this.db.select('zh_notice', {
-                where: { pid, type: pushType.ledger, uid },
-                orders: [['create_time', 'desc']],
-                limit: 10,
-                offset: 0,
-            });
-            notice = notice.map(v => {
-                const extra = JSON.parse(v.content);
-                delete v.content;
-                return { ...v, ...extra };
-            });
-            return notice;
-        }
-
-        /**
          * 用于添加推送所需的content内容
          * @param {Number} id  标段id
          * @param {Number} pid 项目id

+ 2 - 2
app/service/tender_cache.js

@@ -118,12 +118,12 @@ module.exports = app => {
 
         async updateLedgerCache(transaction, tid, status, checkType, next, tp) {
             const orgCache = await this.getDataById(tid);
-            const preInfo = JSON.parse(orgCache.ledger_flow_cur_info);
+            const preInfo = JSON.parse(orgCache.ledger_flow_cur_info || orgCache.ledger_flow_pre_info);
             preInfo.time = new Date();
             preInfo.status = checkType;
             const data = {
                 id: tid, ledger_status: status,
-                ledger_flow_pre_uid: orgCache.ledger_flow_cur_uid, ledger_flow_pre_info: JSON.stringify(preInfo),
+                ledger_flow_pre_uid: orgCache.ledger_flow_cur_uid || orgCache.ledger_flow_pre_uid, ledger_flow_pre_info: JSON.stringify(preInfo),
             };
             if (next) {
                 const info = await this.ctx.service.projectAccount.getAccountCacheData(next.audit_id);

+ 148 - 4
app/view/ledger/audit_modal.ejs

@@ -147,9 +147,8 @@
                                             <div class="card-body p-3">
                                                 <div class="card-text">
                                                     <p class="mb-1"><span class="h5"><%- auditor.name %></span>
-                                                        <span
-                                                            class="pull-right
-                                                                            <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
+                                                        <span class="pull-right <%- auditConst.statusClass[auditor.status] %>">
+                                                            <%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                             <%- auditor.status === auditConst.status.checkNo ? user.name : '' %>
                                                             <%- auditor.status === auditConst.status.checkNoPre ? auditors[index-1].name : '' %>
                                                         </span>
@@ -384,8 +383,62 @@
     </div>
 </div>
 <% } %>
+<% if (tender.ledger_status === auditConst.status.checked && auditors[auditors.length - 1].audit_id === ctx.session.sessionUser.accountId && !tender.hasStage && !tender.hasRevise) { %>
+<% if (!authMobile && ctx.session.sessionUser.loginStatus === 0) { %>
+<!--终审重新审批-->
+<div class="modal fade" id="sp-down-back" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">重新审批</h5>
+            </div>
+            <div class="modal-body">
+                <h5>重新审批需要您的手机短信验证</h5>
+                <h5>您目前还没设置认证手机,请先设置。</h5>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <a href="/profile/sms" class="btn btn-sm btn-primary">去设置</a>
+            </div>
+        </div>
+    </div>
+</div>
+<% } else { %>
+<div class="modal fade" id="sp-down-back" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="<%- preUrl %>/ledger/audit/check/again" method="post"
+              onsubmit="return auditAgainCheck();">
+            <div class="modal-header">
+                <h5 class="modal-title">重新审批</h5>
+            </div>
+            <div class="modal-body">
+                <h5>确认由「终审-<%= auditors[auditors.length-1].name %>」重新审批 ?
+                </h5>
+                <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+                <div class="form-group">
+                    <label>重审需要验证码确认,验证码将发送至尾号<%- authMobile.slice(-4) %>的手机</label>
+                    <div class="input-group input-group-sm mb-3">
+                        <input class="form-control" type="text" readonly="readonly" name="code"
+                               placeholder="输入短信中的6位验证码" />
+                        <div class="input-group-append">
+                            <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                        </div>
+                    </div>
+                </div>
+                <% } %>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                <button <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %> id="re-shenpi-btn"
+                        class="btn btn-warning btn-sm">确定重审</button>
+            </div>
+        </form>
+    </div>
+</div>
+<% } %>
+<% } %>
 <script>
-    const cur_uid  = parseInt('<%- ctx.session.sessionUser.accountId %>');
     $('.sp-location-list').on('shown.bs.modal', function () {
         const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
         const bdiv = (scrollBox.offset() && scrollBox.offset().top) || 0;
@@ -419,4 +472,95 @@
             })
         }
     });
+
+    // 重新审批获取手机验证码
+    // 获取验证码
+    let isPosting = false;
+    $("#get-code").click(function() {
+        if (isPosting) {
+            return false;
+        }
+        const btn = $(this);
+
+        $.ajax({
+            url: '/profile/code?_csrf_j=' + csrf,
+            type: 'post',
+            data: { mobile: authMobile, type: 'shenpi' },
+            dataTye: 'json',
+            error: function() {
+                isPosting = false;
+            },
+            beforeSend: function() {
+                isPosting = true;
+            },
+            success: function(response) {
+                isPosting = false;
+                if (response.err === 0) {
+                    codeSuccess(btn);
+                    $("input[name='code']").removeAttr('readonly');
+                    $("#re-shenpi-btn").removeAttr('disabled');
+                } else {
+                    toast(response.msg, 'error');
+                }
+            }
+        });
+    });
+    /**
+     * 获取成功后的操作
+     *
+     * @param {Object} btn - 点击的按钮
+     * @return {void}
+     */
+    function codeSuccess(btn) {
+        let counter = 60;
+        btn.addClass('disabled').text('重新获取 ' + counter + 'S');
+        btn.parent().siblings('input').removeAttr('readonly').attr('placeholder', '输入短信中的6位验证码');
+        const bindBtn = $("#bind-btn");
+        bindBtn.removeClass('btn-secondary disabled').addClass('btn-primary');
+
+        const countDown = setInterval(function() {
+            const countString = counter - 1 <= 0 ? '' : ' ' + (counter - 1) + 'S';
+            // 倒数结束后
+            if (countString === '') {
+                clearInterval(countDown);
+                btn.removeClass('disabled');
+            }
+            const text = '重新获取' + countString;
+            btn.text(text);
+            counter -= 1;
+        }, 1000);
+    }
+
+    // 重新审批按钮
+    $("#re-shenpi-btn").click(function () {
+        const data = {
+        };
+
+        $.ajax({
+            url: '<%- preUrl %>/audit/check/again',
+            type: 'get',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toast(response.msg, 'error');
+                }
+            }
+        });
+    });
+    function auditAgainCheck() {
+        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+        const code = $("#sp-down-back input[name='code']").val();
+        if ($(this).hasClass('disabled')) {
+            return false;
+        }
+        if (code.length < 6) {
+            toastr.error('请填写正确的验证码');
+            return false;
+        }
+        <% } %>
+        return true;
+    }
 </script>

+ 3 - 0
app/view/ledger/explode.ejs

@@ -69,6 +69,9 @@
                 <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm mr-1">审批通过</a>
                 <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm mr-1">审批退回</a>
                 <% }%>
+                <% if (tender.ledger_status === auditConst.status.checked && auditors[auditors.length - 1].audit_id === ctx.session.sessionUser.accountId && !tender.hasStage && !tender.hasRevise) { %>
+                <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm mr-1">重新审批</a>
+                <% }%>
             </div>
         </div>
     </div>