Преглед на файлове

调差会签或签及管理员修改审批流程

ellisran преди 6 месеца
родител
ревизия
97b9204109

+ 25 - 3
app/const/audit.js

@@ -11,8 +11,8 @@
 const auditType = (function () {
     const types = [
         { key: 'common', name: '个人', value: 1, short: '', long: '', class: '', },
-        { key: 'and', name: '会签', value: 2, short: '会', long: '多人会签', class: 'primary', valid: ['ledger', 'revise', 'stage', 'change', 'financial'] },
-        { key: 'or', name: '或签', value: 3, short: '或', long: '多人或签', class: 'success', valid: ['ledger', 'revise', 'stage', 'change', 'financial'] },
+        { key: 'and', name: '会签', value: 2, short: '会', long: '多人会签', class: 'primary', valid: ['ledger', 'revise', 'stage', 'change', 'material', 'financial'] },
+        { key: 'or', name: '或签', value: 3, short: '或', long: '多人或签', class: 'success', valid: ['ledger', 'revise', 'stage', 'change', 'material', 'financial'] },
         { key: 'union', name: '协同', value: 4, short: '协', long: '多人协同', class: 'warning', valid: ['stage']},
     ];
     const key = {};
@@ -543,6 +543,7 @@ const material = (function() {
         checkNo: 4, // 审批退回原报
         checkNoPre: 5, // 审批退回上一人
         checkAgain: 6, // 终审退回  --该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
+        checkSkip: 8, // 跳过
     };
     // 流程状态提示
     const statusString = [];
@@ -577,6 +578,27 @@ const material = (function() {
     statusButtonClass[status.checkNo] = 'btn-warning';
 
     // 描述文本
+    const auditString = [];
+    auditString[status.uncheck] = '';
+    auditString[status.checking] = '审批中';
+    auditString[status.checked] = '审批通过';
+    auditString[status.checkNo] = '审批退回';
+    auditString[status.checkNoPre] = '审批退回';
+    auditString[status.checkAgain] = '重新审批';
+    auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
+    // 文字样式
+    const auditStringClass = [];
+    auditStringClass[status.uncheck] = '';
+    auditStringClass[status.checking] = 'text-warning';
+    auditStringClass[status.checked] = 'text-success';
+    auditStringClass[status.checkNo] = 'text-warning';
+    auditStringClass[status.checkNoPre] = 'text-warning';
+    auditStringClass[status.checkAgain] = 'text-warning';
+    auditStringClass[status.checkCancel] = 'text-warning';
+    auditStringClass[status.checkSkip] = 'text-success';
+
+    // 描述文本
     const auditProgress = [];
     auditProgress[status.uncheck] = '待上报';
     auditProgress[status.checking] = '审批中';
@@ -588,7 +610,7 @@ const material = (function() {
     auditProgressClass[status.checking] = 'text-warning';
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checkNo] = 'text-warning';
-    return { status, statusString, statusClass, statusButton, statusButtonClass, auditProgress, auditProgressClass };
+    return { status, statusString, statusClass, statusButton, statusButtonClass, auditString, auditStringClass, auditProgress, auditProgressClass };
 })();
 
 // 预付款审批流程

+ 1 - 1
app/controller/login_controller.js

@@ -274,7 +274,7 @@ module.exports = app => {
                         throw '登录账号还没有认证手机,请联系项目管理员。';
                     }
                     // 重置密码并发短信
-                    const newpwd = ctx.helper.generateRandomString(6, 2);
+                    const newpwd = ctx.helper.generateRandomString(6);
                     console.log(newpwd);
                     const result = await ctx.service.projectAccount.resetPassword(pa.id, newpwd);
                     if (!result) {

+ 53 - 38
app/controller/material_controller.js

@@ -10,6 +10,7 @@
 
 const moment = require('moment');
 const auditConst = require('../const/audit').material;
+const auditType = require('../const/audit').auditType;
 const auditStageConst = require('../const/audit').stage;
 const tenderConst = require('../const/tender');
 const measureType = tenderConst.measureType;
@@ -60,6 +61,8 @@ module.exports = app => {
                     materialColShow: material_col_show,
                     qtySourceConst: materialConst.qty_source,
                     qtySourceValueConst: materialConst.qty_source_value,
+                    auditType,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.material.index),
                 };
                 let openMaterialTax = ctx.session.sessionProject.page_show.openMaterialTax;
                 let allMaterialTax = true;
@@ -81,7 +84,17 @@ module.exports = app => {
                 for (const s of renderData.materials) {
                     // s.curAuditor = null;
                     // 根据期状态返回展示用户
-                    s.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(s.id, s.status, s.times);
+                    // s.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(s.id, s.status, s.times);
+                    if (!s.final_auditor_str || s.status !== auditConst.status.checked) {
+                        // 根据期状态返回展示用户
+                        s.curAuditors = await ctx.service.materialAudit.getAuditorsByStatus(s.id, s.status, s.times);
+                        if (s.status === auditConst.status.checked && s.curAuditors.length > 0) {
+                            const final_auditor_str = (s.curAuditors[0].audit_type === auditType.key.common)
+                                ? `${s.curAuditors[0].name}${(s.curAuditors[0].role ? '-' + s.curAuditors[0].role : '')}`
+                                : ctx.helper.transFormToChinese(s.curAuditors[0].audit_order) + '审';
+                            await ctx.service.material.defaultUpdate({ id: s.id, final_auditor_str });
+                        }
+                    }
                     if (!ctx.session.sessionProject.page_show.openMaterialStageRepeat) {
                         const materialStageList = s.stage_id.split(',');
                         for (const ms of materialStageList) {
@@ -133,29 +146,12 @@ module.exports = app => {
                 if (!ctx.session.sessionProject.page_show.openMaterial) {
                     throw '该功能已关闭';
                 }
-                const responseData = {
-                    err: 0, msg: '', data: {},
-                };
                 const order = JSON.parse(ctx.request.body.data).order;
                 const tenderId = ctx.params.id;
                 const materialInfo = await ctx.service.material.getDataByCondition({ tid: tenderId, order });
-                // 获取审批流程中右边列表
-                const auditHistory = [];
-                const times = materialInfo.status === auditConst.status.checkNo ? materialInfo.times - 1 : materialInfo.times;
-                if (times >= 1) {
-                    for (let i = 1; i <= times; i++) {
-                        auditHistory.push(await ctx.service.materialAudit.getAuditors(materialInfo.id, i));
-                    }
-                }
-                responseData.data.auditHistory = auditHistory;
-                // 获取审批流程中左边列表
-                responseData.data.auditors = await ctx.service.materialAudit.getAuditorsWithOwner(materialInfo.id, times);
-
-                responseData.data.user = await ctx.service.projectAccount.getAccountInfoById(materialInfo.user_id);
-                // 获取原报信息
-                // const materialAuditor = await ctx.service.projectAccount.getAccountInfoById(materialInfo.user_id);
-                // responseData.data.materialAuditor = materialAuditor;
-                ctx.body = responseData;
+                await ctx.service.material.loadMaterialUser(materialInfo);
+                await ctx.service.material.loadMaterialAuditViewData(materialInfo);
+                ctx.body = { err: 0, msg: '', data: materialInfo };
             } catch (error) {
                 this.log(error);
                 ctx.body = { err: 1, msg: error.toString(), data: null };
@@ -310,8 +306,9 @@ module.exports = app => {
                 shenpiConst,
                 qtySourceConst: materialConst.qty_source,
                 qtySourceValueConst: materialConst.qty_source_value,
+                auditType,
             };
-            if ((ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.material.user_id || ctx.tender.isTourist)) {
+            if ((ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.material.user_id || ctx.tender.isTourist) || ctx.session.sessionUser.is_admin) {
                 // data.accountGroup = accountGroup;
                 // 获取所有项目参与者
                 const accountList = await ctx.service.projectAccount.getAllDataByCondition({
@@ -340,21 +337,22 @@ module.exports = app => {
          * @private
          */
         async _getMaterialAuditViewData(ctx) {
-            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 (times >= 1) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.material.auditHistory.push(await ctx.service.materialAudit.getAuditors(ctx.material.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            ctx.material.auditors2 = ctx.material.status === auditConst.status.checkNo && ctx.material.user_id !== ctx.session.sessionUser.accountId ?
-                await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, times) :
-                await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, ctx.material.times);
-            if (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) {
-                ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
-            }
+            // 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 (times >= 1) {
+            //     for (let i = 1; i <= times; i++) {
+            //         ctx.material.auditHistory.push(await ctx.service.materialAudit.getAuditors(ctx.material.id, i));
+            //     }
+            // }
+            // // 获取审批流程中左边列表
+            // ctx.material.auditors2 = ctx.material.status === auditConst.status.checkNo && ctx.material.user_id !== ctx.session.sessionUser.accountId ?
+            //     await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, times) :
+            //     await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, ctx.material.times);
+            // if (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) {
+            //     ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
+            // }
+            await ctx.service.material.loadMaterialAuditViewData(ctx.material);
 
             // 是否已验证手机短信
             const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
@@ -1243,7 +1241,7 @@ module.exports = app => {
                     throw '添加审核人失败';
                 }
 
-                const auditors = await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, ctx.material.times);
+                const auditors = await ctx.service.materialAudit.getUserGroup(ctx.material.id, ctx.material.times);
                 ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
                 this.log(err);
@@ -1745,6 +1743,23 @@ module.exports = app => {
                 };
             }
         }
+
+        async saveAudit(ctx) {
+            try {
+                if (!ctx.session.sessionUser.is_admin || ctx.material.status === auditConst.status.checked) {
+                    throw '您无权进行该操作';
+                }
+
+                const data = JSON.parse(ctx.request.body.data);
+                await ctx.service.materialAudit.saveAudit(ctx.material.id, ctx.material.times, 0, data);
+
+                const auditors = await ctx.service.materialAudit.getUniqUserGroup(ctx.material.id, ctx.material.times);
+                ctx.body = { err: 0, msg: '', data: auditors };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '保存审批人数据失败');
+            }
+        }
     }
 
     return MaterialController;

+ 17 - 3
app/controller/tender_controller.js

@@ -519,9 +519,9 @@ module.exports = app => {
                 let allMaterialTax = true;
                 if (materials && materials.length > 0) {
                     materialData = materials[0];
-                    materialData.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(materialData.id, materialData.status, materialData.times);
-                    const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
-                    materialData.auditors = materialData.status === auditConst.material.status.uncheck ? [] : await ctx.service.materialAudit.getFinalAuditGroup(materialData.id, times);
+                    // materialData.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(materialData.id, materialData.status, materialData.times);
+                    // const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
+                    // materialData.auditors = materialData.status === auditConst.material.status.uncheck ? [] : await ctx.service.materialAudit.getFinalAuditGroup(materialData.id, times);
                     for (const s of materials) {
                         if (allMaterialTax && s.material_tax === 0) {
                             allMaterialTax = false;
@@ -531,6 +531,20 @@ module.exports = app => {
                         }
                     }
                     materialData.decimal = materialData.decimal ? JSON.parse(materialData.decimal) : materialConst.decimal;
+                    materialData.status = materialData.status === auditConst.material.status.checkNoPre ? auditConst.material.status.checking : materialData.status;
+                    const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
+                    let cur;
+                    if (materialData.status === auditConst.material.status.checked) {
+                        cur = await this.ctx.service.materialAudit.getLastestAuditors(materialData.id, materialData.times, auditConst.material.status.checked);
+                    } else if (materialData.status === auditConst.material.status.checking) {
+                        cur = await this.ctx.service.materialAudit.getCurAuditors(materialData.id, materialData.times);
+                    } else {
+                        cur = await this.ctx.service.materialAudit.getAuditorsByStatus(materialData.id, materialData.status, materialData.times);
+                    }
+                    // const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(lastStage.id, lastStage.status, lastStage.times);
+                    materialData.status_users = cur.length > 0 ? (cur[0].audit_type === auditConst.auditType.key.common ? cur[0].name : ctx.helper.transFormToChinese(cur[0].audit_order) + '审') : '';
+                    const history = await ctx.service.materialAudit.getAuditorHistory(materialData.id, times);
+                    materialData.auditors = history[history.length - 1];
                 }
                 // 修订完成数目
                 // const reviseNum = await ctx.service.ledgerRevise.count({ tid: tender.id, status: auditConst.revise.status.checked });

+ 43 - 52
app/middleware/material_check.js

@@ -48,17 +48,20 @@ module.exports = options => {
             if (!material) {
                 throw '材料调差期数据错误';
             }
+
+            // 读取原报、审核人数据
+            yield this.service.material.loadMaterialUser(material);
             const openMaterialTax = this.session.sessionProject.page_show.openMaterialTax;
             if ((material.status === status.uncheck || material.status === status.checkNo) && material.material_tax !== openMaterialTax) {
                 yield this.service.material.updateMaterialTax(material.id, openMaterialTax);
                 material.material_tax = openMaterialTax;
             }
             // 读取原报、审核人数据
-            material.auditors = yield this.service.materialAudit.getAuditors(material.id, material.times);
+            // 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);
+            // material.curAuditor = yield this.service.materialAudit.getCurAuditor(material.id, material.times);
 
             // 权限相关
             // todo 校验权限 (标段参与人、分享)
@@ -66,64 +69,21 @@ module.exports = options => {
                 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;
-                // } else {
-                //     material.readOnly = material.status !== status.uncheck && material.status !== status.checkNo;
-                // }
-                material.curTimes = material.times;
-                if (material.status === status.uncheck || material.status === status.checkNo) {
-                    material.curOrder = 0;
-                } else if (material.status === status.checked) {
-                    material.curOrder = _.max(_.map(material.auditors, 'order'));
-                } else {
-                    material.curOrder = material.curAuditor.aid === accountId ? material.curAuditor.order : material.curAuditor.order - 1;
-                }
                 material.filePermission = true;
             } else if (this.tender.isTourist) {
-                material.curTimes = material.times;
-                if (material.status === status.uncheck || material.status === status.checkNo) {
-                    material.curOrder = 0;
-                } else if (material.status === status.checked) {
-                    material.curOrder = _.max(_.map(material.auditors, 'order'));
-                } else {
-                    material.curOrder = material.curAuditor.order;
-                }
                 material.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (material.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
                 // material.readOnly = material.status !== status.checking || accountId !== material.curAuditor.aid;
-                material.curTimes = material.status === status.checkNo ? material.times - 1 : material.times;
-                if (material.status === status.checked) {
-                    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,
-                    });
-                    material.curOrder = audit.order;
-                } else {
-                    material.curOrder = accountId === material.curAuditor.aid ? material.curAuditor.order : material.curAuditor.order - 1;
-                }
                 material.filePermission = true;
             } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
                 if (material.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
-                // material.readOnly = true;
-                material.curTimes = material.status === status.checkNo ? material.times - 1 : material.times;
-                material.curOrder = material.status === status.checked ? _.max(_.map(material.auditors, 'order')) : material.curAuditor.order - 1;
                 material.filePermission = false;
             } else if (this.session.sessionUser.is_admin) {
-                material.curTimes = material.times;
-                if (material.status === status.uncheck || material.status === status.checkNo) {
-                    material.curOrder = 0;
-                } else if (material.status === status.checked) {
-                    material.curOrder = _.max(_.map(material.auditors, 'order'));
-                } else {
-                    material.curOrder = material.curAuditor.order;
-                }
                 material.filePermission = true;
             } else { // 其他不可见
                 throw '您无权查看该数据';
@@ -135,33 +95,64 @@ module.exports = options => {
             });
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
             material.readOnly = !(((material.status === status.uncheck || material.status === status.checkNo) && accountId === material.user_id)
-                || (this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditor.aid === accountId));
-            material.editForAudit = this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditor.aid === accountId;
+                || (this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditorIds.indexOf(accountId) !== -1));
+            material.editForAudit = this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditorIds.indexOf(accountId) !== -1;
             material.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
+            // 判断stage流程可否撤回,是哪一种撤回
+            // yield this.service.material.doCheckMaterialCanCancel(material);
             this.material = material;
             // 根据状态判断是否需要更新审批人列表
             if ((material.status === status.uncheck || material.status === status.checkNo) && this.tender.info.shenpi.material !== shenpiConst.sp_status.sqspr) {
                 const shenpi_status = this.tender.info.shenpi.material;
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
                 const auditList = yield this.service.materialAudit.getAllDataByCondition({ where: { mid: material.id, times: material.times }, orders: [['order', 'asc']] });
-                const auditIdList = _.map(auditList, 'aid');
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
                     const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: { tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status } });
-                    const shenpiIdList = _.map(shenpiList, 'audit_id');
                     // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
-                    if (!_.isEqual(auditIdList, shenpiIdList)) {
-                        yield this.service.materialAudit.updateNewAuditList(material, shenpiIdList);
+                    let sameAudit = auditList.length === shenpiList.length;
+                    if (sameAudit) {
+                        for (const audit of auditList) {
+                            const shenpi = shenpiList.find(x => { return x.audit_id === audit.aid; });
+                            if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type) {
+                                sameAudit = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (!sameAudit) {
+                        yield this.service.materialAudit.updateNewAuditList(material, shenpiList);
+                        yield this.service.material.loadMaterialUser(material);
                     }
                 } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
                     const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status });
                     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
-                    if (shenpiInfo && shenpiInfo.audit_id !== _.last(auditIdList)) {
+                    const lastAuditors = auditList.filter(x => { x.order === auditList[auditList.length - 1].order; });
+                    if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
                         yield this.service.materialAudit.updateLastAudit(material, auditList, shenpiInfo.audit_id);
+                        yield this.service.material.loadMaterialUser(material);
                     } else if (!shenpiInfo) {
                         // 不存在终审人的状态下这里恢复为授权审批人
                         this.tender.info.shenpi.material = shenpiConst.sp_status.sqspr;
                     }
                 }
+                // const auditIdList = _.map(auditList, 'aid');
+                // if (shenpi_status === shenpiConst.sp_status.gdspl) {
+                //     const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: { tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status } });
+                //     const shenpiIdList = _.map(shenpiList, 'audit_id');
+                //     // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
+                //     if (!_.isEqual(auditIdList, shenpiIdList)) {
+                //         yield this.service.materialAudit.updateNewAuditList(material, shenpiIdList);
+                //     }
+                // } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
+                //     const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status });
+                //     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
+                //     if (shenpiInfo && shenpiInfo.audit_id !== _.last(auditIdList)) {
+                //         yield this.service.materialAudit.updateLastAudit(material, auditList, shenpiInfo.audit_id);
+                //     } else if (!shenpiInfo) {
+                //         // 不存在终审人的状态下这里恢复为授权审批人
+                //         this.tender.info.shenpi.material = shenpiConst.sp_status.sqspr;
+                //     }
+                // }
             }
             yield next;
         } catch (err) {

+ 2 - 2
app/middleware/stage_check.js

@@ -101,7 +101,7 @@ module.exports = options => {
                     stage.assist = ass;
                     stage.curOrder = stage.curAuditors[0].order;
                 } else {
-                    stage.curOrder = stage.curAuditors[0].order - 1
+                    stage.curOrder = stage.curAuditors[0].order - 1;
                 }
                 if (!stage.readOnly) {
                     if (stage.curAuditors[0].audit_type === auditType.key.and) {
@@ -133,7 +133,7 @@ module.exports = options => {
                 }
             }
             // 判断stage流程可否撤回,是哪一种撤回
-            yield this.service.stage.doCheckStageCanCancel(stage);
+            // yield this.service.stage.doCheckStageCanCancel(stage);
 
             // 是否台账修订中
             const lastRevise = yield this.service.ledgerRevise.getLastestRevise(this.tender.id);

+ 249 - 33
app/public/js/material_audit.js

@@ -59,7 +59,7 @@ $(document).ready(function () {
     })
 
     // 添加审批流程按钮逻辑
-    $('.book-list').on('click', 'dt', function () {
+    $('.book-list').on('click', '.book-list dt', function () {
         const idx = $(this).find('.acc-btn').attr('data-groupid')
         const type = $(this).find('.acc-btn').attr('data-type')
         if (type === 'hide') {
@@ -78,7 +78,7 @@ $(document).ready(function () {
     })
 
     // 添加到审批流程中
-    $('dl').on('click', 'dd', function () {
+    $('#book-list').on('click', 'dd', function () {
         const id = parseInt($(this).data('id'));
         if (id) {
             postData(getUrlPre() + '/audit/add', { auditorId: id }, (datas) => {
@@ -87,19 +87,23 @@ $(document).ready(function () {
                 const auditorshtml = [];
                 for (const [index,data] of datas.entries()) {
                     if (index !== 0) {
-                        html.push('<li class="list-group-item" auditorId="'+ data.aid +'">');
+                        html.push('<li class="list-group-item d-flex" auditorId="'+ data[0].aid +'">');
+                        html.push(`<div class="col-auto">${index}</div>`);
+                        html.push('<div class="col">');
+                        for (const auditor of data) {
+                            html.push(`<div class="d-inline-block mx-1"><i class="fa fa-user text-muted"></i> ${auditor.name} <small class="text-muted">${auditor.role}</small></div>`);
+                        }
+                        html.push('</div>');
+                        html.push('<div class="col-auto">');
+                        if (data[0].audit_type !== auditType.key.common) {
+                            html.push(`<span class="badge badge-pill badge-${auditType.info[data[0].audit_type].class} badge-bg-small"><small>${auditType.info[data[0].audit_type].long}</small></span>`);
+                        }
                         if (shenpi_status === shenpiConst.sp_status.sqspr || (shenpi_status === shenpiConst.sp_status.gdzs && index+1 !== datas.length)) {
-                            html.push('<a href="javascript: void(0)" class="text-danger pull-right">移除</a>');
+                            html.push('<a href="javascript: void(0)" class="text-danger pull-right ml-1">移除</a>');
                         }
-                        html.push('<span>');
-                        html.push(data.order + ' ');
-                        html.push(data.name + ' ');
-                        html.push('</span>');
-                        html.push('<small class="text-muted">');
-                        html.push(data.role);
-                        html.push('</small></li>');
+                        html.push('</div>');
+                        html.push('</li>');
                     }
-                    // 添加新审批人流程修改
                     auditorshtml.push('<li class="list-group-item" data-auditorid="' + data.aid + '">');
                     auditorshtml.push('<i class="fa ' + (index+1 === datas.length ? 'fa-stop-circle' : 'fa-chevron-circle-down') + '"></i> ');
                     auditorshtml.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
@@ -113,27 +117,7 @@ $(document).ready(function () {
                     auditorshtml.push('</li>');
                 }
                 $('#auditors').html(html.join(''));
-
-
-                // 重新上报时。令其它的审批人流程图标转换
-                // $('#auditors-list li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-                // for (let i = 0; i < $('#auditors-list li').length; i++) {
-                //     $('#auditors-list li').eq(i).find('.pull-right').text(transFormToChinese(i+1) + '审');
-                //     $('#auditors-list2 li').eq(i).find('.pull-right').text(transFormToChinese(i+1) + '审');
-                // }
-
                 $('#auditors-list').html(auditorshtml.join(''));
-
-                // const auditorshtml2 = [];
-                // // 重新上报时。令其它的审批人流程图标转换
-                // $('#auditors-list2 li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-                // // 添加新审批人
-                // auditorshtml2.push('<li class="list-group-item" data-auditid="' + data.aid + '">');
-                // auditorshtml2.push('<h5 class="card-title"><i class="fa fa-stop-circle"></i> ');
-                // auditorshtml2.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
-                // auditorshtml2.push('<span class="pull-right">终审</span>');
-                // auditorshtml2.push('</h5></li>');
-                // $('#auditors-list2').append(auditorshtml2.join(''));
             });
         }
     });
@@ -147,7 +131,7 @@ $(document).ready(function () {
             li.remove();
             for (const rst of result) {
                 const aLi = $('li[auditorId=' + rst.aid + ']');
-                $('span', aLi).text(rst.order + ' ' + rst.name + ' ');
+                $('div:first', aLi).text(rst.order);
             }
 
             // 如果是重新上报
@@ -213,6 +197,238 @@ $(document).ready(function () {
             }
         });
     });
+
+    // 管理员更改审批流程js部分
+    let timer2 = null;
+    let oldSearchVal2 = null;
+    $('body').on('input propertychange', '.gr-search', function(e) {
+        oldSearchVal2 = e.target.value;
+        timer2 && clearTimeout(timer2);
+        timer2 = setTimeout(() => {
+            const newVal = $(this).val();
+            const code = $(this).attr('data-code');
+            let html = '';
+            if (newVal && newVal === oldSearchVal2) {
+                accountList.filter(item => item && item.id !== material_uid && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                });
+                $('#' + code + '_dropdownMenu .book-list').empty();
+                $('#' + code + '_dropdownMenu .book-list').append(html);
+            } else {
+                if (!$('#' + code + '_dropdownMenu .acc-btn').length) {
+                    accountGroup.forEach((group, idx) => {
+                        if (!group) return;
+                        html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`;
+                        group.groupList.forEach(item => {
+                            if (item.id !== material_uid) {
+                                html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`;
+                            }
+                        });
+                        html += '</div>';
+                    });
+                    $('#' + code + '_dropdownMenu .book-list').empty();
+                    $('#' + code + '_dropdownMenu .book-list').append(html);
+                }
+            }
+        }, 400);
+    });
+
+    const getAdminEditShenpiListHtml = function(auditGroup) {
+        const html = [];
+        for (const [i, group] of auditGroup.entries()) {
+            if (i === 0) continue;
+            for (const [j, auditor] of group.entries()) {
+                html.push('<tr>');
+                if (j === 0) {
+                    const auditTypeHtml = auditor.type === auditType.key.common ? '' : `<span class="ml-2 badge badge-pill badge-${auditType.info[auditor.audit_type].class} p-1"><small>${auditType.info[auditor.audit_type].short}</small></span>`;
+                    html.push(`<td class="text-left d-flex">${i + '审'}${auditTypeHtml}</td>`);
+                } else {
+                    html.push(`<td class="text-left d-flex"></td>`);
+                }
+                html.push(`<td></span> ${auditor.name} <small class="text-muted">${auditor.role}</small></td>`);
+                html.push(`<td style="text-align: center"><span class="${sam_auditConst.auditStringClass[auditor.status]}">${ auditor.status !== sam_auditConst.status.uncheck ? sam_auditConst.auditString[auditor.status] : '待审批' }</span></td>`);
+                html.push('<td style="text-align: center">');
+                if (auditor.status === sam_auditConst.status.checking && j === group.length - 1) {
+                    html.push('<span class="dropdown mr-2">',
+                        `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">增加</a>`,
+                        makeSelectAudit(auditor.aid+'_add', auditor.aid, 'add'),'</div>', '</span>');
+                }
+                if (auditor.status === sam_auditConst.status.uncheck) {
+                    if (j === group.length - 1) {
+                        html.push('<span class="dropdown mr-2">',
+                            `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">增加</a>`,
+                            makeSelectAudit(auditor.aid+'_add', auditor.aid, 'add'),'</div>', '</span>');
+                        if (auditor.audit_type !== auditType.key.common) {
+                            html.push('<span class="dropdown mr-2">',
+                                `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">平级</a>`,
+                                makeSelectAudit(auditor.aid+'_add-sibling', auditor.aid, 'add-sibling'),'</div>', '</span>');
+                        }
+                    }
+                    html.push('<span class="dropdown mr-2">',
+                        `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">更换</a>`,
+                        makeSelectAudit(auditor.aid+'_change', auditor.aid, 'change'),'</div>', '</span>');
+                    html.push(`<span class="dropdown">
+                                    <a href="javascript: void(0)" class="text-danger" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">移除</a>
+                                    <div class="dropdown-menu">
+                                        <span class="dropdown-item" href="javascript:void(0);">确认移除审批人?</span>
+                                        <div class="dropdown-divider"></div>
+                                        <div class="px-2 py-1 text-center">
+                                            <button class="remove-audit btn btn-sm btn-danger" data-id="${auditor.aid}">移除</button>
+                                            <button class="btn btn-sm btn-secondary">取消</button>
+                                        </div>
+                                    </div>
+                                    </span>`);
+                }
+                html.push('</td>');
+                html.push('</tr>');
+            }
+        }
+        return html.join('');
+    };
+
+    $('body').on('click', '#admin-edit-shenpi dl dd', function () {
+        const id = parseInt($(this).attr('data-id'));
+        if (!id) return;
+
+        let this_aid = parseInt($(this).parents('.book-list').attr('data-aid'));
+        let this_operate = $(this).parents('.book-list').attr('data-operate');
+        const user = _.find(accountList, function (item) {
+            return item.id === id;
+        });
+        const auditorIndex = _.findIndex(auditorList, { aid: id });
+        if (auditorIndex !== -1) {
+            toastr.warning('该审核人已存在,请勿重复添加');
+            return;
+        }
+        const prop = {
+            operate: this_operate,
+            old_aid: this_aid,
+            new_aid: user.id,
+        };
+        postData(getUrlPre() + '/audit/save', prop, (datas) => {
+            $('#admin-edit-shenpi-list').html(getAdminEditShenpiListHtml(datas));
+            changeLiucheng(datas);
+        });
+    });
+
+    // 移除审批人
+    $('body').on('click', '.remove-audit', function () {
+        const id = parseInt($(this).attr('data-id'));
+        const prop = {
+            operate: 'del',
+            old_aid: id,
+        };
+        postData(getUrlPre() + '/audit/save', prop, (datas) => {
+            $('#admin-edit-shenpi-list').html(getAdminEditShenpiListHtml(datas));
+            changeLiucheng(datas);
+        });
+    });
+
+    const getAuditTypeText = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+    };
+
+    function changeLiucheng(datas) {
+        const auditorshtml = [];
+        let lastAuditorHtml = [];
+        for (const [index,data] of datas.entries()) {
+            auditorshtml.push('<li class="list-group-item d-flex justify-content-between align-items-center">');
+            if (index === 0) {
+                auditorshtml.push('<span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>');
+            } else if (index+1 === datas.length) {
+                auditorshtml.push('<span class="mr-1"><i class="fa fa fa-stop-circle"></i></span>');
+            } else {
+                auditorshtml.push('<span class="mr-1"><i class="fa fa-chevron-circle-down"></i></span>');
+            }
+            auditorshtml.push('<span class="text-muted">');
+            for (const u of data) {
+                auditorshtml.push(`<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`);
+            }
+            auditorshtml.push('</span>');
+            auditorshtml.push('<div class="d-flex ml-auto">');
+            if (data[0].audit_type !== auditType.key.common) auditorshtml.push(`<span class="badge badge-pill badge-${auditType.info[data[0].audit_type].class} p-1"><small>${auditType.info[data[0].audit_type].short}</small></span>`);
+            if (index === 0) {
+                auditorshtml.push('<span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>');
+            } else if (index+1 === datas.length) {
+                auditorshtml.push('<span class="badge badge-light badge-pill"><small>终审</small></span>');
+            } else {
+                auditorshtml.push('<span class="badge badge-light badge-pill"><small>'+ transFormToChinese(index) +'审</small></span>');
+            }
+            auditorshtml.push('</div>');
+            auditorshtml.push('</li>');
+            if (data[0].status === sam_auditConst.status.uncheck) {
+                lastAuditorHtml.push('<li class="timeline-list-item pb-2 is_uncheck">');
+                if (index < datas.length - 1) {
+                    lastAuditorHtml.push('<div class="timeline-item-tail"></div>');
+                }
+                lastAuditorHtml.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+
+                lastAuditorHtml.push('<div class="timeline-item-content">');
+                lastAuditorHtml.push(`<div class="py-1">
+                        <span class="text-black-50">
+                        ${ index !== datas.length - 1 ? data[0].audit_order + '' : '终' }审 ${getAuditTypeText(data[0].audit_type)}
+                        </span>
+                    </div>`);
+                lastAuditorHtml.push('<div class="card"><div class="card-body px-3 py-0">');
+                for (const [i, auditor] of data.entries()) {
+                    lastAuditorHtml.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
+                    lastAuditorHtml.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
+                    lastAuditorHtml.push('<div class="col">');
+                    lastAuditorHtml.push('</div>');
+                    lastAuditorHtml.push('</div>');
+                }
+                lastAuditorHtml.push('</div></div>');
+                lastAuditorHtml.push('</div>');
+                lastAuditorHtml.push('</li>');
+            }
+        }
+        $('.last-auditor-list .is_uncheck').remove();
+        $('.last-auditor-list').append(lastAuditorHtml.join(''));
+        $('.auditors-list').html(auditorshtml.join(''));
+
+    }
+
+    // 审批流程-选择审批人html 生成
+    function makeSelectAudit(code, aid, status) {
+        let divhtml = '';
+        accountGroup.forEach((group, idx) => {
+            let didivhtml = '';
+            if(group) {
+                group.groupList.forEach(item => {
+                    didivhtml += item.id !== material_uid ? '<dd class="border-bottom p-2 mb-0 " data-id="' + item.id + '" >\n' +
+                        '<p class="mb-0 d-flex"><span class="text-primary">' + item.name + '</span><span\n' +
+                        '                                                                                class="ml-auto">' + item.mobile + '</span></p>\n' +
+                        '                                                                    <span class="text-muted">' + item.role + '</span>\n' +
+                        '                                                                    </dd>\n' : '';
+                });
+                divhtml += '<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="' + idx + '" data-type="hide"><i class="fa fa-plus-square"></i></a> ' + group.groupName + '</dt>\n' +
+                    '                                                                <div class="dd-content" data-toggleid="' + idx + '">\n' + didivhtml +
+                    '                                                                </div>\n';
+            }
+        });
+        let html = '<div class="dropdown-menu dropdown-menu-right" id="' + code + '_dropdownMenu" aria-labelledby="' + code + '_dropdownMenuButton" style="width:220px">\n' +
+            '                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"\n' +
+            '                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-code="' + code + '"></div>\n' +
+            '                                                        <dl class="list-unstyled book-list" data-aid="'+ aid +'" data-operate="'+ status +'">\n' + divhtml +
+            '                                                        </dl>\n' +
+            '                                                    </div>\n' +
+            '                                                </div>\n' +
+            '                                            </span>\n' +
+            '                                        </span>\n' +
+            '                                        </li>';
+        return html;
+    }
 });
 // 检查上报情况
 function checkAuditorFrom () {

+ 123 - 142
app/public/js/measure_material.js

@@ -8,166 +8,147 @@
  * @version
  */
 $(function () {
+    const getGroupAuditHtml = function (group) {
+        return group.map(u => { return `<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`; }).join('');
+    };
+
+    const getAuditTypeHtml = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<div class="li-subscript"><span class="badge badge-pill badge-${auditType.info[type].class} p-1 badge-bg-small"><small>${auditType.info[type].short}</small></span></div>`;
+    };
+
+    const getAuditTypeText = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+    };
     // 获取审批流程
     $('a[data-target="#sp-list" ]').on('click', function () {
         const data = {
             order: $(this).attr('m-order'),
         };
         postData('/tender/' + tenderId + '/measure/material/auditors', data, function (result) {
-            const { auditHistory, auditors, user } = result
-            let auditorsHTML = ''
-            let historyHTML = ''
-            auditors.forEach((auditor, idx) => {
+            const { auditHistory, auditors2, user } = result
+            let auditorsHTML = [];
+            auditors2.forEach((group, idx) => {
                 if (idx === 0) {
-                    auditorsHTML += `<li class="list-group-item">
-                        <i class="fa fa fa-play-circle fa-rotate-90"></i> ${auditor.name}
-                        <small class="text-muted">${auditor.role}</small>
-                        <span class="pull-right">原报</span>
-                    </li>`
-                } else if(idx === auditors.length -1 && idx !== 0) {
-                    auditorsHTML += `<li class="list-group-item">
-                        <i class="fa fa fa-stop-circle"></i> ${auditor.name}
-                        <small class="text-muted">${auditor.role}</small>
-                        <span class="pull-right">终审</span>
-                    </li>`
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                    <span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
+                </li>`);
+                } else if(idx === auditors2.length -1 && idx !== 0) {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                    <span class="mr-1"><i class="fa fa fa-stop-circle fa-rotate-90"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <div class="d-flex ml-auto">
+                ${getAuditTypeHtml(group[0].audit_type)}
+                <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                </div>
+                </li>`);
                 } else {
-                    auditorsHTML += `<li class="list-group-item">
-                        <i class="fa fa-chevron-circle-down"></i> ${auditor.name}
-                        <small class="text-muted">${auditor.role}</small>
-                        <span class="pull-right">${transFormToChinese(idx)}审</span>
-                    </li>`
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                    <span class="mr-1"><i class="fa fa fa-chevron-circle-down"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <div class="d-flex ml-auto">
+                ${getAuditTypeHtml(group[0].audit_type)}
+                <span class="badge badge-light badge-pill"><small>${transFormToChinese(idx)}审</small></span>
+                </div>
+                </li>`);
                 }
-            })
+            });
             $('#auditor-list').empty()
-            $('#auditor-list').append(auditorsHTML)
-            const leftAuditors = auditors;
-            auditHistory.forEach((auditors, idx) => {
-                if(idx === auditHistory.length - 1 && auditHistory.length !== 1) {
-                    historyHTML += `<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show"
-                    >展开历史审批流程</a></div>`
+            $('#auditor-list').append(auditorsHTML.join(''));
+
+            let historyHTML = [];
+            auditHistory.forEach((his, idx) => {
+                if (idx === auditHistory.length - 1 && auditHistory.length !== 1) {
+                    historyHTML.push(`<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show">展开历史审批流程</a></div>`);
                 }
-                historyHTML += `<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">
-                <div class="text-center text-muted">${idx + 1}#</div>
-                <ul class="timeline-list list-unstyled mt-2">`
-                auditors.forEach((auditor, index) => {
+                historyHTML.push(`<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">`);
+                historyHTML.push(`<div class="text-center text-muted">${idx+1}#</div>`);
+                historyHTML.push(`<ul class="timeline-list list-unstyled mt-2 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
+                his.forEach((group, index) => {
                     if (index === 0) {
-                        historyHTML += `<li class="timeline-list-item pb-2">
-                            <div class="timeline-item-date">
-                                ${formatDate(auditor.begin_time)}
-                            </div>
-                            <div class="timeline-item-tail"></div>
-                            <div class="timeline-item-icon bg-success text-light">
-                                <i class="fa fa-caret-down"></i>
-                            </div>
-                            <div class="timeline-item-content">
-                                <div class="card">
-                                    <div class="card-body p-3">
-                                        <div class="card-text">
-                                            <p class="mb-1"><span
-                                                    class="h5">${user.name}</span><span
-                                                    class="pull-right text-success">${idx !== 0 ? '重新' : ''}上报审批</span>
-                                            </p>
-                                            <p class="text-muted mb-0">${user.role}</p>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </li>
-                        <li class="timeline-list-item pb-2">
-                            <div class="timeline-item-date">
-                                ${formatDate(auditor.end_time)}
-                            </div>`
-
-                            if(index < auditors.length - 1) {
-                                historyHTML += `<div class="timeline-item-tail"></div>`
-                            }
-                            if(auditor.status === auditConst.status.checked) {
-                                historyHTML += `<div class="timeline-item-icon bg-success text-light">
-                                    <i class="fa fa-check"></i>
-                                </div>`
-
-                            } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                    <i class="fa fa-level-up"></i>
-                                </div>`
-                            } else if(auditor.status === auditConst.status.checking) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                    <i class="fa fa-ellipsis-h"></i>
-                                </div>`
-                            } else {
-                                historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
-
-                            }
-                            historyHTML += `<div class="timeline-item-content">
-                                <div class="card">
-                                    <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]}">${auditConst.statusString[auditor.status]}</span>
-                                            </p>
-                                            <p class="text-muted mb-0">${auditor.role}</p>
-                                        </div>
-                                    </div>`
-                            if (auditor.opinion) {
-                            historyHTML += `<div class="card-body p-3 border-top">
-                                    <p style="margin: 0;">${auditor.opinion}</p>
-                                </div>`
-                            }
-                            historyHTML += `</div></div></li>`
+                        historyHTML.push(`<li class="timeline-list-item pb-2">
+                                            <div class="timeline-item-date">
+                                                ${group.beginYear}
+                                                <span>${group.beginDate}</span>
+                                                <span>${group.beginTime}</span>
+                                            </div>
+                                            <div class="timeline-item-tail"></div>
+                                            <div class="timeline-item-icon bg-success text-light"><i class="fa fa-caret-down"></i></div>
+                                            <div class="timeline-item-content">
+                                                <div class="py-1">
+                                                    <span class="text-black-50">原报</span>
+                                                    <span class="pull-right text-success">${idx !== 0 ? '重新' : '' }上报审批</span>
+                                                </div>
+                                                <div class="card">
+                                                    <div class="card-body px-3 py-0">
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col">
+                                                                <span class="h6">${user.name}</span>
+                                                                <span class="text-muted ml-1">${user.role}</span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </li>`);
+                    }
+                    historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
+                    if (group.endYear) {
+                        historyHTML.push(`<div class="timeline-item-date">${group.endYear}<span>${group.endDate}</span><span>${group.endTime}</span></div>`);
+                    }
+                    if (index < his.length - 1) {
+                        historyHTML.push('<div class="timeline-item-tail"></div>');
+                    }
+                    if (group.status === auditConst.status.checked) {
+                        historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
+                    } else if (group.status === auditConst.status.checkNo || group.status === auditConst.status.checkNoPre || group.status === auditConst.status.checkCancel) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
+                    } else if (group.status === auditConst.status.checking) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
                     } else {
-                        historyHTML += `<li class="timeline-list-item pb-2">
-                        <div class="timeline-item-date">
-                            ${formatDate(auditor.end_time)}
-                        </div>`
-
-                        if(index < auditors.length - 1) {
-                            historyHTML += `<div class="timeline-item-tail"></div>`
-                        }
-                        if(auditor.status === auditConst.status.checked) {
-                            historyHTML += `<div class="timeline-item-icon bg-success text-light">
-                                <i class="fa fa-check"></i>
-                            </div>`
-                        } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre) {
-                            historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                <i class="fa fa-level-up"></i>
-                            </div>`
+                        historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+                    }
 
-                        } else if(auditor.status === auditConst.status.checking) {
-                            historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                <i class="fa fa-ellipsis-h"></i>
-                            </div>`
-                        } else {
-                            historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
+                    historyHTML.push('<div class="timeline-item-content">');
+                    const statuStr = group.status !== auditConst.status.uncheck ?
+                        `<span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>` : '';
+                    historyHTML.push(`<div class="py-1">
+                        <span class="text-black-50">
+                        ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
+                        </span>
+                        ${statuStr}
+                    </div>`);
+                    historyHTML.push('<div class="card"><div class="card-body px-3 py-0">');
+                    for (const [i, auditor] of group.auditors.entries()) {
+                        historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
+                        historyHTML.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
+                        historyHTML.push('<div class="col">');
+                        if (auditor.status === auditConst.status.checked) {
+                            historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
+                        } if (auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre || auditor.status === auditConst.status.checkCancel) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
                         }
-                        historyHTML += `<div class="timeline-item-content">
-                        <div class="card">
-                            <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] : ''}
-                                            ${auditor.status === auditConst.status.checkNo ? user.name : ''}
-                                            ${auditor.status === auditConst.status.checkNoPre ? (leftAuditors.find(item => item.order === auditor.sort-1) ? leftAuditors.find(item => item.order === auditor.sort-1).name : '') : ''}
-                                        </span>
-                                    </p>
-                                    <p class="text-muted mb-0">${auditor.role}</p>
-                                </div>
-                            </div>`
-
+                        historyHTML.push('</div>');
                         if (auditor.opinion) {
-                        historyHTML += `<div class="card-body p-3 border-top">
-                            <p style="margin: 0;">${auditor.opinion} </p>
-                        </div>`
+                            historyHTML.push(`<div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
                         }
-                        historyHTML += `</div></div></li>`
+                        historyHTML.push('</div>');
                     }
-                })
-                historyHTML += '</ul></div>'
-
-            })
-            $('#audit-list').empty()
-            $('#audit-list').append(historyHTML)
+                    historyHTML.push('</div></div>');
+                    historyHTML.push('</div>');
+                    historyHTML.push('</li>');
+                });
+                historyHTML.push('</div>');
+                historyHTML.push('</ul>');
+            });
+            $('#audit-list').empty();
+            $('#audit-list').append(historyHTML.join(''));
         });
     });
 

+ 1 - 0
app/router.js

@@ -673,6 +673,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/material/:order/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, tenderBuildCheck, 'materialController.startAudit');
     app.post('/tender/:id/measure/material/:order/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, tenderBuildCheck, 'materialController.checkAudit');
     app.get('/tender/:id/measure/material/:order/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.checkAuditAgain');
+    app.post('/tender/:id/measure/material/:order/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveAudit');
     // 调差工料
     app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.info');
     app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveBillsData');

+ 48 - 3
app/service/material.js

@@ -9,6 +9,7 @@
  */
 
 const auditConst = require('../const/audit').material;
+const auditType = require('../const/audit').auditType;
 const projectLogConst = require('../const/project_log');
 const materialConst = require('../const/material');
 module.exports = app => {
@@ -24,6 +25,50 @@ module.exports = app => {
             this.tableName = 'material';
         }
 
+        async loadMaterialUser(material) {
+            const status = auditConst.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+
+            material.user = await this.ctx.service.projectAccount.getAccountInfoById(material.user_id);
+            material.auditors = await this.ctx.service.materialAudit.getAuditors(material.id, material.times); // 全部参与的审批人
+            material.auditorIds = this._.map(material.auditors, 'aid');
+            material.curAuditors = material.auditors.filter(x => { return x.status === status.checking; }); // 当前流程中审批中的审批人
+            material.curAuditorIds = this._.map(material.curAuditors, 'aid');
+            material.flowAuditors = material.curAuditors.length > 0 ? material.auditors.filter(x => { return x.order === material.curAuditors[0].order; }) : []; // 当前流程中参与的审批人(包含会签时,审批通过的人)
+            material.flowAuditorIds = this._.map(material.flowAuditors, 'aid');
+            material.nextAuditors = material.curAuditors.length > 0 ? material.auditors.filter(x => { return x.order === material.curAuditors[0].order + 1; }) : [];
+            material.nextAuditorIds = this._.map(material.nextAuditors, 'aid');
+            material.auditorGroups = this.ctx.helper.groupAuditors(material.auditors);
+            material.userGroups = this.ctx.helper.groupAuditorsUniq(material.auditorGroups);
+            material.userGroups.unshift([{
+                aid: material.user.id, order: 0, times: material.times, audit_order: 0, audit_type: auditType.key.common,
+                name: material.user.name, role: material.user.role, company: material.user.company,
+            }]);
+            material.finalAuditorIds = material.userGroups[material.userGroups.length - 1].map(x => { return x.aid; });
+        }
+
+        async loadMaterialAuditViewData(material) {
+            const times = material.status === auditConst.status.checkNo ? material.times - 1 : material.times;
+
+            if (!material.user) material.user = await this.ctx.service.projectAccount.getAccountInfoById(material.user_id);
+            material.auditHistory = await this.ctx.service.materialAudit.getAuditorHistory(material.id, times);
+            // 获取审批流程中左边列表
+            if (material.status === auditConst.status.checkNo && material.user_id !== this.ctx.session.sessionUser.accountId) {
+                const auditors = await this.ctx.service.materialAudit.getAuditors(material.id, times); // 全部参与的审批人
+                const auditorGroups = this.ctx.helper.groupAuditors(auditors);
+                material.auditors2 = this.ctx.helper.groupAuditorsUniq(auditorGroups);
+                material.auditors2.unshift([{
+                    aid: material.user.id, order: 0, times: material.times - 1, audit_order: 0, audit_type: auditType.key.common,
+                    name: material.user.name, role: material.user.role, company: material.user.company,
+                }]);
+            } else {
+                material.auditors2 = material.userGroups;
+            }
+            if (material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) {
+                material.auditorList = await this.ctx.service.materialAudit.getAuditors(material.id, material.times);
+            }
+        }
+
         /**
          * 获取 最新一期 材料调差期计量
          * @param tenderId
@@ -115,11 +160,11 @@ module.exports = app => {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
             if (materials.length > 0 && materials[0].status !== auditConst.status.checked) {
                 const material = materials[0];
-                const curAuditor = await this.ctx.service.materialAudit.getCurAuditor(material.id, material.times);
-                const isActive = curAuditor ? curAuditor.aid === this.ctx.session.sessionUser.accountId : material.user_id === this.ctx.session.sessionUser.accountId;
+                const curAuditors = await this.ctx.service.materialAudit.getCurAuditors(material.id, material.times);
+                const isActive = curAuditors && curAuditors.length > 0 ? this._.findIndex(curAuditors, { aid: this.ctx.session.sessionUser.accountId }) !== -1 : material.user_id === this.ctx.session.sessionUser.accountId;
                 if (isActive) {
                     material.curTimes = material.times;
-                    material.curOrder = curAuditor ? curAuditor.order : 0;
+                    material.curOrder = curAuditors && curAuditors.length > 0 ? curAuditors[0].order : 0;
                 }
             }
             return materials;

Файловите разлики са ограничени, защото са твърде много
+ 623 - 324
app/service/material_audit.js


+ 2 - 2
app/view/material/audit_btn.ejs

@@ -6,7 +6,7 @@
             <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-outline-secondary btn-sm btn-block">上报中</a>
         <% } %>
     <% } else if (ctx.material.status === auditConst.status.checking) { %>
-        <% if (ctx.material.curAuditor && ctx.material.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+        <% if (ctx.material.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
             <a id="sp-done-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm btn-block">审批通过</a>
             <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm btn-block">审批退回</a>
         <% } else { %>
@@ -20,7 +20,7 @@
             <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm btn-block sp-list-btn">重新上报</a>
         <% } %>
     <% } %>
-    <% if (ctx.material.auditors !== undefined && ctx.material.auditors.length !== 0 && ctx.material.auditors[ctx.material.auditors.length-1].aid === ctx.session.sessionUser.accountId && ctx.material.status === auditConst.status.checked && ctx.material.order === ctx.material.highOrder) { %>
+    <% if (ctx.material.auditors !== undefined && ctx.material.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && ctx.material.status === auditConst.status.checked && ctx.material.order === ctx.material.highOrder) { %>
         <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
     <% } %>
 </div>

Файловите разлики са ограничени, защото са твърде много
+ 565 - 417
app/view/material/audit_modal.ejs


+ 12 - 11
app/view/material/index.ejs

@@ -93,8 +93,16 @@
                             <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %><% } else { %><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp), m.rate_tp) %><% } %></td>
                             <% } %>
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
-                                <% if (m.curAuditor) { %>
-                                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditor.name %><%if (m.curAuditor.role !== '' && m.curAuditor.role !== null) { %>-<%- m.curAuditor.role %><% } %></a>
+                                <% if (m.status === auditConst.status.checked && m.final_auditor_str) { %>
+                                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.final_auditor_str %></a>
+                                <% } else { %>
+                                    <% if (m.curAuditors && m.curAuditors.length > 0) { %>
+                                        <% if (m.curAuditors[0].audit_type === auditType.key.common) { %>
+                                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditors[0].name %><%if (m.curAuditors[0].role !== '' && m.curAuditors[0].role !== null) { %>-<%- m.curAuditors[0].role %><% } %></a>
+                                        <% } else { %>
+                                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- ctx.helper.transFormToChinese(m.curAuditors[0].audit_order) + '审' %></a>
+                                        <% } %>
+                                    <% } %>
                                 <% } %>
                                 <%- auditConst.auditProgress[m.status] %>
                             </td>
@@ -103,7 +111,7 @@
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                 <% } else if (m.status === auditConst.status.checkNo && m.curAuditor && m.user_id === ctx.session.sessionUser.accountId) { %>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
-                                <% } else if (m.status === auditConst.status.checking && m.curAuditor && m.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+                                <% } else if (m.status === auditConst.status.checking && m.curAuditors && m.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                 <% } else { %>
                                     <span class="<%- auditConst.auditProgressClass[m.status] %>"><%- auditConst.auditProgress[m.status] %></span>
@@ -120,20 +128,13 @@
         </div>
     </div>
 </div>
-<script src="/public/js/sub_menu.js"></script>
-<script src="/public/js/decimal.min.js"></script>
-<script src="/public/js/zh_calc.js"></script>
-<script src="/public/js/path_tree.js"></script>
-<script src="/public/js/gcl_gather.js"></script>
-<script src="/public/js/datepicker/datepicker.min.js"></script>
-<script src="/public/js/datepicker/datepicker.zh.js"></script>
-
 <script>
     const tenderId = '<%- ctx.tender.id %>';
     const auditConst = JSON.parse('<%- auditConst2 %>');
     const preUrl = JSON.parse(unescape('<%- escape(JSON.stringify(preUrl)) %>'));
     const lastMaterialListNum = parseInt('<%- lastMaterialList ? lastMaterialList.length : 0 %>');
     const materialColShow = JSON.parse(unescape('<%- escape(JSON.stringify(materialColShow)) %>'));
+    const auditType = JSON.parse('<%- JSON.stringify(auditType) %>');
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',

+ 0 - 2
app/view/material/modal.ejs

@@ -228,5 +228,3 @@
         </div>
     </div>
 </div>
-<script src="/public/js/moment/moment.min.js"></script>
-<script src="/public/js/measure_material.js?202308281"></script>

Файловите разлики са ограничени, защото са твърде много
+ 11 - 9
app/view/tender/detail.ejs


+ 13 - 0
config/web.js

@@ -702,6 +702,19 @@ const JsFiles = {
             },
         },
         material: {
+            index: {
+                files: ['/public/js/moment/moment.min.js', '/public/js/decimal.min.js'],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
+                    '/public/js/datepicker/datepicker.min.js',
+                    '/public/js/datepicker/datepicker.zh.js',
+                    '/public/js/measure_material.js',
+                ],
+                mergeFile: 'material_index',
+            },
             info: {
                 files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js',
                     '/public/js/js-xlsx/xlsx.full.min.js',

+ 81 - 0
db_script/material_audit_order.js

@@ -0,0 +1,81 @@
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+const errorStageTimes = [];
+const updateMaterialTimes = async function(material, times) {
+    const auditData = await querySql('SELECT * FROM zh_material_audit WHERE mid = ? AND times = ? ORDER BY `order`', [material.id, times]);
+    if (auditData.length === 0) return;
+
+    const auditIds = [], filterIds = [];
+    for (const ad of auditData) {
+        if (ad.audit_order) {
+            if (filterIds.indexOf(ad.aid) < 0) filterIds.push(ad.aid);
+        } else {
+            const auditId = auditIds.find(x => { return x.aid === ad.aid});
+            if (auditId) {
+                auditId.ids.push(ad.id);
+            } else {
+                auditIds.push({ aid: ad.aid, ids: [ad.id] });
+            }
+        }
+    }
+    if (auditIds.length === 0) return;
+    if (filterIds.length > 0 && auditIds.length > 0) errorStageTimes.push({ tid: material.tid, mid: material.id, times });
+    for (const [i, aid] of auditIds.entries()) {
+        for (const id of aid.ids) {
+            await querySql('UPDATE zh_material_audit SET audit_order = ? WHERE id = ?', [i+1, id]);
+        }
+    }
+};
+
+const updateMaterial = async function (material) {
+    for (let i = 1; i <= material.times; i++) {
+        await updateMaterialTimes(material, i);
+    }
+};
+
+const doCompleteTest = async function(id) {
+    try {
+        const tender = await querySql('Select * From zh_tender where id > ?', [id]);
+        for (const t of tender) {
+            console.log(`Update Tender ${t.name}(${t.id}):`);
+            const materials = await querySql('Select * From zh_material where tid = ?', [t.id]);
+            for (const s of materials) {
+                await updateMaterial(s);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+    if (errorStageTimes.length > 0) {
+        console.log('ERROR material info: ');
+        console.log(errorStageTimes);
+    }
+};
+
+const doComplete = async function() {
+    try {
+        const tenders = await querySql('Select * From zh_tender');
+        for (const t of tenders) {
+            console.log(`Update Tender ${t.name}(${t.id}):`);
+            const materials = await querySql('Select * From zh_material where tid = ?', [t.id]);
+            for (const s of materials) {
+                await updateMaterial(s);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+    if (errorStageTimes.length > 0) {
+        console.log('ERROR material info: ');
+        console.log(errorStageTimes);
+    }
+};
+
+const tenderId = process.argv[3];
+if (tenderId) {
+    doCompleteTest(tenderId);
+} else {
+    doComplete();
+}

+ 7 - 0
sql/update.sql

@@ -88,6 +88,13 @@ CREATE TABLE `zh_sub_project_file`  (
 ALTER TABLE `calculation`.`zh_tender_info`
 ADD COLUMN `over_range_check` varchar(255) NOT NULL DEFAULT '' AFTER `s_type`;
 
+ALTER TABLE `zh_material_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)',
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_material`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关';
+
 ------------------------------------
 -- 表数据
 ------------------------------------