ledger.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. 'use strict';
  2. /**
  3. * 标段--台账 数据模型
  4. *
  5. * @author CaiAoLin
  6. * @date 2017/12/1
  7. * @version
  8. */
  9. const needField = {
  10. id: 'ledger_id',
  11. pid: 'ledger_pid',
  12. order: 'order',
  13. level: 'level',
  14. fullPath: 'full_path',
  15. isLeaf: 'is_leaf',
  16. };
  17. const keyFields = {
  18. table: ['id'],
  19. index: ['tender_id', 'ledger_id'],
  20. };
  21. // 以下字段仅可通过树结构操作改变,不可直接通过update方式从接口提交,发现时过滤
  22. const readOnlyFields = ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'order', 'level', 'full_path', 'is_leaf'];
  23. const calcFields = ['unit_price', 'sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'deal_qty', 'deal_tp', 'dgn_qty1', 'dgn_qty2'];
  24. const upFields = ['unit_price'];
  25. const qtyFields = ['sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity', 'deal_qty'];
  26. const tpFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price', 'deal_tp'];
  27. const rootId = -1;
  28. const keyPre = 'tender_node_maxId:';
  29. const measureType = require('../const/tender').measureType;
  30. const SumLoad = require('../lib/sum_load');
  31. module.exports = app => {
  32. class Ledger extends app.BaseBillsService {
  33. /**
  34. * 构造函数
  35. *
  36. * @param {Object} ctx - egg全局变量
  37. * @return {void}
  38. */
  39. constructor(ctx) {
  40. super(ctx, {
  41. mid: 'tender_id',
  42. kid: 'ledger_id',
  43. pid: 'ledger_pid',
  44. order: 'order',
  45. level: 'level',
  46. isLeaf: 'is_leaf',
  47. fullPath: 'full_path',
  48. keyPre: 'ledger_bills_maxLid:',
  49. uuid: true,
  50. }, 'pos', 'ancillaryGcl');
  51. this.depart = ctx.app.config.table_depart.heavy;
  52. this.tableName = 'ledger';
  53. }
  54. /**
  55. * 新增数据(供内部或其他service类调用, controller不可直接使用)
  56. * @param {Array|Object} data - 新增数据
  57. * @param {Number} tenderId - 标段id
  58. * @param {Object} transaction - 新增事务
  59. * @return {Promise<boolean>} - {Promise<是否正确新增成功>}
  60. */
  61. async innerAdd(data, tenderId, transaction) {
  62. const datas = data instanceof Array ? data : [data];
  63. if (tenderId <= 0) {
  64. throw '标段id错误';
  65. }
  66. if (datas.length <= 0) {
  67. throw '插入数据为空';
  68. }
  69. if (!transaction) {
  70. throw '内部错误';
  71. }
  72. // 整理数据
  73. const insertData = [];
  74. for (const tmp of datas) {
  75. tmp.ledger_id = tmp.template_id;
  76. tmp.ledger_pid = tmp.pid;
  77. tmp.tender_id = tenderId;
  78. delete tmp.template_id;
  79. delete tmp.pid;
  80. tmp.id = this.uuid.v4();
  81. insertData.push(tmp);
  82. }
  83. const operate = await transaction.insert(this.tableName, insertData);
  84. return operate.affectedRows === datas.length;
  85. }
  86. /**
  87. * 新增数据
  88. *
  89. * @param {Object} data - 新增的数据(可批量)
  90. * @param {Number} tenderId - 标段id
  91. * @return {Boolean} - 返回新增的结果
  92. */
  93. async add(data, tenderId) {
  94. this.transaction = await this.db.beginTransaction();
  95. let result = false;
  96. try {
  97. result = await this.innerAdd(data, tenderId, this.transaction);
  98. if (!result) {
  99. throw '新增数据错误';
  100. }
  101. await this.transaction.commit();
  102. } catch (error) {
  103. await this.transaction.rollback();
  104. result = false;
  105. }
  106. return result;
  107. }
  108. /**
  109. * 根据节点Id获取数据
  110. *
  111. * @param {Number} tenderId - 标段id
  112. * @param {Number} nodeId - 项目节/工程量清单节点id
  113. * @return {Object} - 返回查询到的节点数据
  114. */
  115. async getDataByNodeId(tenderId, nodeId) {
  116. if ((nodeId <= 0) || (tenderId <= 0)) {
  117. return undefined;
  118. }
  119. this.initSqlBuilder();
  120. this.sqlBuilder.setAndWhere('tender_id', {
  121. value: tenderId,
  122. operate: '=',
  123. });
  124. this.sqlBuilder.setAndWhere('ledger_id', {
  125. value: nodeId,
  126. operate: '=',
  127. });
  128. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  129. const data = await this.db.queryOne(sql, sqlParam);
  130. return data;
  131. }
  132. /**
  133. * 根据节点Id获取数据
  134. * @param {Number} tenderId - 标段Id
  135. * @param {Array} nodesIds - 节点Id
  136. * @return {Array}
  137. */
  138. async getDataByNodeIds(tenderId, nodesIds) {
  139. if (tenderId <= 0) {
  140. return [];
  141. }
  142. this.initSqlBuilder();
  143. this.sqlBuilder.setAndWhere('tender_id', {
  144. value: tenderId,
  145. operate: '=',
  146. });
  147. this.sqlBuilder.setAndWhere('ledger_id', {
  148. value: nodesIds,
  149. operate: 'in',
  150. });
  151. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  152. const data = await this.db.query(sql, sqlParam);
  153. return this._.sortBy(data, function(d) {
  154. return nodesIds.indexOf(d.ledger_id);
  155. });
  156. }
  157. /**
  158. * 根据主键id获取数据
  159. * @param {Array|Number} id - 主键id
  160. * @return {Promise<*>}
  161. */
  162. async getDataByIds(id) {
  163. if (!id) {
  164. return;
  165. }
  166. const ids = id instanceof Array ? id : [id];
  167. if (ids.length === 0) {
  168. return;
  169. }
  170. this.initSqlBuilder();
  171. this.sqlBuilder.setAndWhere('id', {
  172. value: ids,
  173. operate: 'in',
  174. });
  175. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  176. const data = await this.db.query(sql, sqlParam);
  177. return data;
  178. }
  179. /**
  180. * 根据 父节点id 获取子节点
  181. * @param tenderId
  182. * @param nodeId
  183. * @return {Promise<*>}
  184. */
  185. async getChildrenByParentId(tenderId, nodeId) {
  186. if (tenderId <= 0 || !nodeId) {
  187. return undefined;
  188. }
  189. const nodeIds = nodeId instanceof Array ? nodeId : [nodeId];
  190. if (nodeIds.length === 0) {
  191. return [];
  192. }
  193. this.initSqlBuilder();
  194. this.sqlBuilder.setAndWhere('tender_id', {
  195. value: tenderId,
  196. operate: '=',
  197. });
  198. this.sqlBuilder.setAndWhere('ledger_pid', {
  199. value: nodeIds,
  200. operate: 'in',
  201. });
  202. this.sqlBuilder.orderBy = [['order', 'ASC']];
  203. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  204. const data = await this.db.query(sql, sqlParam);
  205. return data;
  206. }
  207. /**
  208. * 获取项目工程量
  209. * @param tenderId
  210. * @return {Promise<*>}
  211. */
  212. async getGatherGclBills(tenderId) {
  213. const sql = 'SELECT `b_code`, `name`, `unit`, `unit_price`, ' +
  214. ' Sum(`quantity`) As `quantity`, Sum(`total_price`) As `total_price`, ' +
  215. ' Sum(`deal_qty`) As `deal_qty`, Sum(`deal_tp`) As `deal_tp` ' +
  216. ' From ?? ' +
  217. ' WHERE `tender_id` = ? And `b_code` And `is_leaf` ' +
  218. ' GROUP BY `b_code`, `name`, `unit`, `unit_price`';
  219. const sqlParam = [this.tableName, tenderId];
  220. return await this.db.query(sql, sqlParam);
  221. }
  222. async getTenderUsedUnits(tenderId) {
  223. const sql = 'SELECT unit ' +
  224. ' FROM ' + this.tableName +
  225. ' WHERE tender_id = ? and is_leaf = true' +
  226. ' GROUP By unit';
  227. const sqlParam = [tenderId];
  228. const units = await this.db.query(sql, sqlParam);
  229. return this._.map(units, 'unit');
  230. }
  231. /**
  232. * 统计子节点total_price
  233. * @param {Number} tenderId - 标段id
  234. * @param {Number} pid - 父节点id
  235. * @param {Number} order - order取值
  236. * @param {String} orderOperate - order比较操作符
  237. * @return {Promise<void>}
  238. */
  239. async addUpChildren(tenderId, pid, order, orderOperate) {
  240. this.initSqlBuilder();
  241. const sql = ['SELECT SUM(??) As value FROM ?? ', ' WHERE '];
  242. const sqlParam = ['total_price', this.tableName];
  243. sql.push(' ?? = ' + tenderId);
  244. sqlParam.push('tender_id');
  245. sql.push(' And ?? = ' + pid);
  246. sqlParam.push('ledger_pid');
  247. sql.push(' And ?? ' + orderOperate + ' ' + order);
  248. sqlParam.push('order');
  249. const result = await this.db.queryOne(sql.join(''), sqlParam);
  250. return result.value;
  251. }
  252. /**
  253. * 删除相关数据 用于继承
  254. * @param mid
  255. * @param deleteData
  256. * @return {Promise<void>}
  257. * @private
  258. */
  259. async _deleteRelaData(mid, deleteData) {
  260. await this.ctx.service.pos.deletePosData(this.transaction, mid, this._.map(deleteData, 'id'));
  261. await this.ctx.service.ancillaryGcl.deletePartData(this.transaction, mid, this._.map(deleteData, 'id'));
  262. }
  263. _checkField(data, field) {
  264. const fields = field instanceof Array ? field : [field];
  265. for (const prop in data) {
  266. if (fields.indexOf(prop) >= 0) {
  267. return true;
  268. }
  269. }
  270. return false;
  271. }
  272. /**
  273. * 复制粘贴整块
  274. * @param {Number} tenderId - 标段Id
  275. * @param {Number} selectId - 选中几点Id
  276. * @param {Array} block - 复制节点Id
  277. * @return {Object} - 提价后的数据(其中新增粘贴数据,只返回第一层)
  278. */
  279. async pasteBlock(tenderId, selectId, paste) {
  280. if ((tenderId <= 0) || (selectId <= 0)) {
  281. return [];
  282. }
  283. const selectData = await this.getDataByNodeId(this.ctx.tender.id, selectId);
  284. if (!selectData) {
  285. throw '位置数据错误';
  286. }
  287. const newParentPath = selectData.full_path.replace(selectData.ledger_id, '');
  288. const copyNodes = await this.getDataByNodeIds(paste.tid, paste.block);
  289. if (!copyNodes || copyNodes.length <= 0) {
  290. throw '复制数据错误';
  291. }
  292. let bSameParent = true;
  293. for (const node of copyNodes) {
  294. if (node.ledger_pid !== copyNodes[0].ledger_pid) {
  295. bSameParent = false;
  296. break;
  297. }
  298. }
  299. if (!bSameParent) {
  300. throw '复制数据错误:仅可操作同层节点';
  301. }
  302. const pasteBillsData = [],
  303. pastePosData = [];
  304. this.transaction = await this.db.beginTransaction();
  305. try {
  306. // 选中节点的所有后兄弟节点,order+粘贴节点个数
  307. await this._updateChildrenOrder(tenderId, selectData.ledger_pid, selectData.order + 1, copyNodes.length);
  308. // 数据库创建新增节点数据
  309. const leafBillsId = [];
  310. let maxId = await this._getMaxLid(this.ctx.tender.id);
  311. for (let iNode = 0; iNode < copyNodes.length; iNode++) {
  312. const node = copyNodes[iNode];
  313. let datas = await this.getDataByFullPath(paste.tid, node.full_path + '%');
  314. datas = this._.sortBy(datas, 'level');
  315. // 计算粘贴数据中需更新部分
  316. datas.sort(function(x, y) {
  317. return x.level - y.level;
  318. });
  319. for (const data of datas) {
  320. data.children = datas.filter(function(x) {
  321. return x.ledger_pid === data.ledger_id;
  322. });
  323. }
  324. for (let index = 0; index < datas.length; index++) {
  325. const data = datas[index];
  326. const newId = maxId + index + 1;
  327. const idChange = {
  328. org: data.id,
  329. };
  330. data.id = this.uuid.v4();
  331. idChange.new = data.id;
  332. data.tender_id = this.ctx.tender.id;
  333. if (data.children && data.children.length > 0) {
  334. for (const child of data.children) {
  335. child.ledger_pid = newId;
  336. }
  337. }
  338. data.ledger_id = newId;
  339. if (data.ledger_pid === node.ledger_pid) {
  340. data.ledger_pid = selectData.ledger_pid;
  341. data.order = selectData.order + iNode + 1;
  342. }
  343. data.level = data.level + selectData.level - copyNodes[0].level;
  344. if (data.is_leaf) {
  345. leafBillsId.push(idChange);
  346. }
  347. }
  348. for (const data of datas) {
  349. delete data.children;
  350. delete data.crid;
  351. delete data.is_tp;
  352. const p = datas.find(function(x) {
  353. return x.ledger_id === data.ledger_pid;
  354. });
  355. if (p) {
  356. data.full_path = p.full_path + '-' + data.ledger_id;
  357. } else {
  358. data.full_path = newParentPath + '' + data.ledger_id;
  359. }
  360. pasteBillsData.push(data);
  361. }
  362. maxId = maxId + datas.length;
  363. }
  364. const newData = await this.transaction.insert(this.tableName, pasteBillsData);
  365. this._cacheMaxLid(tenderId, maxId);
  366. for (const id of leafBillsId) {
  367. const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: paste.tid, lid: id.org } });
  368. if (posData.length > 0) {
  369. for (const pd of posData) {
  370. delete pd.crid;
  371. pd.id = this.uuid.v4();
  372. pd.lid = id.new;
  373. pd.tid = this.ctx.tender.id;
  374. pd.in_time = new Date();
  375. pastePosData.push(pd);
  376. }
  377. }
  378. }
  379. await this.transaction.insert(this.ctx.service.pos.tableName, pastePosData);
  380. await this.transaction.commit();
  381. } catch (err) {
  382. await this.transaction.rollback();
  383. throw err;
  384. }
  385. // 查询应返回的结果
  386. const order = [];
  387. for (let i = 1; i <= copyNodes.length; i++) {
  388. order.push(selectData.order + i);
  389. }
  390. const updateData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + copyNodes.length);
  391. return {
  392. ledger: { create: pasteBillsData, update: updateData },
  393. pos: pastePosData,
  394. };
  395. }
  396. /**
  397. *
  398. * @param tenderId
  399. * @param xmj
  400. * @param order
  401. * @param parentData
  402. * @return {Promise<*[]>}
  403. * @private
  404. */
  405. async _sortBatchInsertData(tenderId, xmj, order, parentData) {
  406. const result = [],
  407. newIds = [];
  408. let tp = 0;
  409. const maxId = await this._getMaxLid(tenderId);
  410. // 添加xmj数据
  411. const parent = {
  412. tender_id: tenderId,
  413. ledger_id: maxId + 1,
  414. ledger_pid: parentData.ledger_id,
  415. is_leaf: xmj.children.length === 0,
  416. order,
  417. level: parentData.level + 1,
  418. name: xmj.name,
  419. };
  420. parent.full_path = parentData.full_path + '-' + parent.ledger_id;
  421. // 添加gcl数据
  422. for (let i = 0, iLen = xmj.children.length; i < iLen; i++) {
  423. const gcl = xmj.children[i];
  424. const child = {
  425. tender_id: tenderId,
  426. ledger_id: maxId + 1 + i + 1,
  427. ledger_pid: parent.ledger_id,
  428. is_leaf: true,
  429. order: i + 1,
  430. level: parent.level + 1,
  431. b_code: gcl.b_code,
  432. name: gcl.name,
  433. unit: gcl.unit,
  434. unit_price: gcl.unit_price,
  435. quantity: gcl.quantity,
  436. };
  437. child.full_path = parent.full_path + '-' + child.ledger_id;
  438. child.total_price = this.ctx.helper.mul(child.unit_price, child.quantity, this.ctx.tender.info.decimal.tp);
  439. tp = this.ctx.helper.add(tp, child.total_price);
  440. result.push(child);
  441. newIds.push(child.ledger_id);
  442. }
  443. parent.total_price = tp;
  444. result.push(parent);
  445. newIds.push(parent.ledger_id);
  446. return [result, tp, newIds];
  447. }
  448. /**
  449. * 批量插入子项
  450. * @param {Number} tenderId - 标段Id
  451. * @param {Number} selectId - 选中节点Id
  452. * @param {Object} data - 批量插入数据
  453. * @return {Promise<void>}
  454. */
  455. async batchInsertChild(tenderId, selectId, data) {
  456. const result = { ledger: {}, pos: null };
  457. if ((tenderId <= 0) || (selectId <= 0)) return result;
  458. const selectData = await this.getDataByNodeId(tenderId, selectId);
  459. if (!selectData) throw '位置数据错误';
  460. const info = this.ctx.tender.info;
  461. this.transaction = await this.db.beginTransaction();
  462. const lastChild = await this.getLastChildData(tenderId, selectId);
  463. // 计算id
  464. const maxId = await this._getMaxLid(tenderId);
  465. const insertBillsData = [], insertPosData = [], in_time = new Date();
  466. const order = lastChild ? lastChild.order : 0;
  467. for (let i = 0, iLen = data.length; i < iLen; i++) {
  468. // 合并新增数据
  469. const qd = {
  470. id: this.uuid.v4(),
  471. tender_id: tenderId,
  472. ledger_id: maxId + i + 1,
  473. ledger_pid: selectData.ledger_id,
  474. is_leaf: true,
  475. order: order + i + 1,
  476. level: selectData.level + 1,
  477. b_code: data[i].b_code,
  478. name: data[i].name,
  479. unit: data[i].unit,
  480. unit_price: this.ctx.helper.round(data[i].price, info.decimal.up),
  481. };
  482. qd.full_path = selectData.full_path + '-' + qd.ledger_id;
  483. insertBillsData.push(qd);
  484. const precision = this.ctx.helper.findPrecision(info.precision, qd.unit);
  485. if (data[i].pos.length > 0) {
  486. for (const [j, p] of data[i].pos.entries()) {
  487. const inD = {
  488. id: this.uuid.v4(), tid: tenderId, lid: qd.id,
  489. add_stage: 0, add_stage_order: 0, add_times: 0, add_user: this.ctx.session.sessionUser.accountId,
  490. in_time, porder: j + 1,
  491. name: p.name, drawing_code: p.drawing_code,
  492. };
  493. if (p.quantity) {
  494. inD.sgfh_qty = this.round(p.quantity, precision.value);
  495. inD.quantity = inD.sgfh_qty;
  496. qd.sgfh_qty = this.ctx.helper.add(qd.sgfh_qty, inD.sgfh_qty);
  497. }
  498. insertPosData.push(inD);
  499. }
  500. qd.sgfh_tp = this.ctx.helper.mul(qd.sgfh_qty, qd.unit_price, info.decimal.tp);
  501. qd.quantity = qd.sgfh_qty;
  502. qd.total_price = qd.sgfh_tp;
  503. }
  504. }
  505. try {
  506. // 更新父项isLeaf
  507. if (!lastChild) {
  508. await this.transaction.update(this.tableName, {
  509. id: selectData.id,
  510. is_leaf: false,
  511. unit_price: null,
  512. sgfh_qty: null, sgfh_tp: null,
  513. sjcl_qty: null, sjcl_tp: null,
  514. qtcl_qty: null, qtcl_tp: null,
  515. quantity: null, total_price: null,
  516. deal_qty: null, deal_tp: null,
  517. });
  518. }
  519. await this.transaction.insert(this.tableName, insertBillsData);
  520. if (insertPosData.length > 0) await this.transaction.insert(this.ctx.service.pos.tableName, insertPosData);
  521. this._cacheMaxLid(tenderId, maxId + data.length);
  522. await this.transaction.commit();
  523. } catch (err) {
  524. await this.transaction.rollback();
  525. throw err;
  526. }
  527. // 查询应返回的结果
  528. result.ledger.create = insertBillsData;
  529. if (!lastChild) result.ledger.update = await this.getDataByIds([selectData.id]);
  530. result.pos = insertPosData;
  531. return result;
  532. }
  533. /**
  534. * 批量插入后项
  535. * @param {Number} tenderId - 标段Id
  536. * @param {Number} selectId - 选中节点Id
  537. * @param {Object} data - 批量插入数据
  538. * @return {Promise<void>}
  539. */
  540. async batchInsertNext(tenderId, selectId, data) {
  541. const result = { ledger: {}, pos: null };
  542. if ((tenderId <= 0) || (selectId <= 0)) return result;
  543. const selectData = await this.getDataByNodeId(tenderId, selectId);
  544. if (!selectData) throw '位置数据错误';
  545. const parentData = await this.getDataByNodeId(tenderId, selectData.ledger_pid);
  546. if (!parentData) throw '位置数据错误';
  547. const info = this.ctx.tender.info;
  548. this.transaction = await this.db.beginTransaction();
  549. const insertBillsData = [], insertPosData = [], in_time = new Date();
  550. const maxId = await this._getMaxLid(tenderId);
  551. const order = selectData.order;
  552. for (let i = 0, iLen = data.length; i < iLen; i++) {
  553. // 合并新增数据
  554. const qd = {
  555. id: this.uuid.v4(),
  556. tender_id: tenderId,
  557. ledger_id: maxId + i + 1,
  558. ledger_pid: selectData.ledger_pid,
  559. is_leaf: true,
  560. order: order + i + 1,
  561. level: selectData.level,
  562. b_code: data[i].b_code,
  563. name: data[i].name,
  564. unit: data[i].unit,
  565. unit_price: this.ctx.helper.round(data[i].price, info.decimal.up),
  566. };
  567. qd.full_path = selectData.full_path + '-' + qd.ledger_id;
  568. insertBillsData.push(qd);
  569. const precision = this.ctx.helper.findPrecision(info.precision, qd.unit);
  570. if (data[i].pos.length > 0) {
  571. for (const [j, p] of data[i].pos.entries()) {
  572. const inD = {
  573. id: this.uuid.v4(), tid: tenderId, lid: qd.id,
  574. add_stage: 0, add_stage_order: 0, add_times: 0, add_user: this.ctx.session.sessionUser.accountId,
  575. in_time, porder: j + 1,
  576. name: p.name, drawing_code: p.drawing_code,
  577. };
  578. if (p.quantity) {
  579. inD.sgfh_qty = this.round(p.quantity, precision.value);
  580. inD.quantity = inD.sgfh_qty;
  581. qd.sgfh_qty = this.ctx.helper.add(qd.sgfh_qty, inD.sgfh_qty);
  582. }
  583. insertPosData.push(inD);
  584. }
  585. qd.sgfh_tp = this.ctx.helper.mul(qd.sgfh_qty, qd.unit_price, info.decimal.tp);
  586. qd.quantity = qd.sgfh_qty;
  587. qd.total_price = qd.sgfh_tp;
  588. }
  589. }
  590. try {
  591. // 选中节点的所有后兄弟节点,order+粘贴节点个数
  592. await this._updateChildrenOrder(tenderId, selectData.ledger_pid, selectData.order + 1, data.length);
  593. await this.transaction.insert(this.tableName, insertBillsData);
  594. if (insertPosData.length > 0) await this.transaction.insert(this.ctx.service.pos.tableName, insertPosData);
  595. this._cacheMaxLid(tenderId, maxId + data.length);
  596. await this.transaction.commit();
  597. } catch (err) {
  598. await this.transaction.rollback();
  599. throw err;
  600. }
  601. // 查询应返回的结果
  602. result.ledger.create = insertBillsData;
  603. result.ledger.update = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + data.length);
  604. result.pos = insertPosData;
  605. return result;
  606. }
  607. /**
  608. * 导入Excel数据
  609. * @param excelData
  610. * @return {Promise<void>}
  611. */
  612. async importExcel(templateId, excelData, filter) {
  613. const AnalysisExcel = require('../lib/analysis_excel').AnalysisExcelTree;
  614. const analysisExcel = new AnalysisExcel(this.ctx, this.setting);
  615. const tempData = await this.ctx.service.tenderNodeTemplate.getData(templateId, true);
  616. const cacheTree = analysisExcel.analysisData(excelData, tempData, { filterZeroGcl: filter });
  617. cacheTree.calculateLeafWithPos();
  618. const transaction = await this.db.beginTransaction();
  619. try {
  620. await transaction.delete(this.tableName, { tender_id: this.ctx.tender.id });
  621. await transaction.delete(this.ctx.service.pos.tableName, { tid: this.ctx.tender.id });
  622. const datas = [];
  623. for (const node of cacheTree.items) {
  624. const data = {
  625. id: node.id,
  626. tender_id: this.ctx.tender.id,
  627. ledger_id: node.ledger_id,
  628. ledger_pid: node.ledger_pid,
  629. level: node.level,
  630. order: node.order,
  631. is_leaf: !node.children || node.children.length === 0,
  632. full_path: node.full_path,
  633. code: node.code,
  634. b_code: node.b_code,
  635. name: node.name,
  636. unit: node.unit,
  637. unit_price: node.unit_price,
  638. dgn_qty1: node.dgn_qty1,
  639. dgn_qty2: node.dgn_qty2,
  640. memo: node.memo,
  641. drawing_code: node.drawing_code,
  642. node_type: node.node_type,
  643. // 项目节不导入金额
  644. // todo 最底层项目节其实应该导入,但是由于目前项目节不能输入金额,也不能计量,导入金额就会有矛盾,故暂时不导入
  645. sgfh_qty: node.quantity,
  646. sgfh_tp: node.b_code ? node.total_price : 0,
  647. quantity: node.quantity,
  648. total_price: node.b_code ? node.total_price : 0,
  649. deal_qty: node.deal_qty,
  650. deal_tp: node.b_code ? node.deal_tp : 0,
  651. };
  652. datas.push(data);
  653. }
  654. await transaction.insert(this.tableName, datas);
  655. if (this.ctx.tender.data.measure_type === measureType.tz.value
  656. && cacheTree.pos && cacheTree.pos.length > 0) {
  657. await transaction.insert(this.ctx.service.pos.tableName, cacheTree.pos);
  658. }
  659. await transaction.commit();
  660. this._cacheMaxLid(this.ctx.tender.id, cacheTree.keyNodeId);
  661. return { bills: datas, pos: cacheTree.pos };
  662. } catch (err) {
  663. await transaction.rollback();
  664. throw err;
  665. }
  666. }
  667. async importYbp(tender, ybp) {
  668. const YBP = require('../lib/ybp');
  669. const YbpTrees = require('../lib/ybp_tree');
  670. const gatherTreeSetting = {
  671. id: 'ledger_id', pid: 'ledger_pid', order: 'order', full_path: 'full_path', level: 'level', rootId: -1,
  672. calcFields: [ 'total_price' ],
  673. calc(node, helper, decimal) {
  674. if (!node.children || node.children.length === 0) {
  675. node.total_price = helper.mul(node.quantity, node.unit_price, decimal.tp);
  676. }
  677. },
  678. };
  679. const ybpTreeSetting = { id: 'ID', pid: 'parentID', order: 'seq', rootId: '-1' };
  680. const helper = this.ctx.helper;
  681. const ybpAnalysis = new YBP(this.ctx);
  682. const ybpData = ybpAnalysis.decryptBuffer(ybp);
  683. const gatherTree = new YbpTrees.YbpImportTree(gatherTreeSetting, YbpTrees.YbpImportType.flow, helper, tender.info.decimal);
  684. for (const subject of ybpData.subjects) {
  685. if (!subject.bills || subject.bills.length === 0) continue;
  686. const ybpTree = new YbpTrees.YbpTree(ybpTreeSetting);
  687. ybpTree.loadDatas(subject.bills);
  688. const loadRelaFun = function (cur, node) {
  689. if (node.children && node.children.length > 0) return;
  690. const rations = subject.rations.filter(x => { return x.parentID === node.ID; });
  691. if (rations.length === 0) return;
  692. if (!cur.glj) cur.glj = [];
  693. for (const r of rations) {
  694. const gljs = r.gljList || r.rationGljList;
  695. if (!gljs || gljs.length === 0) continue;
  696. for (const g of gljs) {
  697. let curGlj = cur.glj.find(x => {
  698. if (x.projectGljID && g.projectGljID) return x.projectGljID === g.projectGljID;
  699. return x.code === g.code && x.name === g.name && x.unit === g.unit && x.unit_price === g.unit_price;
  700. });
  701. if (!curGlj) {
  702. curGlj = { code: g.code, name: g.name, unit: g.unit, unit_price: g.unit_price, spec: g.specs, projectGljID: g.projectGljID };
  703. cur.glj.push(curGlj);
  704. }
  705. curGlj.quantity = helper.add(curGlj.quantity, g.quantity);
  706. }
  707. }
  708. };
  709. gatherTree.importTree(ybpTree, subject.project.name, loadRelaFun);
  710. }
  711. gatherTree.sort();
  712. gatherTree.calculateAll();
  713. const bills = [], glj = [];
  714. for (const n of gatherTree.nodes) {
  715. const billsId = this.uuid.v4();
  716. bills.push({
  717. id: billsId,
  718. tender_id: tender.id,
  719. ledger_id: n.ledger_id,
  720. ledger_pid: n.ledger_pid,
  721. level: n.level,
  722. order: n.order,
  723. full_path: n.full_path,
  724. is_leaf: !n.children || n.children.length === 0,
  725. code: n.code,
  726. b_code: n.b_code,
  727. name: n.name,
  728. unit: n.unit,
  729. unit_price: n.unit_price || 0,
  730. sgfh_qty: n.quantity || 0,
  731. sgfh_tp: n.total_price || 0,
  732. quantity: n.quantity || 0,
  733. total_price: n.total_price || 0,
  734. memo: n.source.join(';'),
  735. });
  736. if (!n.glj) continue;
  737. for (const g of n.glj) {
  738. glj.push({
  739. tid: tender.id,
  740. lid: billsId,
  741. code: g.code || '',
  742. name: g.name || '',
  743. unit: g.unit || '',
  744. spec: g.spec || '',
  745. quantity: helper.div(g.quantity, n.quantity, 4),
  746. })
  747. }
  748. }
  749. const conn = await this.db.beginTransaction();
  750. try {
  751. await conn.delete(this.ctx.service.ledger.tableName, { tender_id: tender.id });
  752. await conn.delete(this.ctx.service.pos.tableName, { tid: tender.id });
  753. await conn.insert(this.ctx.service.ledger.tableName, bills);
  754. // if (glj.length > 0) await conn.insert(this.ctx.service.ledgerGlj.tableName, glj);
  755. await conn.commit();
  756. return bills;
  757. } catch (err) {
  758. await conn.rollback();
  759. throw err;
  760. }
  761. }
  762. async sumLoad(lid, tenders) {
  763. const maxId = await this._getMaxLid(this.ctx.tender.id);
  764. const select = await this.getDataById(lid);
  765. const sumLoad = new SumLoad(this.ctx);
  766. const loadTree = await sumLoad.loadGatherGcl(select, maxId, tenders, {});
  767. const result = loadTree.getUpdateData();
  768. const conn = await this.db.beginTransaction();
  769. try {
  770. this._cacheMaxLid(this.ctx.tender.id, loadTree.keyNodeId);
  771. await this.ctx.service.sumLoadHistory.saveLedgerHistory(this.ctx.tender.id, lid, tenders);
  772. if (result.update.length > 0) await conn.updateRows(this.tableName, result.update);
  773. if (result.create.length > 0) await conn.insert(this.tableName, result.create);
  774. await conn.commit();
  775. return result;
  776. } catch (err) {
  777. await conn.rollback();
  778. throw (err.stack ? '导入工程量数据出错': err);
  779. }
  780. }
  781. async getMemoData(tid, columns) {
  782. if (!columns || columns.length === 0) return [];
  783. return await this.db.select(this.departTableName(tid), {
  784. where: { tender_id: tid },
  785. columns: ['id', ...columns],
  786. });
  787. }
  788. }
  789. return Ledger;
  790. };