Browse Source

变更撤回功能提交

ellisran 1 year atrás
parent
commit
35f1ce8c39

+ 8 - 0
app/const/audit.js

@@ -399,6 +399,7 @@ const status = {
     back: 5, // 重新上报
     backnew: 6, // 退回
     revise: 9, // 修订变更
+    checkCancel: 11, // 撤回 // 该状态为上一审批人可发起,回到它到审批阶段,并同时新增一条新的审批中记录
 };
 const statusButton = [];
 statusButton[status.uncheck] = '上报';
@@ -408,6 +409,7 @@ statusButton[status.checked] = '';
 statusButton[status.back] = '重新上报';
 statusButton[status.backnew] = '审批';
 statusButton[status.revise] = '修订';
+statusButton[status.checkCancel] = '撤回';
 
 const statusButtonClass = [];
 statusButtonClass[status.uncheck] = 'btn-primary';
@@ -417,6 +419,7 @@ statusButtonClass[status.checked] = '';
 statusButtonClass[status.back] = 'btn-warning';
 statusButtonClass[status.backnew] = 'btn-success';
 statusButtonClass[status.revise] = 'btn-warning';
+statusButtonClass[status.checkCancel] = 'btn-warning';
 
 const statusString = [];
 statusString[status.uncheck] = '未上报';
@@ -426,6 +429,7 @@ statusString[status.checked] = '审批通过';
 statusString[status.back] = '审批退回';
 statusString[status.backnew] = '审批退回';
 statusString[status.revise] = '修订中';
+statusString[status.checkCancel] = '撤回';
 
 const statusClass = [];
 statusClass[status.uncheck] = '';
@@ -435,6 +439,7 @@ statusClass[status.checked] = 'text-success';
 statusClass[status.back] = 'text-warning';
 statusClass[status.backnew] = 'text-warning';
 statusClass[status.revise] = 'text-warning';
+statusClass[status.checkCancel] = 'text-warning';
 
 /* ------------------------------------------------------- */
 
@@ -449,6 +454,7 @@ const auditStatus = {
     checkAgain: 7, // 重新审批
     revise: 9, // 修订变更
     cancelRevise: 10, // 撤销修订
+    checkCancel: 11, // 撤回 // 该状态为上一审批人可发起,回到它到审批阶段,并同时新增一条新的审批中记录
 };
 
 const auditStatusString = [];
@@ -461,6 +467,7 @@ auditStatusString[auditStatus.backnew] = '审批退回';
 auditStatusString[auditStatus.checkAgain] = '重新审批';
 auditStatusString[auditStatus.revise] = '修订变更';
 auditStatusString[auditStatus.cancelRevise] = '撤销修订';
+auditStatusString[auditStatus.checkCancel] = '撤回';
 
 const auditStatusClass = [];
 auditStatusClass[auditStatus.uncheck] = '';
@@ -472,6 +479,7 @@ auditStatusClass[auditStatus.backnew] = 'text-warning';
 auditStatusClass[auditStatus.checkAgain] = 'text-warning';
 auditStatusClass[auditStatus.revise] = 'text-warning';
 auditStatusClass[auditStatus.cancelRevise] = 'text-success';
+auditStatusClass[auditStatus.checkCancel] = 'text-warning';
 
 /* ------------------------------------------------------- */
 

+ 17 - 0
app/controller/change_controller.js

@@ -1193,6 +1193,23 @@ module.exports = app => {
         }
 
         /**
+         * 撤回审批
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkAuditCancel(ctx) {
+            try {
+                if (!ctx.change.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.changeAudit.checkCancel(ctx.change);
+                ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err };
+            }
+        }
+
+        /**
          * 变更公司管理
          * @param {Object} ctx - egg全局变量
          * @return {void}

+ 8 - 0
app/middleware/change_check.js

@@ -58,6 +58,13 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 change.readOnly = true;
+            } else if (change.status === status.back && change.uid !== accountId) {
+                const preAuditors = yield this.service.changeAudit.getListGroupByTimes(change.cid, change.times - 1);
+                const preAuditorIds = _.map(preAuditors, 'uid');
+                if (preAuditorIds.indexOf(accountId) === -1) {
+                    throw '您无权查看该数据';
+                }
+                change.readOnly = true;
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }
@@ -66,6 +73,7 @@ module.exports = options => {
                 this.change.tp_decimal = this.tender.info.decimal.tp;
                 yield this.service.change.updateDecimalAndTp();
             }
+            yield this.service.change.doCheckChangeCanCancel(this.change);
             yield next;
         } catch (err) {
             console.log(err);

+ 1 - 0
app/router.js

@@ -518,6 +518,7 @@ module.exports = app => {
     app.post('/tender/:id/change/:cid/information/copy', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.copyChange');
     app.post('/tender/:id/change/:cid/information/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.addAudit');
     app.post('/tender/:id/change/:cid/information/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.deleteAudit');
+    app.post('/tender/:id/change/cancel/audit', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.checkAuditCancel');
     // 变更新增部位页
     app.get('/tender/:id/change/:cid/information/revise', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.reviseInfo');
     app.post('/tender/:id/change/:cid/information/revise/update', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.updateRevise');

+ 89 - 13
app/service/change.js

@@ -267,7 +267,9 @@ module.exports = app => {
                     case 0: // 包含你的所有变更令
                         sql =
                             'SELECT a.* FROM ?? AS a WHERE a.tid = ? AND ' +
-                            '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )' + stateSql;
+                            '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                            ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid))' +
+                            'OR a.status = ? )' + stateSql;
                         sqlParam = [
                             this.tableName,
                             tenderId,
@@ -275,6 +277,9 @@ module.exports = app => {
                             audit.flow.status.uncheck,
                             this.ctx.service.changeAudit.tableName,
                             this.ctx.session.sessionUser.accountId,
+                            audit.flow.status.back,
+                            this.ctx.service.changeAudit.tableName,
+                            this.ctx.session.sessionUser.accountId,
                             audit.flow.status.checked,
                         ];
                         break;
@@ -284,11 +289,17 @@ module.exports = app => {
                         break;
                     case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                         sql =
-                            'SELECT a.* FROM ?? AS a WHERE ' +
-                            'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? GROUP BY b.cid) AND ' +
-                            '(a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
+                            'SELECT a.* FROM ?? AS a WHERE' +
+                            ' ((a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                            ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid)))' +
+                            // 'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? GROUP BY b.cid)' +
+                            ' AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
                         sqlParam = [
                             this.tableName,
+                            audit.flow.status.uncheck,
+                            this.ctx.service.changeAudit.tableName,
+                            this.ctx.session.sessionUser.accountId,
+                            audit.flow.status.back,
                             this.ctx.service.changeAudit.tableName,
                             this.ctx.session.sessionUser.accountId,
                             audit.flow.status.uncheck,
@@ -350,7 +361,9 @@ module.exports = app => {
                 case 0: // 包含你的所有变更令
                     const sql =
                         'SELECT count(*) AS count FROM ?? AS a WHERE a.tid = ? AND ' +
-                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )' + stateSql;
+                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid))' +
+                        ' OR a.status = ? )' + stateSql;
                     const sqlParam = [
                         this.tableName,
                         tenderId,
@@ -358,6 +371,9 @@ module.exports = app => {
                         audit.flow.status.uncheck,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
                         audit.flow.status.checked,
                     ];
                     const result = await this.db.query(sql, sqlParam);
@@ -374,11 +390,17 @@ module.exports = app => {
                     return result6[0].count;
                 case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                     const sql2 =
-                        'SELECT count(*) AS count FROM ?? AS a WHERE ' +
-                        'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
-                        'AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
+                        'SELECT count(*) AS count FROM ?? AS a WHERE' +
+                        ' ((a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid)))' +
+                        // 'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
+                        ' AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
                     const sqlParam2 = [
                         this.tableName,
+                        audit.flow.status.uncheck,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
                         audit.flow.status.uncheck,
@@ -417,7 +439,9 @@ module.exports = app => {
                 case 0: // 包含你的所有变更令
                     const sql =
                         'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE a.tid = ? AND ' +
-                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )';
+                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid))' +
+                        ' OR a.status = ? )';
                     const sqlParam = [
                         this.tableName,
                         tenderId,
@@ -425,6 +449,9 @@ module.exports = app => {
                         audit.flow.status.uncheck,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
                         audit.flow.status.checked,
                     ];
                     const result = await this.db.query(sql, sqlParam);
@@ -436,11 +463,17 @@ module.exports = app => {
                     return result6[0].total_price ? result6[0].total_price : 0;
                 case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                     const sql2 =
-                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE ' +
-                        'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
-                        'AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?';
+                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE' +
+                        ' ((a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid)))' +
+                        // 'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
+                        ' AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?';
                     const sqlParam2 = [
                         this.tableName,
+                        audit.flow.status.uncheck,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
                         audit.flow.status.uncheck,
@@ -1045,6 +1078,8 @@ module.exports = app => {
                 const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
                     where: { cid: changeInfo.cid },
                 });
+                // 生成内容保存表至zh_change_history中,用于撤回
+                await this.ctx.service.changeHistory.saveHistory(this.transaction, changeData, changeList);
                 let total_price = 0;
                 const tp_decimal = changeData.tp_decimal ? changeData.tp_decimal : this.ctx.tender.info.decimal.tp;
                 for (const cl of changeList) {
@@ -1191,7 +1226,7 @@ module.exports = app => {
                 const tp_decimal = changeData.tp_decimal ? changeData.tp_decimal : this.ctx.tender.info.decimal.tp;
                 for (const cl of changeList) {
                     const audit_amount = cl.audit_amount.split(',');
-                    const last_amount = audit_amount[audit_amount.length - 1];
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
                     audit_amount.splice(-1, 1);
                     const list_update = {
                         id: cl.id,
@@ -1236,6 +1271,7 @@ module.exports = app => {
                 };
                 await this.ctx.helper.sendWechat(lastauditInfo.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
             } catch (error) {
+                console.log(error);
                 await this.transaction.rollback();
                 result = false;
             }
@@ -1830,6 +1866,46 @@ module.exports = app => {
             }
             return 0;
         }
+
+        async doCheckChangeCanCancel(change) {
+            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
+            const status = audit.flow.status;
+            const auditStatus = audit.flow.auditStatus;
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const auditors = await this.ctx.service.changeAudit.getListOrderByTimes(change.cid, change.times);
+            change.cancancel = 0;
+            if (change.status !== status.checked && change.status !== status.uncheck) {
+                if (change.status !== status.back) {
+                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
+                    const onAuditor = this._.find(auditors, function(item) {
+                        return item.uid === change.curAuditor.uid && item.status === auditStatus.checking;
+                    });
+
+                    const preAudit = onAuditor.usort > 1 ? this._.find(auditors, { usort: onAuditor.usort - 1 }) : false;
+                    const preAid = preAudit ? (preAudit.status !== auditStatus.checkAgain ? preAudit.uid : false) : change.uid;
+                    // console.log(onAuditor, preAudit, auditors);
+                    if (onAuditor.uid === preAid && preAudit.status === auditStatus.checkCancel) {
+                        return;// 不可以多次撤回
+                    } else if (preAid === accountId && (preAid !== change.uid || (preAid === change.uid && preAudit.usite !== 0))) {
+                        if (preAudit.status === auditStatus.checked) {
+                            change.cancancel = 2;// 审批人撤回审批通过
+                        } else if (preAudit.status === auditStatus.backnew) {
+                            change.cancancel = 3;// 审批人撤回审批退回上一人
+                        }
+                        change.preAudit = preAudit;
+                    } else if (preAid === accountId && preAid === change.uid && preAudit.usite === 0) {
+                        change.cancancel = 1;// 原报撤回
+                    }
+                } else {
+                    const lastAuditors = await this.service.changeAudit.getAuditors(change.cid, change.times - 1);
+                    const onAuditor = this._.findLast(lastAuditors, { status: auditStatus.back });
+                    if (onAuditor.uid === accountId) {
+                        change.cancancel = 4;// 审批人撤回退回原报
+                    }
+                }
+            }
+        }
+
     }
 
     return Change;

+ 345 - 1
app/service/change_audit.js

@@ -104,7 +104,7 @@ module.exports = app => {
             const statusConst = auditConst.status;
             const auditStatusConst = auditConst.auditStatus;
             const uid = this.ctx.session.sessionUser.accountId;
-            const changeAuditInfo = await this.getAllDataByCondition({ where: { cid: change.cid, times: change.times, uid }, orders: [['id', 'desc']], limit: 1, offset: 0 });
+            const changeAuditInfo = await this.getAllDataByCondition({ where: { cid: change.cid, times: change.times, uid }, orders: [['usort', 'desc']], limit: 1, offset: 0 });
             if (!change.status === statusConst.checked && (changeAuditInfo === null || changeAuditInfo[0] === undefined) && !ctx.tender.isTourist) {
                 // 无权限查看此变更令
                 return 0;
@@ -680,6 +680,350 @@ module.exports = app => {
             const result = await this.db.queryOne(sql, sqlParam);
             return result ? result.num : 0;
         }
+
+        /**
+         * 审批撤回
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkCancel(change) {
+
+            // 分4种情况,根据ctx.cancancel值判断:
+            // 1.原报发起撤回,当前流程删除,并回到待上报
+            // 2.审批人撤回审批通过,增加流程,并回到它审批中
+            // 3.审批人撤回审批退回上一人,并删除退回人,增加流程,并回到它审批中,并更新计量期状态为审批中
+            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
+            switch (change.cancancel) {
+                case 1: await this._userCheckCancel(change); break;
+                case 2: await this._auditCheckCancel(change); break;
+                case 3: await this._auditCheckCancelNoPre(change); break;
+                case 4: await this._auditCheckCancelNo(change); break;
+                default: throw '不可撤回,请刷新页面重试';
+            }
+            // if (stage.cancancel === 5) {
+            //     await this._auditCheckCancelAnd(stage);
+            // } else {
+            //     switch (this.ctx.stage.cancancel) {
+            //         case 1: await this._userCheckCancel(stage); break;
+            //         case 2: await this._auditCheckCancel(stage); break;
+            //         case 3: await this._auditCheckCancelNoPre(stage); break;
+            //         case 4: await this._auditCheckCancelNo(stage); break;
+            //         default: throw '不可撤回,请刷新页面重试';
+            //     }
+            //     // 通知发送 - 第三方更新
+            //     if (this.ctx.session.sessionProject.custom && syncApiConst.notice_type.indexOf(this.ctx.session.sessionProject.customType) !== -1) {
+            //         const base_data = {
+            //             tid: stage.tid,
+            //             sid: stage.id,
+            //             op: 'update',
+            //         };
+            //         this.ctx.helper.syncNoticeSend(this.ctx.session.sessionProject.customType, JSON.stringify(base_data));
+            //         base_data.op = 'update';
+            //         base_data.sid = -1;
+            //         this.ctx.helper.syncNoticeSend(this.ctx.session.sessionProject.customType, JSON.stringify(base_data));
+            //     }
+            // }
+        }
+        /**
+         * 原报撤回,直接改动审批人状态
+         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
+         *
+         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
+         * ...
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _userCheckCancel(change) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, status: auditConst.auditStatus.checking });
+                // // 审批人变成待审批状态
+                await transaction.update(this.tableName, {
+                    id: curAudit.id,
+                    status: auditConst.auditStatus.uncheck,
+                    sin_time: null,
+                    sdesc: null,
+                });
+                // 原报变成checking状态
+                const ybAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, usite: 0, status: auditConst.auditStatus.checked });
+                await transaction.update(this.tableName, {
+                    id: ybAudit.id,
+                    status: auditConst.auditStatus.checking,
+                    sin_time: new Date(),
+                });
+                // 变更令变成待上报状态
+                const options = {
+                    where: {
+                        cid: change.cid,
+                    },
+                };
+                await transaction.update(this.ctx.service.change.tableName, {
+                    status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.back,
+                }, options);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+        }
+        /**
+         * 审批人撤回审批通过,插入两条数据
+         *
+         * 一审 1 A checked             一审 1 A checked
+         * 二审 2 B checked   pre ->    二审 2 B checked
+         * 三审 3 C checking  cur       二审 3 B checkCancel  增                增extra_his     增tp_his
+         * 四审 4 D uncheck             二审 4 B checking     增                增pay_cur
+         *                             三审 5 C uncheck      order、status改
+         *                             四审 6 D uncheck      order改
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancel(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.usite <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `usort` = `usort` + 2 WHERE cid = ? AND times = ? AND `usort` > ?';
+                await transaction.query(sql, [change.cid, change.times, curAudit.usort]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                // 先入撤回记录
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: preAudit.uid,
+                    name: preAudit.name,
+                    jobs: preAudit.jobs,
+                    company: preAudit.company,
+                    times: change.times,
+                    usite: preAudit.usite,
+                    usort: curAudit.usort,
+                    status: auditConst.auditStatus.checkCancel,
+                    sin_time: time,
+                    sdesc: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: preAudit.uid,
+                    name: preAudit.name,
+                    jobs: preAudit.jobs,
+                    company: preAudit.company,
+                    times: change.times,
+                    usite: preAudit.usite,
+                    usort: curAudit.usort + 1,
+                    status: auditConst.auditStatus.checking,
+                    sin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 当前审批人变成待审批
+                await transaction.update(this.tableName, { id: curAudit.id, usort: curAudit.usort + 2, sin_time: null, status: auditConst.auditStatus.uncheck });
+                // 审批列表数据也要回退
+                const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
+                    where: { cid: change.cid },
+                });
+                let total_price = 0;
+                const tp_decimal = change.tp_decimal ? change.tp_decimal : this.ctx.tender.info.decimal.tp;
+                const updateList = [];
+                for (const cl of changeList) {
+                    const audit_amount = cl.audit_amount.split(',');
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
+                    audit_amount.splice(-1, 1);
+                    const list_update = {
+                        id: cl.id,
+                        audit_amount: audit_amount.join(','),
+                        spamount: parseFloat(last_amount),
+                    };
+                    total_price = this.ctx.helper.add(total_price, this.ctx.helper.mul(cl.unit_price, parseFloat(last_amount), tp_decimal));
+                    updateList.push(list_update)
+                }
+                if (updateList.length > 0) await transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateList);
+                // 变更令变成待上报状态
+                const options = {
+                    where: {
+                        cid: change.cid,
+                    },
+                };
+                await transaction.update(this.ctx.service.change.tableName, {
+                    status: auditConst.status.checking,
+                    total_price,
+                    cin_time: Date.parse(time) / 1000,
+                }, options);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回上一人,插入两条数据
+         *
+         * 一审 1 A checked                   一审 1 A checked
+         * 二审 2 B checked                   二审 2 B checked
+         * 三审 3 C checkNoPre  pre   ->      三审 3 C checkNoPre
+         * 二审 4 B checking    cur           三审 4 C checkCancel   删4B 增4C    增extra_his    增tp_his
+         * 三审 5 C uncheck                   三审 5 C checking      status改     增pay_cur
+         * 四审 6 D uncheck                   四审 6 D uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNoPre(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.order <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 整理当前流程审核人状态更新
+                // 删除当前审批人
+                await transaction.delete(this.tableName, { id: curAudit.id });
+                // 添加撤回人到审批流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: preAudit.uid,
+                    name: preAudit.name,
+                    jobs: preAudit.jobs,
+                    company: preAudit.company,
+                    times: change.times,
+                    usite: preAudit.usite,
+                    usort: curAudit.usort,
+                    status: auditConst.auditStatus.checkCancel,
+                    sin_time: time,
+                    sdesc: '',
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 更新上一个人,最新审批状态为审批中
+                await transaction.update(this.tableName,  { sin_time: time, status: auditConst.status.checking }, {
+                    where: { cid: change.cid, times: change.times, usort: curAudit.usort + 1 }
+                });
+                // 回退spamount值数据
+                const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
+                    where: { cid: change.cid },
+                });
+                let total_price = 0;
+                const tp_decimal = change.tp_decimal ? change.tp_decimal : this.ctx.tender.info.decimal.tp;
+                const updateList = [];
+                for (const cl of changeList) {
+                    const audit_amount = cl.audit_amount.split(',');
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
+                    const list_update = {
+                        id: cl.id,
+                        spamount: parseFloat(last_amount),
+                    };
+                    total_price = this.ctx.helper.add(total_price, this.ctx.helper.mul(cl.unit_price, parseFloat(last_amount), tp_decimal));
+                    updateList.push(list_update);
+                }
+                if (updateList.length > 0) await transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateList);
+                // 变更令变成待上报状态
+                const options = {
+                    where: {
+                        cid: change.cid,
+                    },
+                };
+                await transaction.update(this.ctx.service.change.tableName, {
+                    status: auditConst.status.checking,
+                    total_price,
+                    cin_time: Date.parse(time) / 1000,
+                }, options);
+
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回原报
+         *
+         * 1# 一审 1 A checked              1# 一审 1 A checked
+         *    二审 2 B checkNo   pre   ->      二审 2 B checkNo
+         *    三审 3 C uncheck                 二审 3 B checkCancel    增       pay: 2#0 -> 1#3   jl: 2#0 -> 1#3   增tp_his   增extra_his
+         *                                    二审 4 B checking       增       pay: 2#0 -> 1#4
+         *                                    三审 5 C uncheck        order改
+         *
+         * 2# 一审 1 A uncheck              2#                        删       pay: 2#0删   jl: 2#0删
+         *    二审 2 B uncheck
+         *    三审 3 C uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNo(change) {
+
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times - 1, status: auditConst.auditStatus.back });
+                // 整理上一个流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `usort` = `usort` + 2 WHERE cid = ? AND times = ? AND `usort` > ?';
+                await transaction.query(sql, [change.cid, change.times -1, curAudit.usort]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: curAudit.uid,
+                    name: curAudit.name,
+                    jobs: curAudit.jobs,
+                    company: curAudit.company,
+                    times: curAudit.times,
+                    usite: curAudit.usite,
+                    usort: curAudit.usort + 1,
+                    status: auditConst.auditStatus.checkCancel,
+                    sin_time: time,
+                    sdesc: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: curAudit.uid,
+                    name: curAudit.name,
+                    jobs: curAudit.jobs,
+                    company: curAudit.company,
+                    times: curAudit.times,
+                    usite: curAudit.usite,
+                    usort: curAudit.usort + 2,
+                    status: auditConst.auditStatus.checking,
+                    sin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 删除当前次审批流
+                await transaction.delete(this.tableName, { cid: change.cid, times: change.times });
+                // 回退数据
+                await this.ctx.service.changeHistory.returnHistory(transaction, change.cid);
+                await transaction.delete(this.ctx.service.changeHistory.tableName, { cid: change.cid });
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
 
     return ChangeAudit;

+ 3 - 0
app/view/change/information.ejs

@@ -75,6 +75,9 @@
                     </div>
                 </div>
                 <div class="pull-right mr-3" id="sp-btn">
+                    <% if (ctx.change.cancancel) { %>
+                        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm">撤回</a>
+                    <% } %>
                     <!--info状态区分-->
                     <% if (auditStatus === 1) { %>
                         <a href="#sub-ap" data-category="up_change" data-toggle="modal" data-target="#sub-ap" class="btn btn-primary btn-sm">上报审批</a>

+ 45 - 6
app/view/change/information_modal.ejs

@@ -297,7 +297,7 @@
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                        <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -452,7 +452,7 @@
                                                         <div class="timeline-item-icon bg-success text-light">
                                                             <i class="fa fa-check"></i>
                                                         </div>
-                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                         <div class="timeline-item-icon bg-warning text-light">
                                                             <i class="fa fa-level-up"></i>
                                                         </div>
@@ -582,7 +582,7 @@
                                                         <div class="timeline-item-icon bg-success text-light">
                                                             <i class="fa fa-check"></i>
                                                         </div>
-                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                         <div class="timeline-item-icon bg-warning text-light">
                                                             <i class="fa fa-level-up"></i>
                                                         </div>
@@ -671,7 +671,7 @@
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -819,7 +819,7 @@
                                                         <div class="timeline-item-icon bg-success text-light">
                                                             <i class="fa fa-check"></i>
                                                         </div>
-                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                         <div class="timeline-item-icon bg-warning text-light">
                                                             <i class="fa fa-level-up"></i>
                                                         </div>
@@ -908,7 +908,7 @@
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -1197,6 +1197,24 @@
         </div>
     </div>
 </div>
+<% if (ctx.change.cancancel) { %>
+    <div class="modal fade" id="sp-down-cancel" 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>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-danger btn-sm" id="cancel-shenpi-btn">确定撤回</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
 <script type="text/javascript">
     const csrf = '<%= ctx.csrf %>';
     const authMobile = '<%= authMobile %>';
@@ -1320,4 +1338,25 @@
             }
         });
     })
+
+    <% if (ctx.change && ctx.change.cancancel) { %>
+    $("#cancel-shenpi-btn").click(function () {
+        const data = {
+            cid: '<%- change.cid %>',
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/cancel/audit?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toast(response.msg, 'error');
+                }
+            }
+        });
+    });
+    <% } %>
 </script>