浏览代码

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

laiguoran 5 年之前
父节点
当前提交
62f17e3e88

+ 18 - 0
app/const/external_data.js

@@ -0,0 +1,18 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Tag = {
+    FuLong: {
+        exType: 'fulong',
+        exFields: { wbsCode: 'wbs-code' }
+    }
+};
+
+module.exports = Tag;

+ 24 - 0
app/controller/ledger_controller.js

@@ -18,11 +18,13 @@ const auditConst = audit.ledger;
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
 const spreadConst = require('../const/spread');
+const externalDataConst = require('../const/external_data.js');
 const fs = require('fs');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
 const path = require('path');
 const exportExcel = require('../lib/export_excel');
+const billsPosConvert = require('../lib/bills_pos_convert');
 const xlsx = require('js-xlsx');
 
 module.exports = app => {
@@ -617,6 +619,28 @@ module.exports = app => {
             }
         }
 
+        async loadBwtz(ctx) {
+            try {
+                const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+                const posData = this.ctx.tender.data.measure_type === measureType.tz.value
+                    ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
+                const convert = new billsPosConvert(ctx);
+                convert.loadData(ledgerData, posData, []);
+                //const result = await convert.convert();
+                const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1,
+                    externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || [];
+                const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis);
+                if (needUpdate) {
+                    await ctx.service.externalData.saveExValue(ctx.tender.id, -1,
+                        externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode, wbsCodeHis);
+                }
+                ctx.body = {err: 0, msg: '', data: result};
+            } catch(err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '加载合同支付数据错误');
+            }
+        }
+
         /**
          * 台账对比 页面 (Get)
          * @param ctx

+ 24 - 0
app/controller/stage_controller.js

@@ -14,12 +14,14 @@ const changeAudit = require('../const/audit').flow;
 const spreadConst = require('../const/spread');
 const tenderConst = require('../const/tender');
 const payConst = require('../const/deal_pay.js');
+const externalDataConst = require('../const/external_data.js');
 const changeConst = require('../const/change');
 const measureType = tenderConst.measureType;
 const path = require('path');
 const PayCalculator = require('../lib/pay_calc');
 const accountGroup = require('../const/account_group').group;
 const sendToWormhole = require('stream-wormhole');
+const billsPosConvert = require('../lib/bills_pos_convert');
 const fs = require('fs');
 
 module.exports = app => {
@@ -1152,6 +1154,28 @@ module.exports = app => {
             }
         }
 
+        async loadBwtz(ctx) {
+            try {
+                const ledgerData = await this._getStageLedgerData(ctx);
+                const posData = await this._getStagePosData(ctx);
+                const changeData = await this._getStageChangeData(ctx);
+                const convert = new billsPosConvert(ctx);
+                convert.loadData(ledgerData, posData, changeData);
+                // const result = convert.convert();
+                const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1,
+                    externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || [];
+                const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis);
+                if (needUpdate) {
+                    await ctx.service.externalData.saveExValue(ctx.tender.id, -1,
+                        externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode, wbsCodeHis);
+                }
+                ctx.body = {err: 0, msg: '', data: result};
+            } catch(err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '加载合同支付数据错误');
+            }
+        }
+
         /**
          * 报表
          * @param ctx

+ 306 - 0
app/lib/bills_pos_convert.js

@@ -0,0 +1,306 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Ledger = require('./ledger');
+const splitChar = '-';
+
+class BillsPosConvert {
+
+    constructor(ctx) {
+        const self = this;
+        this.ctx = ctx;
+        this._ = this.ctx.helper._;
+
+        this.tpFields = ['total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+            'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp'];
+        this.baseCalcFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty',
+            'pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty', 'end_contract_qty', 'end_qc_qty', 'end_gather_qty'];
+        // mainData
+        this.bpcTree = new Ledger.billsTree(this.ctx, {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id'],
+            stageId: 'id',
+            updateFields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
+            calcFields: [],
+        });
+        this.bpcPos = new Ledger.pos({
+            id: 'id', ledgerId: 'lid',
+            updateFields: ['contract_qty', 'qc_qty'],
+        });
+        this.bpcChange = []
+        // reusltData
+        this.resultTreeSetting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+        };
+        this.resultTree = new Ledger.filterTree(this.ctx, this.resultTreeSetting);
+    }
+
+    // 加载基础数据
+    _loadLedgerData (ledger) {
+        const self = this;
+        // 加载树结构数据
+        this.bpcTree.loadDatas(ledger);
+    }
+    _loadPosData(pos) {
+        this.bpcPos.loadDatas(pos);
+    }
+    loadData (ledger, pos, change) {
+        this._loadLedgerData(ledger);
+        this._loadPosData(pos);
+        this.bpcChange = change;
+    }
+
+    _convertXmj(data) {
+        const xmj = this.resultTree.addData(data, ['ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+            'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'drawing_code', 'postil', 'memo']);
+        return xmj;
+    }
+    // v1
+    _loadField(node, data, fields) {
+        for (const prop in data) {
+            if (fields.indexOf(prop) >= 0) node[prop] = data[prop];
+        }
+    }
+    // v2
+    _loadCalcFields(node, data) {
+        const tpDecimal = this.ctx.tender.info.decimal.tp;
+        node.quantity = this.ctx.helper.add(node.quantity, data.quantity);
+        node.total_price = this.ctx.helper.add(node.total_price, this.ctx.helper.mul(node.unit_price, data.quantity, tpDecimal));
+
+        node.contract_qty = this.ctx.helper.add(node.contract_qty, data.contract_qty);
+        node.contract_tp = this.ctx.helper.add(node.contract_tp, this.ctx.helper.mul(node.unit_price, data.contract_qty, tpDecimal));
+        node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
+        node.qc_tp = this.ctx.helper.add(node.qc_tp, this.ctx.helper.mul(node.unit_price, data.qc_qty, tpDecimal));
+        node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
+
+        node.pre_contract_qty = this.ctx.helper.add(node.pre_contract_qty, data.pre_contract_qty);
+        node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, this.ctx.helper.mul(node.unit_price, data.pre_contract_qty, tpDecimal));
+        node.pre_qc_qty = this.ctx.helper.add(node.pre_qc_qty, data.pre_qc_qty);
+        node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, this.ctx.helper.mul(node.unit_price, data.pre_qc_qty, tpDecimal));
+        node.pre_gather_qty = this.ctx.helper.add(node.pre_gather_qty, data.pre_gather_qty);
+    }
+    _convertGcl(node, xmj) {
+        if (!xmj) return;
+
+        if (!xmj.unitTree) {
+            xmj.unitTree = new Ledger.filterGatherTree(this.ctx, this.resultTreeSetting);
+        }
+        const pos = this.bpcPos.getLedgerPos(node.id);
+        if (pos && pos.length > 0) {
+            for (const p of pos) {
+                const posUnit = xmj.unitTree.addNode({pos_name: p.name,
+                    b_code: null, name: '', unit: null, unit_price: null});
+                const gclUnit = xmj.unitTree.addNode({pos_name: '',
+                    b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price
+                }, posUnit);
+                //loadField(gclUnit, p, baseCalcFields);
+                this._loadCalcFields(gclUnit, p);
+                if (!gclUnit.changes) gclUnit.changes = [];
+                for (const c of this.bpcChange) {
+                    if (c.lid === node.id && c.pid === p.id && c.qty && c.qty !== 0) {
+                        gclUnit.changes.push(c);
+                    }
+                }
+            }
+        } else {
+            const unit = xmj.unitTree.addNode({pos_name: null,
+                b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price});
+            //loadField(unit, node, baseCalcFields);
+            this._loadCalcFields(unit, node);
+            if (!unit.changes) unit.changes = [];
+            for (const c of this.bpcChange) {
+                if (c.lid === node.id && c.pid == -1 && c.qty && c.qty !== 0) {
+                    unit.changes.push(c);
+                }
+            }
+        }
+    }
+    _recursiveConvertNode(nodes, xmj = null) {
+        if (!nodes || !nodes instanceof Array || nodes.length === 0) return;
+
+        for (const node of nodes) {
+            if (node.b_code && node.b_code !== '') {
+                if (!node.children || node.children.length === 0) {
+                    this._convertGcl(node, xmj);
+                }
+                this._recursiveConvertNode(node.children, xmj);
+            } else {
+                const curXmj = this._convertXmj(node);
+                this._recursiveConvertNode(node.children, curXmj);
+                if (!node.children || node.children.length === 0) {
+                    this._loadField(curXmj, node, this.tpFields);
+                }
+            }
+
+        }
+    }
+
+    _calculateChild(child) {
+        //const tpDecimal = this.ctx.tender.info.decimal.tp;
+
+        child.gather_qty = this.ctx.helper.add(child.contract_qty, child.qc_qty);
+        child.pre_gather_qty = this.ctx.helper.add(child.pre_contract_qty, child.pre_gather_qty);
+        child.end_contract_qty = this.ctx.helper.add(child.contract_qty, child.pre_contract_qty);
+        child.end_qc_qty = this.ctx.helper.add(child.qc_qty, child.pre_qc_qty);
+        child.end_gather_qty = this.ctx.helper.add(child.gather_qty, child.pre_gather_qty);
+
+        // v1
+        // child.total_price = this.ctx.helper.mul(child.unit_price, child.quantity, tpDecimal);
+        // child.contract_tp = this.ctx.helper.mul(child.unit_price, child.contract_qty, tpDecimal);
+        // child.qc_tp = this.ctx.helper.mul(child.unit_price, child.qc_qty, tpDecimal);
+        // child.gather_tp = this.ctx.helper.mul(child.unit_price, child.gather_qty, tpDecimal);
+        // child.pre_contract_tp = this.ctx.helper.mul(child.unit_price, child.pre_contract_qty, tpDecimal);
+        // child.pre_qc_tp = this.ctx.helper.mul(child.unit_price, child.pre_qc_qty, tpDecimal);
+        // child.pre_gather_tp = this.ctx.helper.mul(child.unit_price, child.pre_gather_qty, tpDecimal);
+        // child.end_contract_tp = this.ctx.helper.mul(child.unit_price, child.end_contract_qty, tpDecimal);
+        // child.end_qc_tp = this.ctx.helper.mul(child.unit_price, child.end_qc_qty, tpDecimal);
+        // child.end_gather_tp = this.ctx.helper.mul(child.unit_price, child.end_gather_qty, tpDecimal);
+
+        // v2
+        child.gather_tp = this.ctx.helper.add(child.contract_tp, child.qc_tp);
+        child.pre_gather_tp = this.ctx.helper.add(child.pre_contract_tp, child.pre_qc_tp);
+        child.end_contract_tp = this.ctx.helper.add(child.pre_contract_tp, child.contract_tp);
+        child.end_qc_tp = this.ctx.helper.add(child.pre_qc_tp, child.qc_tp);
+        child.end_gather_tp = this.ctx.helper.add(child.end_contract_tp, child.end_qc_tp);
+
+        child.final_tp = this.ctx.helper.add(child.total_price, child.end_qc_tp);
+        child.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(child.end_gather_tp, child.final_tp, 4), 100);
+
+        child.bgl_code = this.ctx.helper._.uniq(this.ctx.helper._.map(child.changes, 'c_code')).join(';');
+    }
+    _calculateNode(node, children) {
+        for (const child of children) {
+            node.total_price = this.ctx.helper.add(node.total_price, child.total_price);
+            node.contract_tp = this.ctx.helper.add(node.contract_tp, child.contract_tp);
+            node.qc_tp = this.ctx.helper.add(node.qc_tp, child.qc_tp);
+            node.gather_tp = this.ctx.helper.add(node.gather_tp, child.gather_tp);
+            node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, child.pre_contract_tp);
+            node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, child.pre_qc_tp);
+            node.pre_gather_tp = this.ctx.helper.add(node.pre_gather_tp, child.pre_gather_tp);
+            node.end_contract_tp = this.ctx.helper.add(node.end_contract_tp, child.end_contract_tp);
+            node.end_qc_tp = this.ctx.helper.add(node.end_qc_tp, child.end_qc_tp);
+            node.end_gather_tp = this.ctx.helper.add(node.end_gather_tp, child.end_gather_tp);
+        }
+        node.final_tp = this.ctx.helper.add(node.total_price, node.end_qc_tp);
+        node.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(node.end_gather_tp, node.final_tp, 4), 100);
+    }
+    _recursiveCalcUnitNodes(nodes) {
+        if (!nodes || !nodes instanceof Array || nodes.length === 0) return;
+
+        for (const node of nodes) {
+            this._recursiveCalcUnitNodes(node.children);
+            if (node.children && node.children.length > 0) {
+                this._calculateNode(node, node.children);
+            } else {
+                this._calculateChild(node);
+            }
+        }
+    }
+    _recursiveCalculateAndSort(nodes) {
+        const self = this;
+        if (!nodes || !nodes instanceof Array || nodes.length === 0) return;
+
+        for (const node of nodes) {
+            this._recursiveCalculateAndSort(node.children);
+            if (node.unitTree) {
+                node.unitTree.sortTreeNodeCustom('b_code', function (a, b) {
+                    const numReg = /^[0-9]+$/;
+                    const aCodes = a ? a.split('-') : [], bCodes = b ? b.split('-') : [];
+                    for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
+                        const iCompare = self.ctx.helper.compareCode(aCodes[i], bCodes[i]);
+                        if (iCompare !== 0) {
+                            return iCompare;
+                        }
+                    }
+                }, false);
+                this._recursiveCalcUnitNodes(node.unitTree.children);
+                this._calculateNode(node, node.unitTree.children, this.tpFields);
+            } else if (node.children && node.children.length > 0) {
+                this._calculateNode(node, node.children, this.tpFields);
+            } else {
+                this._calculateChild(node);
+            }
+        }
+    }
+    _calculateAndSortResult() {
+        this.resultTree.sortTreeNode();
+        this._recursiveCalculateAndSort(this.resultTree.children);
+    }
+
+    _generateCodeByWbsCode(wbsCode) {
+        let needUpdate = false;
+        for (const node of this.resultTree.nodes) {
+            if (!node.unitTree) continue;
+
+            let xmj = wbsCode.find(function (x) {
+                return x.xmj_code === node.code;
+            });
+            for (const unit of node.unitTree.nodes) {
+                if (!unit.pos_name || unit.pos_name === '') continue;
+
+                if (!xmj) {
+                    xmj = {xmj_code: node.code, pos: []};
+                    wbsCode.push(xmj);
+                    needUpdate = true;
+                }
+                let index = xmj.pos.indexOf(unit.pos_name);
+                if (index < 0) {
+                    unit.code = xmj.xmj_code + splitChar + (xmj.pos.length + 1);
+                    xmj.pos.push(unit.pos_name);
+                    needUpdate = true;
+                } else {
+                    unit.code = xmj.xmj_code + splitChar + (index + 1);
+                }
+            }
+        }
+        return needUpdate;
+    }
+
+    _getResultData() {
+        const result = this.resultTree.getDefaultDatas();
+        for (const r of result) {
+            if (r.unitTree) {
+                r.unitTreeData = r.unitTree.getDefaultDatas();
+                delete r.unitTree;
+            }
+        }
+        return result;
+    }
+    // 转换数据
+    convert() {
+        this._recursiveConvertNode(this.bpcTree.children);
+        this._calculateAndSortResult();
+        return this._getResultData();
+    }
+
+    /**
+     * 转换数据,会根据wbsCodeHis生成计量单元的code
+     * 如果计量单元在wbsCodeHis中没有历史,则会更新传入的wbsCodeHis,并返回[result, true], 反之返回[result, false]
+     * @param wbsCodeHis
+     * @returns {*[]}
+     */
+    convertByWbsCode(wbsCodeHis) {
+        this._recursiveConvertNode(this.bpcTree.children);
+        this._calculateAndSortResult();
+        const needUpdate = this._generateCodeByWbsCode(wbsCodeHis);
+        return [this._getResultData(), needUpdate];
+    }
+}
+
+module.exports = BillsPosConvert;

+ 174 - 47
app/lib/ledger.js

@@ -10,7 +10,7 @@
 
 const itemsPre = 'id_';
 
-class billsTree {
+class baseTree {
     /**
      * 构造函数
      */
@@ -60,7 +60,11 @@ class billsTree {
         });
         return children;
     };
-
+    /**
+     * 获取节点的 index
+     * @param node
+     * @returns {number}
+     */
     getNodeSerialNo(node) {
         return this.nodes.indexOf(node);
     }
@@ -95,7 +99,6 @@ class billsTree {
         }
         addSortNodes(this.children);
     }
-
     /**
      * 加载数据(初始化), 并给数据添加部分树结构必须数据
      * @param datas
@@ -165,37 +168,47 @@ class billsTree {
     };
 
     /**
-     * 检查节点是否是最底层项目节
-     * @param node
-     * @returns {boolean}
+     * 根据 字段名称 获取数据
+     * @param fields
+     * @returns {Array}
      */
-    isLeafXmj(node) {
-        if (node.b_code && node.b_code !== '') {
-            return false;
-        }
-        for (const child of node.children) {
-            if (!child.b_code || child.b_code === '') {
-                return false;
+    getDatas (fields) {
+        const datas = [];
+        for (const node of this.nodes) {
+            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
+            const data = {};
+            for (const field of fields) {
+                data[field] = node[field];
             }
+            datas.push(data);
         }
-        return true;
+        return datas;
     }
-
     /**
-     * 查询最底层项目节(本身或父项)
-     * @param {Object} node - 查询节点
-     * @returns {Object}
+     * 排除 某些字段 获取数据
+     * @param fields
+     * @returns {Array}
      */
-    getLeafXmjParent(node) {
-        let parent = node;
-        while (parent) {
-            if (this.isLeafXmj(parent)) {
-                return parent;
-            } else {
-                parent = this.getParent(parent);
+    getDatasWithout (fields) {
+        const datas = [];
+        for (const node of this.nodes) {
+            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
+            const data = {};
+            for (const field in node) {
+                if (fields.indexOf(field) === -1) {
+                    data[field] = node[field];
+                }
             }
+            datas.push(data);
         }
-        return null;
+        return datas;
+    }
+    /**
+     * 获取默认数据 剔除一些树结构需要的缓存数据
+     * @returns {Array}
+     */
+    getDefaultDatas() {
+        return this.getDatasWithout(['expanded', 'visible', 'children', 'index']);
     }
 
     _mapTreeNode () {
@@ -250,37 +263,149 @@ class billsTree {
             }
         }
     }
+}
 
-    getDatas (fields) {
-        const datas = [];
-        for (const node of this.nodes) {
-            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
-            const data = {};
-            for (const field of fields) {
-                data[field] = node[field];
+class billsTree extends baseTree {
+    /**
+     * 检查节点是否是最底层项目节
+     * @param node
+     * @returns {boolean}
+     */
+    isLeafXmj(node) {
+        if (node.b_code && node.b_code !== '') {
+            return false;
+        }
+        for (const child of node.children) {
+            if (!child.b_code || child.b_code === '') {
+                return false;
             }
-            datas.push(data);
         }
-        return datas;
+        return true;
     }
+    /**
+     * 查询最底层项目节(本身或父项)
+     * @param {Object} node - 查询节点
+     * @returns {Object}
+     */
+    getLeafXmjParent(node) {
+        let parent = node;
+        while (parent) {
+            if (this.isLeafXmj(parent)) {
+                return parent;
+            } else {
+                parent = this.getParent(parent);
+            }
+        }
+        return null;
+    }
+}
 
-    getDatasWithout (fields) {
-        const datas = [];
-        for (const node of this.nodes) {
-            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
-            const data = {};
-            for (const field in node) {
-                if (fields.indexOf(field) === -1) {
-                    data[field] = node[field];
+class filterTree extends baseTree {
+    addData(data, fields) {
+        const item = {};
+        for (const prop in data) {
+            if (fields.indexOf(prop) >= 0) {
+                item[prop] = data[prop];
+            }
+        }
+        const keyName = itemsPre + item[this.setting.id];
+        if (!this.items[keyName]) {
+            item.children = [];
+            item.is_leaf = true;
+            item.expanded = true;
+            item.visible = true;
+            this.items[keyName] = item;
+            this.datas.push(item);
+            if (item[this.setting.pid] === this.setting.rootId) {
+                this.children.push(item);
+            } else {
+                const parent = this.getParent(item);
+                if (parent) {
+                    parent.is_leaf = false;
+                    parent.children.push(item);
                 }
             }
-            datas.push(data);
+        } else {
+            return this.items[keyName];
         }
-        return datas;
+        return item;
+    }
+}
+
+class filterGatherTree extends baseTree {
+
+    clearDatas() {
+        this.items = {};
+        this.nodes = [];
+        this.datas = [];
+        this.children = [];
     }
 
-    getDefaultDatas() {
-        return this.getDatasWithout(['expanded', 'visible', 'children', 'index']);
+    get newId() {
+        if (!this._maxId) {
+            this._maxId = 0;
+        }
+        this._maxId++;
+        return this._maxId;
+    }
+
+    addNode(data, parent) {
+        data[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId;
+        let item = this.ctx.helper._.find(this.items, data);
+        if (item) return item;
+
+        item = data;
+        item[this.setting.id] = this.newId;
+        const keyName = itemsPre + item[this.setting.id];
+        item.children = [];
+        item.is_leaf = true;
+        item.expanded = true;
+        item.visible = true;
+        this.items[keyName] = item;
+        this.datas.push(item);
+        if (parent) {
+            item[this.setting.fullPath] = parent[this.setting.fullPath] + '-' + item[this.setting.id];
+            item[this.setting.level] = parent[this.setting.level] + 1;
+            item[this.setting.order] = parent.children.length + 1;
+            parent.is_leaf = false;
+            parent.children.push(item);
+        } else {
+            item[this.setting.fullPath] = '' + item[this.setting.id];
+            item[this.setting.level] = 1;
+            item[this.setting.order] = this.children.length + 1;
+            this.children.push(item);
+        }
+        return item;
+    }
+
+    sortTreeNodeCustom(field, fun, isResort) {
+        const self = this;
+        const sortNodes = function (nodes) {
+            nodes.sort(function (a, b) {
+                return fun(a[field], b[field]);
+            });
+            for (const [i, node] of nodes.entries()) {
+                node.order = i + 1;
+            }
+        };
+        const addSortNodes = function (nodes) {
+            if (!nodes) { return }
+            for (let i = 0; i < nodes.length; i++) {
+                self.nodes.push(nodes[i]);
+                nodes[i].index = self.nodes.length - 1;
+                if (!isResort) {
+                    nodes[i].children = self.getChildren(nodes[i]);
+                }
+                sortNodes(nodes[i].children);
+                addSortNodes(nodes[i].children);
+            }
+        };
+        this.nodes = [];
+        if (!isResort) {
+            this.children = this.getChildren();
+        }
+        sortNodes(this.children);
+        addSortNodes(this.children);
     }
 }
 
@@ -355,4 +480,6 @@ class pos {
 module.exports = {
     billsTree,
     pos,
+    filterTree,
+    filterGatherTree,
 };

+ 3 - 1
app/lib/rpt_data_analysis.js

@@ -231,7 +231,7 @@ const gatherChapter = {
             {name: '已包含在清单合计中的材料、工程设备、专业工程暂估价', order: 2, visible: false},
             {name: '清单合计减去材料、工程设备、专业工程暂估价(即8-9=10)', order_calc: 'o1-o2', order: 3},
             {name: '计日工合计', node_type: '计日工', order: 4},
-            {name: '暂列金额(不含计日工总额)(即10×暂列金额比列)', order: 5, match: [{node_type: '暂列金额'}, {field: 'name', part: '暂列金额'}, {field: 'name', part: '暂定金额'}]},
+            {name: '暂列金额(不含计日工总额)(即10×暂列金额比列)', order: 5, match: [{node_type: '暂列金额'}, {field: 'name', part: '暂列金额'}, {field: 'name', all: '暂定金额'}]},
             {name: '投标报价、台账价(8+11+12)=13', order_calc: 'o1+o4+o5', order: 6},
         ],
         rela: [
@@ -400,6 +400,8 @@ const gatherChapter = {
                 const value = d[m.field];
                 if (m.part && value) {
                     if (value.indexOf(m.part) >= 0) return true;
+                } else if (m.all && value) {
+                    if (m.all === value) return true;
                 }
             }
         }

+ 17 - 4
app/public/js/ledger_bwtz.js

@@ -9,7 +9,6 @@
  */
 
 $(document).ready(() => {
-    const preUrl = window.location.pathname.split('/').slice(0, 4).join('/');
     autoFlashHeight();
     const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
     const xmjSheet = xmjSpread.getActiveSheet();
@@ -34,9 +33,23 @@ $(document).ready(() => {
         unitTreeObj.loadCurUnitData();
     });
 
-    postData(preUrl + '/load', {}, function (result) {
-        billsPosConvertModel.loadData(result.bills, result.pos, [], decimal);
-        const xmjTree = billsPosConvertModel.convert();
+    postData(window.location.pathname + '/load', {}, function (result) {
+        const setting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+        };
+        const xmjTree = createNewPathTree('base', setting);
+        xmjTree.loadDatas(result);
+        for (const n of xmjTree.nodes) {
+            if (n.unitTreeData) {
+                n.unitTree = createNewPathTree('base', setting);
+                n.unitTree.loadDatas(n.unitTreeData);
+            }
+        }
         SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
         unitTreeObj.loadCurUnitData();
     });

+ 17 - 3
app/public/js/stage_bwtz.js

@@ -34,9 +34,23 @@ $(document).ready(() => {
         unitTreeObj.loadCurUnitData();
     });
 
-    postData(preUrl + '/load', { filter: 'ledger;pos;change' }, function (result) {
-        billsPosConvertModel.loadData(result.ledgerData, result.posData, result.changeData, decimal);
-        const xmjTree = billsPosConvertModel.convert();
+    postData(window.location.pathname + '/load', {}, function (result) {
+        const setting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+        };
+        const xmjTree = createNewPathTree('base', setting);
+        xmjTree.loadDatas(result);
+        for (const n of xmjTree.nodes) {
+            if (n.unitTreeData) {
+                n.unitTree = createNewPathTree('base', setting);
+                n.unitTree.loadDatas(n.unitTreeData);
+            }
+        }
         SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
         unitTreeObj.loadCurUnitData();
     });

+ 2 - 0
app/router.js

@@ -113,6 +113,7 @@ module.exports = app => {
 
     // 部位台账
     app.get('/tender/:id/ledger/bwtz', sessionAuth, tenderCheck, 'ledgerController.bwtz');
+    app.post('/tender/:id/ledger/bwtz/load', sessionAuth, tenderCheck, 'ledgerController.loadBwtz');
 
     // 台账对比
     app.get('/tender/:id/ledger/gather', sessionAuth, tenderCheck, 'ledgerController.gather');
@@ -198,6 +199,7 @@ module.exports = app => {
     app.get('/tender/:id/measure/stage/:order/audit/check/again', sessionAuth, tenderCheck, stageCheck, 'stageController.checkAuditAgain');
     // 部位台账
     app.get('/tender/:id/measure/stage/:order/bwtz', sessionAuth, tenderCheck, stageCheck, 'stageController.bwtz');
+    app.post('/tender/:id/measure/stage/:order/bwtz/load', sessionAuth, tenderCheck, stageCheck, 'stageController.loadBwtz');
     // 清单汇总
     app.get('/tender/:id/measure/stage/:order/gather', sessionAuth, tenderCheck, stageCheck, 'stageController.gather');
     // 审核比较

+ 47 - 0
app/service/external_data.js

@@ -0,0 +1,47 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+    class ExternalData extends app.BaseService {
+
+        /**
+         * 构造函数
+         * @param ctx
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'external_data';
+        }
+
+        async getExValue(tid, sid, ex_type, ex_field) {
+            const result = await this.getDataByCondition({
+                tid: tid, sid: sid,
+                ex_type: ex_type, ex_field: ex_field
+            });
+            return result && result.ex_value !== null ? JSON.parse(result.ex_value) : null;
+        }
+
+        async saveExValue(tid, sid, ex_type, ex_field, ex_value) {
+            const filter = {
+                tid: tid, sid: sid,
+                ex_type: ex_type, ex_field: ex_field
+            };
+            const count = this.count(filter);
+            if (count >= 0) {
+                await this.db.update(this.tableName, { ex_value: JSON.stringify(ex_value) }, filter);
+            } else {
+                filter.ex_value = JSON.stringify(ex_value);
+                await this.db.insert(this.tableName, filter);
+            }
+        }
+    }
+
+    return ExternalData;
+};

+ 7 - 2
app/view/ledger/bwtz.ejs

@@ -57,9 +57,14 @@
     };
     const unitSpreadSetting = {
         cols: [
-            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'pos_name', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@'},
+            {
+                title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@',
+                getValue: function (data) {
+                    return data.pos_name + data.name;
+                }
+            },
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},

+ 1 - 1
app/view/stage/bwtz.ejs

@@ -65,7 +65,7 @@
     };
     const unitSpreadSetting = {
         cols: [
-            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'pos_name', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},

+ 28 - 0
sql/update.sql

@@ -0,0 +1,28 @@
+
+-- ----------------------------
+-- Table structure for zh_external_data
+-- ----------------------------
+
+CREATE TABLE `zh_external_data` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `sid` int(11) NOT NULL DEFAULT '-1' COMMENT '所属期id,-1表示整个标段共用',
+  `ex_type` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '额外数据 - 类型(''fl'': 富龙接口数据)',
+  `ex_field` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '额外数据 - 名称(具体细分由ex_type决定)',
+  `ex_value` text CHARACTER SET utf8 COMMENT '额外数据 - 值',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='外部所需数据\r\n非计量支付内部运行所需数据,当接入外部系统时,这些系统可能要求我们提供某些额外的数据,例如:富龙接口,wbs_code\r\n类似数据,均存储在这里';
+
+-- ----------------------------
+-- zh_stage_bonus add column 发文单位
+-- ----------------------------
+ALTER TABLE `zh_stage_bonus`
+ADD COLUMN `doc_co`  varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '发文单位' AFTER `code`;
+
+-- ----------------------------
+-- zh_stage_bonus add column 依据材料 - 文件列表
+-- ----------------------------
+ALTER TABLE `zh_stage_bonus`
+ADD COLUMN `proof_file`  text CHARACTER SET utf8 NULL COMMENT '依据材料 - 文件列表(json)' AFTER `proof`;
+
+