settle.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const auditConst = require('../const/audit');
  10. const projectLogConst = require('../const/project_log');
  11. const shenpiConst = require('../const/shenpi');
  12. module.exports = app => {
  13. class Settle extends app.BaseService {
  14. /**
  15. * 构造函数
  16. *
  17. * @param {Object} ctx - egg全局变量
  18. * @return {void}
  19. */
  20. constructor(ctx) {
  21. super(ctx);
  22. this.tableName = 'settle';
  23. }
  24. /**
  25. * 获取标段下的全部结算期,按倒序
  26. * @param tenderId
  27. * @return {Promise<void>}
  28. */
  29. async getValidSettles(tenderId) {
  30. const settles = await this.db.select(this.tableName, {
  31. where: { tid: tenderId },
  32. orders: [['settle_order', 'desc']],
  33. });
  34. if (settles.length !== 0 && !this.ctx.session.sessionUser.is_admin) {
  35. const last = settles[0];
  36. if (last.audit_status === auditConst.settle.status.uncheck && !this.ctx.tender.isTourist) {
  37. if (last.user_id !== this.ctx.session.sessionUser.accountId) {
  38. settles.splice(0, 1);
  39. }
  40. }
  41. }
  42. return settles;
  43. }
  44. async getLatestCompleteSettle(tenderId) {
  45. const settles = await this.getAllDataByCondition({
  46. where: { tid: tenderId, status: auditConst.settle.status.checked },
  47. order: [['settle_order', 'desc']],
  48. limit: 1, offset: 0
  49. });
  50. return settles[0];
  51. }
  52. /**
  53. * 新增结算期
  54. * @param tenderId - 标段id
  55. * @param date - 结算年月
  56. * @param period - 结算周期
  57. * @return {Promise<void>}
  58. */
  59. async addSettle(tenderId, date, period) {
  60. const settles = await this.getAllDataByCondition({
  61. where: { tid: tenderId },
  62. order: ['order'],
  63. });
  64. const pre = settles[settles.length - 1];
  65. if (settles.length > 0 && pre.audit_status !== auditConst.settle.status.checked) {
  66. throw '上一期未审批通过,请等待上一期审批通过后,再新增数据';
  67. }
  68. const checkedStage = await this.ctx.service.stage.getLastestCompleteStage(tenderId);
  69. if (!checkedStage) throw '不存在审批通过的计量期,请先进行期计量,再结算';
  70. const newSettle = {
  71. tid: tenderId,
  72. add_sid: checkedStage.order, add_sorder: checkedStage.order,
  73. user_id: this.ctx.session.sessionUser.accountId,
  74. settle_order: settles.length + 1, settle_time: date, settle_period: period,
  75. audit_times: 1, audit_status: auditConst.settle.status.uncheck,
  76. };
  77. if (pre) {
  78. newSettle.pre_contract_tp = this.ctx.helper.add(pre.pre_contract_tp, pre.contract_tp);
  79. newSettle.pre_positive_qc_tp = this.ctx.helper.add(pre.pre_positive_qc_tp, pre.positive_qc_tp);
  80. newSettle.pre_negative_qc_tp = this.ctx.helper.add(pre.pre_negative_qc_tp, pre.negative_qc_tp);
  81. newSettle.pre_qc_tp = this.ctx.helper.add(pre.pre_qc_tp, pre.qc_tp);
  82. newSettle.pre_tp = this.ctx.helper.add(pre.tp, pre.pre_tp);
  83. }
  84. const transaction = await this.db.beginTransaction();
  85. try {
  86. // 新增期记录
  87. const result = await transaction.insert(this.tableName, newSettle);
  88. if (result.affectedRows === 1) {
  89. newSettle.id = result.insertId;
  90. } else {
  91. throw '新增期数据失败';
  92. }
  93. await this.ctx.service.settleAudit.copyPreAuditors(transaction, pre, newSettle);
  94. // 存在上一期时
  95. if (pre) {
  96. // todo 复制上一期其他数据
  97. }
  98. await transaction.commit();
  99. } catch (err) {
  100. await transaction.rollback();
  101. throw err;
  102. }
  103. return newSettle;
  104. }
  105. /**
  106. * 编辑结算期
  107. *
  108. * @param {Number} tenderId - 标段Id
  109. * @param {Number} order - 第N期
  110. * @param {String} date - 结算年月
  111. * @param {String} period - 结算周期
  112. * @return {Promise<void>}
  113. */
  114. async saveSettle(tenderId, order, date, period) {
  115. await this.db.update(this.tableName, {
  116. settle_time: date,
  117. settle_period: period,
  118. }, { where: { tid: tenderId, settle_order: order } });
  119. }
  120. /**
  121. * 删除结算期
  122. * @param id
  123. * @returns {Promise<boolean>}
  124. */
  125. async deleteSettle(id) {
  126. const transaction = await this.db.beginTransaction();
  127. try {
  128. const settleInfo = await this.getDataById(id);
  129. await transaction.delete(this.tableName, { id });
  130. await transaction.delete(this.ctx.service.settleAudit.tableName, { sid: id });
  131. // await transaction.delete(this.ctx.service.settleAuditAss.tableName, { sid: id });
  132. // 记录删除日志
  133. await this.ctx.service.projectLog.addProjectLog(transaction, projectLogConst.type.settle, projectLogConst.status.delete, `第${settleInfo.order}期`);
  134. await transaction.commit();
  135. return true;
  136. } catch (err) {
  137. await transaction.rollback();
  138. throw err;
  139. }
  140. }
  141. async loadRelaUser(settle) {
  142. const status = auditConst.settle.status;
  143. const accountId = this.ctx.session.sessionUser.accountId;
  144. settle.user = await this.ctx.service.projectAccount.getAccountInfoById(settle.user_id);
  145. settle.auditors = await this.ctx.service.settleAudit.getAuditors(settle.id, settle.audit_times); // 全部参与的审批人
  146. settle.auditorIds = this._.map(settle.auditors, 'audit_id');
  147. settle.curAuditors = settle.auditors.filter(x => { return x.audit_status === status.checking; }); // 当前流程中审批中的审批人
  148. settle.curAuditorIds = this._.map(settle.curAuditors, 'audit_id');
  149. settle.flowAuditors = settle.curAuditors.length > 0 ? settle.auditors.filter(x => { return x.active_order === settle.curAuditors[0].active_order; }) : []; // 当前流程中参与的审批人(包含会签时,审批通过的人)
  150. settle.flowAuditorIds = this._.map(settle.flowAuditors, 'audit_id');
  151. settle.auditorGroups = this.ctx.helper.groupAuditors(settle.auditors);
  152. settle.userGroups = this.ctx.helper.groupAuditorsUniq(settle.auditorGroups);
  153. settle.finalAuditorIds = settle.userGroups[settle.userGroups.length - 1].map(x => { return x.audit_id; });
  154. // 协作相关
  155. settle.assists = []; //await this.service.settleAuditAss.getData(settle); // 全部协同人
  156. settle.assists = settle.assists.filter(x => {
  157. return x.user_id === settle.user_id || settle.auditorIds.indexOf(x.user_id) >= 0;
  158. }); // 过滤无效协同人
  159. settle.userAssists = settle.assists.filter(x => { return x.user_id === settle.user_id; }); // 原报协同人
  160. settle.userAssistIds = this._.map(settle.userAssists, 'ass_user_id');
  161. settle.auditAssists = settle.assists.filter(x => { return x.user_id !== settle.user_id; }); // 审批协同人
  162. settle.auditAssistIds = this._.map(settle.auditAssists, 'ass_user_id');
  163. settle.curAssists = settle.assists.filter(x => { return settle.curAuditorIds.indexOf(x.user_id) >= 0; }); // 当前审批人的协同人
  164. settle.curAssistsIds = this._.map(settle.curAssists, 'ass_user_id');
  165. settle.relaAssists = settle.assists.filter(x => { return x.user_id === accountId }); // 登录人的协同人
  166. // 当前参与人Id
  167. settle.userIds = settle.audit_status === settle.uncheck // 当前流程下全部参与人id
  168. ? [settle.user_id, ...settle.userAssistIds]
  169. : [settle.user_id, ...settle.userAssistIds, ...settle.auditorIds, ...settle.auditAssistIds];
  170. }
  171. async loadAuditViewData(settle) {
  172. const auditTimes = settle.audit_status === auditConst.settle.status.checkNo ? settle.audit_times - 1 : settle.audit_times;
  173. if (!settle.user) settle.user = await this.ctx.service.projectAccount.getAccountInfoById(settle.user_id);
  174. settle.auditHistory = await this.ctx.service.settleAudit.getAuditorHistory(settle.id, auditTimes);
  175. // 获取审批流程中左边列表
  176. if (settle.audit_status === auditConst.settle.status.checkNo && settle.user_id !== this.ctx.session.sessionUser.accountId) {
  177. const auditors = await this.ctx.service.settleAudit.getAuditors(settle.id, settle.audit_times - 1); // 全部参与的审批人
  178. const auditorGroups = this.ctx.helper.groupAuditors(auditors);
  179. settle.hisUserGroup = this.ctx.helper.groupAuditorsUniq(auditorGroups);
  180. } else {
  181. settle.hisUserGroup = settle.userGroups;
  182. }
  183. }
  184. /**
  185. * cancancel = 0 不可撤回
  186. * cancancel = 1 原报撤回
  187. * cancancel = 2 审批人撤回 审批通过
  188. * cancancel = 3 审批人撤回 审批退回上一人
  189. * cancancel = 4 审批人撤回 退回原报
  190. * cancancel = 5 会签未全部审批通过时,审批人撤回 审批通过
  191. *
  192. * @param settle
  193. * @returns {Promise<void>}
  194. */
  195. async _doCheckSettleCanCancel(settle) {
  196. // 默认不可撤回
  197. settle.cancancel = 0;
  198. // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
  199. const status = auditConst.settle.status;
  200. if (settle.audit_status === status.checked || settle.audit_status === status.uncheck) return;
  201. const accountId = this.ctx.session.sessionUser.accountId;
  202. if (settle.audit_status !== status.checkNo) {
  203. // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
  204. if (settle.flowAuditors.find(x => { return x.audit_status !== status.checking}) && settle.flowAuditorIds.indexOf(accountId) < 0) return; // 当前流程存在审批人审批通过时,不可撤回
  205. const flowAssists = settle.auditAssists.filter(x => { return settle.flowAuditorIds.indexOf(x.user_id) >= 0; });
  206. if (flowAssists.find(x => { return x.confirm; })) return; //当前流程存在协审人确认时,不可撤回
  207. if (settle.curAuditorIds.indexOf(accountId) < 0 && settle.flowAuditorIds.indexOf(accountId) >= 0) {
  208. settle.cancancel = 5; // 会签未全部审批通过时,审批人撤回审批通过
  209. return;
  210. }
  211. const preAuditors = settle.curAuditors[0] && settle.curAuditors[0].order !== 1 ? settle.auditors.filter(x => { return x.order === settle.curAuditors[0].order - 1; }) : [];
  212. const preAuditorCheckAgain = preAuditors.find(pa => { return pa.audit_status === status.checkAgain; });
  213. const preAuditorCheckCancel = preAuditors.find(pa => { return pa.audit_status === status.checkCancel; });
  214. const preAuditorHasOld = preAuditors.find(pa => { return pa.is_old === 1; });
  215. const preAuditorIds = (preAuditorCheckAgain ? [] : preAuditors.map(x => { return x.audit_id })); // 重审不可撤回
  216. if ((this._.isEqual(settle.flowAuditorIds, preAuditorIds) && preAuditorCheckCancel) || preAuditorHasOld) {
  217. return; // 不可以多次撤回
  218. }
  219. const preAuditChecked = preAuditors.find(pa => { return pa.audit_status === status.checked && pa.audit_id === accountId; });
  220. const preAuditCheckNoPre = preAuditors.find(pa => { return pa.audit_status === status.checkNoPre && pa.audit_id === accountId; });
  221. if (preAuditorIds.indexOf(accountId) >= 0) {
  222. if (preAuditChecked) {
  223. settle.cancancel = 2;// 审批人撤回审批通过
  224. } else if (preAuditCheckNoPre) {
  225. settle.cancancel = 3;// 审批人撤回审批退回上一人
  226. }
  227. settle.preAuditors = preAuditors;
  228. } else if (preAuditors.length === 0 && accountId === settle.user_id) {
  229. settle.cancancel = 1;// 原报撤回
  230. }
  231. } else {
  232. const lastAuditors = await this.ctx.service.settleAudit.getAuditors(settle.id, settle.audit_times - 1);
  233. const onAuditor = this._.findLast(lastAuditors, { audit_status: status.checkNo });
  234. if (onAuditor.audit_id === accountId) {
  235. settle.cancancel = 4;// 审批人撤回退回原报
  236. settle.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order });
  237. }
  238. }
  239. }
  240. async _doCheckSettleReadOnly(settle) {
  241. const status = auditConst.settle.status;
  242. // 校验权限(参与人、分享、游客)
  243. const accountId = this.ctx.session.sessionUser.accountId;
  244. const shareIds = [];
  245. // 是否只读
  246. if (settle.audit_status === status.uncheck || settle.audit_status === status.checkNo) {
  247. settle.readOnly = accountId !== settle.user_id && settle.userAssistIds.indexOf(accountId) < 0;
  248. } else {
  249. settle.readOnly = true;
  250. }
  251. // 读取数据相关
  252. settle.curTimes = settle.audit_status === status.checkNo && settle.readOnly ? settle.audit_times - 1 : settle.audit_times;
  253. // 协作人相关
  254. if (settle.audit_status === status.uncheck) {
  255. if (!settle.readOnly) {
  256. settle.assist = settle.userAssists.find(x => { return x.ass_user_id === accountId; });
  257. }
  258. settle.curTimes = settle.audit_times;
  259. } else if (settle.audit_status === status.checkNo) {
  260. if (!settle.readOnly) {
  261. settle.assist = settle.userAssists.find(x => { return x.ass_user_id === accountId; });
  262. settle.curTimes = settle.audit_times;
  263. }
  264. } else {
  265. const ass = settle.auditAssists.find(x => { return settle.flowAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
  266. if (!settle.readOnly) {
  267. settle.assist = ass;
  268. }
  269. if (!settle.readOnly) {
  270. settle.readOnly = !this._.isEqual(settle.flowAuditorIds, settle.curAuditorIds);
  271. settle.canCheck = true;
  272. }
  273. }
  274. if (settle.readOnly) {
  275. settle.assist = accountId === settle.user_id || settle.auditorIds.indexOf(accountId) >= 0 ? null : settle.auditAssists.find(x => { return x.ass_user_id === accountId});
  276. }
  277. // 上传文件权限
  278. const permission = this.ctx.session.sessionUser.permission;
  279. if (settle.userIds.indexOf(accountId) >= 0 || this.ctx.session.sessionUser.is_admin) {
  280. settle.filePermission = true;
  281. } else {
  282. if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) {// 分享人
  283. if (settle.audit_status === status.uncheck) throw '您无权查看该数据';
  284. settle.filePermission = false;
  285. } else if (this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) {
  286. settle.filePermission = this.ctx.tender.touristPermission.file || settle.auditorIds.indexOf(accountId) !== -1;
  287. } else {
  288. throw '您无权查看该数据';
  289. }
  290. }
  291. }
  292. async doCheckSettle(settle) {
  293. // 读取原报、审核人等参与人数据
  294. await this.loadRelaUser(settle);
  295. // 是否只读等权限
  296. await this._doCheckSettleReadOnly(settle);
  297. // 可否撤回,是哪一种撤回
  298. await this._doCheckSettleCanCancel(settle);
  299. }
  300. async checkSettleShenpi(settle) {
  301. const status = auditConst.settle.status;
  302. const info = this.ctx.tender.info;
  303. const shenpi_status = info.shenpi.settle;
  304. if ((settle.audit_status === status.uncheck || settle.audit_status === status.checkNo) && shenpi_status !== shenpiConst.sp_status.sqspr) {
  305. // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
  306. const auditList = await this.ctx.service.settleAudit.getAllDataByCondition({ where: { settle_id: settle.id, audit_times: settle.audit_times }, orders: [['audit_order', 'asc']] });
  307. auditList.shift();
  308. if (shenpi_status === shenpiConst.sp_status.gdspl) {
  309. const shenpiList = await this.ctx.service.shenpiAudit.getAllDataByCondition({ where: { tid: settle.tid, sp_type: shenpiConst.sp_type.settle, sp_status: shenpi_status } });
  310. // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
  311. let sameAudit = auditList.length === shenpiList.length;
  312. if (sameAudit) {
  313. for (const audit of auditList) {
  314. const shenpi = shenpiList.find(x => { return x.audit_id === audit.audit_id; });
  315. if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type) {
  316. sameAudit = false;
  317. break;
  318. }
  319. }
  320. }
  321. if (!sameAudit) {
  322. await this.ctx.service.settleAudit.updateNewAuditList(settle, shenpiList);
  323. await this.loadRelaUser(settle);
  324. }
  325. } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
  326. const shenpiInfo = await this.ctx.service.shenpiAudit.getDataByCondition({ tid: settle.tid, sp_type: shenpiConst.sp_type.settle, sp_status: shenpi_status });
  327. // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
  328. const lastAuditors = auditList.filter(x => { x.order === auditList.order; });
  329. if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].audit_id))) {
  330. await this.ctx.service.settleAudit.updateLastAudit(settle, auditList, shenpiInfo.audit_id);
  331. await this.loadRelaUser(settle);
  332. } else if (!shenpiInfo) {
  333. // 不存在终审人的状态下这里恢复为授权审批人
  334. this.tender.info.shenpi.settle = shenpiConst.sp_status.sqspr;
  335. }
  336. }
  337. }
  338. }
  339. async doSettle(transaction, settle) {
  340. const SettleObj = require('../lib/settle');
  341. const settleObj = new SettleObj(this.ctx);
  342. const [settleBills, settlePos, settleSum] = await settleObj.doSettle(settle);
  343. await transaction.delete(this.ctx.service.settleBills.tableName, { settle_id: settle.id });
  344. await transaction.insert(this.ctx.service.settleBills.tableName, settleBills);
  345. await transaction.delete(this.ctx.service.settlePos.tableName, { settle_id: settle.id });
  346. await transaction.insert(this.ctx.service.settlePos.tableName, settlePos);
  347. return settleSum;
  348. }
  349. }
  350. return Settle;
  351. };