rptCustomData.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. 'use strict';
  2. /**
  3. * 定制报表 注意不做任何混用,也不做任何继承
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. const auditConst = require('../const/audit');
  10. /**
  11. * 季华项目 定制报表
  12. * 汇总表,流水汇总2个标段(N个),汇总到期,每期汇总3个人的数据(3个),工程量清单流水表,并根据截至本期变更令使用情况筛选
  13. *
  14. * 借用 通用汇总标段选的界面
  15. * 故配置可与汇总标段选择相同
  16. *
  17. * define: {
  18. * "title": "请选择汇总的标段", "type": "month/final/checked-final/stage",
  19. * "defaultCompare": [1, 2, 3], // 结果按序 rn_qty, rn_tp
  20. * "match": { "quality": [2, 3], "qty": "<0" }, // class根据变更类型过滤,qty根据数量过滤
  21. * "merge": true,
  22. * }
  23. * defaultCompare为默认选择的审批人,0为原报,1-N为1-N审
  24. * match为保留的类型
  25. * 如需要用户选择审批人,则应配置selectCompare(目前不可用,不可配置,如需使用,则需要额外界面),为后期可能的变动预留
  26. *
  27. */
  28. class jhHelper {
  29. constructor (ctx) {
  30. this.ctx = ctx;
  31. this.result = [];
  32. }
  33. async _getValidStages(tenderId) {
  34. const stages = await this.ctx.service.stage.db.select(this.ctx.service.stage.tableName, {
  35. where: { tid: tenderId },
  36. orders: [['order', 'desc']],
  37. });
  38. if (stages.length !== 0) {
  39. const lastStage = stages[0];
  40. if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
  41. stages.splice(0, 1);
  42. }
  43. }
  44. return stages;
  45. }
  46. async _getCheckedStages(tenderId) {
  47. const stages = await this.db.select(this.ctx.service.stage.tableName, {
  48. where: { tid: tenderId },
  49. orders: [['order', 'desc']],
  50. });
  51. if (stages.length !== 0) {
  52. const lastStage = stages[0];
  53. if (lastStage.status !== auditConst.stage.status.checked) {
  54. stages.splice(0, 1);
  55. }
  56. }
  57. return stages;
  58. }
  59. /**
  60. * 查询本期所有变更明细
  61. * @param tid
  62. * @param sid
  63. * @returns {Promise<void>}
  64. */
  65. async getCurChangeDetailData(tid, sid) {
  66. const sql = 'SELECT sc.*, c.type As c_type, c.class As c_class, c.quality As c_quality FROM ' + this.ctx.service.stageChange.tableName + ' sc' +
  67. ' Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
  68. ' WHERE sc.tid = ? and sc.sid = ?';
  69. return await this.ctx.service.stageChange.db.query(sql, [tid, sid]);
  70. }
  71. async getPreChangeDetailData(tid, sOrder) {
  72. const sql = 'SELECT sc.*, c.type As c_type, c.class As c_class, c.quality As c_quality FROM ' + this.ctx.service.stageChangeFinal.tableName + ' sc' +
  73. ' Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
  74. ' Left Join ' + this.ctx.service.stage.tableName + ' s ON sc.sid = s.id' +
  75. ' WHERE sc.tid = ? and s.order < ?';
  76. return await this.ctx.service.stageChangeFinal.db.query(sql, [tid, sOrder]);
  77. }
  78. getLastestAuditors(auditors) {
  79. const index = {};
  80. for (const auditor of auditors) {
  81. if (!index[auditor.aid] || auditor.order > index[auditor.aid].order) index[auditor.aid] = auditor;
  82. }
  83. const result = [];
  84. for (const i in index) {
  85. result.push(index[i]);
  86. }
  87. result.sort((x, y) => { return x.order - y.order; });
  88. return result;
  89. }
  90. _loadChangeDetail(billsIndex, changeDetail, gsDefine, prefix) {
  91. for (const cd of changeDetail) {
  92. if (!cd.qty) continue;
  93. let match = false;
  94. for (const m of gsDefine.match) {
  95. if (m.quality === cd.c_quality && ((m.minus && cd.qty < 0) || (!m.minus && cd.qty > 0))) match = true;
  96. }
  97. if (!match) continue;
  98. const bills = billsIndex[cd.lid];
  99. if (!bills) continue;
  100. if (!bills[prefix + 'cd']) bills[prefix + 'cd'] = [];
  101. bills[prefix + 'cd'].push(cd);
  102. if (cd.pid) {
  103. const pos = bills.pos.find(x => {return x.id === cd.pid});
  104. if (pos) {
  105. if (!pos[prefix + 'cd']) pos[prefix + 'cd'] = [];
  106. pos[prefix + 'cd'].push(cd);
  107. }
  108. }
  109. }
  110. }
  111. _loadMergeResult(bills, prefixes, decimal) {
  112. const rst = {
  113. id: bills.id,
  114. tid: bills.tender_id,
  115. b_code: bills.b_code,
  116. name: bills.name,
  117. unit: bills.unit,
  118. unit_price: bills.unit_price
  119. };
  120. if (bills.pos && bills.pos.length > 0) {
  121. for (const p of bills.pos) {
  122. let gather = false;
  123. if (p.pre_cd && p.pre_cd.length > 0) gather = true;
  124. for (const prefix of prefixes) {
  125. if (p[prefix + 'cd'] && p[prefix + 'cd'].length > 0) gather = true;
  126. }
  127. if (gather) {
  128. rst.qc_qty = this.ctx.helper.add(rst.qc_qty, p.qc_qty);
  129. rst.pre_qc_qty = this.ctx.helper.add(rst.pre_qc_qty, p.pre_qc_qty);
  130. rst.end_qc_qty = this.ctx.helper.add(rst.end_qc_qty, p.end_qc_qty);
  131. for (const prefix of prefixes) {
  132. rst[prefix + 'qc_qty'] = this.ctx.helper.add(rst[prefix + 'qc_qty'], p[prefix + 'qc_qty']);
  133. }
  134. }
  135. }
  136. rst.qc_tp = this.ctx.helper.mul(rst.unit_price, rst.qc_qty, decimal.tp);
  137. rst.pre_qc_tp = this.ctx.helper.mul(rst.unit_price, rst.pre_qc_qty, decimal.tp);
  138. rst.end_qc_tp = this.ctx.helper.add(rst.pre_qc_tp, rst.qc_tp);
  139. for (const prefix of prefixes) {
  140. rst[prefix + 'qc_tp'] = this.ctx.helper.mul(rst.unit_price, rst[prefix + 'qc_qty'], decimal.tp);
  141. }
  142. } else {
  143. rst.qc_qty = bills.qc_qty;
  144. rst.qc_tp = bills.qc_tp;
  145. rst.pre_qc_qty = bills.pre_qc_qty;
  146. rst.pre_qc_tp = bills.pre_qc_tp;
  147. rst.end_qc_qty = bills.end_qc_qty;
  148. rst.end_qc_tp = bills.end_qc_tp;
  149. for (const prefix of prefixes) {
  150. rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
  151. rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
  152. }
  153. }
  154. this.result.push(rst);
  155. }
  156. _loadResult(bills, prefixes, decimal) {
  157. if (bills.pos) {
  158. for (const p of bills.pos) {
  159. let load = false;
  160. if (p.pre_cd && p.pre_cd.length > 0) load = true;
  161. for (const prefix of prefixes) {
  162. if (p[prefix + 'cd'] && p[prefix + 'cd'].length > 0) load = true;
  163. }
  164. if (!load) continue;
  165. const rst = {
  166. b_code: bills.b_code,
  167. name: bills.name,
  168. unit: bills.unit,
  169. unit_price: bills.unit_price,
  170. };
  171. rst.qc_qty = p.qc_qty;
  172. rst.qc_tp = this.ctx.helper.mul(bills.unit_price, p.qc_qty, decimal.tp);
  173. rst.pre_qc_qty = p.pre_qc_qty;
  174. rst.pre_qc_tp = this.ctx.helper.mul(bills.unit_price, p.pre_qc_qty, decimal.tp);
  175. rst.end_qc_qty = p.end_qc_qty;
  176. rst.end_qc_tp = this.ctx.helper.add(rst.qc_tp, p.pre_qc_tp);
  177. for (const prefix of prefixes) {
  178. rst[prefix + 'qc_qty'] = p[prefix + 'qc_qty'];
  179. rst[prefix + 'qc_tp'] = this.ctx.helper.mul(bills.unit_price, p[prefix + 'qc_qty'], decimal.tp);
  180. }
  181. this.result.push(rst);
  182. }
  183. } else {
  184. const rst = {
  185. b_code: bills.b_code,
  186. name: bills.name,
  187. unit: bills.unit,
  188. unit_price: bills.unit_price,
  189. };
  190. rst.qc_qty = bills.qc_qty;
  191. rst.qc_tp = bills.qc_tp;
  192. rst.pre_qc_qty = bills.pre_qc_qty;
  193. rst.pre_qc_tp = bills.pre_qc_tp;
  194. rst.end_qc_qty = bills.end_qc_qty;
  195. rst.end_qc_tp = bills.end_qc_tp;
  196. for (const prefix of prefixes) {
  197. rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
  198. rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
  199. }
  200. this.result.push(rst);
  201. }
  202. }
  203. _generateResult(billsData, gsDefine, decimal) {
  204. for (const bills of billsData) {
  205. let load = false;
  206. if (bills.pre_cd && bills.pre_cd.length > 0) load = true;
  207. for (const dc of gsDefine.defaultCompare) {
  208. if (bills['r' + dc + '_cd'] && bills['r' + dc + '_cd'].length > 0) load = true;
  209. }
  210. if (!load) continue;
  211. gsDefine.merge ? this._loadMergeResult(bills, this.prefixes, decimal) : this._loadResult(bills, this.prefixes, decimal);
  212. }
  213. }
  214. async _loadStageBillsData(tender, stage, gsDefine, auditors) {
  215. const helper = this.ctx.helper;
  216. // 加载截止上期/本期
  217. let billsData = await this.ctx.service.ledger.getData(tender.id);
  218. billsData = billsData.filter(x => { return x.b_code && x.is_leaf });
  219. const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
  220. const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
  221. const loadData = [
  222. { data: curStage, fields: ['qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
  223. { data: preStage, fields: ['qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' }
  224. ];
  225. for (const dc of gsDefine.defaultCompare) {
  226. const auditor = auditors[dc];
  227. const auditorStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id, stage.id, auditor.times, auditor.order);
  228. loadData.push({ data: auditorStage, fields: ['qc_qty', 'qc_tp'], prefix: `r${dc}_`, relaId: 'lid' });
  229. }
  230. helper.assignRelaData(billsData, loadData);
  231. // 计算截止本期
  232. billsData.forEach(x => {
  233. x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
  234. x.end_qc_tp = helper.add(x.qc_tp, x.pre_qc_tp);
  235. });
  236. return billsData;
  237. }
  238. async _loadStagePosData(tender, stage, gsDefine, auditors) {
  239. const helper = this.ctx.helper;
  240. const posData = await this.ctx.service.pos.getPosData({tid: tender.id});
  241. const curStage = await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
  242. const preStage = stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(tender, stage.order - 1) : [];
  243. const loadData = [
  244. { data: curStage, fields: ['qc_qty'], prefix: '', relaId: 'pid' },
  245. { data: preStage, fields: ['qc_qty'], prefix: 'pre_', relaId: 'pid' }
  246. ];
  247. for (const dc of gsDefine.defaultCompare) {
  248. const auditor = auditors[dc];
  249. const auditorStage = await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
  250. loadData.push({ data: auditorStage, fields: ['qc_qty'], prefix: `r${dc}_`, relaId: 'pid' });
  251. }
  252. helper.assignRelaData(posData, loadData);
  253. posData.forEach(x => {
  254. x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
  255. });
  256. return posData;
  257. }
  258. async _gatherStageData(tender, stage, gsDefine) {
  259. if (!stage) return;
  260. const helper = this.ctx.helper;
  261. await this.ctx.service.stage.doCheckStage(stage);
  262. const auditors = this.getLastestAuditors(stage.auditors);
  263. const user = await this.ctx.service.projectAccount.getDataById(stage.user_id);
  264. auditors.unshift({
  265. aid: user.id, name: user.name, company: user.company, role: user.role,
  266. times: stage.curTimes, order: 0
  267. });
  268. const billsData = await this._loadStageBillsData(tender, stage, gsDefine, auditors);
  269. const posData = await this._loadStagePosData(tender, stage, gsDefine, auditors);
  270. // 创建索引
  271. const billsIndex = {};
  272. for (const b of billsData) {
  273. billsIndex[b.id] = b;
  274. b.pos = posData.filter(x => { return x.lid === b.id; });
  275. }
  276. // 查询比较人数据
  277. this.prefixes = [];
  278. const stageChangeDetail = await this.getCurChangeDetailData(tender.id, stage.id);
  279. this.ctx.helper.saveBufferFile(JSON.stringify(stageChangeDetail, '', '\t'), this.ctx.app.baseDir + '/temp.json');
  280. for (const dc of gsDefine.defaultCompare) {
  281. const scd = helper.filterTimesOrderData(stageChangeDetail, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder', auditors[dc].times, auditors[dc].order);
  282. this._loadChangeDetail(billsIndex, scd, gsDefine, `r${dc}_`);
  283. this.prefixes.push(`r${dc}_`);
  284. }
  285. const finalChangeData = await this.getPreChangeDetailData(tender.id, stage.order);
  286. this._loadChangeDetail(billsIndex, finalChangeData, gsDefine, 'pre_');
  287. this._generateResult(billsData, gsDefine, tender.info.decimal);
  288. }
  289. async _gatherMonthData(tender, month, defaultCompare) {
  290. const stages = await this._getValidStages(tender.id);
  291. const stage = this.ctx.helper._.find(stages, {s_time: month});
  292. await this._gatherStageData(tender, stage, defaultCompare);
  293. }
  294. async _gatherFinalData(tender, defaultCompare) {
  295. const stages = await this._getValidStages(tender.id);
  296. await this._gatherStageData(tender, stages[0], defaultCompare);
  297. }
  298. async _gatherCheckedFinalData(tender, defaultCompare) {
  299. const stages = await this._getCheckedStages(tender.id);
  300. await this._gatherStageData(tender, stages[0], defaultCompare);
  301. }
  302. async _gatherIndexData(tender, index, defaultCompare) {
  303. const stages = await this._getValidStages(tender.id);
  304. const stage = this.ctx.helper._.find(stages, {order: index});
  305. await this._gatherStageData(tender, stage, defaultCompare);
  306. }
  307. /**
  308. *
  309. * @param {Array} memFieldKeys 报表添加的指标字段
  310. * @param {object} gsDefine
  311. * @param {object} gsCustom
  312. * @returns {Promise<Array>}
  313. */
  314. async gather(memFieldKeys, gsDefine, gsCustom) {
  315. if (!gsDefine || !gsDefine.enable) return [];
  316. if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return [];
  317. const gsSetting = JSON.parse(gsDefine.setting);
  318. if (!gsSetting.defaultCompare || !gsSetting.match) return[];
  319. for (const t of gsCustom.tenders) {
  320. const tender = await this.ctx.service.tender.getCheckTender(t.tid);
  321. switch (gsSetting.type) {
  322. case 'month':
  323. await this._gatherMonthData(tender, gsCustom.month, gsSetting);
  324. break;
  325. case 'final':
  326. await this._gatherFinalData(tender, gsSetting);
  327. break;
  328. case 'checked-final':
  329. await this._gatherCheckedFinalData(tender, gsSetting);
  330. break;
  331. case 'stage':
  332. await this._gatherIndexData(tender, gsCustom.stage, gsSetting);
  333. break;
  334. default: throw '未知汇总类型';
  335. }
  336. }
  337. const helper = this.ctx.helper;
  338. // 排序
  339. this.result.sort((x, y) => { return helper.compareCode(x.b_code, y.b_code); });
  340. return this.result;
  341. }
  342. async convert(tid, sid, memFieldKeys, option) {
  343. if (!option) return [];
  344. const setting = JSON.parse(option);
  345. if (!setting || !setting.defaultCompare) return [];
  346. const tender = await this.ctx.service.tender.getCheckTender(tid);
  347. const stage = await this.ctx.service.stage.getDataById(sid);
  348. await this._gatherStageData(tender, stage, setting);
  349. const helper = this.ctx.helper;
  350. // 排序
  351. this.result.sort((x, y) => { return helper.compareCode(x.b_code, y.b_code); });
  352. return this.result;
  353. }
  354. }
  355. module.exports = {
  356. jhHelper,
  357. };