pay_controller.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. 'use strict';
  2. /**
  3. * 合同支付
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const audit = require('../const/audit');
  10. const shenpiConst = require('../const/shenpi');
  11. const sendToWormhole = require('stream-wormhole');
  12. const path = require('path');
  13. module.exports = app => {
  14. class PayController extends app.BaseController {
  15. /**
  16. * 构造函数
  17. *
  18. * @param {Object} ctx - egg全局变量
  19. * @return {void}
  20. */
  21. constructor(ctx) {
  22. super(ctx);
  23. }
  24. async index(ctx) {
  25. try {
  26. const phasePays = await this.ctx.service.phasePay.getAllPhasePay(ctx.tender.id, 'DESC');
  27. const relaStage = [];
  28. for (const p of phasePays) {
  29. // todo 加载当前审批人
  30. // if (p.audit_status !== checked) await this.ctx.service.phasePay.loadUser(p);
  31. p.curAuditors = [];
  32. relaStage.push(...p.rela_stage);
  33. }
  34. const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id }, orders: [['order', 'AEC']] });
  35. const validStages = stages.filter(s => {
  36. return !relaStage.find(r => { return s.id === r.id; });
  37. });
  38. this.ctx.service.phasePay.calculatePhasePay(phasePays);
  39. const renderData = {
  40. phasePays,
  41. validStages,
  42. auditConst: audit.common,
  43. jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.phasePay.list)
  44. };
  45. await this.layout('phase_pay/index.ejs', renderData, 'phase_pay/modal.ejs');
  46. } catch (err) {
  47. ctx.helper.log(err);
  48. }
  49. }
  50. async add(ctx) {
  51. try {
  52. if (ctx.session.sessionUser.accountId !== ctx.tender.data.user_id && ctx.tender.userAssistsId.indexOf(ctx.session.sessionUser.accountId) < 0) {
  53. throw '您无权创建计量期';
  54. }
  55. const date = ctx.request.body.date;
  56. if (!date) throw '请选择支付年月';
  57. const stage = ctx.request.body.stage;
  58. if (!stage) throw '请选择计量期';
  59. const memo = ctx.request.body.memo;
  60. const pays = await ctx.service.phasePay.getAllPhasePay(ctx.tender.id, 'DESC');
  61. const unCompleteCount = pays.filter(s => { return s.status !== audit.common.status.checked; }).length;
  62. if (unCompleteCount.length > 0) throw `最新一起未审批通过,请审批通过后再新增`;
  63. // 预留可以关联多期
  64. const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id, order: stage } });
  65. const newPhase = await ctx.service.phasePay.add(ctx.tender.id, stages, date, memo);
  66. if (!newPhase) throw '新增期失败';
  67. newPhase.curTimes = 1;
  68. newPhase.curOrder = 0;
  69. await ctx.service.phasePayDetail.calculateSave(newPhase);
  70. ctx.redirect('/tender/' + ctx.tender.id + '/pay' + newPhase.phase_order);
  71. } catch (err) {
  72. this.log(err);
  73. ctx.postError(err, '新增期失败');
  74. ctx.redirect(ctx.request.header.referer);
  75. }
  76. }
  77. async del(ctx) {
  78. try {
  79. if (!ctx.session.sessionUser.is_admin && ctx.request.body.confirm !== '确认删除本期') throw '请输入文本确认删除本期';
  80. const phase_id = ctx.request.body.phase_id;
  81. const phasePay = await ctx.service.phasePay.getDataById(phase_id);
  82. if (!phasePay) throw '删除的期不存在,请刷新页面';
  83. if (!ctx.session.sessionUser.is_admin && phasePay.create_user_id !== ctx.session.sessionUser.accountId) throw '您无权删除本期';
  84. // 获取最新的期数
  85. const phasePayCount = await ctx.service.phasePay.count({ tid: ctx.tender.id });
  86. if (phasePay.phase_order !== phasePayCount) throw '非最新一期,不可删除';
  87. await ctx.service.phasePay.delete(phase_id);
  88. // todo 刷新金额概况缓存
  89. // await ctx.service.tenderCache.refreshPayCache(phasePay.tenderId);
  90. ctx.redirect('/tender/' + ctx.tender.id + '/pay');
  91. } catch (err) {
  92. ctx.log(err);
  93. ctx.redirect(ctx.request.header.referer);
  94. }
  95. }
  96. async save(ctx) {
  97. try {
  98. const phase_id = ctx.request.body.phase_id;
  99. const data = {
  100. phase_date: ctx.request.body.date,
  101. memo: ctx.request.body.memo,
  102. };
  103. const phasePay = await ctx.service.phasePay.getPhasePay(phase_id);
  104. if (!phasePay) throw '删除的期不存在,请刷新页面';
  105. if (!ctx.session.sessionUser.is_admin && phasePay.create_user_id !== ctx.session.sessionUser.accountId) throw '您无权修改该数据';
  106. await this.ctx.service.phasePay.save(phasePay, data);
  107. if (phasePay.audit_status === audit.common.status.uncheck && ctx.request.body.stage) {
  108. const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id, order: ctx.request.body.stage } });
  109. await this.ctx.service.phasePay.resetRelaStageId(phasePay, stages);
  110. }
  111. ctx.redirect('/tender/' + ctx.tender.id + '/pay');
  112. } catch (err) {
  113. console.log(err);
  114. this.log(err);
  115. ctx.redirect('/tender/' + ctx.tender.id + '/pay');
  116. }
  117. }
  118. async detail(ctx) {
  119. try {
  120. // await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay);
  121. const pays = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay);
  122. const calcBase = this.ctx.service.phasePay.getPhasePayCalcBase(ctx.phasePay, ctx.tender.info);
  123. const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
  124. const lastStage = await this.ctx.service.stage.getLastestCompleteStage(ctx.tender.id);
  125. const accountList = await ctx.service.projectAccount.getAllDataByCondition({
  126. where: { project_id: ctx.session.sessionProject.id, enable: 1 },
  127. columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
  128. });
  129. const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
  130. const accountGroup = unitList.map(item => {
  131. const groupList = accountList.filter(item1 => item1.company === item.name);
  132. return { groupName: item.name, groupList };
  133. });
  134. const renderData = {
  135. pays,
  136. calcBase,
  137. lockPayExpr: projectFunInfo.lockPayExpr,
  138. auditConst: audit.common,
  139. deadlineType: this.ctx.service.phasePayDetail.deadlineType,
  140. maxStageOrder: lastStage.order,
  141. jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.phasePay.detail),
  142. accountList,
  143. accountGroup,
  144. shenpiConst,
  145. };
  146. await this.layout('phase_pay/detail.ejs', renderData, 'phase_pay/detail_modal.ejs');
  147. } catch (err) {
  148. ctx.helper.log(err);
  149. ctx.postError(err, '读取合同支付数据错误');
  150. ctx.redirect(ctx.request.headers.referer);
  151. }
  152. }
  153. async detailLoad(ctx) {
  154. try {
  155. const data = JSON.parse(ctx.request.body.data);
  156. if (!data.filter) throw '参数错误';
  157. const filter = data.filter.split(',');
  158. const result = {};
  159. for (const f of filter) {
  160. switch(f) {
  161. case 'pay':
  162. result.pay = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay);
  163. break;
  164. case 'base':
  165. result.base = this.ctx.service.phasePay.getPhasePayCalcBase(ctx.phasePay, ctx.tender.info);
  166. break;
  167. case 'add':
  168. result.add = ctx.phasePay.calc_base;
  169. break;
  170. case 'file':
  171. result.file = await this.ctx.service.phasePayFile.getData(ctx.phasePay.id, 'pay');
  172. }
  173. }
  174. ctx.body = { err: 0, msg: '', data: result };
  175. } catch (err) {
  176. ctx.log(err);
  177. ctx.ajaxErrorBody(err, '读取合同支付数据错误');
  178. }
  179. }
  180. async detailUpdate(ctx) {
  181. try {
  182. const data = JSON.parse(ctx.request.body.data);
  183. if (!data.postType || !data.postData) throw '数据错误';
  184. const responseData = { err: 0, msg: '', data: {} };
  185. switch (data.postType) {
  186. case 'add':
  187. responseData.data = await this.ctx.service.phasePayDetail.addDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1);
  188. break;
  189. case 'delete':
  190. await this.ctx.service.phasePayDetail.deleteDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1);
  191. await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay);
  192. responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay);
  193. break;
  194. case 'up-move':
  195. responseData.data = await this.ctx.service.phasePayDetail.upMoveDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1);
  196. break;
  197. case 'down-move':
  198. responseData.data = await this.ctx.service.phasePayDetail.downMoveDetailNode(ctx.phasePay, data.postData.id, data.postData.count || 1);
  199. break;
  200. case 'update':
  201. const updateDetail = await this.ctx.service.phasePayDetail.updateDetail(ctx.phasePay, data.postData);
  202. if (this.ctx.service.phasePayDetail.checkCalc(data.postData)) {
  203. await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay);
  204. responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay);
  205. } else {
  206. responseData.data.update = updateDetail;
  207. }
  208. break;
  209. case 'calc':
  210. await this.ctx.service.phasePayDetail.calculateSave(ctx.phasePay);
  211. responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay);
  212. break;
  213. case 'refreshBase':
  214. await this.ctx.service.phasePay.refreshCalcBase(ctx.phasePay);
  215. responseData.data.reload = await this.ctx.service.phasePayDetail.getDetailData(ctx.phasePay);
  216. responseData.data.calcBase = this.ctx.service.phasePay.getPhasePayCalcBase(ctx.phasePay, ctx.tender.info);
  217. responseData.data.calcBase.forEach(x => { x.formatValue = ctx.tender.info.display.thousandth ? ctx.helper.formatNum(x.value, '#,##0.######') : x.value; });
  218. responseData.data.addBase = ctx.phasePay.calc_base;
  219. break;
  220. default:
  221. throw '未知操作';
  222. }
  223. ctx.body = responseData;
  224. } catch (err) {
  225. console.log(err);
  226. this.log(err);
  227. ctx.body = this.ajaxErrorBody(err, '数据错误');
  228. }
  229. }
  230. async uploadFile(ctx) {
  231. let stream;
  232. try {
  233. const parts = ctx.multipart({autoFields: true});
  234. let index = 0;
  235. const create_time = Date.parse(new Date()) / 1000;
  236. let stream = await parts();
  237. const user = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
  238. const rela_type = parts.fields.type;
  239. const rela_id = parts.field.rela_id;
  240. const uploadfiles = [];
  241. while (stream !== undefined) {
  242. if (!stream.filename) throw '未发现上传文件!';
  243. const fileInfo = path.parse(stream.filename);
  244. const filepath = `app/public/upload/${ctx.phasePay.tid}/phasePay/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + index + fileInfo.ext}`;
  245. // 保存文件
  246. await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
  247. await sendToWormhole(stream);
  248. // 插入到stage_pay对应的附件列表中
  249. uploadfiles.push({
  250. rela_id,
  251. filename: fileInfo.name,
  252. fileext: fileInfo.ext,
  253. filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
  254. filepath,
  255. });
  256. ++index;
  257. if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
  258. stream = await parts();
  259. } else {
  260. stream = undefined;
  261. }
  262. }
  263. const result = await ctx.service.phasePayFile.addFiles(ctx.phasePay, 'pay', uploadfiles, user);
  264. ctx.body = {err: 0, msg: '', data: result};
  265. } catch (error) {
  266. ctx.log(error);
  267. // 失败需要消耗掉stream 以防卡死
  268. if (stream) await sendToWormhole(stream);
  269. ctx.body = this.ajaxErrorBody(error, '上传附件失败,请重试');
  270. }
  271. }
  272. async deleteFile(ctx) {
  273. try{
  274. const data = JSON.parse(ctx.request.body.data);
  275. if (!data) throw '缺少参数';
  276. const result = await ctx.service.phasePayFile.delFiles(data);
  277. ctx.body = { err: 0, msg: '', data: result };
  278. } catch(error) {
  279. this.log(error);
  280. ctx.ajaxErrorBody(error, '删除附件失败');
  281. }
  282. }
  283. async auditUpdate(ctx) {
  284. try {
  285. if (!ctx.session.sessionUser.accountId !== ctx.phasePay.create_user_id) throw '您无权进行操作';
  286. if (ctx.phasePay.status !== audit.common.status.uncheck && ctx.phasePay.status !== audit.common.status.checkNo) throw '已上报,不可修改审批流程';
  287. const data = JSON.parse(ctx.request.body.data);
  288. await ctx.service.phasePayAudit.saveAudit(ctx.phasePay, data);
  289. const auditors = await ctx.service.phasePayAudit.getUniqUserGroup(ctx.phasePay.id, ctx.phasePay.audit_times);
  290. ctx.body = { err: 0, msg: '', data: auditors };
  291. } catch (err) {
  292. ctx.log(err);
  293. ctx.body = this.ajaxErrorBody(err, '保存审批人数据失败');
  294. }
  295. }
  296. async auditSave(ctx) {
  297. try {
  298. if (!ctx.session.sessionUser.is_admin) throw '您无权进行操作';
  299. if (ctx.phasePay.status === audit.common.status.checked) throw '已审批通过,不可修改审批流程';
  300. const data = JSON.parse(ctx.request.body.data);
  301. await ctx.service.phasePayAudit.saveAudit(ctx.phasePay, data);
  302. const auditors = await ctx.service.phasePayAudit.getUniqUserGroup(ctx.phasePay.id, ctx.phasePay.audit_times);
  303. ctx.body = { err: 0, msg: '', data: auditors };
  304. } catch (err) {
  305. ctx.log(err);
  306. ctx.body = this.ajaxErrorBody(err, '保存审批人数据失败');
  307. }
  308. }
  309. }
  310. return PayController;
  311. };