budget_final.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const itemsPre = 'id_';
  10. const BillsTree = require('./ledger').billsTree;
  11. const auditConst = require('../const/audit');
  12. class FinalTree extends BillsTree {
  13. constructor(ctx, setting) {
  14. super(ctx, setting);
  15. this._newId = 1;
  16. }
  17. get newId() {
  18. return this._newId++;
  19. }
  20. loadNode(node, parent, loadFun) {
  21. if (node.b_code) return;
  22. const siblings = parent ? parent.children : this.children;
  23. let cur = siblings.find(function (x) {
  24. return x.code === node.code && x.name === node.name;
  25. });
  26. if (!cur) {
  27. cur = { children: [], code: node.code || '', name: node.name || '', unit: node.unit || '' };
  28. const id = this.newId;
  29. cur[this.setting.id] = id;
  30. cur[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId;
  31. cur[this.setting.fullPath] = parent ? parent[this.setting.fullPath] + '-' + id : '' + id;
  32. cur[this.setting.level] = parent ? parent[this.setting.level] + 1 : 1;
  33. cur[this.setting.order] = siblings.length + 1;
  34. siblings.push(cur);
  35. this.datas.push(cur);
  36. }
  37. loadFun(cur, node);
  38. for (const c of node.children) {
  39. this.loadNode(c, cur, loadFun);
  40. }
  41. }
  42. loadTree(tree, loadFun) {
  43. for (const node of tree.children) {
  44. this.loadNode(node, null, loadFun);
  45. }
  46. }
  47. generateSortNodes() {
  48. const self = this;
  49. const addSortNode = function (node) {
  50. self.nodes.push(node);
  51. for (const c of node.children) {
  52. addSortNode(c);
  53. }
  54. };
  55. this.nodes = [];
  56. for (const n of this.children) {
  57. addSortNode(n);
  58. }
  59. }
  60. afterLoad(fun) {
  61. for (const d of this.datas) {
  62. fun && fun(d);
  63. d.is_leaf = d.children.length === 0;
  64. d.expanded = true;
  65. d.visible = true;
  66. this.items[itemsPre + d[this.setting.id]] = d;
  67. }
  68. this.generateSortNodes();
  69. }
  70. resortChildrenByCustom(fun) {
  71. for (const n of this.nodes) {
  72. if (n.children && n.children.length > 1) {
  73. n.children.sort(fun);
  74. n.children.forEach((x, y) => { x.order = y + 1; });
  75. }
  76. }
  77. this.generateSortNodes();
  78. }
  79. }
  80. class BudgetFinal {
  81. constructor (ctx) {
  82. this.ctx = ctx;
  83. this.budgetSetting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
  84. this.tenderSetting = { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price', 'end_gather_tp'] };
  85. this.finalTree = new FinalTree(this.ctx, { id: 'id', pid: 'pid', order: 'order', level: 'level', fullPath: 'full_path', rootId: -1 });
  86. }
  87. async _loadGu(budget) {
  88. const helper = this.ctx.helper;
  89. const gu = await this.ctx.service.budgetGu.getData(budget.id);
  90. const guTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
  91. guTree.loadDatas(gu);
  92. guTree.calculateAll();
  93. this.finalTree.loadTree(guTree, function (cur, source) {
  94. cur.base = true;
  95. cur.gu_dgn_qty1 = helper.add(cur.gu_dgn_qty1, source.dgn_qty1);
  96. cur.gu_dgn_qty2 = helper.add(cur.gu_dgn_qty2, source.dgn_qty2);
  97. cur.gu_tp = helper.add(cur.gu_tp, source.total_price);
  98. });
  99. }
  100. async _loadGai(budget) {
  101. const helper = this.ctx.helper;
  102. const gai = await this.ctx.service.budgetGai.getData(budget.id);
  103. const gaiTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
  104. gaiTree.loadDatas(gai);
  105. gaiTree.calculateAll();
  106. this.finalTree.loadTree(gaiTree, function (cur, source) {
  107. cur.base = true;
  108. cur.gai_dgn_qty1 = helper.add(cur.gai_dgn_qty1, source.dgn_qty1);
  109. cur.gai_dgn_qty2 = helper.add(cur.gai_dgn_qty2, source.dgn_qty2);
  110. cur.gai_tp = helper.add(cur.gai_tp, source.total_price);
  111. });
  112. }
  113. async _loadYu(budget) {
  114. const helper = this.ctx.helper;
  115. const yu = await this.ctx.service.budgetYu.getData(budget.id);
  116. const yuTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
  117. yuTree.loadDatas(yu);
  118. yuTree.calculateAll();
  119. this.finalTree.loadTree(yuTree, function (cur, source) {
  120. cur.base = true;
  121. cur.yu_dgn_qty1 = helper.add(cur.yu_dgn_qty1, source.dgn_qty1);
  122. cur.yu_dgn_qty2 = helper.add(cur.yu_dgn_qty2, source.dgn_qty2);
  123. cur.yu_tp = helper.add(cur.yu_tp, source.total_price);
  124. });
  125. }
  126. async _loadTender(id) {
  127. const helper = this.ctx.helper;
  128. const bills = await this.ctx.service.ledger.getFinalData(id, ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
  129. 'code', 'b_code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'total_price']);
  130. const dgnData = await this.ctx.service.stageBillsDgn.getDgnData(id);
  131. // 使用最新一期对比
  132. const stage = await this.ctx.service.stage.getLastestStage(id);
  133. if (!stage) {
  134. helper.assignRelaData(bills, [
  135. { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
  136. ]);
  137. this.final.tender_info.push({ id, stageCount: 0 });
  138. } else if (stage.status === auditConst.stage.status.checked) {
  139. const finalBills = await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.order);
  140. helper.assignRelaData(bills, [
  141. { data: finalBills, fields: ['contract_tp', 'qc_tp'], prefix: 'end_', relaId: 'lid' },
  142. { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
  143. ]);
  144. bills.forEach(b => {
  145. b.end_gather_tp = helper.add(b.end_qc_tp, b.end_contract_tp);
  146. });
  147. this.final.tender_info.push({ id, stageOrder: stage.order });
  148. } else {
  149. await this.ctx.service.stage.doCheckStage(stage);
  150. const curBills = stage.readOnly
  151. ? await this.ctx.service.stageBills.getAuditorStageData2(id, stage.id, stage.curTimes, stage.curOrder)
  152. : await this.ctx.service.stageBills.getLastestStageData2(id, stage.id);
  153. const preBills = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.order - 1) : [];
  154. const bpcData = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
  155. helper.assignRelaData(bills, [
  156. { data: curBills, fields: ['contract_tp', 'qc_tp'], prefix: '', relaId: 'lid' },
  157. { data: preBills, fields: ['contract_tp', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
  158. { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
  159. { data: bpcData, fields: ['pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
  160. ]);
  161. bills.forEach(b => {
  162. b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp, b.pc_tp]);
  163. });
  164. this.final.tender_info.push({ id, stageOrder: stage.order, stageStatus: stage.status, stageFlow: stage.curTimes + '-' + stage.curOrder });
  165. }
  166. const tree = new BillsTree(this.ctx, this.tenderSetting);
  167. tree.loadDatas(bills);
  168. tree.calculateAll();
  169. this.finalTree.loadTree(tree, function (cur, source) {
  170. cur.total_price = helper.add(cur.total_price, source.total_price);
  171. cur.dgn_qty1 = helper.add(cur.dgn_qty1, source.dgn_qty1);
  172. cur.dgn_qty2 = helper.add(cur.dgn_qty2, source.dgn_qty2);
  173. cur.final_dgn_qty1 = helper.sum([cur.final_dgn_qty1, source.deal_dgn_qty1, source.c_dgn_qty1]);
  174. cur.final_dgn_qty2 = helper.sum([cur.final_dgn_qty2, source.deal_dgn_qty2, source.c_dgn_qty2]);
  175. cur.final_tp = helper.add(cur.final_tp, source.end_gather_tp);
  176. });
  177. }
  178. async _afterLoad() {
  179. const helper = this.ctx.helper;
  180. this.finalTree.afterLoad(node => {
  181. node.dgn_price = helper.div(node.total_price, node.dgn_qty1, 2);
  182. node.dgn_qty = node.dgn_qty1
  183. ? (node.dgn_qty2 ? node.dgn_qty1 + '/' + node.dgn_qty2 : node.dgn_qty1)
  184. : (node.dgn_qty2 ? '/' + node.dgn_qty2 : '');
  185. node.gu_dgn_price = helper.div(node.gu_tp, node.gu_dgn_qty1, 2);
  186. node.gu_dgn_qty = node.gu_dgn_qty1
  187. ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1 + '')
  188. : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
  189. node.gai_dgn_price = helper.div(node.gai_tp, node.gai_dgn_qty1, 2);
  190. node.gai_dgn_qty = node.gai_dgn_qty1
  191. ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1 + '')
  192. : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
  193. node.yu_dgn_price = helper.div(node.yu_tp, node.yu_dgn_qty1, 2);
  194. node.yu_dgn_qty = node.yu_dgn_qty1
  195. ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1 + '')
  196. : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
  197. node.final_dgn_price = helper.div(node.final_tp, node.final_dgn_qty1, 2);
  198. node.final_dgn_qty = node.final_dgn_qty1
  199. ? (node.final_dgn_qty2 ? node.final_dgn_qty1 + '/' + node.final_dgn_qty2 : node.final_dgn_qty1)
  200. : (node.final_dgn_qty2 ? '/' + node.final_dgn_qty2 : '');
  201. node.grow_dgn_qty1 = helper.mul(helper.div(helper.sub(node.final_dgn_qty1, node.gai_dgn_qty1), node.gai_dgn_qty1, 4), 100);
  202. node.grow_dgn_qty2 = helper.mul(helper.div(helper.sub(node.final_dgn_qty2, node.gai_dgn_qty2), node.gai_dgn_qty2, 4), 100);
  203. node.grow_dgn_qty = node.grow_dgn_qty1
  204. ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
  205. : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
  206. node.grow_tp = helper.mul(helper.div(helper.sub(node.final_tp, node.gai_tp), node.gai_tp, 4), 100);
  207. });
  208. this.finalTree.resortChildrenByCustom(function (x, y) {
  209. const iCode = helper.compareCode(x.code, y.code);
  210. if (iCode) return iCode;
  211. if (!x.name) return -1;
  212. if (!y.name) return 1;
  213. return x.name.localeCompare(y.name);
  214. });
  215. }
  216. getFinalData() {
  217. const data = [], ctx = this.ctx, bid = this.budget.id, final_id = this.final.id;
  218. this.finalTree.datas.forEach(x => {
  219. data.push({
  220. id: ctx.app.uuid.v4(), bid, final_id,
  221. tree_id: x.id, tree_pid: x.pid, order: x.order, level: x.level, full_path: x.full_path, is_leaf: x.is_leaf,
  222. code: x.code, name: x.name, unit: x.unit,
  223. gu_dgn_qty1: x.gu_dgn_qty1 || 0, gu_dgn_qty2: x.gu_dgn_qty2 || 0, gu_dgn_qty: x.gu_dgn_qty, gu_dgn_price: x.gu_dgn_price || 0, gu_tp: x.gu_tp || 0,
  224. gai_dgn_qty1: x.gai_dgn_qty1 || 0, gai_dgn_qty2: x.gai_dgn_qty2 || 0, gai_dgn_qty: x.gai_dgn_qty, gai_dgn_price: x.gai_dgn_price || 0, gai_tp: x.gai_tp || 0,
  225. yu_dgn_qty1: x.yu_dgn_qty1 || 0, yu_dgn_qty2: x.yu_dgn_qty2 || 0, yu_dgn_qty: x.yu_dgn_qty, yu_dgn_price: x.yu_dgn_price || 0, yu_tp: x.yu_tp || 0,
  226. dgn_qty1: x.dgn_qty1 || 0, dgn_qty2: x.dgn_qty2 || 0, total_price: x.total_price || 0,
  227. final_dgn_qty1: x.final_dgn_qty1 || 0, final_dgn_qty2: x.final_dgn_qty2 || 0, final_tp: x.final_tp || 0,
  228. dgn_price: x.dgn_price || 0, dgn_qty: x.dgn_qty,
  229. final_dgn_price: x.final_dgn_price || 0, final_dgn_qty: x.final_dgn_qty,
  230. grow_dgn_qty1: x.grow_dgn_qty1 || 0, grow_dgn_qty2: x.grow_dgn_qty2 || 0, grow_dgn_qty: x.grow_dgn_qty, grow_tp: x.grow_tp || 0,
  231. })
  232. });
  233. return data;
  234. }
  235. async doFinal(budget, final) {
  236. this.budget = budget;
  237. this.final = final;
  238. await this._loadGai(budget);
  239. await this._loadGu(budget);
  240. await this._loadYu(budget);
  241. for (const t of final.tender) {
  242. await this._loadTender(t);
  243. }
  244. this._afterLoad();
  245. return this.getFinalData();
  246. }
  247. }
  248. module.exports = BudgetFinal;