Explorar o código

动态决算,造价对比

MaiXinRong %!s(int64=3) %!d(string=hai) anos
pai
achega
21ba781ae8

+ 18 - 50
app/controller/budget_controller.js

@@ -166,10 +166,16 @@ module.exports = app => {
 
         async compareLoad(ctx) {
             try {
-                const gu = await ctx.service.budgetGu.getData(ctx.budget.id);
-                const gai = await ctx.service.budgetGai.getData(ctx.budget.id);
-                const yu = await ctx.service.budgetYu.getData(ctx.budget.id);
-                ctx.body = { err: 0, msg: '', data: { gu, gai, yu } }
+                const data = {};
+                if (ctx.budget.final_id) {
+                    data.final = await ctx.service.budgetFinal.getAllDataByCondition({ where: { final_id: ctx.budget.final_id } });
+                    data.finalInfo = await ctx.service.budgetFinalList.getFinal(ctx.budget.final_id);
+                } else {
+                    data.gu = await ctx.service.budgetGu.getData(ctx.budget.id);
+                    data.gai = await ctx.service.budgetGai.getData(ctx.budget.id);
+                    data.yu = await ctx.service.budgetYu.getData(ctx.budget.id);
+                }
+                ctx.body = { err: 0, msg: '', data };
             } catch (err) {
                 ctx.log(err);
                 ctx.ajaxErrorBody(err, '获取数据错误');
@@ -178,53 +184,15 @@ module.exports = app => {
         async compareFinal(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
-                const final = [], helper = this.ctx.helper;
-                for (const id of data.id) {
-                    const bills = await ctx.service.ledger.getFinalData(id, ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
-                        'code', 'b_code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'total_price']);
-                    // 使用完成数据对比
-                    // const stage = ctx.service.stage.getLastestCompleteStage(id);
-                    // const finalBills = ctx.service.stageBillsFinal.getFinalData(id, stage.id);
-                    // ctx.helper.assignRelaData(bills, [
-                    //     { data: finalBills, fields: ['contract_tp', 'qc_tp', 'used'], prefix: 'end_', relaId: 'lid' },
-                    // ]);
-
-                    const dgnData = await ctx.service.stageBillsDgn.getDgnData(id);
-                    // 使用最新一期对比
-                    const stage = await ctx.service.stage.getLastestStage(id);
-                    if (stage.status === auditConst.stage.status.checked) {
-                        const finalBills = await ctx.service.stageBillsFinal.getFinalData({id}, stage.order);
-                        ctx.helper.assignRelaData(bills, [
-                            { data: finalBills, fields: ['contract_tp', 'qc_tp'], prefix: 'end_', relaId: 'lid' },
-                            { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
-                        ]);
-                        bills.forEach(b => {
-                            b.end_gather_tp = helper.add(b.end_qc_tp, b.end_contract_tp);
-                            delete b.end_contract_tp;
-                            delete b.end_qc_tp;
-                        });
-                    } else {
-                        await ctx.service.stage.doCheckStage(stage);
-                        const curBills = stage.readOnly
-                            ? await ctx.service.stageBills.getAuditorStageData2(id, stage.id, stage.curTimes, stage.curOrder)
-                            : await ctx.service.stageBills.getLastestStageData2(id, stage.id);
-                        const preBills = stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData({id}, stage.order - 1) : [];
-                        ctx.helper.assignRelaData(bills, [
-                            { data: curBills, fields: ['contract_tp', 'qc_tp'], prefix: '', relaId: 'lid' },
-                            { data: preBills, fields: ['contract_tp', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
-                            { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
-                        ]);
-                        bills.forEach(b => {
-                            b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp]);
-                            delete b.contract_tp;
-                            delete b.qc_tp;
-                            delete b.pre_contract_tp;
-                            delete b.pre_qc_tp;
-                        });
-                    }
-                    final.push(bills);
+                if (ctx.budget.final_id && data.final_id !== ctx.budget.final_id) {
+                    const final = await ctx.service.budgetFinal.getAllDataByCondition({ where: { final_id: ctx.budget.final_id } });
+                    const finalInfo = await ctx.service.budgetFinalList.getFinal(ctx.budget.final_id);
+                    ctx.body = { err: 0, msg: `决算数据已在${ctx.moment(finalInfo.update_time).format('YYYY-DD-MM HH:mm:ss')}更新,请先查看后再决定是否生成`, data: { final, finalInfo } };
+                } else {
+                    const finalInfo = await ctx.service.budgetFinalList.addFinal(ctx.budget, data.id);
+                    const final = await ctx.service.budget.doFinal(ctx.budget, finalInfo);
+                    ctx.body = { err: 0, msg: '', data: { final, finalInfo } };
                 }
-                ctx.body = { err: 0, msg: '', data: final }
             } catch (err) {
                 ctx.log(err);
                 ctx.ajaxErrorBody(err, '获取决算数据错误');

+ 244 - 0
app/lib/budget_final.js

@@ -0,0 +1,244 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const itemsPre = 'id_';
+const BillsTree = require('./ledger').billsTree;
+const auditConst = require('../const/audit');
+
+class FinalTree extends BillsTree {
+    constructor(ctx, setting) {
+        super(ctx, setting);
+        this._newId = 1;
+    }
+    get newId() {
+        return this._newId++;
+    }
+
+    loadNode(node, parent, loadFun) {
+        if (node.b_code) return;
+        const siblings = parent ? parent.children : this.children;
+        let cur = siblings.find(function (x) {
+            return x.code === node.code && x.name === node.name;
+        });
+        if (!cur) {
+            cur = { children: [], code: node.code || '', name: node.name || '', unit: node.unit || '' };
+            const id = this.newId;
+            cur[this.setting.id] = id;
+            cur[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId;
+            cur[this.setting.fullPath] = parent ? parent[this.setting.fullPath] + '-' + id : '' + id;
+            cur[this.setting.level] = parent ? parent[this.setting.level] + 1 : 1;
+            cur[this.setting.order] = siblings.length + 1;
+            siblings.push(cur);
+            this.datas.push(cur);
+        }
+        loadFun(cur, node);
+        for (const c of node.children) {
+            this.loadNode(c, cur, loadFun);
+        }
+    }
+    loadTree(tree, loadFun) {
+        for (const node of tree.children) {
+            this.loadNode(node, null, loadFun);
+        }
+    }
+
+    generateSortNodes() {
+        const self = this;
+        const addSortNode = function (node) {
+            self.nodes.push(node);
+            for (const c of node.children) {
+                addSortNode(c);
+            }
+        };
+        this.nodes = [];
+        for (const n of this.children) {
+            addSortNode(n);
+        }
+    }
+    afterLoad(fun) {
+        for (const d of this.datas) {
+            fun && fun(d);
+            d.is_leaf = d.children.length === 0;
+            d.expanded = true;
+            d.visible = true;
+            this.items[itemsPre + d[this.setting.id]] = d;
+        }
+        this.generateSortNodes();
+    }
+    resortChildrenByCustom(fun) {
+        for (const n of this.nodes) {
+            if (n.children && n.children.length > 1) {
+                n.children.sort(fun);
+                n.children.forEach((x, y) => { x.order = y + 1; });
+            }
+        }
+        this.generateSortNodes();
+    }
+}
+
+class BudgetFinal {
+    constructor (ctx) {
+        this.ctx = ctx;
+        this.budgetSetting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
+        this.tenderSetting = { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price', 'end_gather_tp'] };
+        this.finalTree = new FinalTree(this.ctx, { id: 'id', pid: 'pid', order: 'order', level: 'level', fullPath: 'full_path', rootId: -1 });
+    }
+
+    async _loadGu(budget) {
+        const helper = this.ctx.helper;
+        const gu = await this.ctx.service.budgetGu.getData(budget.id);
+        const guTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        guTree.loadDatas(gu);
+        guTree.calculateAll();
+        this.finalTree.loadTree(guTree, function (cur, source) {
+            cur.base = true;
+            cur.gu_dgn_qty1 = helper.add(cur.gu_dgn_qty1, source.dgn_qty1);
+            cur.gu_dgn_qty2 = helper.add(cur.gu_dgn_qty2, source.dgn_qty2);
+            cur.gu_tp = helper.add(cur.gu_tp, source.total_price);
+        });
+    }
+
+    async _loadGai(budget) {
+        const helper = this.ctx.helper;
+        const gai = await this.ctx.service.budgetGai.getData(budget.id);
+        const gaiTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        gaiTree.loadDatas(gai);
+        gaiTree.calculateAll();
+        this.finalTree.loadTree(gaiTree, function (cur, source) {
+            cur.base = true;
+            cur.gai_dgn_qty1 = helper.add(cur.gai_dgn_qty1, source.dgn_qty1);
+            cur.gai_dgn_qty2 = helper.add(cur.gai_dgn_qty2, source.dgn_qty2);
+            cur.gai_tp = helper.add(cur.gai_tp, source.total_price);
+        });
+    }
+
+    async _loadYu(budget) {
+        const helper = this.ctx.helper;
+        const yu = await this.ctx.service.budgetYu.getData(budget.id);
+        const yuTree = new BillsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        yuTree.loadDatas(yu);
+        yuTree.calculateAll();
+        this.finalTree.loadTree(yuTree, function (cur, source) {
+            cur.base = true;
+            cur.yu_dgn_qty1 = helper.add(cur.yu_dgn_qty1, source.dgn_qty1);
+            cur.yu_dgn_qty2 = helper.add(cur.yu_dgn_qty2, source.dgn_qty2);
+            cur.yu_tp = helper.add(cur.yu_tp, source.total_price);
+        });
+    }
+
+    async _loadTender(id) {
+        const helper = this.ctx.helper;
+        const bills = await this.ctx.service.ledger.getFinalData(id, ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
+            'code', 'b_code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'total_price']);
+
+        const dgnData = await this.ctx.service.stageBillsDgn.getDgnData(id);
+        // 使用最新一期对比
+        const stage = await this.ctx.service.stage.getLastestStage(id);
+        if (!stage) {
+            helper.assignRelaData(bills, [
+                { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
+            ]);
+            this.final.tender_info.push({ id, stageCount: 0 });
+        } else if (stage.status === auditConst.stage.status.checked) {
+            const finalBills = await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.order);
+            helper.assignRelaData(bills, [
+                { data: finalBills, fields: ['contract_tp', 'qc_tp'], prefix: 'end_', relaId: 'lid' },
+                { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
+            ]);
+            bills.forEach(b => {
+                b.end_gather_tp = helper.add(b.end_qc_tp, b.end_contract_tp);
+            });
+            this.final.tender_info.push({ id, stageOrder: stage.order });
+        } else {
+            await this.ctx.service.stage.doCheckStage(stage);
+            const curBills = stage.readOnly
+                ? await this.ctx.service.stageBills.getAuditorStageData2(id, stage.id, stage.curTimes, stage.curOrder)
+                : await this.ctx.service.stageBills.getLastestStageData2(id, stage.id);
+            const preBills = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.order - 1) : [];
+            helper.assignRelaData(bills, [
+                { data: curBills, fields: ['contract_tp', 'qc_tp'], prefix: '', relaId: 'lid' },
+                { data: preBills, fields: ['contract_tp', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
+                { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
+            ]);
+            bills.forEach(b => {
+                b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp]);
+            });
+            this.final.tender_info.push({ id, stageOrder: stage.order, stageStatus: stage.status, stageFlow: stage.curTimes + '-' + stage.curOrder });
+        }
+        const tree = new BillsTree(this.ctx, this.tenderSetting);
+        tree.loadDatas(bills);
+        tree.calculateAll();
+        this.finalTree.loadTree(tree, function (cur, source) {
+            cur.total_price = helper.add(cur.total_price, source.total_price);
+            cur.dgn_qty1 = helper.add(cur.dgn_qty1, source.dgn_qty1);
+            cur.dgn_qty2 = helper.add(cur.dgn_qty2, source.dgn_qty2);
+            cur.final_dgn_qty1 = helper.sum([cur.final_dgn_qty1, source.deal_dgn_qty1, source.c_dgn_qty1]);
+            cur.final_dgn_qty2 = helper.sum([cur.final_dgn_qty2, source.deal_dgn_qty2, source.c_dgn_qty2]);
+            cur.final_tp = helper.add(cur.final_tp, source.end_gather_tp);
+        });
+    }
+
+    async _afterLoad() {
+        const helper = this.ctx.helper;
+        this.finalTree.afterLoad(node => {
+            node.dgn_price = helper.div(node.total_price, node.dgn_qty1, 2);
+            node.dgn_qty = node.dgn_qty1
+                ? (node.dgn_qty2 ? node.dgn_qty1 + '/' + node.dgn_qty2 : node.dgn_qty1)
+                : (node.dgn_qty2 ? '/' + node.dgn_qty2 : '');
+            node.final_dgn_price = helper.div(node.final_tp, node.final_dgn_qty1, 2);
+            node.final_dgn_qty = node.final_dgn_qty1
+                ? (node.final_dgn_qty2 ? node.final_dgn_qty1 + '/' + node.final_dgn_qty2 : node.final_dgn_qty1)
+                : (node.final_dgn_qty2 ? '/' + node.final_dgn_qty2 : '');
+            node.grow_dgn_qty1 = helper.mul(helper.div(helper.sub(node.final_dgn_qty1, node.gai_dgn_qty1), node.gai_dgn_qty1, 4), 100);
+            node.grow_dgn_qty2 = helper.mul(helper.div(helper.sub(node.final_dgn_qty2, node.gai_dgn_qty2), node.gai_dgn_qty2, 4), 100);
+            node.grow_dgn_qty = node.grow_dgn_qty1
+                ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
+                : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
+            node.grow_tp = helper.mul(helper.div(helper.sub(node.final_tp, node.gai_tp), node.gai_tp, 4), 100);
+        });
+    }
+
+    getFinalData() {
+        const data = [], ctx = this.ctx, bid = this.budget.id, final_id = this.final.id;
+        this.finalTree.datas.forEach(x => {
+            data.push({
+                id: ctx.app.uuid.v4(), bid, final_id,
+                tree_id: x.id, tree_pid: x.pid, order: x.order, level: x.level, full_path: x.full_path, is_leaf: x.is_leaf,
+                code: x.code, name: x.name, unit: x.unit,
+                gu_dgn_qty1: x.gu_dgn_qty1 || 0, gu_dgn_qty2: x.gu_dgn_qty2 || 0, gu_tp: x.gu_tp || 0,
+                gai_dgn_qty1: x.gai_dgn_qty1 || 0, gai_dgn_qty2: x.gu_dgn_qty2 || 0, gai_tp: x.gu_tp || 0,
+                yu_dgn_qty1: x.yu_dgn_qty1 || 0, yu_dgn_qty2: x.yu_dgn_qty2 || 0, yu_tp: x.yu_tp || 0,
+
+                dgn_qty1: x.dgn_qty1 || 0, dgn_qty2: x.dgn_qty2 || 0, total_price: x.total_price || 0,
+                final_dgn_qty1: x.final_dgn_qty1 || 0, final_dgn_qty2: x.final_dgn_qty2 || 0, final_tp: x.final_tp || 0,
+                dgn_price: x.dgn_price || 0, dgn_qty: x.dgn_qty,
+                final_dgn_price: x.final_dgn_price || 0, final_dgn_qty: x.final_dgn_qty,
+                grow_dgn_qty1: x.grow_dgn_qty1 || 0, grow_dgn_qty2: x.grow_dgn_qty2 || 0, grow_dgn_qty: x.grow_dgn_qty, grow_tp: x.grow_tp || 0,
+            })
+        });
+        return data;
+    }
+
+    async doFinal(budget, final) {
+        this.budget = budget;
+        this.final = final;
+
+        await this._loadGai(budget);
+        await this._loadGu(budget);
+        await this._loadYu(budget);
+        for (const t of final.tender) {
+            await this._loadTender(t);
+        }
+        this._afterLoad();
+        return this.getFinalData();
+    }
+}
+
+module.exports = BudgetFinal;

+ 107 - 104
app/public/js/budget_compare.js

@@ -39,13 +39,100 @@ $(document).ready(() => {
     sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.jz);
     SpreadJsObj.initSheet(compareSheet, spreadSetting);
 
-    const compareTree = createNewPathTree('final', {
-        id: 'id',
-        pid: 'pid',
-        order: 'order',
-        level: 'level',
-        rootId: -1,
-    });
+    const compareObj = {
+        curFinalId() {
+            return this.finalInfo ? this.finalInfo.id : undefined;
+        },
+        initFinalCol() {
+            if (spreadSetting.cols.length < 13) {
+                spreadSetting.cols.push(...[
+                    {title: '台账|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80},
+                    {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'dgn_price', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '决算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80},
+                    {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'final_dgn_price', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '增幅%|数量1/数量2', colSpan: '2|1', rowSpan: '1|1', field: 'grow_dgn_qty', hAlign: 2, width: 80},
+                    {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'grow_tp', hAlign: 2, width: 80, type: 'Number'},
+                ]);
+                SpreadJsObj.reLoadSheetHeader(compareSheet);
+            };
+        },
+        loadBudgetData(result) {
+            const compareTree = createNewPathTree('final', {
+                id: 'id',
+                pid: 'pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+            });
+            const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
+            const guTree = createNewPathTree('ledger', setting);
+            guTree.loadDatas(result.gu);
+            treeCalc.calculateAll(guTree);
+            compareTree.loadTree(guTree, function (cur, source) {
+                cur.base = true;
+                cur.gu_dgn_qty1 = ZhCalc.add(cur.gu_dgn_qty1, source.dgn_qty1);
+                cur.gu_dgn_qty2 = ZhCalc.add(cur.gu_dgn_qty2, source.dgn_qty2);
+                cur.gu_tp = ZhCalc.add(cur.gu_tp, source.total_price);
+            });
+            const gaiTree = createNewPathTree('ledger', setting);
+            gaiTree.loadDatas(result.gai);
+            treeCalc.calculateAll(gaiTree);
+            compareTree.loadTree(gaiTree, function (cur, source) {
+                cur.base = true;
+                cur.gai_dgn_qty1 = ZhCalc.add(cur.gai_dgn_qty1, source.dgn_qty1);
+                cur.gai_dgn_qty2 = ZhCalc.add(cur.gai_dgn_qty2, source.dgn_qty2);
+                cur.gai_tp = ZhCalc.add(cur.gai_tp, source.total_price);
+            });
+            const yuTree = createNewPathTree('ledger', setting);
+            yuTree.loadDatas(result.yu);
+            treeCalc.calculateAll(yuTree);
+            compareTree.loadTree(yuTree, function (cur, source) {
+                cur.base = true;
+                cur.yu_dgn_qty1 = ZhCalc.add(cur.yu_dgn_qty1, source.dgn_qty1);
+                cur.yu_dgn_qty2 = ZhCalc.add(cur.yu_dgn_qty2, source.dgn_qty2);
+                cur.yu_tp = ZhCalc.add(cur.yu_tp, source.total_price);
+            });
+            compareTree.afterLoad(node => {
+                if (node.code === '1')console.log(node);
+                node.gu_dgn_price = ZhCalc.div(node.gu_tp, node.gu_dgn_qty1, 2);
+                node.gu_dgn_qty = node.gu_dgn_qty1
+                    ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1)
+                    : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
+                node.gai_dgn_price = ZhCalc.div(node.gai_tp, node.gai_dgn_qty1, 2);
+                node.gai_dgn_qty = node.gai_dgn_qty1
+                    ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1)
+                    : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
+                node.yu_dgn_price = ZhCalc.div(node.yu_tp, node.yu_dgn_qty1, 2);
+                node.yu_dgn_qty = node.yu_dgn_qty1
+                    ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1)
+                    : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
+            });
+            compareTree.resortChildrenByCustom(function (x, y) {
+                const iCode = compareCode(x.code, y.code);
+                if (iCode) return iCode;
+                if (!x.name) return -1;
+                if (!y.name) return 1;
+                return x.name.localeCompare(y.name);
+            });
+            SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
+        },
+        loadFinalData(result) {
+            this.finalInfo = result.finalInfo;
+            $('#final-info').html(`${moment(result.finalInfo.update_time).format('YYYY-MM-DD HH:mm:ss')} ${result.finalInfo.u_name}(${result.finalInfo.u_role})`);
+            this.initFinalCol();
+            const finalTree = createNewPathTree('ledger', {
+                id: 'tree_id',
+                pid: 'tree_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+            });
+            finalTree.loadDatas(result.final);
+            SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, finalTree);
+        }
+    };
 
     function compareCode(str1, str2, symbol = '-') {
         if (!str1) {
@@ -81,57 +168,12 @@ $(document).ready(() => {
     }
 
     postData(window.location.pathname + '/load', {}, function (result) {
-        const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
-        const guTree = createNewPathTree('ledger', setting);
-        guTree.loadDatas(result.gu);
-        treeCalc.calculateAll(guTree);
-        compareTree.loadTree(guTree, function (cur, source) {
-            cur.base = true;
-            cur.gu_dgn_qty1 = ZhCalc.add(cur.gu_dgn_qty1, source.dgn_qty1);
-            cur.gu_dgn_qty2 = ZhCalc.add(cur.gu_dgn_qty2, source.dgn_qty2);
-            cur.gu_tp = ZhCalc.add(cur.gu_tp, source.total_price);
-        });
-        const gaiTree = createNewPathTree('ledger', setting);
-        gaiTree.loadDatas(result.gai);
-        treeCalc.calculateAll(gaiTree);
-        compareTree.loadTree(gaiTree, function (cur, source) {
-            cur.base = true;
-            cur.gai_dgn_qty1 = ZhCalc.add(cur.gai_dgn_qty1, source.dgn_qty1);
-            cur.gai_dgn_qty2 = ZhCalc.add(cur.gai_dgn_qty2, source.dgn_qty2);
-            cur.gai_tp = ZhCalc.add(cur.gai_tp, source.total_price);
-        });
-        const yuTree = createNewPathTree('ledger', setting);
-        yuTree.loadDatas(result.yu);
-        treeCalc.calculateAll(yuTree);
-        compareTree.loadTree(yuTree, function (cur, source) {
-            cur.base = true;
-            cur.yu_dgn_qty1 = ZhCalc.add(cur.yu_dgn_qty1, source.dgn_qty1);
-            cur.yu_dgn_qty2 = ZhCalc.add(cur.yu_dgn_qty2, source.dgn_qty2);
-            cur.yu_tp = ZhCalc.add(cur.yu_tp, source.total_price);
-        });
-        compareTree.afterLoad(node => {
-            if (node.code === '1')console.log(node);
-            node.gu_dgn_price = ZhCalc.div(node.gu_tp, node.gu_dgn_qty1, 2);
-            node.gu_dgn_qty = node.gu_dgn_qty1
-                ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1)
-                : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
-            node.gai_dgn_price = ZhCalc.div(node.gai_tp, node.gai_dgn_qty1, 2);
-            node.gai_dgn_qty = node.gai_dgn_qty1
-                ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1)
-                : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
-            node.yu_dgn_price = ZhCalc.div(node.yu_tp, node.yu_dgn_qty1, 2);
-            node.yu_dgn_qty = node.yu_dgn_qty1
-                ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1)
-                : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
-        });
-        compareTree.resortChildrenByCustom(function (x, y) {
-            const iCode = compareCode(x.code, y.code);
-            if (iCode) return iCode;
-            if (!x.name) return -1;
-            if (!y.name) return 1;
-            return x.name.localeCompare(y.name);
-        });
-        SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
+        if (result.final) {
+            compareObj.loadFinalData(result);
+        } else {
+            compareObj.loadBudgetData(result);
+        }
+
     });
 
     $.subMenu({
@@ -186,6 +228,11 @@ $(document).ready(() => {
                 node.lastStageOrder =`第${source.lastStageOrder}期`;
                 node.lastStageStatus = source.lastStageStatus;
             });
+            if (compareObj.finalInfo) {
+                this.selectTree.datas.forEach(x => {
+                    x.selected = compareObj.finalInfo.tender.indexOf(x.tid + '') >= 0;
+                })
+            }
             const sfSpreadSetting = {
                 cols: [
                     {title: '选择', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox'},
@@ -229,52 +276,8 @@ $(document).ready(() => {
             $('#select-final-ok').click(() => {
                 const rela = self.getSelects();
                 if (rela.length === 0) return;
-                postData(window.location.pathname + '/final', {id: rela}, function(result) {
-                    if (spreadSetting.cols.length < 13) {
-                        spreadSetting.cols.push(...[
-                            {title: '台账|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80},
-                            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'dgn_price', hAlign: 2, width: 80, type: 'Number'},
-                            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
-                            {title: '决算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80},
-                            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'final_dgn_price', hAlign: 2, width: 80, type: 'Number'},
-                            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number'},
-                            {title: '增幅%|数量1/数量2', colSpan: '2|1', rowSpan: '1|1', field: 'grow_dgn_qty', hAlign: 2, width: 80},
-                            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'grow_tp', hAlign: 2, width: 80, type: 'Number'},
-                        ]);
-                    }
-                    const setting = { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price', 'end_gather_tp'] };
-                    compareTree.clearFinal();
-                    for (const r of result) {
-                        const tree = createNewPathTree('ledger', setting);
-                        tree.loadDatas(r);
-                        treeCalc.calculateAll(tree);
-                        compareTree.loadTree(tree, function (cur, source) {
-                            cur.total_price = ZhCalc.add(cur.total_price, source.total_price);
-                            cur.dgn_qty1 = ZhCalc.add(cur.dgn_qty1, source.dgn_qty1);
-                            cur.dgn_qty2 = ZhCalc.add(cur.dgn_qty2, source.dgn_qty2);
-                            cur.final_dgn_qty1 = ZhCalc.sum([cur.final_dgn_qty1, source.deal_dgn_qty1, source.c_dgn_qty1]);
-                            cur.final_dgn_qty2 = ZhCalc.sum([cur.final_dgn_qty2, source.deal_dgn_qty2, source.c_dgn_qty2]);
-                            cur.final_tp = ZhCalc.add(cur.final_tp, source.end_gather_tp);
-                        });
-                    }
-                    compareTree.afterLoad(node => {
-                        node.dgn_price = ZhCalc.div(node.total_price, node.dgn_qty1, 2);
-                        node.dgn_qty = node.dgn_qty1
-                            ? (node.dgn_qty2 ? node.dgn_qty1 + '/' + node.dgn_qty2 : node.dgn_qty1)
-                            : (node.dgn_qty2 ? '/' + node.dgn_qty2 : '');
-                        node.final_dgn_price = ZhCalc.div(node.final_tp, node.final_dgn_qty1, 2);
-                        node.final_dgn_qty = node.final_dgn_qty1
-                            ? (node.final_dgn_qty2 ? node.final_dgn_qty1 + '/' + node.final_dgn_qty2 : node.final_dgn_qty1)
-                            : (node.final_dgn_qty2 ? '/' + node.final_dgn_qty2 : '');
-                        node.grow_dgn_qty1 = ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node.final_dgn_qty1, node.gai_dgn_qty1), node.gai_dgn_qty1, 4), 100);
-                        node.grow_dgn_qty2 = ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node.final_dgn_qty2, node.gai_dgn_qty2), node.gai_dgn_qty2, 4), 100);
-                        node.grow_dgn_qty = node.grow_dgn_qty1
-                            ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
-                            : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
-                        node.grow_tp = ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node.final_tp, node.gai_tp), node.gai_tp, 4), 100);
-                    });
-                    SpreadJsObj.reLoadSheetHeader(compareSheet);
-                    SpreadJsObj.reLoadSheetData(compareSheet);
+                postData(window.location.pathname + '/final', {final_id: compareObj.curFinalId(), id: rela}, function(result) {
+                    compareObj.loadFinalData(result);
                     $('#select-final').modal('hide');
                 });
             });

+ 27 - 0
app/service/budget.js

@@ -12,6 +12,7 @@ const defaultDecimal = {
     tp: 0,
     up: 2,
 };
+const FinalObj = require('../lib/budget_final');
 
 module.exports = app => {
 
@@ -244,6 +245,32 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        async doFinal(budget, final) {
+            const finalObj = new FinalObj(this.ctx);
+            let finalData;
+            try {
+                finalData = await finalObj.doFinal(budget, final);
+            } catch (err) {
+                this.db.update(this.ctx.service.budgetFinalList.tableName, { id: final.id, status: 4});
+                this.ctx.log(err);
+                throw '生成决算数据错误';
+            }
+
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.update(this.tableName, {id: budget.id, final_id: final.id, final_time: new Date() });
+                await conn.insert(this.ctx.service.budgetFinal.tableName, finalData);
+                await conn.update(this.ctx.service.budgetFinalList.tableName, { id: final.id, tender_info: JSON.stringify(final.tender_info), status: 3});
+                await conn.commit();
+                return finalData;
+            } catch (err) {
+                this.ctx.log(err);
+                await conn.rollback();
+                this.db.update(this.ctx.service.budgetFinalList.tableName, { id: final.id, status: 4});
+                throw '保存决算数据错误';
+            }
+        }
     }
 
     return Budget;

+ 27 - 0
app/service/budget_final.js

@@ -0,0 +1,27 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class BudgetFinal extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'budget_final';
+        }
+    }
+
+    return BudgetFinal;
+};

+ 57 - 0
app/service/budget_final_list.js

@@ -0,0 +1,57 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const status = {
+    cancel: 0,
+    wait: 1,
+    ext: 2,
+    done: 3,
+};
+
+module.exports = app => {
+
+    class BudgetFinalList extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'budget_final_list';
+        }
+
+        _analysisFinal(data) {
+            data.tender = data.tender ? data.tender.split(',') : [];
+            data.tender_info = data.tender_info ? JSON.parse(data.tender_info) : [];
+        }
+
+        async getFinal(id) {
+            const data = await this.getDataById(id);
+            this._analysisFinal(data);
+            return data;
+        }
+
+        async addFinal(budget, tender) {
+            const user = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
+            const final = {
+                bid: budget.id, uid: user.id,
+                u_name: user.name, u_role: user.role, u_company: user.company, u_mobile: user.mobile,
+                u_login: this.ctx.session.sessionUser.loginType + ';' + this.ctx.session.sessionUser.loginStatus,
+                tender: tender ? tender.join(',') : '', status: status.ext,
+            };
+            const result = await this.db.insert(this.tableName, final);
+            return await this.getFinal(result.insertId);
+        }
+    }
+
+    return BudgetFinalList;
+};

+ 2 - 0
app/view/budget/compare.ejs

@@ -22,6 +22,8 @@
                 <div class="d-inline-block ml-3">
                     <a class="btn btn-sm btn-primary" href="#select-final" data-toggle="modal" data-target="#select-final">决算对比</a>
                 </div>
+                <div class="d-inline-block ml-2" id="final-info">
+                </div>
             </div>
         </div>
     </div>

+ 17 - 58
sql/update.sql

@@ -1,58 +1,17 @@
-ALTER TABLE `zh_material` ADD `is_new` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否是新建的调差,用于区分清单新建规则' AFTER `in_time`;
-
-ALTER TABLE `zh_change_plan` ADD `expr` TEXT NULL DEFAULT NULL COMMENT '工程量数量计算式' AFTER `memo`;
-
-ALTER TABLE `zh_change_plan_list` ADD `new_up` tinyint(1) NOT NULL DEFAULT '0' COMMENT '新增单价' AFTER `spamount`;
-
-ALTER TABLE `zh_change_plan_list` ADD `ex_memo1` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注1' AFTER `new_up`;
-
-ALTER TABLE `zh_change_plan_list` ADD `ex_memo2` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注2' AFTER `ex_memo1`;
-
-ALTER TABLE `zh_project_account` ADD `stamp_path` VARCHAR(255) NULL DEFAULT NULL COMMENT '用户签章oss地址' AFTER `sign_path`;
-
-ALTER TABLE `zh_advance_pay` ADD `pay_time` DATETIME NULL DEFAULT NULL COMMENT '支付时间' AFTER `end_time`;
-UPDATE `zh_advance_pay` SET `pay_time`= `create_time`;
-
-CREATE TABLE `zh_material_list_gcl`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `mid` int(11) NOT NULL COMMENT '调差id',
-  `order` tinyint(4) NOT NULL COMMENT '添加的历史期',
-  `gcl_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '工程量id',
-  `mb_id` int(11) NOT NULL COMMENT '工料id',
-  `quantity` decimal(30, 8) NULL DEFAULT NULL COMMENT '数目',
-  `expr` varchar(500) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '公式',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '用于新建期时清单关联已使用过的工程量,创建新的工料与清单的联系表';
-
-CREATE TABLE `zh_construction_unit`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `pid` int(11) NOT NULL COMMENT '项目id',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '单位名称',
-  `type` tinyint(2) NOT NULL COMMENT '单位类型',
-  `corporation` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '法人代表',
-  `credit_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '企业信用代码',
-  `tel` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '电话',
-  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '地址',
-  `region` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '地区',
-  `website` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '网站',
-  `basic` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注',
-  `sign_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '签章图片地址',
-  `create_time` datetime NOT NULL COMMENT '创建时间',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '参建单位表';
-
-CREATE TABLE `zh_stage_import_change` (
-  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
-  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sid` int(11) unsigned NOT NULL COMMENT '期id',
-  `lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '台账节点id',
-  `import_lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '导入的最底层项目节id',
-  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
-  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
-  `rela_lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '关联台账id',
-  `rela_cid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '关联变更令id',
-  `rela_cbid` int(11) unsigned NOT NULL COMMENT '关联变更清单id',
-  `rela_qty` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '关联数量',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=2278 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ALTER TABLE `zh_budget`
+ADD COLUMN `final_time`  timestamp NULL DEFAULT NULL COMMENT '生成动态决算时间' AFTER `decimal`,
+ADD COLUMN `final_id`  int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `final_time`;
+
+CREATE TABLE `zh_budget_final` (
+`id`  bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT ,
+`bid`  int(11) UNSIGNED NOT NULL COMMENT '动态决算id' ,
+`uid`  int(11) UNSIGNED NOT NULL COMMENT '生成决算人' ,
+`create_time`  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ,
+`update_time`  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ,
+`tender`  varchar(1000) NOT NULL DEFAULT '' COMMENT '关联标段id(\',\'分隔)' ,
+`tender_info`  text NULL COMMENT '关联标段信息(json)' ,
+`status`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '0: 取消, 1: 等待, 2: 进行中, 3: 完成' ,
+PRIMARY KEY (`id`),
+INDEX `idx_bid` (`bid`, `create_time`) USING BTREE
+)
+;

+ 58 - 0
sql/update20220416.sql

@@ -0,0 +1,58 @@
+ALTER TABLE `zh_material` ADD `is_new` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否是新建的调差,用于区分清单新建规则' AFTER `in_time`;
+
+ALTER TABLE `zh_change_plan` ADD `expr` TEXT NULL DEFAULT NULL COMMENT '工程量数量计算式' AFTER `memo`;
+
+ALTER TABLE `zh_change_plan_list` ADD `new_up` tinyint(1) NOT NULL DEFAULT '0' COMMENT '新增单价' AFTER `spamount`;
+
+ALTER TABLE `zh_change_plan_list` ADD `ex_memo1` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注1' AFTER `new_up`;
+
+ALTER TABLE `zh_change_plan_list` ADD `ex_memo2` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注2' AFTER `ex_memo1`;
+
+ALTER TABLE `zh_project_account` ADD `stamp_path` VARCHAR(255) NULL DEFAULT NULL COMMENT '用户签章oss地址' AFTER `sign_path`;
+
+ALTER TABLE `zh_advance_pay` ADD `pay_time` DATETIME NULL DEFAULT NULL COMMENT '支付时间' AFTER `end_time`;
+UPDATE `zh_advance_pay` SET `pay_time`= `create_time`;
+
+CREATE TABLE `zh_material_list_gcl`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `mid` int(11) NOT NULL COMMENT '调差id',
+  `order` tinyint(4) NOT NULL COMMENT '添加的历史期',
+  `gcl_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '工程量id',
+  `mb_id` int(11) NOT NULL COMMENT '工料id',
+  `quantity` decimal(30, 8) NULL DEFAULT NULL COMMENT '数目',
+  `expr` varchar(500) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '公式',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '用于新建期时清单关联已使用过的工程量,创建新的工料与清单的联系表';
+
+CREATE TABLE `zh_construction_unit`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `pid` int(11) NOT NULL COMMENT '项目id',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '单位名称',
+  `type` tinyint(2) NOT NULL COMMENT '单位类型',
+  `corporation` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '法人代表',
+  `credit_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '企业信用代码',
+  `tel` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '电话',
+  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '地址',
+  `region` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '地区',
+  `website` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '网站',
+  `basic` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `sign_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '签章图片地址',
+  `create_time` datetime NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '参建单位表';
+
+CREATE TABLE `zh_stage_import_change` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) unsigned NOT NULL COMMENT '期id',
+  `lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '台账节点id',
+  `import_lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '导入的最底层项目节id',
+  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
+  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
+  `rela_lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '关联台账id',
+  `rela_cid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '关联变更令id',
+  `rela_cbid` int(11) unsigned NOT NULL COMMENT '关联变更清单id',
+  `rela_qty` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '关联数量',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2278 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;