'use strict'; /** * * * @author Mai * @date 2019/2/27 * @version */ const auditConst = require('../const/audit').financial; const auditType = require('../const/audit').auditType; const shenpiConst = require('../const/shenpi'); const pushType = require('../const/audit').pushType; module.exports = app => { class FinancialPayAudit extends app.BaseService { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @return {void} */ constructor(ctx) { super(ctx); this.tableName = 'financial_pay_audit'; } async getAuditorGroup(fpId, times) { const auditors = await this.getAuditors(fpId, times); // 全部参与的审批人 return this.ctx.helper.groupAuditors(auditors, 'order', true); } async getUserGroup(fpId, times) { const group = await this.getAuditorGroup(fpId, times); const sql = 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As fpid, 0 As `order`, 1 As audit_type, 0 As audit_order' + ' FROM ' + this.ctx.service.financialPay.tableName + ' As s' + ' LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' + ' ON s.uid = pa.id' + ' WHERE s.id = ?'; const sqlParam = [times, fpId, fpId]; const user = await this.db.queryOne(sql, sqlParam); user.audit_order = 0; group.unshift([ user ]); return group; } async getUniqUserGroup(fpId, times) { const group = await this.getAuditorGroup(fpId, times); const sql = 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As fpid, 0 As `order`, 1 As audit_type, 0 As audit_order' + ' FROM ' + this.ctx.service.financialPay.tableName + ' As s' + ' LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' + ' ON s.uid = pa.id' + ' WHERE s.id = ?'; const sqlParam = [times, fpId, fpId]; const user = await this.db.queryOne(sql, sqlParam); user.audit_order = 0; group.unshift([ user ]); return this.ctx.helper.groupAuditorsUniq(group); } async getAuditorHistory(fpId, times, reverse = false) { const history = []; if (times >= 1) { for (let i = 1; i <= times; i++) { const auditors = await this.getAuditors(fpId, i); const group = this.ctx.helper.groupAuditors(auditors); const historyGroup = []; // 找出group里audit_order最大值 const max_info = group.length > 0 ? this._.maxBy(group, function(item) { return item && item[0] && item[0].audit_order; }) : null; const max_order = max_info ? max_info[0].audit_order : -1; for (const g of group) { const his = { beginYear: '', beginDate: '', beginTime: '', endYear: '', endDate: '', endTime: '', begin_time: null, end_time: null, audit_type: g[0].audit_type, audit_order: g[0].audit_order, auditors: g, }; if (his.audit_type === auditType.key.common) { his.name = g[0].name; } else { his.name = this.ctx.helper.transFormToChinese(his.audit_order) + '审'; } his.is_final = his.audit_order === max_order; if (g[0].begin_time) { his.begin_time = g[0].begin_time; const beginTime = this.ctx.moment(g[0].begin_time); his.beginYear = beginTime.format('YYYY'); his.beginDate = beginTime.format('MM-DD'); his.beginTime = beginTime.format('HH:mm:ss'); } let end_time; g.forEach(x => { if (x.status === auditConst.status.checkSkip) return; if (!his.status || x.status === auditConst.status.checking) his.status = x.status; if (x.end_time && (!end_time || x.end_time > end_time)) { end_time = x.end_time; if (his.status !== auditConst.status.checking) his.status = x.status; } }); if (end_time) { his.end_time = end_time; const endTime = this.ctx.moment(end_time); his.endYear = endTime.format('YYYY'); his.endDate = endTime.format('MM-DD'); his.endTime = endTime.format('HH:mm:ss'); } historyGroup.push(his); } if (reverse) { history.push(historyGroup.reverse()); } else { history.push(historyGroup); } } } return history; } async getUniqAuditor(fpId, times) { const auditors = await this.getAuditors(fpId, times); // 全部参与的审批人 const result = []; auditors.forEach(x => { if (result.findIndex(r => { return x.aid === r.aid && x.audit_order === r.audit_order; }) < 0) { result.push(x); } }); return result; } /** * 获取 审核列表信息 * * @param {Number} cpId - 变更立项id * @param {Number} times - 第几次审批 * @return {Promise<*>} */ async getAuditors(fpId, times = 1, order_sort = 'asc', noYB = false) { // const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' + // 'FROM ?? AS la, ?? AS pa, (SELECT t1.`aid`,(@i:=@i+1) as `sort` FROM (SELECT t.`aid`, t.`order` FROM (select `aid`, `order` from ?? WHERE `cpid` = ? AND `times` = ? ORDER BY `order` LIMIT 200) t GROUP BY t.`aid` ORDER BY t.`order`) t1, (select @i:=0) as it) as g ' + // 'WHERE la.`cpid` = ? and la.`times` = ? and la.`aid` = pa.`id` and g.`aid` = la.`aid` order by la.`order`'; // const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.tableName, cpId, times, cpId, times]; // const result = await this.db.query(sql, sqlParam); // const sql2 = 'SELECT COUNT(a.`aid`) as num FROM (SELECT `aid` FROM ?? WHERE `cpid` = ? AND `times` = ? GROUP BY `aid`) as a'; // const sqlParam2 = [this.tableName, cpId, times]; // const count = await this.db.queryOne(sql2, sqlParam2); // for (const i in result) { // result[i].max_sort = count.num; // } const ybSql = noYB ? ' AND la.audit_order != 0' : ''; const sql = 'SELECT la.id, la.aid, la.times, la.order, la.status, la.opinion, la.begin_time, la.end_time, la.audit_type, la.audit_order,' + ' pa.name, pa.company, pa.role, pa.mobile, pa.telephone' + ` FROM ${this.tableName} la LEFT JOIN ${this.ctx.service.projectAccount.tableName} pa ON la.aid = pa.id` + ' WHERE la.fpid = ? AND la.times = ?' + ybSql + ' ORDER BY la.order ' + order_sort; const sqlParam = [fpId, times]; const result = await this.db.query(sql, sqlParam); const max_sort = this._.max(result.map(x => { return x.audit_order; })); for (const i in result) { result[i].max_sort = max_sort; } return result; } async getCurAuditors(fpId, times = 1) { const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, la.audit_type, la.audit_order ' + ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' + ' WHERE la.`fpid` = ? and la.`status` = ? and la.`times` = ?'; const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, auditConst.status.checking, times]; return await this.db.query(sql, sqlParam); } /** * 获取审核人流程列表 * * @param auditorId * @return {Promise<*>} */ async getAuditGroupByList(fpId, times, transaction = false) { const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`fpid`, la.`aid`, la.`order`, la.`status`, la.audit_type, la.audit_order ' + ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' + ' WHERE la.`fpid` = ? and la.`times` = ? GROUP BY la.`aid` ORDER BY la.`order`'; const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, times]; return transaction !== false ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam); } async getAuditorsByStatus(fpId, status, times = 1) { let auditor = []; let sql = ''; let sqlParam = ''; let cur; switch (status) { case auditConst.status.checking : case auditConst.status.checked : case auditConst.status.cancelRevise : cur = await this.db.queryOne('SELECT * From ?? where fpid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, fpId, times, status]); if (!cur) return []; sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`fpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' + ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' + ' WHERE la.`fpid` = ? and la.`order` = ? and times = ?'; sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, cur.order, times]; auditor = await this.db.query(sql, sqlParam); break; case auditConst.status.checkNo : cur = await this.db.queryOne('SELECT * From ?? where fpid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, fpId, parseInt(times) - 1, status]); if (!cur) return []; sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`fpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' + ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' + ' WHERE la.`fpid` = ? and la.`order` = ? and la.`times` = ?'; sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, cur.order, parseInt(times) - 1]; auditor = await this.db.query(sql, sqlParam); break; case auditConst.status.uncheck: default: break; } return auditor; } /** * 获取审核人流程列表(包括原报) * @param {Number} materialId 调差id * @param {Number} times 审核次数 * @return {Promise} 查询结果集(包括原报) */ async getAuditorsWithOwner(fpId, times = 1) { const result = await this.getAuditGroupByList(fpId, times); const sql = 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As fpid, 0 As `order`' + ' FROM ' + this.ctx.service.financialPay.tableName + ' As s' + ' LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' + ' ON s.uid = pa.id' + ' WHERE s.id = ?'; const sqlParam = [times, fpId, fpId]; const user = await this.db.queryOne(sql, sqlParam); result.unshift(user); return result; } async updateNewAuditList(financialPay, newList) { const transaction = await this.db.beginTransaction(); try { await this.updateNewAuditors(financialPay, newList, transaction); await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } } async updateNewAuditors(financialPay, newList, transaction) { // 先删除旧的审批流,再添加新的 await transaction.delete(this.tableName, { fpid: financialPay.id, times: financialPay.times }); const newAuditors = []; for (const auditor of newList) { newAuditors.push({ spid: financialPay.spid, tid: financialPay.tid, fpid: financialPay.id, aid: auditor.audit_id, times: financialPay.times, order: auditor.audit_order, status: auditConst.status.uncheck, audit_type: auditor.audit_type, audit_order: auditor.audit_order, }); } if (newAuditors.length > 0) await transaction.insert(this.tableName, newAuditors); } /** * 开始审批 * @param {Number} cpId - 方案id * @param {Number} times - 第几次审批 * @return {Promise} */ async start(fpId, times = 1) { const audits = await this.getAllDataByCondition({ where: { fpid: fpId, times, order: 1 } }); if (audits.length === 0) { throw '请联系管理员添加审批人'; } const transaction = await this.db.beginTransaction(); try { const begin_time = new Date(); const updateData = audits.map(x => { return {id: x.id, status: auditConst.status.checking, begin_time}; }); await transaction.updateRows(this.tableName, updateData); await transaction.update(this.ctx.service.financialPay.tableName, { id: fpId, status: auditConst.status.checking, // entities: await this.ctx.service.financialPayContract.getEntities(fpId), }); // todo 更新标段tender状态 ? await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } return true; } /** * 审批 * @param {Object} fp - 资金支付信息 * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果 * @return {Promise} */ async check(fp, checkData) { if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo) { throw '提交数据错误'; } const pid = this.ctx.session.sessionProject.id; switch (checkData.checkType) { case auditConst.status.checked: await this._checked(pid, fp, checkData); break; case auditConst.status.checkNo: await this._checkNo(pid, fp, checkData); break; default: throw '无效审批操作'; } } async _checked(pid, fp, checkData) { const accountId = this.ctx.session.sessionUser.accountId; const time = new Date(); // 整理当前流程审核人状态更新 const audits = fp.curAuditors; if (audits.length === 0) throw '审核数据错误'; const selfAudit = audits.find(x => { return x.aid === accountId; }); if (!selfAudit) throw '当前标段您无权审批'; // const flowAudits = await this.getAllDataByCondition({ where: { cpid: cpId, times, order: selfAudit.order } }); const nextAudits = fp.nextAuditors; const transaction = await this.db.beginTransaction(); try { // 更新本人审批状态 await transaction.update(this.tableName, { id: selfAudit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time, }); // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, selfAudit.id); // 获取推送必要信息 const noticeContent = await this.getNoticeContent(pid, fp.spid, fp.id, selfAudit.aid, checkData.opinion); // 添加推送 const records = []; const auditors = await this.getAuditorsWithOwner(fp.id, fp.times); auditors.forEach(audit => { records.push({ pid, spid: fp.spid, type: pushType.financial, uid: audit.aid, status: auditConst.status.checked, content: noticeContent }); }); await transaction.insert('zh_notice', records); if (audits.length === 1 || selfAudit.audit_type !== auditType.key.and) { // 或签更新他人审批状态 if (selfAudit.audit_type === auditType.key.or) { const updateOther = []; for (const audit of audits) { if (audit.aid === selfAudit.aid) continue; updateOther.push({ id: audit.id, status: auditConst.status.checkSkip, opinion: '', end_time: time, }); // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id); } if (updateOther.length > 0) transaction.updateRows(this.tableName, updateOther); } // 无下一审核人表示,审核结束 if (nextAudits.length > 0) { // 流程至下一审批人 const updateData = nextAudits.map(x => { return { id: x.id, status: auditConst.status.checking, begin_time: time }; }); await transaction.updateRows(this.tableName, updateData); // 同步 期信息 await transaction.update(this.ctx.service.financialPay.tableName, { id: fp.id, status: auditConst.status.checking, }); } else { // 本期结束 // 生成截止本期数据 final数据 // 同步 期信息 await transaction.update(this.ctx.service.financialPay.tableName, { id: fp.id, status: checkData.checkType, entities: await this.ctx.service.financialPayContract.getEntities(fp.id), }); } } else { // 同步 期信息 await transaction.update(this.ctx.service.financialPay.tableName, { id: fp.id, status: auditConst.status.checking, }); } await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } } async _checkNo(pid, fp, checkData) { const accountId = this.ctx.session.sessionUser.accountId; const time = new Date(); const audits = fp.curAuditors; if (!audits) throw '审核数据错误'; const selfAudit = audits.find(x => { return x.aid === accountId; }); if (!selfAudit) throw '当前标段您无权审批'; const auditors = await this.getUniqAuditor(fp.id, fp.times); // 全部参与的审批人 const newAuditors = auditors.map(x => { return { aid: x.aid, spid: fp.spid, tid: fp.tid, fpid: fp.id, times: fp.times + 1, order: x.audit_order, status: auditConst.status.uncheck, audit_type: x.audit_type, audit_order: x.audit_order, }; }); const transaction = await this.db.beginTransaction(); try { const updateData = []; audits.forEach(x => { updateData.push({ id: x.id, status: x.aid === selfAudit.aid ? checkData.checkType : auditConst.status.checkSkip, opinion: x.aid === selfAudit.aid ? checkData.opinion : '', end_time: x.aid === selfAudit.aid ? time : null, }); }); await transaction.updateRows(this.tableName, updateData); // 添加到消息推送表 const noticeContent = await this.getNoticeContent(pid, fp.spid, fp.id, selfAudit.aid, checkData.opinion); const records = [{ pid, spid: fp.spid, type: pushType.financial, uid: fp.uid, status: auditConst.status.checkNo, content: noticeContent }]; auditors.forEach(audit => { records.push({ pid, spid: fp.spid, type: pushType.financial, uid: audit.aid, status: auditConst.status.checkNo, content: noticeContent }); }); await transaction.insert(this.ctx.service.noticePush.tableName, records); // 同步期信息 await transaction.update(this.ctx.service.financialPay.tableName, { id: fp.id, status: checkData.checkType, times: fp.times + 1, entities: '', }); // 拷贝新一次审核流程列表 await transaction.insert(this.tableName, newAuditors); await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } } /** * 获取审核人需要审核的期列表 * * @param auditorId * @return {Promise<*>} */ async getAuditFinancial(auditorId, spid = '') { const spSql = spid ? ' and ma.`spid` = "' + spid + '"' : ''; const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`spid`, ma.`fpid`,' + ' m.`status` As `fpstatus`, m.`code` As `fpcode`,' + ' sp.`name`, sp.`project_id` ' + ' FROM ?? AS ma LEFT JOIN ?? AS m ON ma.`fpid` = m.`id` LEFT JOIN ?? As sp ON ma.`spid` = sp.`id`' + ' WHERE ((ma.`aid` = ? and ma.`status` = ?) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' + spSql + ' ORDER BY ma.`begin_time` DESC'; const sqlParam = [this.tableName, this.ctx.service.financialPay.tableName, this.ctx.service.subProject.tableName, auditorId, auditConst.status.checking, auditorId, auditConst.status.checkNo, auditConst.status.checkNo]; const result = await this.db.query(sql, sqlParam); // 过滤result中存在重复sid的值, 保留最新的一条 const filterResult = []; const fpidArr = []; for (const r of result) { if (fpidArr.indexOf(r.fpid) === -1) { filterResult.push(r); fpidArr.push(r.fpid); } } return filterResult; } /** * 获取审核人审核的次数 * * @param auditorId * @return {Promise<*>} */ async getCountByChecked(auditorId, spid = '') { if (spid) { return await this.db.count(this.tableName, { spid, aid: auditorId, status: [auditConst.status.checked, auditConst.status.checkNo] }); } return await this.db.count(this.tableName, { aid: auditorId, status: [auditConst.status.checked, auditConst.status.checkNo] }); } /** * 获取最近一次审批结束时间 * * @param auditorId * @return {Promise<*>} */ async getLastEndTimeByChecked(auditorId, spid = '') { const spSql = spid ? ' and `spid` = "' + spid + '"' : ''; const sql = 'SELECT `end_time` FROM ?? WHERE `aid` = ? ' + 'AND `status` in (' + this.ctx.helper.getInArrStrSqlFilter([auditConst.status.checked, auditConst.status.checkNo]) + ')' + spSql + ' ORDER BY `end_time` DESC'; const sqlParam = [this.tableName, auditorId]; const result = await this.db.queryOne(sql, sqlParam); return result ? result.end_time : null; } /** * 用于添加推送所需的content内容 * @param {Number} pid 项目id * @param {Number} tid 台账id * @param {Number} cpId 方案id * @param {Number} uid 审批人id */ async getNoticeContent(pid, spid, fpId, uid, opinion = '') { const noticeSql = 'SELECT * FROM (SELECT ' + ' sp.`id` As `spid`, ma.`fpid`, m.`code`, sp.`name`, pa.`name` As `su_name`, pa.role As `su_role`' + ' FROM (SELECT * FROM ?? WHERE `id` = ? ) As sp' + ' LEFT JOIN ?? As m On sp.`id` = m.`spid` AND m.`id` = ?' + ' LEFT JOIN ?? As ma ON m.`id` = ma.`fpid`' + ' LEFT JOIN ?? As pa ON pa.`id` = ?' + ' WHERE sp.`project_id` = ? ) as new_t GROUP BY new_t.`spid`'; const noticeSqlParam = [this.ctx.service.subProject.tableName, spid, this.ctx.service.financialPay.tableName, fpId, this.tableName, this.ctx.service.projectAccount.tableName, uid, pid]; const content = await this.db.query(noticeSql, noticeSqlParam); if (content.length) { content[0].opinion = opinion; } return content.length ? JSON.stringify(content[0]) : ''; } } return FinancialPayAudit; };