rptCustomData.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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], // 结果按序 t_n_qty, t_n_tp
  20. * "match": { "quality": [2, 3], "qty": "<0" }, // class根据变更类型过滤,qty根据数量过滤
  21. * "selectCompare": [{ "key": "jl", "title": "驻地监理" }, ...] // 结果按key t_key_qty, t_key_tp
  22. * "merge": true,
  23. * }
  24. * defaultCompare为默认选择的审批人,0为原报,1-N为1-N审
  25. * match为保留的类型
  26. * 如需要用户选择审批人,则应配置selectCompare(目前不可用,不可配置,如需使用,则需要额外界面),为后期可能的变动预留
  27. *
  28. */
  29. class jhHelper {
  30. constructor (ctx) {
  31. this.ctx = ctx;
  32. this.result = [];
  33. }
  34. async _getValidStages(tenderId) {
  35. const stages = await this.ctx.service.stage.db.select(this.ctx.service.stage.tableName, {
  36. where: { tid: tenderId },
  37. orders: [['order', 'desc']],
  38. });
  39. if (stages.length !== 0) {
  40. const lastStage = stages[0];
  41. if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
  42. stages.splice(0, 1);
  43. }
  44. }
  45. return stages;
  46. }
  47. async _getCheckedStages(tenderId) {
  48. const stages = await this.db.select(this.ctx.service.stage.tableName, {
  49. where: { tid: tenderId },
  50. orders: [['order', 'desc']],
  51. });
  52. if (stages.length !== 0) {
  53. const lastStage = stages[0];
  54. if (lastStage.status !== auditConst.stage.status.checked) {
  55. stages.splice(0, 1);
  56. }
  57. }
  58. return stages;
  59. }
  60. /**
  61. * 查询本期所有变更明细
  62. * @param tid
  63. * @param sid
  64. * @returns {Promise<void>}
  65. */
  66. async getCurChangeDetailData(tid, sid) {
  67. 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' +
  68. ' Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
  69. ' WHERE sc.tid = ? and sc.sid = ?';
  70. return await this.ctx.service.stageChange.db.query(sql, [tid, sid]);
  71. }
  72. async getPreChangeDetailData(tid, sOrder) {
  73. 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' +
  74. ' Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
  75. ' Left Join ' + this.ctx.service.stage.tableName + ' s ON sc.sid = s.id' +
  76. ' WHERE sc.tid = ? and s.order < ?';
  77. return await this.ctx.service.stageChangeFinal.db.query(sql, [tid, sOrder]);
  78. }
  79. getLastestAuditors(auditors) {
  80. const index = {};
  81. for (const auditor of auditors) {
  82. if (!index[auditor.aid] || auditor.order > index[auditor.aid].order) index[auditor.aid] = auditor;
  83. }
  84. const result = [];
  85. for (const i in index) {
  86. result.push(index[i]);
  87. }
  88. result.sort((x, y) => { return x.order - y.order; });
  89. return result;
  90. }
  91. _loadChangeDetail(billsIndex, changeDetail, gsDefine, prefix) {
  92. for (const cd of changeDetail) {
  93. if (!cd.qty) continue;
  94. let match = false;
  95. for (const m of gsDefine.match) {
  96. if (m.quality === cd.c_quality && ((m.minus && cd.qty < 0) || (!m.minus && cd.qty > 0))) match = true;
  97. }
  98. if (!match) continue;
  99. const bills = billsIndex[cd.lid];
  100. if (!bills) continue;
  101. if (!bills[prefix + 'cd']) bills[prefix + 'cd'] = [];
  102. bills[prefix + 'cd'].push(cd);
  103. if (cd.pid) {
  104. const pos = bills.pos.find(x => {return x.id === cd.pid});
  105. if (pos) {
  106. if (!pos[prefix + 'cd']) pos[prefix + 'cd'] = [];
  107. pos[prefix + 'cd'].push(cd);
  108. }
  109. }
  110. }
  111. }
  112. _loadMergeResult(bills, prefixes) {
  113. const rst = {
  114. id: bills.id,
  115. tid: bills.tender_id,
  116. b_code: bills.b_code,
  117. name: bills.name,
  118. unit: bills.unit,
  119. unit_price: bills.unit_price
  120. };
  121. if (bills.pos && bills.pos.length > 0) {
  122. for (const p of bills.pos) {
  123. let gather = false;
  124. if (p.pre_cd && p.pre_cd.length > 0) gather = true;
  125. for (const prefix of prefixes) {
  126. if (p[prefix + '_cd'] && p[prefix + '_cd'].length > 0) gather = true;
  127. }
  128. if (gather) {
  129. rst.qc_qty = this.ctx.helper.add(rst.qc_qty, p.qc_qty);
  130. rst.qc_tp = this.ctx.helper.add(rst.qc_qty, p.qc_tp);
  131. rst.pre_qc_qty = this.ctx.helper.add(rst.pre_qc_qty, p.pre_qc_qty);
  132. rst.pre_qc_tp = this.ctx.helper.add(rst.pre_qc_tp, p.pre_qc_tp);
  133. rst.end_qc_qty = this.ctx.helper.add(rst.end_qc_qty, p.end_qc_qty);
  134. rst.end_qc_tp = this.ctx.helper.add(rst.end_qc_tp, p.end_qc_tp);
  135. for (const prefix of prefixes) {
  136. rst[prefix + 'qc_qty'] = this.ctx.helper.add(rst[prefix + 'qc_qty'], p[prefix + 'qc_qty']);
  137. }
  138. }
  139. }
  140. for (const prefix of prefixes) {
  141. rst[prefix + 'qc_tp'] = this.ctx.helper.mul(rst.unit_price, rst[prefix + 'qc_qty'], 2);
  142. }
  143. } else {
  144. rst.qc_qty = bills.qc_qty;
  145. rst.qc_tp = bills.qc_tp;
  146. rst.pre_qc_qty = bills.pre_qc_qty;
  147. rst.pre_qc_tp = bills.pre_qc_tp;
  148. rst.end_qc_qty = bills.end_qc_qty;
  149. rst.end_qc_tp = bills.end_qc_tp;
  150. for (const prefix of prefixes) {
  151. rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
  152. rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
  153. }
  154. }
  155. this.result.push(rst);
  156. }
  157. _loadResult(bills, prefixes) {
  158. if (bills.pos) {
  159. for (const p of bills.pos) {
  160. let load = false;
  161. if (p.pre_cd && p.pre_cd.length > 0) load = true;
  162. for (const prefix of prefixes) {
  163. if (p[prefix + '_cd'] && p[prefix + '_cd'].length > 0) load = true;
  164. }
  165. if (!load) continue;
  166. const rst = {
  167. b_code: bills.b_code,
  168. name: bills.name,
  169. unit: bills.unit,
  170. unit_price: bills.unit_price,
  171. };
  172. rst.qc_qty = p.qc_qty;
  173. rst.qc_tp = p.qc_tp;
  174. rst.pre_qc_qty = p.pre_qc_qty;
  175. rst.pre_qc_tp = p.pre_qc_tp;
  176. rst.end_qc_qty = p.end_qc_qty;
  177. rst.end_qc_tp = p.end_qc_tp;
  178. for (const prefix of prefixes) {
  179. rst[prefix + 'qc_qty'] = p[prefix + 'qc_qty'];
  180. rst[prefix + 'qc_tp'] = p[prefix + 'qc_tp'];
  181. }
  182. this.result.push(rst);
  183. }
  184. } else {
  185. const rst = {
  186. b_code: bills.b_code,
  187. name: bills.name,
  188. unit: bills.unit,
  189. unit_price: bills.unit_price,
  190. };
  191. rst.qc_qty = bills.qc_qty;
  192. rst.qc_tp = bills.qc_tp;
  193. rst.pre_qc_qty = bills.pre_qc_qty;
  194. rst.pre_qc_tp = bills.pre_qc_tp;
  195. rst.end_qc_qty = bills.end_qc_qty;
  196. rst.end_qc_tp = bills.end_qc_tp;
  197. for (const prefix of prefixes) {
  198. rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
  199. rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
  200. }
  201. this.result.push(rst);
  202. }
  203. }
  204. _generateResult(billsData, gsDefine) {
  205. for (const bills of billsData) {
  206. let load = false;
  207. if (bills.pre_cd && bills.pre_cd.length > 0) load = true;
  208. for (const dc of gsDefine.defaultCompare) {
  209. if (bills['t_' + dc + '_cd'] && bills['t_' + dc + '_cd'].length > 0) load = true;
  210. }
  211. if (!load) continue;
  212. gsDefine.merge ? this._loadMergeResult(bills, this.prefixes) : this._loadResult(bills, this.prefixes);
  213. }
  214. }
  215. async _loadStageBillsData(tender, stage, gsDefine, auditors) {
  216. const helper = this.ctx.helper;
  217. // 加载截止上期/本期
  218. let billsData = await this.ctx.service.ledger.getData(tender.id);
  219. billsData = billsData.filter(x => { return x.b_code && x.is_leaf });
  220. const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
  221. const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
  222. const loadData = [
  223. { data: curStage, fields: ['qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
  224. { data: preStage, fields: ['qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' }
  225. ];
  226. for (const dc of gsDefine.defaultCompare) {
  227. const auditor = auditors[dc];
  228. const auditorStage = await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
  229. loadData.push({ data: auditorStage, fields: ['qc_qty', 'qc_tp'], prefix: `t_${dc}_`, relaId: 'pid' });
  230. }
  231. helper.assignRelaData(billsData, loadData);
  232. // 计算截止本期
  233. billsData.forEach(x => {
  234. x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
  235. x.end_qc_tp = helper.add(x.qc_tp, x.pre_qc_tp);
  236. });
  237. return billsData;
  238. }
  239. async _loadStagePosData(tender, stage, gsDefine, auditors) {
  240. const helper = this.ctx.helper;
  241. const posData = await this.ctx.service.pos.getPosData({tid: tender.id});
  242. const curStage = await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
  243. const preStage = stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(tender, stage.order - 1) : [];
  244. const loadData = [
  245. { data: curStage, fields: ['qc_qty'], prefix: '', relaId: 'pid' },
  246. { data: preStage, fields: ['qc_qty'], prefix: 'pre_', relaId: 'pid' }
  247. ];
  248. for (const dc of gsDefine.defaultCompare) {
  249. const auditor = auditors[dc];
  250. const auditorStage = await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
  251. loadData.push({ data: auditorStage, fields: ['qc_qty'], prefix: `t_${dc}_`, relaId: 'pid' });
  252. }
  253. helper.assignRelaData(posData, loadData);
  254. posData.forEach(x => {
  255. x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
  256. });
  257. return posData;
  258. }
  259. async _gatherStageData(tender, stage, gsDefine) {
  260. if (!stage) return;
  261. const helper = this.ctx.helper;
  262. await this.ctx.service.stage.doCheckStage(stage);
  263. const auditors = this.getLastestAuditors(stage.auditors);
  264. const billsData = await this._loadStageBillsData(tender, stage, gsDefine, auditors);
  265. const posData = await this._loadStagePosData(tender, stage, gsDefine, auditors);
  266. // 创建索引
  267. const billsIndex = {};
  268. for (const b of billsData) {
  269. billsIndex[b.id] = b;
  270. b.pos = posData.filter(x => { return x.lid === b.id; });
  271. b.pos.forEach(x => {
  272. x.qc_tp = helper.mul(b.unit_price, x.qc_qty, 2);
  273. x.pre_qc_tp = helper.mul(b.unit_price, x.pre_qc_qty, 2);
  274. x.end_qc_tp = helper.add(x.qc_tp, x.pre_qc_tp);
  275. })
  276. }
  277. // 查询比较人数据
  278. this.prefixes = [];
  279. const stageChangeDetail = await this.getCurChangeDetailData(tender.id, stage.id);
  280. for (const dc of gsDefine.defaultCompare) {
  281. const scd = helper.filterTimesOrderData(stageChangeDetail, ['lid', 'pid', 'cid', 'cbid'], auditors[dc].times, auditors[dc].order);
  282. this._loadChangeDetail(billsIndex, scd, gsDefine, `t_${dc}_`);
  283. this.prefixes.push(`t_${dc}_`);
  284. }
  285. const finalChangeData = await this.getPreChangeDetailData(tender.id, stage.order);
  286. this._loadChangeDetail(billsIndex, finalChangeData, gsDefine, 'pre_');
  287. this._generateResult(billsData, gsDefine);
  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, setting) {
  343. if (!setting || !setting.defaultCompare) return [];
  344. const tender = await this.ctx.service.tender.getCheckTender(tid);
  345. const stage = await this.ctx.service.stage.getDataById(sid);
  346. await this._gatherStageData(tender, stage, { defaultCompare: setting.defaultCompare });
  347. }
  348. }
  349. module.exports = {
  350. jhHelper,
  351. };