Procházet zdrojové kódy

生成wbs编号相关

MaiXinRong před 5 roky
rodič
revize
abebde16c6

+ 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;

+ 9 - 1
app/controller/ledger_controller.js

@@ -18,6 +18,7 @@ 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;
@@ -625,7 +626,14 @@ module.exports = app => {
                     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
                 const convert = new billsPosConvert(ctx);
                 convert.loadData(ledgerData, posData, []);
-                const result = convert.convert();
+                //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);

+ 9 - 1
app/controller/stage_controller.js

@@ -14,6 +14,7 @@ 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');
@@ -1160,7 +1161,14 @@ module.exports = app => {
                 const changeData = await this._getStageChangeData(ctx);
                 const convert = new billsPosConvert(ctx);
                 convert.loadData(ledgerData, posData, changeData);
-                const result = convert.convert();
+                // 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);

+ 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;

+ 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`;
+
+