payment_safe_bills.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. 'use strict';
  2. /**
  3. *
  4. * 支付审批-安全生产
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const defaultBills = require('../const/payment').defaultSafeBills;
  10. const billsUtils = require('../lib/bills_utils');
  11. const SafeBillsFields = {
  12. textFields: ['b_code', 'name', 'unit', 'spec', 'invoice_code', 'memo'],
  13. calcFields: ['unit_price', 'cur_qty', 'cur_tp', 'end_qty', 'end_tp'],
  14. fixedFields: ['safe_id', 'tender_id', 'pre_qty', 'pre_tp', 'cur_his_qty', 'cur_his_tp', 'add_user_id', 'add_time'],
  15. treeFields: ['detail_id', 'tree_id', 'tree_pid', 'tree_level', 'tree_order', 'tree_full_path', 'tree_is_leaf'],
  16. };
  17. module.exports = app => {
  18. class Ledger extends app.BaseTreeService {
  19. /**
  20. * 构造函数
  21. *
  22. * @param {Object} ctx - egg全局变量
  23. * @return {void}
  24. */
  25. constructor(ctx) {
  26. super(ctx, {
  27. mid: 'detail_id',
  28. kid: 'tree_id',
  29. pid: 'tree_pid',
  30. order: 'tree_order',
  31. level: 'tree_level',
  32. isLeaf: 'tree_is_leaf',
  33. fullPath: 'tree_full_path',
  34. keyPre: 'safe_bills_maxLid:',
  35. uuid: true,
  36. });
  37. // this.depart = 10;
  38. this.tableName = 'payment_safe_bills';
  39. this.decimal = { tp: 0, up: 2, qty: 2 };
  40. }
  41. // 继承方法
  42. clearParentingData(data) {
  43. for (const f of SafeBillsFields.calcFields) {
  44. data[f] = 0;
  45. }
  46. }
  47. _getDefaultData(data, detail) {
  48. data.id = this.uuid.v4();
  49. data.safe_id = data.id;
  50. data.tender_id = detail.tender_id;
  51. data.detail_id = detail.id;
  52. data.add_user_id = this.ctx.session.sessionUser.accountId;
  53. }
  54. async init(detail, transaction) {
  55. if (!detail || !transaction) throw '安全生产费数据错误';
  56. const insertData = [];
  57. for (const b of defaultBills) {
  58. const bills = JSON.parse(JSON.stringify(b));
  59. this._getDefaultData(bills, detail);
  60. insertData.push(bills);
  61. }
  62. const operate = await transaction.insert(this.tableName, insertData);
  63. return operate.affectedRows === insertData.length;
  64. }
  65. /**
  66. * 新增数据(供内部或其他service类调用, controller不可直接使用)
  67. * @param {Array|Object} data - 新增数据
  68. * @param {Number} detailId - 标段id
  69. * @param {Object} transaction - 新增事务
  70. * @return {Promise<boolean>} - {Promise<是否正确新增成功>}
  71. */
  72. async innerAdd(data, detailId, transaction) {
  73. const datas = data instanceof Array ? data : [data];
  74. if (detailId <= 0) {
  75. throw '标段id错误';
  76. }
  77. if (datas.length <= 0) {
  78. throw '插入数据为空';
  79. }
  80. if (!transaction) {
  81. throw '内部错误';
  82. }
  83. // 整理数据
  84. const insertData = [];
  85. for (const tmp of datas) {
  86. tmp[this.setting.id] = tmp.template_id;
  87. tmp[this.setting.pid] = tmp.pid;
  88. tmp[this.setting.mid] = detailId;
  89. delete tmp.template_id;
  90. delete tmp.pid;
  91. tmp.id = this.uuid.v4();
  92. insertData.push(tmp);
  93. }
  94. const operate = await transaction.insert(this.tableName, insertData);
  95. return operate.affectedRows === datas.length;
  96. }
  97. /**
  98. * 新增数据
  99. *
  100. * @param {Object} data - 新增的数据(可批量)
  101. * @param {Number} detailId - 支付审批期id
  102. * @return {Boolean} - 返回新增的结果
  103. */
  104. async add(data, detailId) {
  105. this.transaction = await this.db.beginTransaction();
  106. let result = false;
  107. try {
  108. result = await this.innerAdd(data, detailId, this.transaction);
  109. if (!result) {
  110. throw '新增数据错误';
  111. }
  112. await this.transaction.commit();
  113. } catch (error) {
  114. await this.transaction.rollback();
  115. result = false;
  116. }
  117. return result;
  118. }
  119. /**
  120. * 根据节点Id获取数据
  121. *
  122. * @param {Number} detailId - 标段id
  123. * @param {Number} nodeId - 项目节/工程量清单节点id
  124. * @return {Object} - 返回查询到的节点数据
  125. */
  126. async getDataByNodeId(detailId, nodeId) {
  127. if ((nodeId <= 0) || (detailId <= 0)) {
  128. return undefined;
  129. }
  130. const where = {};
  131. where[this.setting.mid] = detailId;
  132. where[this.setting.id] = nodeId;
  133. const data = await this.db.getDataByCondition(where);
  134. return data;
  135. }
  136. /**
  137. * 根据节点Id获取数据
  138. * @param {Number} detailId - 期Id
  139. * @param {Array} nodesIds - 节点Id
  140. * @return {Array}
  141. */
  142. async getDataByNodeIds(detailId, nodesIds) {
  143. if (detailId <= 0) {
  144. return [];
  145. }
  146. const where = {};
  147. where[this.setting.mid] = detailId;
  148. where[this.setting.id] = nodesIds;
  149. const data = await this.db.getAllDataByCondition({ where });
  150. return this._.sortBy(data, function(d) {
  151. return nodesIds.indexOf(d.ledger_id);
  152. });
  153. }
  154. /**
  155. * 根据主键id获取数据
  156. * @param {Array|Number} id - 主键id
  157. * @return {Promise<*>}
  158. */
  159. async getDataByIds(id) {
  160. if (!id) {
  161. return [];
  162. }
  163. const ids = id instanceof Array ? id : [id];
  164. if (ids.length === 0) {
  165. return [];
  166. }
  167. const data = await this.db.getAllDataByCondition({ where: { id: ids } });
  168. return data;
  169. }
  170. /**
  171. * 根据 父节点id 获取子节点
  172. * @param detailId
  173. * @param nodeId
  174. * @return {Promise<*>}
  175. */
  176. async getChildrenByParentId(detailId, nodeId) {
  177. if (detailId <= 0 || !nodeId) {
  178. return undefined;
  179. }
  180. const nodeIds = nodeId instanceof Array ? nodeId : [nodeId];
  181. if (nodeIds.length === 0) {
  182. return [];
  183. }
  184. const where = {};
  185. where[this.setting.mid] = detailId;
  186. where[this.setting.pid] = nodeIds;
  187. const data = await this.getAllDataByCondition({ where, orders: [[this.setting.order, 'ASC']] });
  188. return data;
  189. }
  190. async pasteBlockData(detailId, targetId, pasteData, defaultData) {
  191. const setting = this.setting;
  192. if ((detailId <= 0) || (sid <= 0)) return [];
  193. if (!pasteData || pasteData.length <= 0) throw '复制数据错误';
  194. for (const pd of pasteData) {
  195. if (!pd || pd.length <= 0) throw '复制数据错误';
  196. pd.sort(function (x, y) {
  197. return x[setting.level] - y[setting.level]
  198. });
  199. if (pd[0][this.setting.pid] !== pasteData[0][0][this.setting.pid]) throw '复制数据错误:仅可操作同层节点';
  200. }
  201. this.newBills = false;
  202. const targetData = await this.getDataByKid(detailId, targetId);
  203. if (!targetData) throw '粘贴数据错误';
  204. const newParentPath = targetData.full_path.replace(targetData.ledger_id, '');
  205. const tpDecimal = this.decimal;
  206. const pasteBillsData = [], leafBillsId = [];
  207. let maxId = await this._getMaxLid(this.ctx.paymentTender.id);
  208. for (const [i, pd] of pasteData.entries()) {
  209. for (const d of pd) {
  210. d.children = pd.filter(function (x) {
  211. return x[setting.pid] === d[setting.id];
  212. });
  213. }
  214. const pbd = [];
  215. for (const [j, d] of pd.entries()) {
  216. const newBills = {
  217. id: this.uuid.v4(),
  218. safe_id: this.uuid.v4(),
  219. b_code: d.b_code,
  220. name: d.name,
  221. unit: d.unit,
  222. unit_price: this.ctx.helper.round(d.unit_price, tpDecimal.up),
  223. memo: d.memo,
  224. };
  225. newBills[setting.mid] = detailId;
  226. newBills[setting.id] = maxId + j + 1;
  227. newBills[setting.pid] = j === 0 ? targetData[setting.pid] : d[setting.pid];
  228. newBills[setting.level] = d[setting.level] + targetData[setting.level] - pd[0][setting.level];
  229. newBills[setting.order] = j === 0 ? targetData[setting.order] + i + 1 : d[setting.order];
  230. newBills[setting.isLeaf] = d[setting.isLeaf];
  231. for (const c of d.children) {
  232. c[setting.pid] = newBills[setting.id];
  233. }
  234. newBills.quantity = this.ctx.helper.round(d.quantity, tpDecimal.qty);
  235. newBills.total_price = this.ctx.helper.mul(newBills.quantity, newBills.unit_price, tpDecimal.tp);
  236. if (defaultData) this.ctx.helper._.assignIn(newBills, defaultData);
  237. pbd.push(newBills);
  238. }
  239. for (const d of pbd) {
  240. const parent = pbd.find(function (x) {
  241. return x[setting.id] === d[setting.pid];
  242. });
  243. d[setting.fullPath] = parent
  244. ? parent[setting.fullPath] + '-' + d[setting.id]
  245. : newParentPath + d[setting.id];
  246. if (defaultData) this.ctx.helper._.assignIn(pbd, defaultData);
  247. pasteBillsData.push(d);
  248. }
  249. maxId = maxId + pbd.length;
  250. }
  251. this.transaction = await this.db.beginTransaction();
  252. try {
  253. // 选中节点的所有后兄弟节点,order+粘贴节点个数
  254. await this._updateChildrenOrder(tid, targetData[setting.pid], targetData[setting.order] + 1, pasteData.length);
  255. // 数据库创建新增节点数据
  256. if (pasteBillsData.length > 0) {
  257. const newData = await this.transaction.insert(this.tableName, pasteBillsData);
  258. }
  259. this._cacheMaxLid(tid, maxId);
  260. await this.transaction.commit();
  261. } catch (err) {
  262. await this.transaction.rollback();
  263. throw err;
  264. }
  265. // 查询应返回的结果
  266. const updateData = await this.getNextsData(targetData[setting.mid], targetData[setting.pid], targetData[setting.order] + pasteData.length);
  267. return { create: pasteBillsData, update: updateData };
  268. }
  269. async addSafeBillsNode(detail, targetId, count) {
  270. if (!detail) return null;
  271. const select = targetId ? await this.getDataByKid(detail.id, targetId) : null;
  272. if (targetId && !select) throw '新增节点数据错误';
  273. this.transaction = await this.db.beginTransaction();
  274. try {
  275. if (select) await this._updateChildrenOrder(detail.id, select[this.setting.pid], select[this.setting.order] + 1, count);
  276. const newDatas = [];
  277. const maxId = await this._getMaxLid(detail.id);
  278. for (let i = 1; i < count + 1; i++) {
  279. const newData = {};
  280. newData[this.setting.kid] = maxId + i;
  281. newData[this.setting.pid] = select ? select[this.setting.pid] : this.rootId;
  282. newData[this.setting.mid] = detail.id;
  283. newData[this.setting.level] = select ? select[this.setting.level] : 1;
  284. newData[this.setting.order] = select ? select[this.setting.order] + i : i;
  285. newData[this.setting.fullPath] = newData[this.setting.level] > 1
  286. ? select[this.setting.fullPath].replace('-' + select[this.setting.kid], '-' + newData[this.setting.kid])
  287. : newData[this.setting.kid] + '';
  288. newData[this.setting.isLeaf] = true;
  289. this._getDefaultData(newData, detail);
  290. newDatas.push(newData);
  291. }
  292. const insertResult = await this.transaction.insert(this.tableName, newDatas);
  293. this._cacheMaxLid(detail.id, maxId + count);
  294. if (insertResult.affectedRows !== count) throw '新增节点数据错误';
  295. await this.transaction.commit();
  296. this.transaction = null;
  297. } catch (err) {
  298. await this.transaction.rollback();
  299. this.transaction = null;
  300. throw err;
  301. }
  302. if (select) {
  303. const createData = await this.getChildBetween(detail.id, select[this.setting.pid], select[this.setting.order], select[this.setting.order] + count + 1);
  304. const updateData = await this.getNextsData(detail.id, select[this.setting.pid], select[this.setting.order] + count);
  305. return {create: createData, update: updateData};
  306. } else {
  307. const createData = await this.getChildBetween(detail.id, -1, 0, count + 1);
  308. return {create: createData};
  309. }
  310. }
  311. async addStdNodeWithParent(detail, targetId, stdData) {
  312. const findPreData = function(list, a) {
  313. if (!list || list.length === 0) { return null; }
  314. for (let i = 0, iLen = list.length; i < iLen; i++) {
  315. if (billsUtils.compareCode(list[i].b_code, a.b_code) > 0) {
  316. return i > 0 ? list[i - 1] : null;
  317. }
  318. }
  319. return list[list.length - 1];
  320. };
  321. let parent = await this.getDataByKid(detail.id, targetId);
  322. if (targetId && !parent) throw '新增节点数据错误,请刷新页面重试';
  323. let children = await this.getChildrenByParentId(detail.id, targetId);
  324. const updateParent = children.length === 0;
  325. const insertData = [];
  326. const maxId = await this._getMaxLid(detail.id);
  327. this.transaction = await this.db.beginTransaction();
  328. try {
  329. if (updateParent) {
  330. const updateData = { id: parent.id };
  331. updateData[this.setting.isLeaf] = false;
  332. this.clearParentingData(updateData);
  333. await this.transaction.update(this.tableName, updateData);
  334. }
  335. // 从最顶层节点依次查询是否存在,否则添加
  336. for (let i = 0, len = stdData.length; i < len; i++) {
  337. const newData = { b_code: stdData[i].b_code, name: stdData[i].name, unit: stdData[i].unit };
  338. newData[this.setting.kid] = maxId + i + 1;
  339. newData[this.setting.pid] = parent ? parent[this.setting.kid] : this.rootId;
  340. newData[this.setting.level] = parent ? parent[this.setting.level] + 1 : 1;
  341. newData[this.setting.fullPath] = parent ? `${parent[this.setting.fullPath]}-${newData[this.setting.kid]}` : `${newData[this.setting.kid]}`;
  342. const pre = findPreData(children, newData);
  343. newData[this.setting.order] = pre ? pre[this.setting.order] + 1 : 1;
  344. if (!pre || children.indexOf(pre) < children.length - 1) {
  345. await this._updateChildrenOrder(detail.id, parent ? parent[this.setting.kid] : this.rootId, pre ? pre[this.setting.order] + 1 : 1);
  346. }
  347. newData[this.setting.isLeaf] = (i === len - 1);
  348. this._getDefaultData(newData, detail);
  349. insertData.push(newData);
  350. parent = newData;
  351. children = [];
  352. }
  353. await this.transaction.insert(this.tableName, insertData);
  354. await this.transaction.commit();
  355. } catch (err) {
  356. await this.transaction.rollback();
  357. throw err;
  358. }
  359. this._cacheMaxLid(detail.id, maxId + stdData.length);
  360. // 查询应返回的结果
  361. const createData = await this.getDataByFullPath(detail.id, insertData[0][this.setting.fullPath] + '%');
  362. const updateData = await this.getNextsData(detail.id, targetId, insertData[0][this.setting.order]);
  363. if (updateParent) {
  364. updateData.push(await this.getDataByCondition({ id: updateParent.id }));
  365. }
  366. return { create: createData, update: updateData };
  367. }
  368. async updateCalc(detail, data) {
  369. const helper = this.ctx.helper;
  370. const decimal = this.decimal;
  371. // 简单验证数据
  372. if (!detail) throw '安全生产费不存在';
  373. if (!data) throw '提交数据错误';
  374. const datas = data instanceof Array ? data : [data];
  375. const ids = datas.map(x => { return x.id; });
  376. const orgData = await this.getAllDataByCondition({ where: { id: ids }});
  377. const updateData = [];
  378. for (const row of datas) {
  379. const oData = orgData.find(x => { return x.id === row.id });
  380. if (!oData || oData.detail_id !== detail.id || oData.tree_id !== row.tree_id) throw '提交数据错误';
  381. let nData = { id: oData.id, tree_id: oData.tree_id, update_user_id: this.ctx.session.sessionUser.accountId };
  382. // 计算相关
  383. if (row.cur_qty !== undefined || row.unit_price !== undefined || row.cur_tp !== undefined) {
  384. nData.unit_price = row.unit_price !== undefined ? helper.round(row.unit_price, decimal.up) : oData.unit_price;
  385. nData.cur_qty = row.cur_qty !== undefined ? helper.round(row.cur_qty, decimal.qty) : oData.cur_qty;
  386. nData.cur_tp = row.cur_tp !== undefined ? helper.round(row.cur_tp, decimal.tp) : helper.mul(nData.unit_price, nData.cur_qty, decimal.tp);
  387. nData.end_qty = helper.add(nData.cur_qty, oData.pre_qty);
  388. nData.end_tp = helper.add(nData.cur_tp, oData.pre_tp);
  389. }
  390. console.log(nData);
  391. for (const field of SafeBillsFields.textFields) {
  392. if (row[field] !== undefined) nData[field] = row[field];
  393. }
  394. updateData.push(nData);
  395. }
  396. await this.db.updateRows(this.tableName, updateData);
  397. return { update: updateData };
  398. }
  399. }
  400. return Ledger;
  401. };