weapp_tender_controller.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. 'use strict';
  2. const auditConst = require('../const/audit');
  3. const changeMap = require('../const/weapp').changeMap;
  4. const measureType = require('../const/tender').measureType;
  5. const advanceConst = require('../const/advance');
  6. const shenpiConst = require('../const/shenpi');
  7. const stdConst = require('../const/standard');
  8. const _ = require('lodash');
  9. module.exports = app => {
  10. class WeappTenderController extends app.BaseController {
  11. async listManage(ctx) {
  12. try {
  13. const projectId = ctx.query.projectId;
  14. if (!projectId) {
  15. ctx.body = { err: 1, msg: '缺少projectId参数', data: null };
  16. return;
  17. }
  18. const accountInfo = ctx.session.sessionUser;
  19. const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
  20. if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) {
  21. const subProject = await this.ctx.service.subProject.getDataById(projectId);
  22. const tenderList = await this.ctx.service.tender.getList('', userPermission, ctx.session.sessionUser.is_admin, '', subProject);
  23. for (const tender of tenderList) {
  24. // 查询当前审批期以及状态
  25. const stage = await this.ctx.service.stage.getLastestStage(tender.id, true);
  26. tender.stage_order = stage && stage.order ? stage.order : null;
  27. tender.stage_status = stage && stage.status ? stage.status : null;
  28. }
  29. const categoryData = await this.ctx.service.category.getAllCategory(subProject);
  30. const renderData = {
  31. categoryData,
  32. tenderList,
  33. };
  34. ctx.body = { err: 0, msg: '', data: renderData };
  35. } else {
  36. ctx.body = { err: 1, msg: '您没有管理权限', data: null };
  37. }
  38. } catch (err) {
  39. this.log(err);
  40. ctx.body = { err: 1, msg: err.toString(), data: null };
  41. }
  42. }
  43. async detail(ctx) {
  44. try {
  45. const tender = ctx.tender.data;
  46. let bCalcTp = ctx.tender.data.user_id === ctx.session.sessionUser.accountId && (
  47. ctx.tender.ledger_status === auditConst.ledger.status.checkNo || ctx.tender.ledger_status === auditConst.ledger.status.uncheck);
  48. const stages = await ctx.service.stage.getValidStages(ctx.tender.id);
  49. const lastStage = stages.length > 0 ? stages[0] : null; // await ctx.service.stage.getLastestStage(ctx.tender.id);
  50. const [change_tp, change_p_tp, change_n_tp, change_valuation_tp, change_unvaluation_tp] = await ctx.service.change.getChangeTp(ctx.tender.id);
  51. tender.change_tp = change_tp;
  52. tender.change_p_tp = change_p_tp;
  53. tender.change_n_tp = change_n_tp;
  54. tender.change_valuation_tp = change_valuation_tp;
  55. tender.change_unvaluation_tp = change_unvaluation_tp;
  56. if (lastStage) {
  57. await this.ctx.service.stage.checkStageGatherData(lastStage, ctx.session.sessionUser.is_admin);
  58. if ((!bCalcTp) && tender.measure_type === measureType.gcl.value) {
  59. bCalcTp = lastStage.status !== auditConst.stage.status.checked && !lastStage.readOnly;
  60. }
  61. if (bCalcTp) {
  62. const sum = await this.ctx.service.ledger.addUp({ tender_id: ctx.tender.id/* , is_leaf: true*/ });
  63. tender.total_price = sum.total_price;
  64. tender.deal_tp = sum.deal_tp;
  65. }
  66. tender.sum = ctx.tender.info.calc_type === 'tp' ? ctx.helper.add(tender.total_price, tender.change_valuation_tp) : ctx.helper.add(tender.total_price, tender.change_tp);
  67. tender.gather_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.qc_tp, lastStage.pc_tp]);
  68. tender.end_contract_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.pre_contract_tp, lastStage.contract_pc_tp]);
  69. tender.end_qc_tp = ctx.helper.sum([lastStage.qc_tp, lastStage.pre_qc_tp, lastStage.qc_pc_tp]);
  70. tender.end_positive_qc_tp = ctx.helper.sum([lastStage.positive_qc_tp, lastStage.pre_positive_qc_tp, lastStage.positive_qc_pc_tp]);
  71. tender.end_negative_qc_tp = ctx.helper.sum([lastStage.negative_qc_tp, lastStage.pre_negative_qc_tp, lastStage.negative_qc_pc_tp]);
  72. tender.end_gather_tp = ctx.helper.add(tender.end_contract_tp, tender.end_qc_tp);
  73. tender.pre_gather_tp = ctx.helper.add(lastStage.pre_contract_tp, lastStage.pre_qc_tp);
  74. tender.yf_tp = lastStage.yf_tp;
  75. tender.sf_tp = lastStage.sf_tp;
  76. tender.qc_ratio = ctx.helper.mul(ctx.helper.div(tender.end_qc_tp, ctx.tender.info.deal_param.contractPrice, 2), 100);
  77. tender.pre_ratio = ctx.helper.mul(ctx.helper.div(tender.pre_gather_tp, tender.sum, 2), 100);
  78. tender.cur_ratio = ctx.helper.mul(ctx.helper.div(tender.gather_tp, tender.sum, 2), 100);
  79. tender.other_tp = ctx.helper.sub(ctx.helper.sub(tender.sum, tender.pre_gather_tp), tender.gather_tp);
  80. tender.other_ratio = Math.max(0, 100 - tender.pre_ratio - tender.cur_ratio);
  81. tender.end_yf_tp = ctx.helper.add(lastStage.yf_tp, lastStage.pre_yf_tp);
  82. tender.end_sf_tp = ctx.helper.add(lastStage.sf_tp, lastStage.pre_sf_tp);
  83. tender.undone_tp = ctx.helper.sub(ctx.helper.sub(ctx.helper.add(tender.total_price, change_tp), tender.end_contract_tp), tender.end_qc_tp);
  84. } else {
  85. if (bCalcTp) {
  86. const sum = await this.ctx.service.ledger.addUp({ tender_id: ctx.tender.id/* , is_leaf: true*/ });
  87. tender.total_price = sum.total_price;
  88. tender.deal_tp = sum.deal_tp;
  89. }
  90. tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
  91. }
  92. // const monthProgress = [];
  93. // for (const s of stages) {
  94. // if (s.s_time) {
  95. // let progress = monthProgress.find(function(x) {
  96. // return x.month === s.s_time;
  97. // });
  98. // if (!progress) {
  99. // progress = { month: s.s_time };
  100. // monthProgress.push(progress);
  101. // }
  102. // progress.tp = ctx.helper.add(ctx.helper.add(progress.tp, s.contract_tp), s.qc_tp);
  103. // }
  104. // }
  105. // monthProgress.sort(function(x, y) {
  106. // return Date.parse(x.month) - Date.parse(y.month);
  107. // });
  108. // let sum = 0;
  109. // for (const p of monthProgress) {
  110. // p.ratio = ctx.helper.mul(ctx.helper.div(p.tp, tender.sum, 4), 100);
  111. // sum = ctx.helper.add(sum, p.tp);
  112. // p.end_tp = sum;
  113. // p.end_ratio = ctx.helper.mul(ctx.helper.div(p.end_tp, tender.sum, 4), 100);
  114. // }
  115. ctx.body = {
  116. code: 0, msg: '', data: {
  117. tender,
  118. // monthProgress,
  119. // stagesEcharts: JSON.parse(JSON.stringify(stages)).reverse(),
  120. },
  121. };
  122. } catch (error) {
  123. this.log(error);
  124. ctx.body = { code: -1, msg: error.toString(), data: null };
  125. }
  126. }
  127. async advanceList(ctx) {
  128. try {
  129. const { advanceType } = ctx.query;
  130. const typeCol = advanceConst.typeCol.find(x => x.type === Number(advanceType));
  131. if (!typeCol) {
  132. ctx.body = { code: -1, msg: '预付款类型错误', data: null };
  133. return;
  134. }
  135. const { decimal } = ctx.tender.info;
  136. const advancePayTotal = ctx.tender.info.deal_param[typeCol.key + 'Advance'];
  137. const advances = await ctx.service.advance.getAdvanceList(ctx.tender.id, typeCol.type, decimal.pay ? decimal.payTp : decimal.tp, advancePayTotal);
  138. ctx.body = { code: 0, msg: '', data: advances };
  139. } catch (error) {
  140. this.log(error);
  141. ctx.body = { code: -1, msg: error.toString(), data: null };
  142. }
  143. }
  144. async advanceCheck(advance) {
  145. if (!advance) {
  146. throw '预付款数据错误';
  147. }
  148. const times = advance.status === auditConst.advance.status.checkNo ? advance.times - 1 : advance.times;
  149. advance.user = await this.service.projectAccount.getAccountInfoById(advance.uid);
  150. // 读取审核人列表数据
  151. advance.auditors = await this.service.advanceAudit.getAuditorsWithOwner(advance.id, advance.status === auditConst.advance.status.checkNo && this.ctx.session.sessionUser.accountId !== advance.uid ? times : advance.times);
  152. advance.auditHistory = await this.service.advanceAudit.getAuditors(advance.id, advance.times);
  153. advance.curAuditor = await this.service.advanceAudit.getCurAuditor(advance.id, advance.times);
  154. // 根据状态判断是否需要更新审批人列表
  155. if ((advance.status === auditConst.advance.status.uncheck || advance.status === auditConst.advance.status.checkNo) && this.ctx.tender.info.shenpi.advance !== shenpiConst.sp_status.sqspr) {
  156. const shenpi_status = this.ctx.tender.info.shenpi.advance;
  157. // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
  158. const auditList = await this.service.advanceAudit.getAllDataByCondition({ where: { vid: advance.id, times: advance.times }, orders: [['order', 'asc']] });
  159. const auditIdList = _.map(auditList, 'audit_id');
  160. if (shenpi_status === shenpiConst.sp_status.gdspl) {
  161. const shenpiList = await this.service.shenpiAudit.getAllDataByCondition({ where: { tid: advance.tid, sp_type: shenpiConst.sp_type.advance, sp_status: shenpi_status } });
  162. const shenpiIdList = _.map(shenpiList, 'audit_id');
  163. // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
  164. if (!_.isEqual(auditIdList, shenpiIdList)) {
  165. await this.service.advanceAudit.updateNewAuditList(advance, shenpiIdList);
  166. }
  167. } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
  168. const shenpiInfo = await this.service.shenpiAudit.getDataByCondition({ tid: advance.tid, sp_type: shenpiConst.sp_type.advance, sp_status: shenpi_status });
  169. // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
  170. if (shenpiInfo && shenpiInfo.audit_id !== _.last(auditIdList)) {
  171. await this.service.advanceAudit.updateLastAudit(advance, auditList, shenpiInfo.audit_id);
  172. } else if (!shenpiInfo) {
  173. // 不存在终审人的状态下这里恢复为授权审批人
  174. this.ctx.tender.info.shenpi.advance = shenpiConst.sp_status.sqspr;
  175. }
  176. }
  177. }
  178. advance.advancePayTotal = this.ctx.tender.info.deal_param[advanceConst.typeCol[advance.type].key + 'Advance'];
  179. if (advance.status === auditConst.advance.status.checkNo) {
  180. advance.curAuditor = await this.service.advanceAudit.getAuditorByStatus(advance.id, advance.status, times);
  181. advance.auditHistory = await this.service.advanceAudit.getAuditors(advance.id, times);
  182. }
  183. }
  184. async advanceDetail(ctx) {
  185. try {
  186. const { id } = ctx.query;
  187. const advance = await this.service.advance.getDataById(id);
  188. if (!advance) {
  189. throw '预付款数据错误';
  190. }
  191. await this.advanceCheck(advance);
  192. advance.auditHistory = advance.auditHistory.reduce((prev, curr, idx) => {
  193. if (idx === 0) {
  194. const reportor = {
  195. audit_id: advance.uid,
  196. audit_order: 0,
  197. audit_type: 1,
  198. status: advance.status === auditConst.advance.status.uncheck ? auditConst.advance.status.uncheck : auditConst.advance.status.checked,
  199. times: curr.times,
  200. begin_time: advance.pay_time,
  201. end_time: advance.auditHistory.length ? advance.auditHistory[0].create_time : null,
  202. name: advance.user.name,
  203. company: advance.user.company,
  204. role: advance.user.role,
  205. mobile: '',
  206. opinion: '',
  207. };
  208. prev.push({
  209. name: '原报',
  210. status: reportor.status,
  211. audit_type: reportor.audit_type,
  212. auditors: [reportor],
  213. });
  214. }
  215. const currentOrder = advance.auditors.find(auditor => auditor.audit_id === curr.audit_id).order;
  216. prev.push(
  217. {
  218. name: currentOrder === curr.max_sort ? '终审' : `${currentOrder}审`,
  219. status: curr.status,
  220. audit_type: 1,
  221. auditors: [{
  222. audit_id: curr.audit_id,
  223. audit_order: curr.order,
  224. audit_type: curr.type,
  225. status: curr.status,
  226. max_sort: curr.max_sort,
  227. begin_time: curr.create_time,
  228. end_time: curr.end_time,
  229. name: curr.name,
  230. company: curr.company,
  231. mobile: curr.mobile,
  232. opinion: curr.opinion,
  233. role: curr.role,
  234. times: curr.times,
  235. }],
  236. }
  237. );
  238. return prev;
  239. }, []);
  240. if (advance.curAuditor) {
  241. advance.curAuditor.audit_order = advance.auditors.find(auditor => auditor.audit_id === advance.curAuditor.audit_id).order;
  242. }
  243. ctx.body = { code: 0, msg: '', data: { advance } };
  244. } catch (error) {
  245. this.log(error);
  246. ctx.body = { code: -1, msg: error.toString(), data: null };
  247. }
  248. }
  249. async advanceAudit(ctx) {
  250. try {
  251. const advance = await this.service.advance.getDataById(ctx.request.body.id);
  252. await this.advanceCheck(advance);
  253. ctx.advance = advance;
  254. const data = {
  255. checkType: parseInt(ctx.request.body.checkType),
  256. opinion: ctx.request.body.opinion,
  257. };
  258. if (!advance || advance.status !== auditConst.advance.status.checking) {
  259. throw '当前预付款数据有误';
  260. }
  261. if (!advance.curAuditor || advance.curAuditor.audit_id !== ctx.session.sessionUser.accountId) {
  262. throw '您无权进行该操作';
  263. }
  264. if (!data.checkType || isNaN(data.checkType)) {
  265. throw '提交数据错误';
  266. }
  267. if (data.checkType === auditConst.advance.status.checkNo) {
  268. if (!data.checkType || isNaN(data.checkType)) {
  269. throw '提交数据错误';
  270. }
  271. }
  272. await ctx.service.advanceAudit.check(advance.id, data, advance.times, advance.type);
  273. ctx.body = { code: 0, msg: '操作成功', data: null };
  274. } catch (error) {
  275. this.log(error);
  276. ctx.body = { code: -1, msg: error.toString(), data: null };
  277. }
  278. }
  279. async ledgerAuditCheck(ctx) {
  280. const tender = ctx.tender.data;
  281. const info = ctx.tender.info;
  282. if (info.shenpi.ledger === shenpiConst.sp_status.sqspr) return;
  283. if (tender.ledger_status !== auditConst.ledger.status.uncheck && tender.ledger_status !== auditConst.ledger.status.checkNo) return;
  284. const shenpi_status = info.shenpi.ledger;
  285. // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
  286. const auditList = await ctx.service.ledgerAudit.getAuditors(tender.id, tender.ledger_times);
  287. if (shenpi_status === shenpiConst.sp_status.gdspl) {
  288. const shenpiList = await ctx.service.shenpiAudit.getAllDataByCondition({ where: { tid: tender.id, sp_type: shenpiConst.sp_type.ledger, sp_status: shenpi_status } });
  289. // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
  290. let sameAudit = auditList.length === shenpiList.length;
  291. if (sameAudit) {
  292. for (const audit of auditList) {
  293. const shenpi = shenpiList.find(x => { return x.audit_id === audit.audit_id; });
  294. if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type || shenpi.audit_ledger_id !== audit.audit_ledger_id) {
  295. sameAudit = false;
  296. break;
  297. }
  298. }
  299. }
  300. if (!sameAudit) await ctx.service.ledgerAudit.updateNewAuditList(tender, shenpiList);
  301. } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
  302. const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: tender.id, sp_type: shenpiConst.sp_type.ledger, sp_status: shenpi_status });
  303. // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
  304. const lastAuditors = auditList.filter(x => { x.audit_order === auditList[auditList.length - 1].audit_order; });
  305. if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].audit_id))) {
  306. await ctx.service.ledgerAudit.updateLastAudit(tender, auditList, shenpiInfo.audit_id);
  307. } else if (!shenpiInfo) {
  308. // 不存在终审人的状态下这里恢复为授权审批人
  309. info.shenpi.ledger = shenpiConst.sp_status.sqspr;
  310. }
  311. }
  312. }
  313. async ledgerDetail(ctx) {
  314. try {
  315. await this.ledgerAuditCheck(ctx);
  316. const tender = ctx.tender.data;
  317. await ctx.service.ledgerAudit.loadLedgerUser(tender);
  318. await ctx.service.ledgerAudit.loadLedgerAuditViewData(tender);
  319. const auditHistory = tender.auditHistory && tender.auditHistory.length && tender.auditHistory[tender.auditHistory.length - 1].reduce((prev, curr, idx) => {
  320. if (idx === 0) {
  321. const reportor = {
  322. audit_id: tender.user.id,
  323. audit_order: 0,
  324. audit_type: 1,
  325. status: tender.ledger_status === auditConst.ledger.status.uncheck ? auditConst.ledger.status.uncheck : auditConst.ledger.status.checked,
  326. times: tender.ledger_status === auditConst.ledger.status.checkNo ? tender.ledger_times - 1 : tender.ledger_times,
  327. begin_time: curr.begin_time,
  328. end_time: curr.begin_time,
  329. name: tender.user.name,
  330. company: tender.user.company,
  331. role: tender.user.role,
  332. mobile: '',
  333. opinion: '',
  334. };
  335. prev.push({
  336. name: '原报',
  337. status: reportor.status,
  338. audit_type: reportor.audit_type,
  339. auditors: [reportor],
  340. });
  341. }
  342. prev.push({
  343. name: curr.is_final ? '终审' : `${curr.order}审`,
  344. audit_type: curr.audit_type,
  345. status: curr.status,
  346. auditors: curr.auditors,
  347. });
  348. return prev;
  349. }, []);
  350. const curAuditors = tender.curAuditors;
  351. const ledger = {
  352. contractPrice: ctx.tender.info.deal_param.contractPrice,
  353. totalPrice: tender.total_price || 0,
  354. auditHistory,
  355. curAuditors,
  356. };
  357. ctx.body = { code: 0, msg: '', data: { ledger } };
  358. } catch (error) {
  359. this.log(error);
  360. ctx.body = { code: -1, msg: error.toString(), data: null };
  361. }
  362. }
  363. async ledgerAudit(ctx) {
  364. try {
  365. await this.ledgerAuditCheck(ctx);
  366. const tender = ctx.tender;
  367. if (!tender.data || tender.data.ledger_status !== auditConst.ledger.status.checking) {
  368. throw '当前标段数据有误';
  369. }
  370. const curAudits = await ctx.service.ledgerAudit.getCurAuditors(tender.id, tender.data.ledger_times);
  371. const curAuditIds = curAudits.map(x => { return x.audit_id; });
  372. if (curAuditIds.indexOf(ctx.session.sessionUser.accountId) < 0) {
  373. throw '审批失败';
  374. }
  375. const checkType = parseInt(ctx.request.body.checkType);
  376. if (!checkType || isNaN(checkType)) {
  377. throw '提交数据错误';
  378. }
  379. await ctx.service.ledgerAudit.check(tender.id, checkType, ctx.request.body.opinion, tender.data.ledger_times);
  380. ctx.body = { code: 0, msg: '操作成功', data: null };
  381. } catch (error) {
  382. this.log(error);
  383. ctx.body = { code: -1, msg: error.toString(), data: null };
  384. }
  385. }
  386. async changeList(ctx) {
  387. try {
  388. const { type } = ctx.request.query;
  389. const curChangeMap = changeMap[type];
  390. if (!curChangeMap) {
  391. throw '变更类型错误';
  392. }
  393. const tender = ctx.tender.data;
  394. const list = await ctx.service[curChangeMap.service].getListByStatus(tender.id, 0, 1, 0, 0, 0);
  395. ctx.body = { code: 0, msg: '', data: list };
  396. } catch (error) {
  397. this.log(error);
  398. ctx.body = { code: -1, msg: error.toString(), data: null };
  399. }
  400. }
  401. async changeCheck(ctx, curChangeMap) {
  402. try {
  403. const subProject = await ctx.service.subProject.getDataById(ctx.tender.data.spid);
  404. if (subProject.page_show && typeof subProject.page_show === 'string') {
  405. subProject.page_show = JSON.parse(subProject.page_show);
  406. };
  407. if (curChangeMap && curChangeMap.pageShowKey && !subProject.page_show[curChangeMap.pageShowKey]) {
  408. throw '该功能已关闭';
  409. }
  410. const id = ctx.request.query.id || ctx.request.body.id;
  411. if (!id) {
  412. throw '您访问的变更数据不存在';
  413. }
  414. const condition = { [curChangeMap.idKey]: id };
  415. const change = await ctx.service[curChangeMap.service].getDataByCondition(condition);
  416. if (!change) throw '变更方案数据有误';
  417. // 读取原报、审核人数据
  418. await ctx.service[curChangeMap.service].loadChangeUser(change);
  419. await ctx.service[curChangeMap.service].loadChangeAuditViewData(change);
  420. // decimal小数位设置
  421. change.decimal = change.decimal ? JSON.parse(change.decimal) : { tp: ctx.tender.info.decimal.tp, up: ctx.tender.info.decimal.up, precision: ctx.tender.info.precision };
  422. // 权限相关
  423. // todo 校验权限 (标段参与人、分享)
  424. const accountId = ctx.session.sessionUser.accountId,
  425. shareIds = [];
  426. const permission = ctx.session.sessionUser.permission;
  427. const type = ctx.request.query.type || ctx.request.body.type;
  428. const status = auditConst[type].status;
  429. if (accountId === change.uid) { // 原报
  430. change.curTimes = change.times;
  431. change.filePermission = true;
  432. } else if (change.auditorIds.indexOf(accountId) !== -1) { // 审批人
  433. if (change.status === status.uncheck) {
  434. throw '您无权查看该数据';
  435. }
  436. // change.readOnly = change.status !== status.checking || accountId !== change.curAuditor.aid;
  437. change.curTimes = change.status === status.checkNo || change.status === status.revise ? change.times - 1 : change.times;
  438. change.filePermission = true;
  439. } else if ((change.status === status.checkNo || change.status === status.revise) && change.uid !== accountId) {
  440. const preAuditors = await ctx.service[curChangeMap.auditService].getAuditors(change.id, change.times - 1);
  441. const preAuditorIds = _.map(preAuditors, 'aid');
  442. if (preAuditorIds.indexOf(accountId) === -1) {
  443. throw '您无权查看该数据';
  444. }
  445. change.filePermission = true;
  446. } else if (ctx.tender.isTourist || ctx.session.sessionUser.is_admin) {
  447. change.curTimes = change.times;
  448. change.filePermission = ctx.tender.touristPermission.file || change.auditorIds.indexOf(accountId) !== -1;
  449. } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
  450. if (change.status === auditConsts[type].status.uncheck) {
  451. throw '您无权查看该数据';
  452. }
  453. // change.readOnly = true;
  454. change.curTimes = change.status === auditConsts[type].status.checkNo || change.status === auditConsts[type].status.revise ? change.times - 1 : change.times;
  455. change.filePermission = false;
  456. } else { // 其他不可见
  457. throw '您无权查看该数据';
  458. }
  459. // 调差的readOnly 指表格和页面只能看不能改,和审批无关
  460. change.readOnly = !((change.status === status.uncheck || change.status === status.checkNo || change.status === status.revise) && accountId === change.uid);
  461. change.shenpiPower = change.status === status.checking && change.curAuditorIds.indexOf(accountId) !== -1;
  462. ctx.change = change;
  463. await ctx.service[curChangeMap.service].doCheckChangeCanCancel(ctx.change);
  464. } catch (error) {
  465. this.log(error);
  466. }
  467. }
  468. async changeDetail(ctx) {
  469. try {
  470. const type = ctx.request.query.type;
  471. const curChangeMap = changeMap[type];
  472. await this.changeCheck(ctx, curChangeMap);
  473. const tpUnit = ctx.tender.info.decimal.tp;
  474. const subProject = await ctx.service.subProject.getDataById(ctx.tender.data.spid);
  475. const fun_set = subProject.fun_set;
  476. const auditHistory = ctx.change.auditHistory && ctx.change.auditHistory.length && ctx.change.auditHistory[ctx.change.auditHistory.length - 1].reduce((prev, curr, idx) => {
  477. prev.push({
  478. name: curr.is_final ? '终审' : curr.audit_order === 0 ? '原报' : `${curr.audit_order}审`,
  479. audit_type: curr.audit_type,
  480. status: curr.status,
  481. auditors: curr.auditors.map(a => ({...a, audit_id: a.uid})),
  482. });
  483. return prev;
  484. }, []);
  485. const curAuditors = ctx.change.curAuditors;
  486. const change = {
  487. id: ctx.change.id || ctx.change.cid,
  488. tid: ctx.tender.id,
  489. name: ctx.change.name,
  490. status: ctx.change.status,
  491. code: ctx.change.code,
  492. p_code: ctx.change.p_code,
  493. quality: ctx.change.quality,
  494. auditHistory,
  495. curAuditors: curAuditors.map(a => ({...a, audit_id: a.uid})),
  496. total_price: ctx.helper.add(ctx.change.total_price, tpUnit),
  497. valuation_tp: ctx.helper.add(ctx.change.valuation_tp, tpUnit),
  498. unvaluation_tp: ctx.helper.add(ctx.change.unvaluation_tp, tpUnit),
  499. state_name: subProject.page_show.openChangeState ? ctx.helper._.find(fun_set.change_state, { order: ctx.change.state }).name : '',
  500. plan_code: ctx.change.plan_code,
  501. peg: ctx.change.peg,
  502. org_name: ctx.change.org_name,
  503. };
  504. ctx.body = { code: 0, msg: '', data: {
  505. change,
  506. } };
  507. } catch (error) {
  508. this.log(error);
  509. ctx.body = { code: -1, msg: error.toString(), data: null };
  510. }
  511. }
  512. async changeAuditCheck(ctx) {
  513. const id = ctx.request.body.id;
  514. if (!id) {
  515. throw '您访问的变更数据不存在';
  516. }
  517. const type = ctx.request.body.type;
  518. const curChangeMap = changeMap[type];
  519. if (!ctx.change) {
  520. const change = await this.service[curChangeMap.service].getDataByCondition({
  521. [curChangeMap.idKey]: id,
  522. });
  523. if (!change) throw '变更数据有误';
  524. await this.service[curChangeMap.service].loadChangeUser(change);
  525. ctx.change = change;
  526. }
  527. const change = ctx.change;
  528. const status = auditConst[type].status;
  529. if ((change.status === status.uncheck || change.status === status.checkNo || change.status === status.revise) && ctx.tender.info.shenpi.change !== shenpiConst.sp_status.sqspr) {
  530. const shenpi_status = ctx.tender.info.shenpi.change;
  531. // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
  532. const auditList = await ctx.service[curChangeMap.auditService].getAllDataByCondition({ where: { caid: change.id, times: change.times }, orders: [['order', 'asc']] });
  533. if (shenpi_status === shenpiConst.sp_status.gdspl) {
  534. // 判断并获取审批组
  535. const group = await ctx.service.shenpiGroup.getSelectGroupByChangeType(ctx.tender.id, shenpiConst.sp_type.change, type, change.sp_group);
  536. if ((group && change.sp_group !== group.id) || (!group && change.sp_group !== 0)) {
  537. change.sp_group = group ? group.id : 0;
  538. await ctx.service[curChangeMap.service].defaultUpdate({ id: change.id, sp_group: change.sp_group });
  539. }
  540. const condition = { tid: ctx.tender.id, sp_type: shenpiConst.sp_type.change, sp_status: shenpi_status, sp_group: change.sp_group };
  541. const shenpiList = await ctx.service.shenpiAudit.getAllDataByCondition({ where: condition, orders: [['audit_order', 'asc']] });
  542. await ctx.service.shenpiAudit.noYbShenpiList(change.uid, shenpiList);
  543. if (change.sp_group === 0 && shenpiList.length === 0) {
  544. ctx.tender.info.shenpi.change = shenpiConst.sp_status.sqspr;
  545. } else {
  546. // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
  547. let sameAudit = auditList.length === shenpiList.length;
  548. if (sameAudit) {
  549. for (const audit of auditList) {
  550. const shenpi = shenpiList.find(x => { return x.audit_id === audit.aid; });
  551. if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type) {
  552. sameAudit = false;
  553. break;
  554. }
  555. }
  556. }
  557. if (!sameAudit) {
  558. await ctx.service[curChangeMap.auditService].updateNewAuditList(change, shenpiList);
  559. await ctx.service[curChangeMap.service].loadChangeUser(change);
  560. await ctx.service[curChangeMap.service].doCheckChangeCanCancel(change);
  561. }
  562. }
  563. } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
  564. const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: ctx.tender.id, sp_type: shenpiConst.sp_type.change, sp_status: shenpi_status });
  565. if (!shenpiInfo || (shenpiInfo && shenpiInfo.audit_id === change.uid)) {
  566. // 不存在终审人 或 存在终审但与原报相同时, 这里恢复为授权审批人
  567. ctx.tender.info.shenpi.change = shenpiConst.sp_status.sqspr;
  568. } else {
  569. // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
  570. const lastAuditors = auditList.filter(x => {
  571. return x.order === auditList.length - 1;
  572. });
  573. if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
  574. await ctx.service[curChangeMap.auditService].updateLastAudit(change, auditList, shenpiInfo.audit_id);
  575. await ctx.service[curChangeMap.service].loadChangeUser(change);
  576. await ctx.service[curChangeMap.service].doCheckChangeCanCancel(change);
  577. }
  578. }
  579. }
  580. }
  581. }
  582. async changeAudit(ctx) {
  583. try {
  584. const type = ctx.request.body.type;
  585. const curChangeMap = changeMap[type];
  586. await this.changeCheck(ctx, curChangeMap);
  587. await this.changeAuditCheck(ctx);
  588. if (!ctx.change || ctx.change.status !== auditConst[type].status.checking) {
  589. throw '当前变更申请数据有误';
  590. }
  591. if (ctx.change.curAuditorIds.length === 0 || ctx.change.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) === -1) {
  592. throw '您无权进行该操作';
  593. }
  594. const data = {
  595. checkType: parseInt(ctx.request.body.checkType),
  596. opinion: ctx.request.body.opinion,
  597. };
  598. if (!data.checkType || isNaN(data.checkType)) {
  599. throw '提交数据错误';
  600. }
  601. if (curChangeMap.auditService === 'change') {
  602. // 单独处理变更令审批
  603. const status = parseInt(data.checkType);
  604. // 判断是否到你审批,如果不是则无法审批
  605. const curAuditor = await ctx.service.changeAudit.getCurAuditors(ctx.change.cid, ctx.change.times);
  606. if (!curAuditor || (curAuditor && ctx.helper._.findIndex(curAuditor, { uid: ctx.session.sessionUser.accountId }) === -1)) {
  607. throw '该变更令当前您无权操作';
  608. }
  609. const readySettle = await ctx.service.settle.getReadySettle(ctx.change.tid);
  610. if (readySettle && readySettle.settle_order !== ctx.tender.data.settle_order) {
  611. throw '结算数据发生变化,请刷新页面再提交';
  612. }
  613. let result = false;
  614. const pid = this.ctx.session.sessionProject.id;
  615. const checkData = {
  616. checkType: status,
  617. sdesc: data.opinion,
  618. w_code: ctx.request.body.w_code
  619. }
  620. switch (status) {
  621. case 3:// 审批通过
  622. const revising = await ctx.service.change.checkRevising(ctx.change.tid, ctx.change.cid);
  623. if (revising) {
  624. throw '台账修订中并本变更令存在新增清单,无法审批通过';
  625. }
  626. result = await ctx.service.change.approvalSuccess(pid, checkData, ctx.change);
  627. break;
  628. // case 4:// 审批终止
  629. // result = await ctx.service.change.approvalStop(ctx.request.body);
  630. // break;
  631. case 5:// 审批退回到原报人
  632. result = await ctx.service.change.approvalCheckNo(pid, checkData, ctx.change);
  633. break;
  634. case 6:// 审批退回到上一个审批人
  635. result = await ctx.service.change.approvalCheckNoPre(pid, checkData, ctx.change);
  636. break;
  637. default:break;
  638. }
  639. if (!result) {
  640. throw '审批失败';
  641. }
  642. } else {
  643. await ctx.service[curChangeMap.auditService].check(ctx.change.id, data, ctx.change.times);
  644. }
  645. ctx.body = { code: 0, msg: '操作成功', data: null };
  646. } catch (error) {
  647. this.log(error);
  648. ctx.body = { code: -1, msg: error.toString(), data: null };
  649. }
  650. }
  651. async reviseList(ctx) {
  652. try {
  653. const tenderId = ctx.tender.id;
  654. const show_revise_invalid = 0;
  655. const ledgerRevise = await ctx.service.ledgerRevise.getReviseList(tenderId, show_revise_invalid);
  656. ctx.body = { code: 0, msg: '', data: ledgerRevise };
  657. } catch (err) {
  658. this.log(err);
  659. ctx.body = { code: -1, msg: err.toString(), data: null };
  660. }
  661. }
  662. async reviseDetail(ctx) {
  663. try {
  664. const { rid } = ctx.query;
  665. const revise = rid
  666. ? await ctx.service.ledgerRevise.getRevise(ctx.tender.id, rid)
  667. : await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
  668. if (!revise) throw '修订数据不存在或已失效';
  669. revise.preHis = revise.pre_his_id ? await ctx.service.ledgerHistory.getDataById(revise.pre_his_id) : null;
  670. revise.curHis = revise.his_id ? await ctx.service.ledgerHistory.getDataById(revise.his_id) : null;
  671. await ctx.service.reviseAudit.loadReviseUser(revise);
  672. await ctx.service.reviseAudit.loadReviseAuditViewData(revise);
  673. revise.auditHistory = revise.auditors.reduce((prev, curr, idx) => {
  674. if (idx === 0) {
  675. const reportor = {
  676. audit_id: revise.uid,
  677. audit_order: 0,
  678. audit_type: 1,
  679. status: revise.status === auditConst.revise.status.uncheck ? auditConst.revise.status.uncheck : auditConst.revise.status.checked,
  680. times: curr.times,
  681. begin_time: revise.in_time,
  682. end_time: revise.auditors.length ? revise.auditors[0].begin_time : null,
  683. name: revise.user.name,
  684. company: revise.user.company,
  685. role: revise.user.role,
  686. mobile: '',
  687. opinion: '',
  688. };
  689. prev.push({
  690. name: '原报',
  691. status: reportor.status,
  692. audit_type: reportor.audit_type,
  693. auditors: [reportor],
  694. });
  695. }
  696. const currentOrder = curr.audit_order;
  697. prev.push(
  698. {
  699. name: currentOrder === curr.max_sort ? '终审' : `${currentOrder}审`,
  700. status: curr.status,
  701. audit_type: 1,
  702. auditors: [{
  703. audit_id: curr.audit_id,
  704. audit_order: curr.audit_order,
  705. audit_type: curr.audit_type,
  706. status: curr.status,
  707. max_sort: curr.max_sort,
  708. begin_time: curr.begin_time,
  709. end_time: curr.end_time,
  710. name: curr.name,
  711. company: curr.company,
  712. mobile: curr.mobile,
  713. opinion: curr.opinion,
  714. role: curr.role,
  715. times: curr.times,
  716. }],
  717. }
  718. );
  719. return prev;
  720. }, []);
  721. const GclGather = require('../lib/gcl_gather');
  722. const gclGatherModal = new GclGather.gclCompareGather(this.ctx);
  723. const spec = { zlj: stdConst.zlj, jrg: stdConst.jrg };
  724. spec.zlj.deal_bills_tp = ctx.tender.info.deal_param.zanLiePrice;
  725. gclGatherModal.init(ctx.tender.info.chapter, spec);
  726. const reviseBillsData = revise.readOnly && revise.curHis
  727. ? await ctx.helper.loadLedgerDataFromOss(revise.curHis.bills_file)
  728. : await ctx.service.reviseBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
  729. const revisePosData = revise.readOnly && revise.curHis
  730. ? await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.pos_file)
  731. : await ctx.service.revisePos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
  732. const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } });
  733. gclGatherModal.gatherReviseLedgerData(reviseBillsData, revisePosData, { prefix: 'new_' }, price, ctx.tender.info.decimal);
  734. const billsData = revise.preHis ? await this.ctx.helper.loadLedgerDataFromOss(revise.preHis.bills_file) : [];
  735. const posData = revise.preHis ? await this.ctx.helper.loadLedgerDataFromOss(revise.preHis.pos_file) : [];
  736. gclGatherModal.gatherLedgerData(billsData, posData, { prefix: 'org_' });
  737. revise.chapterList = gclGatherModal.chapterData();
  738. revise.new_tp = gclGatherModal.otherChapter.hj.new_total_price || 0;
  739. revise.org_tp = gclGatherModal.otherChapter.hj.org_total_price || 0;
  740. revise.tenderName = ctx.tender.data.name;
  741. ctx.body = { code: 0, msg: '', data: { revise } };
  742. } catch (error) {
  743. ctx.log(error);
  744. ctx.body = { code: -1, msg: error.toString(), data: null };
  745. }
  746. }
  747. async reviseCheck(ctx) {
  748. try {
  749. const reviseId = ctx.params.rid;
  750. const revise = await ctx.service.ledgerRevise.getDataById(reviseId);
  751. if (!revise) {
  752. ctx.body = { code: -1, msg: '修订不存在', data: null };
  753. return;
  754. }
  755. if (revise.status !== audit.revise.status.checking) {
  756. ctx.body = {
  757. code: -1,
  758. msg: `修订状态不正确,当前状态为"${audit.revise.statusString[revise.status] || '未知'}",无法审批`,
  759. data: { currentStatus: revise.status }
  760. };
  761. return;
  762. }
  763. revise.curHis = revise.his_id ? await ctx.service.ledgerHistory.getDataById(revise.his_id) : null;
  764. const curAudits = await ctx.service.reviseAudit.getCurAuditors(revise.id, revise.times);
  765. const curAuditorIds = curAudits.map(x => { return x.audit_id; });
  766. if (curAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0) {
  767. ctx.body = { code: -1, msg: '您不是当前审批人,无权进行该操作', data: null };
  768. return;
  769. }
  770. const data = ctx.request.body;
  771. const checkType = parseInt(data.checkType);
  772. if (!checkType || isNaN(checkType)) {
  773. ctx.body = { code: -1, msg: '提交数据错误:缺少审批类型参数', data: null };
  774. return;
  775. }
  776. const opinion = data.opinion || '';
  777. await ctx.service.reviseAudit.check(revise, checkType, opinion, revise.times);
  778. ctx.body = { code: 0, msg: '审批成功', data: null };
  779. } catch (err) {
  780. ctx.body = { code: -1, msg: err.toString(), data: null };
  781. }
  782. }
  783. }
  784. return WeappTenderController;
  785. };