'use strict'; /** * 合同支付 * * @author Mai * @date * @version */ const audit = require('../const/audit'); const shenpiConst = require('../const/shenpi'); const sendToWormhole = require('stream-wormhole'); const path = require('path'); module.exports = app => { class PayController extends app.BaseController { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @return {void} */ constructor(ctx) { super(ctx); } async index(ctx) { try { const phasePays = await this.ctx.service.phasePay.getAllPhasePay(ctx.tender.id, 'DESC'); const relaStage = []; for (const p of phasePays) { if (p.audit_status !== audit.common.status.checked) await this.ctx.service.phasePay.loadUser(p); relaStage.push(...p.rela_stage); } const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id }, orders: [['order', 'AEC']] }); const validStages = stages.filter(s => { if (s.status !== audit.stage.status.checked) return false; return !relaStage.find(r => { return s.id === r.stage_id; }); }); this.ctx.service.phasePay.calculatePhasePay(phasePays); const renderData = { auditType: audit.auditType, phasePays, validStages, auditConst: audit.common, jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.phasePay.list) }; await this.layout('phase_pay/index.ejs', renderData, 'phase_pay/modal.ejs'); } catch (err) { ctx.log(err); ctx.redirect(ctx.request.header.referer); } } async add(ctx) { try { if (ctx.session.sessionUser.accountId !== ctx.tender.data.user_id && ctx.tender.userAssistsId.indexOf(ctx.session.sessionUser.accountId) < 0) { throw '您无权创建计量期'; } const date = ctx.request.body.date; if (!date) throw '请选择支付年月'; const stage = ctx.request.body.stage; if (!stage) throw '请选择计量期'; const memo = ctx.request.body.memo; const pays = await ctx.service.phasePay.getAllPhasePay(ctx.tender.id, 'DESC'); const unCompleteCount = pays.filter(s => { return s.status !== audit.common.status.checked; }).length; if (unCompleteCount.length > 0) throw `最新一起未审批通过,请审批通过后再新增`; // 预留可以关联多期 const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id, order: stage } }); const newPhase = await ctx.service.phasePay.add(ctx.tender.id, stages, date, memo); if (!newPhase) throw '新增期失败'; newPhase.curTimes = 1; newPhase.curSort = 0; await ctx.service.phasePayDetail.calculateSave(newPhase); ctx.redirect('/tender/' + ctx.tender.id + '/pay/' + newPhase.phase_order + '/detail'); } catch (err) { ctx.log(err); ctx.postError(err, '新增期失败'); ctx.redirect('/tender/' + ctx.tender.id + '/pay'); } } async del(ctx) { try { if (!ctx.session.sessionUser.is_admin && ctx.request.body.confirm !== '确认删除本期') throw '请输入文本确认删除本期'; const phase_id = ctx.request.body.phase_id; const phasePay = await ctx.service.phasePay.getDataById(phase_id); if (!phasePay) throw '删除的期不存在,请刷新页面'; if (!ctx.session.sessionUser.is_admin && phasePay.create_user_id !== ctx.session.sessionUser.accountId) throw '您无权删除本期'; // 获取最新的期数 const phasePayCount = await ctx.service.phasePay.count({ tid: ctx.tender.id }); if (phasePay.phase_order !== phasePayCount) throw '非最新一期,不可删除'; await ctx.service.phasePay.delete(phase_id); // todo 刷新金额概况缓存 // await ctx.service.tenderCache.refreshPayCache(phasePay.tenderId); ctx.redirect('/tender/' + ctx.tender.id + '/pay'); } catch (err) { ctx.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/pay'); } } async save(ctx) { try { const phase_id = ctx.request.body.phase_id; const data = { phase_date: ctx.request.body.date, memo: ctx.request.body.memo, }; const phasePay = await ctx.service.phasePay.getPhasePay(phase_id); if (!phasePay) throw '删除的期不存在,请刷新页面'; if (!ctx.session.sessionUser.is_admin && phasePay.create_user_id !== ctx.session.sessionUser.accountId) throw '您无权修改该数据'; await this.ctx.service.phasePay.save(phasePay, data); if (phasePay.audit_status === audit.common.status.uncheck && ctx.request.body.stage) { const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id, order: ctx.request.body.stage } }); await this.ctx.service.phasePay.resetRelaStageId(phasePay, stages); } ctx.redirect('/tender/' + ctx.tender.id + '/pay'); } catch (err) { ctx.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/pay'); } } /** * 期审批流程(POST) * @param ctx * @return {Promise} */ async loadAuditors(ctx) { try { const order = JSON.parse(ctx.request.body.data).order; const tenderId = ctx.params.id; const phasePay = await ctx.service.phasePay.getPhasePayByOrder(tenderId, order); await ctx.service.phasePay.loadUser(phasePay); await ctx.service.phasePay.loadAuditViewData(phasePay); ctx.body = { err: 0, msg: '', data: phasePay }; } catch (error) { ctx.log(error); ctx.body = { err: 1, msg: error.toString(), data: null }; } } async detail(ctx) { try { // await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay); await this.ctx.service.phasePay.loadAuditViewData(ctx.phasePay); const pays = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay); const calcBase = this.ctx.service.phasePay.getPhasePayCalcBase(ctx.phasePay, ctx.tender.info); const projectFunInfo = this.ctx.subProject.fun_rela; const lastStage = await this.ctx.service.stage.getLastestCompleteStage(ctx.tender.id); const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: ctx.session.sessionProject.id, enable: 1 }, columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'], }); const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } }); const accountGroup = unitList.map(item => { const groupList = accountList.filter(item1 => item1.company === item.name); return { groupName: item.name, groupList }; }); // 是否已验证手机短信 const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId); const renderData = { pays, calcBase, lockPayExpr: projectFunInfo.lockPayExpr, auditConst: audit.common, deadlineType: this.ctx.service.phasePayDetail.deadlineType, maxStageOrder: lastStage.order, jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.phasePay.detail), accountList, accountGroup, shenpiConst, auditType: audit.auditType, authMobile: pa.auth_mobile, }; await this.layout('phase_pay/detail.ejs', renderData, 'phase_pay/detail_modal.ejs'); } catch (err) { ctx.log(err); ctx.postError(err, '读取合同支付数据错误'); ctx.redirect('/tender/' + ctx.tender.id + '/pay'); } } async detailLoad(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data.filter) throw '参数错误'; const filter = data.filter.split(','); const result = {}; for (const f of filter) { switch(f) { case 'pay': result.pay = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay); break; case 'base': result.base = this.ctx.service.phasePay.getPhasePayCalcBase(ctx.phasePay, ctx.tender.info); break; case 'add': result.add = ctx.phasePay.calc_base; break; case 'file': result.file = await this.ctx.service.phasePayFile.getData(ctx.phasePay.id, 'pay'); } } ctx.body = { err: 0, msg: '', data: result }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '读取合同支付数据错误'); } } async detailUpdate(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data.postType || !data.postData) throw '数据错误'; const responseData = { err: 0, msg: '', data: {} }; switch (data.postType) { case 'add': responseData.data = await this.ctx.service.phasePayDetail.addDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1); break; case 'delete': await this.ctx.service.phasePayDetail.deleteDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1); await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay); responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay); break; case 'up-move': responseData.data = await this.ctx.service.phasePayDetail.upMoveDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1); break; case 'down-move': responseData.data = await this.ctx.service.phasePayDetail.downMoveDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1); break; case 'update': const updateDetail = await this.ctx.service.phasePayDetail.updateDetail(ctx.phasePay, data.postData); if (this.ctx.service.phasePayDetail.checkCalc(data.postData)) { await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay); responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay); } else { responseData.data.update = updateDetail; } break; case 'calc': await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay); responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay); break; case 'refreshBase': await this.ctx.service.phasePay.refreshCalcBase(ctx.phasePay); responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay); responseData.data.calcBase = this.ctx.service.phasePay.getPhasePayCalcBase(ctx.phasePay, ctx.tender.info); responseData.data.calcBase.forEach(x => { x.formatValue = ctx.tender.info.display.thousandth ? ctx.helper.formatNum(x.value, '#,##0.######') : x.value; }); responseData.data.addBase = ctx.phasePay.calc_base; break; default: throw '未知操作'; } ctx.body = responseData; } catch (err) { ctx.log(err); ctx.body = this.ajaxErrorBody(err, '数据错误'); } } async uploadFile(ctx) { let stream; try { const parts = ctx.multipart({autoFields: true}); let index = 0; const create_time = Date.parse(new Date()) / 1000; let stream = await parts(); const user = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId); const rela_type = parts.fields.type; const rela_id = parts.field.rela_id; const uploadfiles = []; while (stream !== undefined) { if (!stream.filename) throw '未发现上传文件!'; const fileInfo = path.parse(stream.filename); const filepath = `app/public/upload/${ctx.phasePay.tid}/phasePay/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + index + fileInfo.ext}`; // 保存文件 await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream); await sendToWormhole(stream); // 插入到stage_pay对应的附件列表中 uploadfiles.push({ rela_id, filename: fileInfo.name, fileext: fileInfo.ext, filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size, filepath, }); ++index; if (Array.isArray(parts.field.size) && index < parts.field.size.length) { stream = await parts(); } else { stream = undefined; } } const result = await ctx.service.phasePayFile.addFiles(ctx.phasePay, 'pay', uploadfiles, user); ctx.body = {err: 0, msg: '', data: result}; } catch (error) { ctx.log(error); // 失败需要消耗掉stream 以防卡死 if (stream) await sendToWormhole(stream); ctx.body = this.ajaxErrorBody(error, '上传附件失败,请重试'); } } async deleteFile(ctx) { try{ const data = JSON.parse(ctx.request.body.data); if (!data && !data.id) throw '缺少参数'; const result = await ctx.service.phasePayFile.delFiles(data.id); ctx.body = { err: 0, msg: '', data: result }; } catch(error) { ctx.log(error); ctx.ajaxErrorBody(error, '删除附件失败'); } } /** * 添加审批人 * @param ctx * @return {Promise} */ async addAudit(ctx) { try { const data = JSON.parse(ctx.request.body.data); const id = this.app._.toInteger(data.auditorId); if (isNaN(id) || id <= 0) throw '参数错误'; // 检查权限等 if (ctx.phasePay.create_user_id !== ctx.session.sessionUser.accountId) throw '您无权添加审核人'; if (ctx.phasePay.audit_status !== audit.common.status.uncheck && ctx.phasePay.audit_status !== audit.common.status.checkNo) { throw '当前不允许添加审核人'; } // 检查审核人是否已存在 const exist = await ctx.service.phasePayAudit.getDataByCondition({ phase_id: ctx.phasePay.id, audit_times: ctx.phasePay.audit_times, audit_id: id }); if (exist) throw '该审核人已存在,请勿重复添加'; const auditorInfo = await this.ctx.service.projectAccount.getDataById(id); if (!auditorInfo) throw '添加的审批人不存在'; const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: ctx.tender.id, sp_type: shenpiConst.sp_type.phasePay, sp_status: shenpiConst.sp_status.gdzs }); const is_gdzs = shenpiInfo && ctx.tender.info.shenpi.phasePay === shenpiConst.sp_status.gdzs ? 1 : 0; const result = await ctx.service.phasePayAudit.addAuditor(ctx.phasePay.id, auditorInfo, ctx.phasePay.audit_times, is_gdzs); if (!result) throw '添加审核人失败'; const auditors = await ctx.service.phasePayAudit.getAuditorGroup(ctx.phasePay.id, ctx.phasePay.audit_times); ctx.body = { err: 0, msg: '', data: auditors }; } catch (err) { ctx.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 移除审批人 * @param ctx * @return {Promise} */ async deleteAudit(ctx) { try { const data = JSON.parse(ctx.request.body.data); const id = data.auditorId instanceof Number ? data.auditorId : this.app._.toNumber(data.auditorId); if (isNaN(id) || id <= 0) throw '参数错误'; const result = await ctx.service.phasePayAudit.deleteAuditor(ctx.phasePay.id, id, ctx.phasePay.audit_times); if (!result) throw '移除审核人失败'; const auditors = await ctx.service.phasePayAudit.getAuditors(ctx.phasePay.id, ctx.phasePay.audit_times); ctx.body = { err: 0, msg: '', data: auditors }; } catch (err) { ctx.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async auditStart(ctx) { try { if (ctx.phasePay.create_user_id !== ctx.session.sessionUser.accountId) throw '您无权上报该期数据'; if (ctx.phasePay.revising) throw '台账修订中,不可上报'; if (ctx.phasePay.audit_status !== audit.common.status.uncheck && ctx.phasePay.audit_status !== audit.common.status.checkNo) throw '该期数据当前无法上报'; await ctx.service.phasePayAudit.start(ctx.phasePay); ctx.redirect(ctx.request.header.referer); } catch (err) { ctx.log(err); ctx.postError(err, '上报失败'); ctx.redirect(`/tender/${ctx.phasePay.tid}/pay/${ctx.phasePay.phase_order}/detail`); } } async auditCheck(ctx) { try { if (!ctx.phasePay || (ctx.phasePay.audit_status !== audit.common.status.checking && ctx.phasePay.audit_status !== audit.common.status.checkNoPre)) { throw '当前期数据有误'; } if (ctx.phasePay.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0) { throw '您无权进行该操作'; } if (ctx.phasePay.revising) throw '台账修订中,不可审批'; const checkType = parseInt(ctx.request.body.checkType); const opinion = ctx.request.body.opinion.replace(/\r\n/g, '
').replace(/\n/g, '
').replace(/\s/g, ' '); await ctx.service.phasePayAudit.check(ctx.phasePay, checkType, opinion); } catch (err) { ctx.log(err); ctx.postError(err, '审批失败'); } ctx.redirect(ctx.request.header.referer); } async auditCheckAgain(ctx) { try { if (!ctx.phasePay.isLatest) throw '非最新一期,不可重新审批'; if (ctx.phasePay.audit_status !== audit.common.status.checked) throw '未审批完成,不可重新审批'; if (ctx.phasePay.revising) throw '台账修订中,不可重审'; if (ctx.session.sessionUser.loginStatus === 0) { const user = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId); if (!user.auth_mobile) throw '未绑定手机号'; const code = ctx.request.body.code; const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId; const cacheCode = await app.redis.get(cacheKey); if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) { throw '验证码不正确!'; } } const adminCheckAgain = ctx.request.body.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin; if (ctx.phasePay.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0 && !adminCheckAgain) throw '您无权重新审批'; await ctx.service.phasePayAudit.checkAgain(ctx.phasePay, adminCheckAgain); } catch (err) { ctx.log(err); ctx.postError(err, '重新审批失败'); } ctx.redirect(ctx.request.header.referer); } async auditCheckCancel(ctx) { try { if (ctx.phasePay.revising) throw '台账修订中,不可撤回'; if (!ctx.phasePay.cancancel) throw '您无权进行该操作'; await ctx.service.phasePayAudit.checkCancel(ctx.phasePay); } catch (err) { ctx.log(err); ctx.postError(err, '撤回失败'); } ctx.redirect(ctx.request.header.referer); } } return PayController; };