|
@@ -0,0 +1,381 @@
|
|
|
+'use strict';
|
|
|
+
|
|
|
+/**
|
|
|
+ * 定制报表 注意不做任何混用,也不做任何继承
|
|
|
+ *
|
|
|
+ * @author Mai
|
|
|
+ * @date
|
|
|
+ * @version
|
|
|
+ */
|
|
|
+const auditConst = require('../const/audit');
|
|
|
+
|
|
|
+/**
|
|
|
+ * 季华项目 定制报表
|
|
|
+ * 汇总表,流水汇总2个标段(N个),汇总到期,每期汇总3个人的数据(3个),工程量清单流水表,并根据截至本期变更令使用情况筛选
|
|
|
+ *
|
|
|
+ * 借用 通用汇总标段选的界面
|
|
|
+ * 故配置可与汇总标段选择相同
|
|
|
+ *
|
|
|
+ * define: {
|
|
|
+ * "title": "请选择汇总的标段", "type": "month/final/checked-final/stage",
|
|
|
+ * "defaultCompare": [1, 2, 3], // 结果按序 t_n_qty, t_n_tp
|
|
|
+ * "match": { "quality": [2, 3], "qty": "<0" }, // class根据变更类型过滤,qty根据数量过滤
|
|
|
+ * "selectCompare": [{ "key": "jl", "title": "驻地监理" }, ...] // 结果按key t_key_qty, t_key_tp
|
|
|
+ * "merge": true,
|
|
|
+ * }
|
|
|
+ * defaultCompare为默认选择的审批人,0为原报,1-N为1-N审
|
|
|
+ * match为保留的类型
|
|
|
+ * 如需要用户选择审批人,则应配置selectCompare(目前不可用,不可配置,如需使用,则需要额外界面),为后期可能的变动预留
|
|
|
+ *
|
|
|
+ */
|
|
|
+class jhHelper {
|
|
|
+
|
|
|
+ constructor (ctx) {
|
|
|
+ this.ctx = ctx;
|
|
|
+ this.result = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ async _getValidStages(tenderId) {
|
|
|
+ const stages = await this.ctx.service.stage.db.select(this.ctx.service.stage.tableName, {
|
|
|
+ where: { tid: tenderId },
|
|
|
+ orders: [['order', 'desc']],
|
|
|
+ });
|
|
|
+ if (stages.length !== 0) {
|
|
|
+ const lastStage = stages[0];
|
|
|
+ if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
|
|
|
+ stages.splice(0, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return stages;
|
|
|
+ }
|
|
|
+
|
|
|
+ async _getCheckedStages(tenderId) {
|
|
|
+ const stages = await this.db.select(this.ctx.service.stage.tableName, {
|
|
|
+ where: { tid: tenderId },
|
|
|
+ orders: [['order', 'desc']],
|
|
|
+ });
|
|
|
+ if (stages.length !== 0) {
|
|
|
+ const lastStage = stages[0];
|
|
|
+ if (lastStage.status !== auditConst.stage.status.checked) {
|
|
|
+ stages.splice(0, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return stages;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询本期所有变更明细
|
|
|
+ * @param tid
|
|
|
+ * @param sid
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ */
|
|
|
+ async getCurChangeDetailData(tid, sid) {
|
|
|
+ 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' +
|
|
|
+ ' Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
|
|
|
+ ' WHERE sc.tid = ? and sc.sid = ?';
|
|
|
+ return await this.ctx.service.stageChange.db.query(sql, [tid, sid]);
|
|
|
+ }
|
|
|
+
|
|
|
+ async getPreChangeDetailData(tid, sOrder) {
|
|
|
+ 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' +
|
|
|
+ ' Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
|
|
|
+ ' Left Join ' + this.ctx.service.stage.tableName + ' s ON sc.sid = s.id' +
|
|
|
+ ' WHERE sc.tid = ? and s.order < ?';
|
|
|
+ return await this.ctx.service.stageChangeFinal.db.query(sql, [tid, sOrder]);
|
|
|
+ }
|
|
|
+
|
|
|
+ getLastestAuditors(auditors) {
|
|
|
+ const index = {};
|
|
|
+ for (const auditor of auditors) {
|
|
|
+ if (!index[auditor.aid] || auditor.order > index[auditor.aid].order) index[auditor.aid] = auditor;
|
|
|
+ }
|
|
|
+ const result = [];
|
|
|
+ for (const i in index) {
|
|
|
+ result.push(index[i]);
|
|
|
+ }
|
|
|
+ result.sort((x, y) => { return x.order - y.order; });
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ _loadChangeDetail(billsIndex, changeDetail, gsDefine, prefix) {
|
|
|
+ for (const cd of changeDetail) {
|
|
|
+ if (!cd.qty) continue;
|
|
|
+
|
|
|
+ let match = false;
|
|
|
+ for (const m of gsDefine.match) {
|
|
|
+ if (m.quality === cd.c_quality && ((m.minus && cd.qty < 0) || (!m.minus && cd.qty > 0))) match = true;
|
|
|
+ }
|
|
|
+ if (!match) continue;
|
|
|
+
|
|
|
+ const bills = billsIndex[cd.lid];
|
|
|
+ if (!bills) continue;
|
|
|
+
|
|
|
+ if (!bills[prefix + 'cd']) bills[prefix + 'cd'] = [];
|
|
|
+ bills[prefix + 'cd'].push(cd);
|
|
|
+ if (cd.pid) {
|
|
|
+ const pos = bills.pos.find(x => {return x.id === cd.pid});
|
|
|
+ if (pos) {
|
|
|
+ if (!pos[prefix + 'cd']) pos[prefix + 'cd'] = [];
|
|
|
+ pos[prefix + 'cd'].push(cd);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _loadMergeResult(bills, prefixes) {
|
|
|
+ const rst = {
|
|
|
+ id: bills.id,
|
|
|
+ tid: bills.tender_id,
|
|
|
+ b_code: bills.b_code,
|
|
|
+ name: bills.name,
|
|
|
+ unit: bills.unit,
|
|
|
+ unit_price: bills.unit_price
|
|
|
+ };
|
|
|
+ if (bills.pos && bills.pos.length > 0) {
|
|
|
+ for (const p of bills.pos) {
|
|
|
+ let gather = false;
|
|
|
+ if (p.pre_cd && p.pre_cd.length > 0) gather = true;
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ if (p[prefix + '_cd'] && p[prefix + '_cd'].length > 0) gather = true;
|
|
|
+ }
|
|
|
+ if (gather) {
|
|
|
+ rst.qc_qty = this.ctx.helper.add(rst.qc_qty, p.qc_qty);
|
|
|
+ rst.qc_tp = this.ctx.helper.add(rst.qc_qty, p.qc_tp);
|
|
|
+ rst.pre_qc_qty = this.ctx.helper.add(rst.pre_qc_qty, p.pre_qc_qty);
|
|
|
+ rst.pre_qc_tp = this.ctx.helper.add(rst.pre_qc_tp, p.pre_qc_tp);
|
|
|
+ rst.end_qc_qty = this.ctx.helper.add(rst.end_qc_qty, p.end_qc_qty);
|
|
|
+ rst.end_qc_tp = this.ctx.helper.add(rst.end_qc_tp, p.end_qc_tp);
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ rst[prefix + 'qc_qty'] = this.ctx.helper.add(rst[prefix + 'qc_qty'], p[prefix + 'qc_qty']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ rst[prefix + 'qc_tp'] = this.ctx.helper.mul(rst.unit_price, rst[prefix + 'qc_qty'], 2);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ rst.qc_qty = bills.qc_qty;
|
|
|
+ rst.qc_tp = bills.qc_tp;
|
|
|
+ rst.pre_qc_qty = bills.pre_qc_qty;
|
|
|
+ rst.pre_qc_tp = bills.pre_qc_tp;
|
|
|
+ rst.end_qc_qty = bills.end_qc_qty;
|
|
|
+ rst.end_qc_tp = bills.end_qc_tp;
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
|
|
|
+ rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.result.push(rst);
|
|
|
+ }
|
|
|
+
|
|
|
+ _loadResult(bills, prefixes) {
|
|
|
+ if (bills.pos) {
|
|
|
+ for (const p of bills.pos) {
|
|
|
+ let load = false;
|
|
|
+ if (p.pre_cd && p.pre_cd.length > 0) load = true;
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ if (p[prefix + '_cd'] && p[prefix + '_cd'].length > 0) load = true;
|
|
|
+ }
|
|
|
+ if (!load) continue;
|
|
|
+
|
|
|
+ const rst = {
|
|
|
+ b_code: bills.b_code,
|
|
|
+ name: bills.name,
|
|
|
+ unit: bills.unit,
|
|
|
+ unit_price: bills.unit_price,
|
|
|
+ };
|
|
|
+ rst.qc_qty = p.qc_qty;
|
|
|
+ rst.qc_tp = p.qc_tp;
|
|
|
+ rst.pre_qc_qty = p.pre_qc_qty;
|
|
|
+ rst.pre_qc_tp = p.pre_qc_tp;
|
|
|
+ rst.end_qc_qty = p.end_qc_qty;
|
|
|
+ rst.end_qc_tp = p.end_qc_tp;
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ rst[prefix + 'qc_qty'] = p[prefix + 'qc_qty'];
|
|
|
+ rst[prefix + 'qc_tp'] = p[prefix + 'qc_tp'];
|
|
|
+ }
|
|
|
+ this.result.push(rst);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const rst = {
|
|
|
+ b_code: bills.b_code,
|
|
|
+ name: bills.name,
|
|
|
+ unit: bills.unit,
|
|
|
+ unit_price: bills.unit_price,
|
|
|
+ };
|
|
|
+ rst.qc_qty = bills.qc_qty;
|
|
|
+ rst.qc_tp = bills.qc_tp;
|
|
|
+ rst.pre_qc_qty = bills.pre_qc_qty;
|
|
|
+ rst.pre_qc_tp = bills.pre_qc_tp;
|
|
|
+ rst.end_qc_qty = bills.end_qc_qty;
|
|
|
+ rst.end_qc_tp = bills.end_qc_tp;
|
|
|
+ for (const prefix of prefixes) {
|
|
|
+ rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
|
|
|
+ rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
|
|
|
+ }
|
|
|
+ this.result.push(rst);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _generateResult(billsData, gsDefine) {
|
|
|
+ for (const bills of billsData) {
|
|
|
+ let load = false;
|
|
|
+ if (bills.pre_cd && bills.pre_cd.length > 0) load = true;
|
|
|
+ for (const dc of gsDefine.defaultCompare) {
|
|
|
+ if (bills['t_' + dc + '_cd'] && bills['t_' + dc + '_cd'].length > 0) load = true;
|
|
|
+ }
|
|
|
+ if (!load) continue;
|
|
|
+ gsDefine.merge ? this._loadMergeResult(bills, this.prefixes) : this._loadResult(bills, this.prefixes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async _loadStageBillsData(tender, stage, gsDefine, auditors) {
|
|
|
+ const helper = this.ctx.helper;
|
|
|
+ // 加载截止上期/本期
|
|
|
+ let billsData = await this.ctx.service.ledger.getData(tender.id);
|
|
|
+ billsData = billsData.filter(x => { return x.b_code && x.is_leaf });
|
|
|
+ const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
|
|
|
+ const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
|
|
|
+ const loadData = [
|
|
|
+ { data: curStage, fields: ['qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
|
|
|
+ { data: preStage, fields: ['qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' }
|
|
|
+ ];
|
|
|
+ for (const dc of gsDefine.defaultCompare) {
|
|
|
+ const auditor = auditors[dc];
|
|
|
+ const auditorStage = await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
|
|
|
+ loadData.push({ data: auditorStage, fields: ['qc_qty', 'qc_tp'], prefix: `t_${dc}_`, relaId: 'pid' });
|
|
|
+ }
|
|
|
+ helper.assignRelaData(billsData, loadData);
|
|
|
+ // 计算截止本期
|
|
|
+ billsData.forEach(x => {
|
|
|
+ x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
|
|
|
+ x.end_qc_tp = helper.add(x.qc_tp, x.pre_qc_tp);
|
|
|
+ });
|
|
|
+ return billsData;
|
|
|
+ }
|
|
|
+
|
|
|
+ async _loadStagePosData(tender, stage, gsDefine, auditors) {
|
|
|
+ const helper = this.ctx.helper;
|
|
|
+ const posData = await this.ctx.service.pos.getPosData({tid: tender.id});
|
|
|
+ const curStage = await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
|
|
|
+ const preStage = stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(tender, stage.order - 1) : [];
|
|
|
+ const loadData = [
|
|
|
+ { data: curStage, fields: ['qc_qty'], prefix: '', relaId: 'pid' },
|
|
|
+ { data: preStage, fields: ['qc_qty'], prefix: 'pre_', relaId: 'pid' }
|
|
|
+ ];
|
|
|
+ for (const dc of gsDefine.defaultCompare) {
|
|
|
+ const auditor = auditors[dc];
|
|
|
+ const auditorStage = await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
|
|
|
+ loadData.push({ data: auditorStage, fields: ['qc_qty'], prefix: `t_${dc}_`, relaId: 'pid' });
|
|
|
+ }
|
|
|
+ helper.assignRelaData(posData, loadData);
|
|
|
+ posData.forEach(x => {
|
|
|
+ x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
|
|
|
+ });
|
|
|
+ return posData;
|
|
|
+ }
|
|
|
+
|
|
|
+ async _gatherStageData(tender, stage, gsDefine) {
|
|
|
+ if (!stage) return;
|
|
|
+ const helper = this.ctx.helper;
|
|
|
+
|
|
|
+ await this.ctx.service.stage.doCheckStage(stage);
|
|
|
+ const auditors = this.getLastestAuditors(stage.auditors);
|
|
|
+ const billsData = await this._loadStageBillsData(tender, stage, gsDefine, auditors);
|
|
|
+ const posData = await this._loadStagePosData(tender, stage, gsDefine, auditors);
|
|
|
+ // 创建索引
|
|
|
+ const billsIndex = {};
|
|
|
+ for (const b of billsData) {
|
|
|
+ billsIndex[b.id] = b;
|
|
|
+ b.pos = posData.filter(x => { return x.lid === b.id; });
|
|
|
+ b.pos.forEach(x => {
|
|
|
+ x.qc_tp = helper.mul(b.unit_price, x.qc_qty, 2);
|
|
|
+ x.pre_qc_tp = helper.mul(b.unit_price, x.pre_qc_qty, 2);
|
|
|
+ x.end_qc_tp = helper.add(x.qc_tp, x.pre_qc_tp);
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询比较人数据
|
|
|
+ this.prefixes = [];
|
|
|
+ const stageChangeDetail = await this.getCurChangeDetailData(tender.id, stage.id);
|
|
|
+ for (const dc of gsDefine.defaultCompare) {
|
|
|
+ const scd = helper.filterTimesOrderData(stageChangeDetail, ['lid', 'pid', 'cid', 'cbid'], auditors[dc].times, auditors[dc].order);
|
|
|
+ this._loadChangeDetail(billsIndex, scd, gsDefine, `t_${dc}_`);
|
|
|
+ this.prefixes.push(`t_${dc}_`);
|
|
|
+ }
|
|
|
+ const finalChangeData = await this.getPreChangeDetailData(tender.id, stage.order);
|
|
|
+ this._loadChangeDetail(billsIndex, finalChangeData, gsDefine, 'pre_');
|
|
|
+ this._generateResult(billsData, gsDefine);
|
|
|
+ }
|
|
|
+
|
|
|
+ async _gatherMonthData(tender, month, defaultCompare) {
|
|
|
+ const stages = await this._getValidStages(tender.id);
|
|
|
+ const stage = this.ctx.helper._.find(stages, {s_time: month});
|
|
|
+ await this._gatherStageData(tender, stage, defaultCompare);
|
|
|
+ }
|
|
|
+
|
|
|
+ async _gatherFinalData(tender, defaultCompare) {
|
|
|
+ const stages = await this._getValidStages(tender.id);
|
|
|
+ await this._gatherStageData(tender, stages[0], defaultCompare);
|
|
|
+ }
|
|
|
+
|
|
|
+ async _gatherCheckedFinalData(tender, defaultCompare) {
|
|
|
+ const stages = await this._getCheckedStages(tender.id);
|
|
|
+ await this._gatherStageData(tender, stages[0], defaultCompare);
|
|
|
+ }
|
|
|
+
|
|
|
+ async _gatherIndexData(tender, index, defaultCompare) {
|
|
|
+ const stages = await this._getValidStages(tender.id);
|
|
|
+ const stage = this.ctx.helper._.find(stages, {order: index});
|
|
|
+ await this._gatherStageData(tender, stage, defaultCompare);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param {Array} memFieldKeys 报表添加的指标字段
|
|
|
+ * @param {object} gsDefine
|
|
|
+ * @param {object} gsCustom
|
|
|
+ * @returns {Promise<Array>}
|
|
|
+ */
|
|
|
+ async gather(memFieldKeys, gsDefine, gsCustom) {
|
|
|
+ if (!gsDefine || !gsDefine.enable) return [];
|
|
|
+ if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return [];
|
|
|
+
|
|
|
+ const gsSetting = JSON.parse(gsDefine.setting);
|
|
|
+ if (!gsSetting.defaultCompare || !gsSetting.match) return[];
|
|
|
+ for (const t of gsCustom.tenders) {
|
|
|
+ const tender = await this.ctx.service.tender.getCheckTender(t.tid);
|
|
|
+
|
|
|
+ switch (gsSetting.type) {
|
|
|
+ case 'month':
|
|
|
+ await this._gatherMonthData(tender, gsCustom.month, gsSetting);
|
|
|
+ break;
|
|
|
+ case 'final':
|
|
|
+ await this._gatherFinalData(tender, gsSetting);
|
|
|
+ break;
|
|
|
+ case 'checked-final':
|
|
|
+ await this._gatherCheckedFinalData(tender, gsSetting);
|
|
|
+ break;
|
|
|
+ case 'stage':
|
|
|
+ await this._gatherIndexData(tender, gsCustom.stage, gsSetting);
|
|
|
+ break;
|
|
|
+ default: throw '未知汇总类型';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const helper = this.ctx.helper;
|
|
|
+ // 排序
|
|
|
+ this.result.sort((x, y) => { return helper.compareCode(x.b_code, y.b_code); });
|
|
|
+ return this.result;
|
|
|
+ }
|
|
|
+
|
|
|
+ async convert(tid, sid, memFieldKeys, setting) {
|
|
|
+ if (!setting || !setting.defaultCompare) return [];
|
|
|
+ const tender = await this.ctx.service.tender.getCheckTender(tid);
|
|
|
+ const stage = await this.ctx.service.stage.getDataById(sid);
|
|
|
+ await this._gatherStageData(tender, stage, { defaultCompare: setting.defaultCompare });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+module.exports = {
|
|
|
+ jhHelper,
|
|
|
+};
|