payment_detail.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. 'use strict';
  2. const auditConst = require('../const/audit').stage;
  3. const paymentConst = require('../const/payment');
  4. module.exports = app => {
  5. class PaymentDetail extends app.BaseService {
  6. constructor(ctx) {
  7. super(ctx);
  8. this.tableName = 'payment_detail';
  9. }
  10. async getValidDetails(tr_id) {
  11. const details = await this.db.select(this.tableName, {
  12. column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time'],
  13. where: { tr_id },
  14. orders: [['order', 'desc']],
  15. });
  16. if (details.length !== 0) {
  17. const lastDetail = details[details.length - 1];
  18. if (lastDetail.status === auditConst.status.uncheck && lastDetail.uid !== this.ctx.session.sessionUser.accountId) {
  19. details.splice(details.length - 1, 1);
  20. }
  21. }
  22. // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
  23. if (details.length > 0 && details[0].status !== auditConst.status.checked) {
  24. const detail = details[0];
  25. const curAuditor = await this.ctx.service.paymentDetailAudit.getCurAuditor(detail.id, detail.times);
  26. const isActive = curAuditor ? curAuditor.id === this.ctx.session.sessionUser.accountId : detail.uid === this.ctx.session.sessionUser.accountId;
  27. if (isActive) {
  28. detail.curTimes = detail.times;
  29. detail.curOrder = curAuditor ? curAuditor.order : 0;
  30. }
  31. }
  32. return details;
  33. }
  34. async getDetail(id) {
  35. const details = await this.db.select(this.tableName, {
  36. column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time'],
  37. where: { id },
  38. orders: [['order', 'desc']],
  39. });
  40. return details;
  41. }
  42. async hadDetail(trId) {
  43. const result = await this.count({ tr_id: trId });
  44. return result !== 0;
  45. }
  46. async addCommomCheck(trInfo, code) {
  47. const details = await this.getAllDataByCondition({
  48. where: { tr_id: trInfo.id },
  49. order: ['order'],
  50. });
  51. const preDetail = details[details.length - 1];
  52. if (details.length > 0 && details[details.length - 1].status !== auditConst.status.checked) {
  53. throw '上一期未审批通过,请等待上一期审批通过后,再新增';
  54. }
  55. if (this._.findIndex(details, { code }) !== -1) {
  56. throw '编号不能重复';
  57. }
  58. trInfo.rpt_audit = JSON.parse(trInfo.rpt_audit);
  59. if (this._.findIndex(trInfo.rpt_audit, { uid: null }) !== -1) {
  60. throw '未配置好表单角色,无法新建';
  61. }
  62. return preDetail;
  63. }
  64. // async addFormDetail(trInfo, code, s_time) {
  65. async addFormDetail(trInfo, code, s_time) {
  66. const transaction = await this.db.beginTransaction();
  67. try {
  68. if (!(trInfo.is_del === 0 && trInfo.rpt_audit)) {
  69. throw '报表已删除或表单人员数据有误,无法新建';
  70. }
  71. const preDetail = await this.addCommomCheck(trInfo, code);
  72. const rptTpl = await this.ctx.service.rptTpl.getDataById(trInfo.rpt_id);
  73. const pageRst = await this.ctx.service.jpcReport.getAllPreviewPagesCommon(rptTpl, 'A4');
  74. const newDetail = {
  75. tender_id: this.ctx.paymentTender.id,
  76. tr_id: trInfo.id,
  77. order: preDetail && preDetail.order ? preDetail.order + 1 : 1,
  78. times: 1,
  79. status: auditConst.status.uncheck,
  80. uid: this.ctx.session.sessionUser.accountId,
  81. s_time,
  82. code,
  83. report_json: JSON.stringify(pageRst),
  84. in_time: new Date(),
  85. };
  86. const result = await transaction.insert(this.tableName, newDetail);
  87. if (result.affectedRows === 1) {
  88. newDetail.id = result.insertId;
  89. } else {
  90. throw '新增支付审批数据失败';
  91. }
  92. // 报表角色创建
  93. const insertRptAudit = [];
  94. for (const [i, r] of trInfo.rpt_audit.entries()) {
  95. insertRptAudit.push({
  96. tender_id: this.ctx.paymentTender.id,
  97. tr_id: trInfo.id,
  98. td_id: newDetail.id,
  99. uid: r.uid,
  100. signature_index: i,
  101. signature_name: r.rpt_name,
  102. in_time: new Date(),
  103. });
  104. }
  105. if (insertRptAudit.length > 0) await transaction.insert(this.ctx.service.paymentRptAudit.tableName, insertRptAudit);
  106. // 存在上一期时,复制上一期审批流程
  107. if (preDetail) {
  108. const auditResult = await this.ctx.service.paymentDetailAudit.copyPreDetailAuditors(transaction, preDetail, newDetail);
  109. if (!auditResult) {
  110. throw '复制上一期审批流程失败';
  111. }
  112. }
  113. // 更新is_change值
  114. await transaction.update(this.ctx.service.paymentTenderRpt.tableName, { id: trInfo.id, is_change: 0 });
  115. await transaction.commit();
  116. return newDetail;
  117. } catch (err) {
  118. await transaction.rollback();
  119. throw err;
  120. }
  121. }
  122. async addSafeDetail(trInfo, code, s_time) {
  123. const transaction = await this.db.beginTransaction();
  124. try {
  125. const preDetail = await this.addCommomCheck(trInfo, code);
  126. const newDetail = {
  127. tender_id: this.ctx.paymentTender.id,
  128. tr_id: trInfo.id,
  129. order: preDetail && preDetail.order ? preDetail.order + 1 : 1,
  130. times: 1,
  131. status: auditConst.status.uncheck,
  132. uid: this.ctx.session.sessionUser.accountId,
  133. s_time,
  134. code,
  135. in_time: new Date(),
  136. type: paymentConst.modes_value_object.safe,
  137. };
  138. const result = await transaction.insert(this.tableName, newDetail);
  139. if (result.affectedRows === 1) {
  140. newDetail.id = result.insertId;
  141. } else {
  142. throw '新增支付审批数据失败';
  143. }
  144. // 初始化安全生产费
  145. if (preDetail) {
  146. await this.ctx.service.paymentSafeBills.initByPre(newDetail, preDetail, transaction);
  147. } else {
  148. await this.ctx.service.paymentSafeBills.init(newDetail, transaction);
  149. }
  150. // 存在上一期时,复制上一期审批流程
  151. if (preDetail) {
  152. const auditResult = await this.ctx.service.paymentDetailAudit.copyPreDetailAuditors(transaction, preDetail, newDetail);
  153. if (!auditResult) {
  154. throw '复制上一期审批流程失败';
  155. }
  156. }
  157. // 更新is_change值
  158. await transaction.update(this.ctx.service.paymentTenderRpt.tableName, { id: trInfo.id, is_change: 0 });
  159. await transaction.commit();
  160. return newDetail;
  161. } catch (err) {
  162. await transaction.rollback();
  163. throw err;
  164. }
  165. }
  166. async addDetail(trInfo, code, s_time) {
  167. switch (trInfo.type) {
  168. case paymentConst.modes_value_object.form:
  169. return this.addFormDetail(trInfo, code, s_time);
  170. case paymentConst.modes_value_object.safe:
  171. return this.addSafeDetail(trInfo, code, s_time);
  172. default:
  173. throw '未知类型,新建失败';
  174. }
  175. }
  176. async updateReportJson(id, report_json) {
  177. return await this.db.update(this.tableName, { id, report_json: JSON.stringify(report_json) });
  178. }
  179. async signOneSignatureData(report_json, signature_name, sign_msg) {
  180. // 签名签章
  181. const signCells = this._.find(report_json.items[0].signature_cells, { signature_name });
  182. if (signCells && (sign_msg.sign_path || sign_msg.company_stamp || sign_msg.stamp_path)) {
  183. const signArray = [];
  184. sign_msg.sign_path ? signArray.push('/public/upload/sign/' + sign_msg.sign_path) : signArray.push('');
  185. sign_msg.company_stamp ? signArray.push(sign_msg.company_stamp) : signArray.push('');
  186. sign_msg.stamp_path ? signArray.push(sign_msg.stamp_path) : signArray.push('');
  187. signCells.path = signArray.join('!;!');
  188. }
  189. // 日期
  190. const dateCells = this._.find(report_json.items[0].signature_date_cells, { signature_name: signature_name + '_签字日期' });
  191. if (dateCells && sign_msg.date) {
  192. dateCells.Value = this.ctx.helper.dateTranChinese(sign_msg.date);
  193. }
  194. // 意见
  195. const contentCells = this._.find(report_json.items[0].signature_audit_cells, { signature_name: signature_name + '_审核意见' });
  196. if (contentCells && sign_msg.content) {
  197. contentCells.Value = sign_msg.content;
  198. }
  199. return report_json;
  200. }
  201. async clearOneSignatureData(report_json, rptAudit) {
  202. // 签名签章
  203. const signCells = this._.find(report_json.items[0].signature_cells, { signature_name: rptAudit.signature_name });
  204. if (signCells) {
  205. signCells.path = null;
  206. }
  207. // 日期
  208. const dateCells = this._.find(report_json.items[0].signature_date_cells, { signature_name: rptAudit.signature_name + '_签字日期' });
  209. if (dateCells) {
  210. dateCells.Value = '';
  211. }
  212. // 意见
  213. const contentCells = this._.find(report_json.items[0].signature_audit_cells, { signature_name: rptAudit.signature_name + '_审核意见' });
  214. if (contentCells) {
  215. contentCells.Value = '';
  216. }
  217. return report_json;
  218. }
  219. async clearAllSignatureData(report_json) {
  220. if (report_json.items[0].signature_cells.length > 0) {
  221. for (const cell of report_json.items[0].signature_cells) {
  222. cell.path = null;
  223. }
  224. }
  225. if (report_json.items[0].signature_audit_cells.length > 0) {
  226. for (const cell of report_json.items[0].signature_audit_cells) {
  227. cell.Value = '';
  228. }
  229. }
  230. if (report_json.items[0].signature_date_cells.length > 0) {
  231. for (const cell of report_json.items[0].signature_date_cells) {
  232. cell.Value = '';
  233. }
  234. }
  235. return report_json;
  236. }
  237. /**
  238. * 删除报表表单详情
  239. *
  240. * @param {Number} id - 期Id
  241. * @return {Promise<void>}
  242. */
  243. async deleteDetail(id) {
  244. const transaction = await this.db.beginTransaction();
  245. try {
  246. await transaction.delete(this.ctx.service.paymentDetailAudit.tableName, { td_id: id });
  247. await transaction.delete(this.ctx.service.paymentRptAudit.tableName, { td_id: id });
  248. await transaction.delete(this.ctx.service.paymentSafeBills.tableName, { detail_id: id });
  249. await transaction.delete(this.tableName, { id });
  250. await transaction.commit();
  251. return true;
  252. } catch (err) {
  253. await transaction.rollback();
  254. throw err;
  255. }
  256. }
  257. async haveNotice2Tender(tid, uid) {
  258. const sql = 'SELECT pd.`id`, pd.`tr_id`, pd.`order` FROM ?? as pd LEFT JOIN ?? as pda' +
  259. ' ON pd.`id` = pda.`td_id` LEFT JOIN ?? as pra ON pd.`id` = pra.`td_id` WHERE pd.`tender_id` = ? AND' +
  260. ' ((pd.`uid` = ? AND (pd.`status` = ? OR pd.`status` = ?))' +
  261. ' OR ((pd.`status` = ? OR pd.`status` = ?) AND pda.aid = ? AND pda.`status` = ?)' +
  262. ' OR (pra.`uid` = ? AND pra.`signature_msg` is null AND pd.`status` != ? AND pd.`status` != ?))';
  263. const params = [this.tableName, this.ctx.service.paymentDetailAudit.tableName, this.ctx.service.paymentRptAudit.tableName, tid,
  264. uid, auditConst.status.uncheck, auditConst.status.checkNo,
  265. auditConst.status.checking, auditConst.status.checkNoPre, uid, auditConst.status.checking,
  266. uid, auditConst.status.uncheck, auditConst.status.checkNo];
  267. const result = await this.db.query(sql, params);
  268. if (result && result.length > 0) {
  269. for (const one of this._.uniqBy(result, 'id')) {
  270. const maxOrder = await this.count({
  271. tr_id: one.tr_id,
  272. });
  273. if (one.order === maxOrder) {
  274. return 1;
  275. }
  276. }
  277. }
  278. return 0;
  279. }
  280. async haveNotice2TenderRpt(tr_id, uid) {
  281. const sql = 'SELECT pd.`id`, pd.`tr_id`, pd.`order` FROM ?? as pd LEFT JOIN ?? as pda' +
  282. ' ON pd.`id` = pda.`td_id` LEFT JOIN ?? as pra ON pd.`id` = pra.`td_id` WHERE pd.`tr_id` = ? AND ((pd.`uid` = ? AND (pd.`status` = ? OR pd.`status` = ?))' +
  283. ' OR ((pd.`status` = ? OR pd.`status` = ?) AND pda.aid = ? AND pda.`status` = ?)' +
  284. ' OR (pra.`uid` = ? AND pra.`signature_msg` is null AND pd.`status` != ? AND pd.`status` != ?))';
  285. const params = [this.tableName, this.ctx.service.paymentDetailAudit.tableName, this.ctx.service.paymentRptAudit.tableName, tr_id,
  286. uid, auditConst.status.uncheck, auditConst.status.checkNo,
  287. auditConst.status.checking, auditConst.status.checkNoPre, uid, auditConst.status.checking,
  288. uid, auditConst.status.uncheck, auditConst.status.checkNo];
  289. const result = await this.db.query(sql, params);
  290. if (result && result.length > 0) {
  291. for (const one of this._.uniqBy(result, 'id')) {
  292. const maxOrder = await this.count({
  293. tr_id: one.tr_id,
  294. });
  295. if (one.order === maxOrder) {
  296. return 1;
  297. }
  298. }
  299. }
  300. return 0;
  301. }
  302. async haveDetail2Tender(tid) {
  303. return this.count({ tender_id: tid });
  304. }
  305. async getCountByPidType(pid, type = 1) {
  306. const sql = 'SELECT count(pd.`id`) as count FROM ?? as pd LEFT JOIN ?? as pt ON pd.`tender_id` = pt.`id` WHERE pid = ? AND type = ?';
  307. const params = [this.tableName, this.ctx.service.paymentTender.tableName, pid, type];
  308. const result = await this.db.queryOne(sql, params);
  309. return result ? result.count : 0;
  310. }
  311. async updateReport(transaction, tr_id, uid) {
  312. const details = await this.db.select(this.tableName, {
  313. column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time'],
  314. where: { tr_id },
  315. orders: [['order', 'desc']],
  316. });
  317. if (details && details.length > 0) {
  318. const detailInfo = details[0];
  319. if (detailInfo.status === auditConst.status.uncheck || detailInfo.status === auditConst.status.checkNo) {
  320. // 判断该人是否在审批流程里,是则移除并调整order
  321. const result2 = await this.ctx.service.paymentDetailAudit.updateAuditByReport(transaction, detailInfo.id, detailInfo.times, uid);
  322. // 更新为上报人
  323. return await transaction.update(this.tableName, { id: detailInfo.id, uid });
  324. }
  325. }
  326. return true;
  327. }
  328. async doCheckDetail(id) {
  329. const status = auditConst.status;
  330. const accountId = this.ctx.session.sessionUser.accountId;
  331. const auditPermission = await this.service.paymentPermissionAudit.getOnePermission(this.ctx.session.sessionUser.is_admin, accountId);
  332. if (!auditPermission) throw '权限不足';
  333. const detail = await this.getDataById(id);
  334. if (!detail) throw '支付审批表单不存在';
  335. const trInfo = await this.ctx.service.paymentTenderRpt.getDataById(detail.tr_id);
  336. if (!trInfo) throw '支付审批报表不存在';
  337. // 读取原报、审核人数据
  338. detail.auditors = await this.ctx.service.paymentDetailAudit.getAuditors(detail.id, detail.times);
  339. detail.curAuditor = await this.ctx.service.paymentDetailAudit.getCurAuditor(detail.id, detail.times);
  340. detail.rptAudits = await this.ctx.service.paymentRptAudit.getListByDetail(detail.id);
  341. const auditorIds = this._.map(detail.auditors, 'aid'),
  342. rptAuditIds = this._.map(detail.rptAudits, 'uid');
  343. if (accountId === detail.uid) { // 原报
  344. detail.curTimes = detail.times;
  345. if (detail.status === status.uncheck || detail.status === status.checkNo) {
  346. detail.curOrder = 0;
  347. } else if (detail.status === status.checked) {
  348. detail.curOrder = this._.max(this._.map(detail.auditors, 'order'));
  349. } else {
  350. detail.curOrder = detail.curAuditor.aid === accountId ? detail.curAuditor.order : detail.curAuditor.order - 1;
  351. }
  352. detail.filePermission = true;
  353. } else if (auditorIds.indexOf(accountId) !== -1 || rptAuditIds.indexOf(accountId) !== -1 || auditPermission.view_all) { // 审批人及签署人及查看所有权人
  354. if (detail.status === status.uncheck) {
  355. throw '您无权查看该数据';
  356. }
  357. detail.curTimes = detail.status === status.checkNo ? detail.times - 1 : detail.times;
  358. if (detail.status === status.checked) {
  359. detail.curOrder = this._.max(this._.map(detail.auditors, 'order'));
  360. } else if (detail.status === status.checkNo) {
  361. const audit = await this.ctx.service.paymentDetailAudit.getDataByCondition({
  362. td_id: detail.id, times: detail.times, status: status.checkNo,
  363. });
  364. detail.curOrder = audit.order;
  365. } else {
  366. detail.curOrder = accountId === detail.curAuditor.aid ? detail.curAuditor.order : detail.curAuditor.order - 1;
  367. }
  368. detail.filePermission = auditorIds.indexOf(accountId) !== -1 || rptAuditIds.indexOf(accountId) !== -1;
  369. } else { // 其他不可见
  370. throw '您无权查看该数据';
  371. }
  372. // 获取最新的期
  373. detail.highOrder = await this.service.paymentDetail.count({ tr_id: detail.tr_id });
  374. detail.readOnly = !((detail.status === status.uncheck || detail.status === status.checkNo) && accountId === detail.uid);
  375. if (detail.readOnly && detail.type === paymentConst.modes_value_object.safe) {
  376. if ((detail.status === status.checking || detail.status === status.checkNoPre) && detail.curAuditor && detail.curAuditor.aid === accountId) {
  377. detail.readOnly = false;
  378. }
  379. }
  380. return detail;
  381. }
  382. }
  383. return PaymentDetail;
  384. };