瀏覽代碼

报表,数据预处理,添加工程量清单汇总2

MaiXinRong 4 年之前
父節點
當前提交
b242db4b65
共有 4 個文件被更改,包括 443 次插入1 次删除
  1. 0 1
      app/lib/bills_pos_convert.js
  2. 339 0
      app/lib/gcl_gather.js
  3. 26 0
      app/lib/rpt_data_analysis.js
  4. 78 0
      builder_report_index_define.js

+ 0 - 1
app/lib/bills_pos_convert.js

@@ -126,7 +126,6 @@ class BillsPosConvert {
                 const posUnit = xmj.unitTree.addNode({pos_name: p.name,
                     b_code: null, name: '', unit: null, unit_price: null,
                     gxby_status: p.gxby_status, dagl_status: p.dagl_status, dagl_url: p.dagl_url});
-                if (p.dagl_status > 0) console.log(posUnit);
                 if (p.drawing_code && posUnit.drawing_code.indexOf(p.drawing_code) < 0)
                     posUnit.drawing_code.push(p.drawing_code);
                 if (p.memo) posUnit.memo.push(p.memo);

+ 339 - 0
app/lib/gcl_gather.js

@@ -0,0 +1,339 @@
+'use strict';
+
+const { x } = require("pdfkit");
+
+/**
+ *
+ * 清单汇总
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const mergeChar = ';';
+
+const gclGatherModel = class {
+
+    /**
+     * 构造函数
+     * 
+     * @param {Object} ctx - egg 全局变量 
+     */
+    constructor(ctx) {
+        this.ctx = ctx;
+        this._ = ctx.helper._;
+        // mainData
+        this.billsTree = new Ledger.billsTree(this.ctx, {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id']
+        });
+        this.pos = new Ledger.pos({
+            id: 'id', ledgerId: 'lid', order: 'order'
+        });
+    }
+
+    /**
+     * 根据node新增工程量清单
+     * 
+     * @param {Object}} node 
+     * @returns {Object}
+     */
+    newGclNode(node) {
+        const gcl = {
+            id: this.gclList.length + 1,
+            b_code: node.b_code,
+            name: node.name,
+            unit: node.unit,
+            unit_price: node.unit_price,
+            leafXmjs: [],
+        };
+        this.gclList.push(gcl);
+        return gcl;
+    }
+
+    /**
+     * 获取node对应的工程量清单
+     * 
+     * @param {Object} node
+     * @returns {Object}  
+     */
+    getGclNode(node) {
+        const helper = this.ctx.helper;
+        const gcl = this.gclList.find(function (g) {
+            return g.b_code === node.b_code &&
+                (g.name || node.name ? g.name === node.name : true) &&
+                (g.unit || node.unit ? g.unit === node.unit : true) &&
+                helper.checkZero(helper.sub(g.unit_price, node.unit_price));
+        });
+        if (gcl) {
+            return gcl;
+        } else {
+            return this.newGclNode(node);
+        }
+    }
+
+    /**
+     * 检查 text 是否是Peg
+     * e.g. K123+000(true) Kab+123(false) K123.234+234(false) K12+324.234(true)
+     *
+     * @param text
+     * @returns {*}
+     * @constructor
+     */
+    CheckPeg(text) {
+        const pegReg = /[a-zA-Z]?[kK][0-9]+[++][0-9]{3}([.][0-9]+)?/;
+        return pegReg.test(text);
+    }
+
+    /**
+     * 基于node向上查找桩号节点(特别的,对于路基工程等,桩号节点应该在计量单元中)
+     * 
+     * @param {Object} node - 清单树节点 
+     */
+    getPegNode(node) {
+        if (node) {
+            if (this.CheckPeg(node.name)) {
+                return node;
+            } else {
+                const parent = this.billsTree.getParent(node);
+                return parent ? getPegNode(parent) : null;
+            }
+        }
+    }
+
+    /**
+     * 获取节点的第N层父节点
+     *
+     * @param node - 节点(检索起点)
+     * @param level - 第N层
+     * @returns {*}
+     */
+    getNodeByLevel(node, level) {
+        let cur = node;
+        while (cur && cur.level > level) {
+            cur = this.billsTree.getParent(cur);
+        }
+        return cur;
+    }
+
+    /**
+     * 获取 单位工程
+     *
+     * @param xmj - 计量单元(最底层项目节)
+     * @returns {string}
+     */
+    getDwgc(peg, xmj) {
+        if (peg) {
+            return peg.name;
+        } else {
+            const node = this.getNodeByLevel(xmj, 2);
+            return node ? node.name : '';
+        }
+    }
+
+    /**
+     * 获取 分部工程
+     *
+     * @param peg - 桩号节点
+     * @param xmj - 计量单元(最底层项目节)
+     * @returns {string}
+     */
+    getFbgc(peg, xmj) {
+        if (peg && peg.id !== xmj.id) {
+            const node = this.getNodeByLevel(xmj, peg.level + 1);
+            return node ? node.name : '';
+        } else {
+            const node = this.getNodeByLevel(xmj, 3);
+            return node ? node.name : '';
+        }
+    }
+
+    /**
+     * 获取 分项工程
+     *
+     * @param peg - 桩号节点
+     * @param xmj - 计量单元(最底层项目节)
+     * @returns {string}
+     */
+    getFxgc (peg, xmj) {
+        if (!peg) {
+            const node = this.getNodeByLevel(xmj, 4);
+            return node ? node.name : '';
+        } else if (peg.id === xmj.id) {
+            if (xmj.level > 4) {
+                let value = '';
+                for (let level = 4; level < xmj.level; level++) {
+                    const node = this.getNodeByLevel(xmj, level);
+                    value = value === '' ? node.name : value + mergeChar + node.name;
+                }
+                return value;
+            } else {
+                return '';
+            }
+        } else {
+            if (peg.level + 2 < xmj.level) {
+                let value = '';
+                for (let level = peg.level + 2; level < xmj.level; level++) {
+                    const node = this.getNodeByLevel(xmj, level);
+                    value = value === '' ? node.name : value + mergeChar + node.name;
+                }
+                return value;
+            } else {
+                return '';
+            }
+        }
+    }
+
+    /**
+     * 生成缓存数据(缓存仅为了不用每次都运算分部工程等)
+     * 
+     * @param {Object}} leafXmj - 清单树节点 
+     * @returns {Object} 最底层项目节缓存数据
+     */
+    newCacheLeafXmj(leafXmj) {
+        const peg = this.getPegNode(leafXmj);
+        const cacheLX = {
+            id: leafXmj.id,
+            code: leafXmj.code,
+            jldy: leafXmj.name,
+            fbgc: this.getFbgc(peg, leafXmj),
+            fxgc: this.getFxgc(peg, leafXmj),
+            dwgc: this.getDwgc(peg, leafXmj),
+            drawing_code: leafXmj.drawing_code,
+        };
+        this.leafXmjs.push(cacheLX);
+        return cacheLX;
+    }
+
+    /**
+     * 获取缓存数据(有缓存则直接读取,无则生成缓存)
+     * 
+     * @param {Object} leafXmj - 最底层项目节 
+     * @returns {Object} 最底层项目缓存数据
+     */
+    getCacheLeafXmj(leafXmj) {
+        const cacheLX = leafXmjs.find(lx => { return lx.id === leafXmj.id; });
+        if (!cacheLX) {
+            return this.newCacheLeafXmj(leafXmj);
+        } else {
+            return cacheLX;
+        }
+    }
+
+    /**
+     * 汇总工程量清单数据
+     * 
+     * @param {Object} node 最底层工程量清单树节点 
+     * @param {*} leafXmj node所属的最底层项目节
+     */
+    loadGatherGclNode(node, leafXmj) {
+        const gcl = this.getGclNode(node);
+        for (const prop in node) {
+            if (prop === 'quantity' || prop === 'total_price' || prop.indexOf('qty') >= 0 || prop.indexOf('tp') >= 0) {
+                gcl[prop] = this.ctx.helper.add(gcl[prop], node[prop]);
+            }
+        }
+        const cacheLeafXmj = this.getCacheLeafXmj(leafXmj);
+        const posRange = this.pos.getLedgerPos(node.id);
+
+        const loadLeafXmj = function (detail, calcSource) {
+            const dx = _.assign({}, cacheLeafXmj);
+            dx.gcl_id = gcl.id;
+            if (detail.name !== node.name) {
+                dx.bwmx = detail.name;
+                dx.mx_id = detail.id;
+            }
+            if (detail.drawing_code) {
+                dx.drawing_code = detail.drawing_code;
+            }
+            for (const prop in calcSource) {
+                if (prop === 'quantity' || prop.indexOf('qty') > 0) dx[prop] = calcSource[prop];
+            }
+            gcl.leafXmjs.push(dx);
+        };
+
+        if (posRange && posRange.length > 0) {
+            for (const pr of posRange) {
+                loadLeafXmj(pr, pr);
+            }
+        } else {
+            loadLeafXmj(leafXmj, node);
+        }
+    }
+
+    /**
+     * 递归生成工程量清单汇总数据
+     * 
+     * @param {Array<Object>} nodes 清单子节点
+     * @param {Object} leafXmj 最底层项目节 
+     */
+    recursiveGatherGclData(nodes, leafXmj) {
+        for (const node of nodes) {
+            if (node.b_code) {
+                if (node.children.length > 0) {
+                    this.recursiveGatherGclData(node.children, leafXmj);
+                } else {
+                    this.loadGatherGclNode(node, leafXmj);
+                }
+            } else if (node.children.length > 0) {
+                this.recursiveGatherGclData(node.children, node);
+            }
+        }
+    }
+
+    gatherDealBillsData(deal) {
+        if (deal && deal.length > 0) {
+            for (const node of deal) {
+                node.b_code = node.code;
+                const gcl = this.getGclNode(node);
+                if (!node.quantity || !node.unit_price) continue;
+                gcl.deal_bills_qty = node.quantity;
+                gcl.deal_bills_tp = node.total_price;
+            }
+        }
+    }
+
+    convertResultData() {
+        this.leafXmjs = [];
+        for (const gcl of this.gclList) {
+            if (gcl.leafXmjs.length === 0) return;
+            for (const lx of gcl.leafXmjs) {
+                this.leafXmjs.push(lx);
+            }
+        }
+    }
+
+    /**
+     * 汇总
+     * 
+     * @param {Array<Object>} bills 清单数据 
+     * @param {Array<Object>} pos 计量单元数据
+     */
+    gather(bills, pos) {
+        const helper = this.ctx.helper;
+        this.billsTree.loadDatas(bills);
+        this.pos.loadDatas(pos);
+
+        this.gclList = [];
+        this.leafXmjs = [];
+        this.xmjPos = [];
+
+        this.recursiveGatherGclData(this.billsTree.children, null);
+        this.gatherDealBillsData();
+        this.gclList.sort(function (a, b) {
+            return helper.compareCode(a.b_code, b.b_code);
+        });
+
+        this.convertResultData();
+        return [this.gclList, this.leafXmjs];
+    }
+};
+
+module.exports = {
+    gclGather: gclGatherModel,
+};

+ 26 - 0
app/lib/rpt_data_analysis.js

@@ -1729,6 +1729,31 @@ const treeFilter = {
         });
     },
 };
+const gatherGcl2 = {
+    name: '工程量清单汇总2',
+    hint: '会生成新的工程量清单指标&相关项目节指标',
+    defaultSetting: {
+        billsTable: 'mem_stage_bills',
+        posTable: 'mem_stage_pos',
+        dealTable: 'deal_bills',
+    },
+    fun(ctx, data, fieldsKey, options, csRela) {
+        if (!options || !options.billsTable || !options.posTable) return;
+
+        const bills = data[options.billsTable];
+        if (!bills) return;
+        const pos = data[options.posTable];
+        if (!pos) return;
+        const deal = data[options.dealTable];
+
+        const gclGatherModel = require('./gcl_gather').gclGather;
+        const gatherUtil = new gclGatherModel(ctx);
+        gatherUtil.gather(bills, pos, deal);
+
+        data.mem_gcl_gather_bills = gatherUtil.gclList;
+        data.mem_gcl_gather_xmj = gatherUtil.leafXmjs;
+    }
+};
 
 const analysisObj = {
     changeSort,
@@ -1751,6 +1776,7 @@ const analysisObj = {
     loadCooperationData,
     getChangeLedger,
     treeFilter,
+    gatherGcl2,
 };
 const analysisDefine = (function(obj) {
     const result = [];

+ 78 - 0
builder_report_index_define.js

@@ -1309,6 +1309,83 @@ const stage_change_ledger = {
     ],
 };
 
+const gcl_gather_bills = {
+    name: '工程量清单汇总2专用 - 工程量清单',
+    remark: '',
+    key: 'mem_gcl_gather_bills',
+    id: 50,
+    prefix: '工程量清单汇总2-清单',
+    cols: [
+        { name: 'id', field: 'id', type: dataType.int },
+        { name: '清单编号', field: 'b_code', type: dataType.str },
+        { name: '名称', field: 'name', type: dataType.str },
+        { name: '单位', field: 'unit', type: dataType.str },
+        { name: '单价', field: 'unit_price', type: dataType.currency },
+        { name: '签约清单-数量', field: 'deal_bills_qty', type: dataType.currency },
+        { name: '签约清单-金额', field: 'deal_bills_tp', type: dataType.currency },
+        { name: '签约-数量', field: 'deal_qty', type: dataType.currency },
+        { name: '签约-金额', field: 'deal_tp', type: dataType.currency },
+        { name: '数量', field: 'quantity', type: dataType.currency },
+        { name: '金额', field: 'total_price', type: dataType.currency },
+        { name: '本期-合同-数量', field: 'contract_qty', type: dataType.currency },
+        { name: '本期-合同-金额', field: 'contract_tp', type: dataType.currency },
+        { name: '本期-变更-数量', field: 'qc_qty', type: dataType.currency },
+        { name: '本期-变更-金额', field: 'qc_tp', type: dataType.currency },
+        { name: '本期-完成-数量', field: 'gather_qty', type: dataType.currency },
+        { name: '本期-完成-金额', field: 'gather_tp', type: dataType.currency },
+        { name: '截止本期-合同-数量', field: 'end_contract_qty', type: dataType.currency },
+        { name: '截止本期-合同-金额', field: 'end_contract_tp', type: dataType.currency },
+        { name: '截止本期-变更-数量', field: 'end_qc_qty', type: dataType.currency },
+        { name: '截止本期-变更-金额', field: 'end_qc_tp', type: dataType.currency },
+        { name: '截止本期-完成-数量', field: 'end_gather_qty', type: dataType.currency },
+        { name: '截止本期-完成-金额', field: 'end_gather_tp', type: dataType.currency },
+        { name: '备用1-数量', field: 'spec1_qty', type: dataType.currency },
+        { name: '备用1-金额', field: 'spec1_tp', type: dataType.currency },
+        { name: '备用2-数量', field: 'spec2_qty', type: dataType.currency },
+        { name: '备用2-金额', field: 'spec2_tp', type: dataType.currency },
+        { name: '备用3-数量', field: 'spec3_qty', type: dataType.currency },
+        { name: '备用3-金额', field: 'spec3_tp', type: dataType.currency },
+        { name: '备用4-数量', field: 'spec4_qty', type: dataType.currency },
+        { name: '备用4-金额', field: 'spec4_tp', type: dataType.currency },
+        { name: '备用5-数量', field: 'spec5_qty', type: dataType.currency },
+        { name: '备用5-金额', field: 'spec5_tp', type: dataType.currency },
+        { name: '备用6-数量', field: 'spec6_qty', type: dataType.currency },
+        { name: '备用6-金额', field: 'spec6_tp', type: dataType.currency },
+    ],
+};
+
+const gcl_gather_xmj = {
+    name: '工程量清单汇总2专用 - 相关项目节',
+    remark: '',
+    key: 'mem_gcl_gather_xmj',
+    id: 51,
+    prefix: '工程量清单汇总2-项目节',
+    cols: [
+        { name: '清单id(所属工程量清单id)', field: 'gcl_id', type: dataType.int },
+        { name: '明细id(在台账中的id)', field: 'mx_id', type: dataType.str },
+        { name: '编号', field: 'code', type: dataType.str },
+        { name: '计量单元', field: 'jldy', type: dataType.str },
+        { name: '分部工程', field: 'fbgc', type: dataType.str },
+        { name: '分项工程', field: 'fxgc', type: dataType.str },
+        { name: '单位工程', field: 'dwgc', type: dataType.str },
+        { name: '部位明细', field: 'bwmx', type: dataType.str },
+        { name: '图册号', field: 'drawing_code', type: dataType.str },
+        { name: '数量', field: 'quantity', type: dataType.currency },
+        { name: '本期-合同-数量', field: 'contract_qty', type: dataType.currency },
+        { name: '本期-变更-数量', field: 'qc_qty', type: dataType.currency },
+        { name: '本期-完成-数量', field: 'gather_qty', type: dataType.currency },
+        { name: '截止本期-合同-数量', field: 'end_contract_qty', type: dataType.currency },
+        { name: '截止本期-变更-数量', field: 'end_qc_qty', type: dataType.currency },
+        { name: '截止本期-完成-数量', field: 'end_gather_qty', type: dataType.currency },
+        { name: '备用1-数量', field: 'spec1_qty', type: dataType.currency },
+        { name: '备用2-数量', field: 'spec2_qty', type: dataType.currency },
+        { name: '备用3-数量', field: 'spec3_qty', type: dataType.currency },
+        { name: '备用4-数量', field: 'spec4_qty', type: dataType.currency },
+        { name: '备用5-数量', field: 'spec5_qty', type: dataType.currency },
+        { name: '备用6-数量', field: 'spec6_qty', type: dataType.currency },
+    ],
+};
+
 const recursiveMkdirSync = async function(pathName) {
     if (!fs.existsSync(pathName)) {
         const upperPath = path.dirname(pathName);
@@ -1414,6 +1491,7 @@ const defines = [
     material, materialGl,
     stage_sum_bills, stage_sum_pay, stage_audit, sign_select,
     stage_change_bills, stage_change_ledger,
+    gcl_gather_bills, gcl_gather_xmj
 ];
 for (const d of defines) {
     exportTableDefine(d);