stage_pos.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const measureType = require('../const/tender').measureType;
  10. module.exports = app => {
  11. class StagePos extends app.BaseService {
  12. /**
  13. * 构造函数
  14. *
  15. * @param {Object} ctx - egg全局变量
  16. * @return {void}
  17. */
  18. constructor(ctx) {
  19. super(ctx);
  20. this.tableName = 'stage_pos';
  21. this.qtyFields = ['contract_qty', 'qc_qty']
  22. }
  23. /**
  24. * 查询期计量最后审核人数据
  25. * @param {Number} tid - 标段id
  26. * @param {Number} sid - 期id
  27. * @param {Number|Array} pid - 部位明细id(可以为空)
  28. * @returns {Promise<*>}
  29. */
  30. async getLastestStageData(tid, sid, pid) {
  31. let pidSql = '';
  32. if (pid) {
  33. if (pid instanceof Array) {
  34. pidSql = pid.length > 0 ? (' And pid in (' + pid.join(', ') + ')') : '';
  35. } else {
  36. pidSql = (pid instanceof String || pid instanceof Number) ? ' And pid = ' + pid : '';
  37. }
  38. }
  39. const sql = 'SELECT * FROM ' + this.tableName + ' As Pos ' +
  40. ' INNER JOIN ( ' +
  41. ' SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `tid`, `sid`, `pid` From ' + this.tableName +
  42. ' WHERE `tid` = ? And sid = ?' + pidSql +
  43. ' GROUP BY `pid`' +
  44. ' ) As MaxFilter ' +
  45. ' ON Pos.times = MaxFilter.times And Pos.order = MaxFilter.order And Pos.pid = MaxFilter.pid' +
  46. ' And Pos.`tid` = MaxFilter.`tid` And Pos.`sid` = MaxFilter.`sid`';
  47. const sqlParam = [tid, sid];
  48. if (!pid) {
  49. return await this.db.query(sql, sqlParam);
  50. } else if (pid instanceof Array) {
  51. return await this.db.query(sql, sqlParam);
  52. } else {
  53. return await this.db.queryOne(sql, sqlParam);
  54. }
  55. }
  56. /**
  57. * 查询 某期 某轮审批 某审核人数据
  58. * @param {Number} tid - 标段id
  59. * @param {Number} sid - 期id
  60. * @param {Number} times - 期第几轮审批
  61. * @param {Number} order - 审核人顺序
  62. * @param {Number|Array|Null} pid - 部位明细id - 为空则查询全部
  63. * @returns {Promise<*>}
  64. */
  65. async getAuditorStageData(tid, sid, times, order, pid) {
  66. let pidSql;
  67. if (pid instanceof Array) {
  68. pidSql = pid.length > 0 ? ' And Pos.pid in (' + pid.join(', ') + ')' : '';
  69. } else {
  70. pidSql = pid ? 'And Pos.pid = ' + pid.toString() : '';
  71. }
  72. const sql = 'SELECT * FROM ' + this.tableName + ' As Pos ' +
  73. ' INNER JOIN ( ' +
  74. ' SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `pid` From ' + this.tableName +
  75. ' WHERE `times` < ? OR (`times` = ? AND `order` <= ?)' +
  76. ' GROUP BY `pid`' +
  77. ' ) As MaxFilter ' +
  78. ' ON Pos.times = MaxFilter.times And Pos.order = MaxFilter.order And Pos.pid = MaxFilter.pid' +
  79. ' WHERE Pos.tid = ? And Pos.sid = ?' + pidSql;
  80. const sqlParam = [times, times, order, tid, sid];
  81. if (!pid) {
  82. return await this.db.query(sql, sqlParam);
  83. } else if (pid instanceof Array) {
  84. return await this.db.query(sql, sqlParam);
  85. } else {
  86. return await this.db.queryOne(sql, sqlParam);
  87. }
  88. }
  89. /**
  90. * 新增部位明细数据(仅供updateStageData调用)
  91. *
  92. * @param transaction - 事务
  93. * @param data - 新增数据
  94. * @returns {Promise<{}>}
  95. * @private
  96. */
  97. async _addStagePosData(transaction, data) {
  98. const bills = await this.ctx.service.ledger.getDataById(data.lid);
  99. const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
  100. const result = {};
  101. // 在主表pos中新增数据
  102. const p = JSON.parse(JSON.stringify(data.updateData));
  103. p.tid = this.ctx.tender.id;
  104. p.add_stage = this.ctx.stage.id;
  105. p.add_times = this.ctx.stage.curTimes;
  106. p.add_user = this.ctx.session.sessionUser.accountId;
  107. if (p.contract_qty) { delete p.contract_qty; }
  108. if (p.qc_qty) { delete p.qc_qty; }
  109. if (p.postil) { delete p.postil; }
  110. if (p.quantity) {
  111. p.quantity = this.round(p.quantity, precision.value);
  112. }
  113. const addRst = await transaction.insert(this.ctx.service.pos.tableName, data.updateData);
  114. p.id = addRst.insertId;
  115. result.pos = p.id;
  116. // 如果存在复核数据,更新计算主表清单
  117. if (p.quantity) {
  118. await this.ctx.service.ledger.calc(this.ctx.tender.id, p.lid, transaction);
  119. result.ledger = p.lid;
  120. }
  121. // 如果存在本期计算数据,更新计算清单本期计量数据
  122. if (data.contract_qty || data.qc_qty || data.postil) {
  123. const ps = {
  124. pid: p.id,
  125. lid: p.lid,
  126. tid: this.ctx.tender.id,
  127. sid: this.ctx.stage.id,
  128. said: this.ctx.session.sessionUser.accountId,
  129. times: this.ctx.stage.curTimes,
  130. order: this.ctx.stage.curOrder,
  131. };
  132. if (data.contract_qty) { ps.contract_qty = this.round(data.contract_qty, precision.value); }
  133. if (data.qc_qty) { ps.qc_qty = this.round(data.qc_qty, precision.value); }
  134. if (data.postil) { ps.postil = data.postil; }
  135. await transaction.insert(ps);
  136. await this.ctx.service.stageBills.calc(ctx.tender.id, ctx.stage.id, ps.lid, transaction);
  137. result.stageUpdate = true;
  138. }
  139. return result;
  140. }
  141. /**
  142. * 更新部位明细数据(仅供updateStageData调用)
  143. *
  144. * @param transaction - 事务
  145. * @param data - 更新数据(允许一次性提交多条)
  146. * @returns {Promise<{ledger: Array, pos: Array}>}
  147. * @private
  148. */
  149. async _updateStagePosData(transaction, data) {
  150. let bills, precision;
  151. const result = {ledger: [], pos: [], stageUpdate: true};
  152. const datas = data instanceof Array ? data : [data];
  153. const orgStagePos = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, this._.map(datas, 'pid'));
  154. for (const d of datas) {
  155. const osp = this._.find(orgStagePos, function (p) { return p.pid === d.pid; });
  156. if (d.contract_qty || d.qc_qty) {
  157. if (!bills || bills.id !== data.lid) {
  158. bills = await this.ctx.service.ledger.getDataById(d.lid);
  159. precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
  160. }
  161. this.ctx.helper.checkFieldPrecision(d, this.qtyFields, precision.value);
  162. }
  163. if (osp && osp.times === this.ctx.stage.curTimes && osp.order === this.ctx.stage.curOrder) {
  164. await transaction.update(this.tableName, d, {where: {id: osp.id}});
  165. } else {
  166. d.tid = this.ctx.tender.id;
  167. d.sid = this.ctx.stage.id;
  168. d.said = this.ctx.session.sessionUser.accountId;
  169. d.times = this.ctx.stage.curTimes;
  170. d.order = this.ctx.stage.curOrder;
  171. await transaction.insert(this.tableName, d);
  172. }
  173. result.pos.push(d.pid);
  174. if ((d.contract_qty === undefined || d.qc_qty === undefined) && (result.ledger.indexOf(d.lid) === -1)) {
  175. result.ledger.push(d.lid);
  176. }
  177. }
  178. for (const lid of result.ledger) {
  179. await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, lid, transaction);
  180. }
  181. return result;
  182. }
  183. /**
  184. * 删除部位明细数据(仅供updateStageData调用)
  185. *
  186. * @param transaction - 事务
  187. * @param data - 删除的部位明细(允许一次提醒多条,也允许跨清单(但前端操作不允许))
  188. * @returns {Promise<{}>}
  189. * @private
  190. */
  191. async _deleteStagePosData(transaction, data) {
  192. const result = {};
  193. const pos = await this.ctx.service.pos.getPosData({tid: this.ctx.tender.id, id: data});
  194. if (pos instanceof Array) {
  195. for (const p of pos) {
  196. if (p.add_stage !== this.ctx.stage.id || p.add_times !== this.ctx.stage.curTimes || p.add_user !== this.ctx.session.sessionUser.accountId) {
  197. throw '您无权删除该数据';
  198. }
  199. }
  200. } else if (pos.add_stage !== this.ctx.stage.id || pos.add_times !== this.ctx.stage.curTimes || pos.add_user !== this.ctx.session.sessionUser.accountId) {
  201. throw '您无权删除该数据';
  202. }
  203. const ledgerIds = this._.map(pos, 'lid');
  204. // 删除部位明细
  205. await transaction.delete(this.ctx.service.pos.tableName, {tid: this.ctx.tender.id, id: data});
  206. for (const lid of ledgerIds) {
  207. await this.ctx.service.ledger.calc(tid, lid, transaction);
  208. }
  209. // 删除部位明细计量数据
  210. await transaction.delete(this.tableName, {tid: this.ctx.tender.id, lid: data});
  211. for (const lid of ledgerIds) {
  212. await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, lid, transaction);
  213. }
  214. // 获取需要更新的数据
  215. result.ledger = ledgerIds;
  216. result.stageUpdate = true;
  217. return result;
  218. }
  219. /**
  220. * 根据前端提交数据,更新并计算
  221. *
  222. * @param data
  223. * @returns {Promise<{ledger: {}, pos: {}}>}
  224. */
  225. async updateStageData(data) {
  226. let refreshData;
  227. const transaction = await this.db.beginTransaction();
  228. try {
  229. if ((data.updateType === 'add' || data.upateType === 'delete') && this.ctx.tender.measure_type === measureType.tz) {
  230. throw '台账模式下,不可在计量中新增或删除部位明细,如需操作,请进行台账修订';
  231. }
  232. if (data.updateType === 'add') {
  233. refreshData = await this._addStagePosData(transaction, data.updateData);
  234. } else if (data.updateType === 'update') {
  235. refreshData = await this._updateStagePosData(transaction, data.updateData);
  236. } else if (data.updateType === 'delete') {
  237. if (!data.updateData || data.updateData.length === 0) {
  238. throw '提交数据错误';
  239. }
  240. refreshData = await this._deleteStagePosData(transaction, data.updateData);
  241. } else {
  242. throw '提交数据错误';
  243. }
  244. await transaction.commit();
  245. } catch (err) {
  246. await transaction.rollback();
  247. throw err;
  248. }
  249. try {
  250. const result = {ledger: {}, pos: {}};
  251. if (refreshData.ledger && refreshData.ledger.length > 0) {
  252. result.ledger.bills = await this.ctx.service.ledger.getDataByIds(refreshData.ledger);
  253. if (refreshData.stageUpdate) {
  254. result.ledger.curStageData = await await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, refreshData.ledger);
  255. }
  256. }
  257. if (refreshData.pos && refreshData.pos.length > 0) {
  258. result.pos.pos = await this.ctx.service.pos.getPosData({id: refreshData.pos});
  259. if (refreshData.stageUpdate) {
  260. result.pos.curStageData = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, refreshData.pos);
  261. }
  262. }
  263. return result;
  264. } catch(err) {
  265. throw '获取数据异常,请刷新页面。';
  266. }
  267. }
  268. async updateChangeQuantity(transaction, pos, qty) {
  269. const orgPos = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, pos.id);
  270. if (orgPos && orgPos.times === this.ctx.stage.curTimes && orgPos.order === this.ctx.stage.curOrder) {
  271. await transaction.update(this.tableName, {qc_qty: qty}, {where: {id: orgPos.id}});
  272. } else {
  273. await transaction.insert(this.tableName, {
  274. tid: this.ctx.tender.id,
  275. sid: this.ctx.stage.id,
  276. lid: pos.lid,
  277. pid: pos.id,
  278. said: this.ctx.session.sessionUser.accountId,
  279. times: this.ctx.stage.curTimes,
  280. order: this.ctx.stage.curOrder,
  281. qc_qty: qty,
  282. });
  283. }
  284. await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, pos.lid, transaction);
  285. }
  286. /**
  287. * 统计清单下部位明细合计
  288. * @param {Number} tid - 标段id
  289. * @param {Number} sid - 期id
  290. * @param {Number} lid - 清单节点id
  291. * @param transaction - 事务(不为空则在事务中查询,反之在数据库中查询)
  292. * @returns {Promise<*>}
  293. */
  294. async getPosGather(tid, sid, lid, transaction) {
  295. const calcQtySql = 'SELECT SUM(`contract_qty`) As `contract_qty`, SUM(`qc_qty`) As `qc_qty` FROM (' +
  296. ' SELECT `contract_qty`, `qc_qty` FROM ' + this.ctx.service.stagePos.tableName + ' As Pos ' +
  297. ' INNER JOIN (' +
  298. ' SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `pid` ' +
  299. ' FROM ' + this.ctx.service.stagePos.tableName +
  300. ' WHERE `tid` = ? And sid = ? And `lid` = ? ' +
  301. ' GROUP BY `pid`' +
  302. ' ) As MaxFilter ' +
  303. ' ON Pos.times = MaxFilter.times And Pos.order = MaxFilter.order And Pos.pid = MaxFilter.pid ' +
  304. ' WHERE Pos.tid = ? And Pos.sid = ? And Pos.lid = ?' +
  305. ' ) As Gather';
  306. const param = [tid, sid, lid];
  307. const sqlParam = param.concat(param);
  308. if (transaction) {
  309. return await transaction.queryOne(calcQtySql, sqlParam);
  310. } else {
  311. return await this.db.queryOne(calcQtySql, sqlParam);
  312. }
  313. }
  314. }
  315. return StagePos;
  316. };