Browse Source

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

Tony Kang 3 years ago
parent
commit
277d11f332
47 changed files with 2031 additions and 871 deletions
  1. 1 1
      app/const/wechat_template.js
  2. 18 50
      app/controller/budget_controller.js
  3. 51 1
      app/controller/material_controller.js
  4. 3 3
      app/controller/tender_controller.js
  5. 244 0
      app/lib/budget_final.js
  6. 84 0
      app/lib/rm/budget.js
  7. 0 1
      app/middleware/auto_finish_logger.js
  8. BIN
      app/public/images/juecedaping01.png
  9. BIN
      app/public/images/juecedaping02.png
  10. 119 106
      app/public/js/budget_compare.js
  11. 1 1
      app/public/js/global.js
  12. 59 13
      app/public/js/material.js
  13. 59 0
      app/public/js/material_audit.js
  14. 3 2
      app/public/js/material_checklist.js
  15. 62 15
      app/public/js/material_exponent.js
  16. 9 4
      app/public/js/material_list.js
  17. 2 0
      app/public/js/measure_material.js
  18. 2 0
      app/public/js/stage_gather.js
  19. 1 1
      app/public/report/js/rpt_signature.js
  20. 1 1
      app/router.js
  21. 27 0
      app/service/budget.js
  22. 27 0
      app/service/budget_final.js
  23. 57 0
      app/service/budget_final_list.js
  24. 5 3
      app/service/ledger_audit.js
  25. 22 1
      app/service/material.js
  26. 18 19
      app/service/material_audit.js
  27. 2 2
      app/service/material_list.js
  28. 40 0
      app/service/material_list_gcl.js
  29. 18 0
      app/service/report.js
  30. 2 1
      app/service/rpt_stage_sum_memory.js
  31. 5 4
      app/service/stage.js
  32. 2 0
      app/view/budget/compare.ejs
  33. 3 0
      app/view/material/audit_btn.ejs
  34. 630 537
      app/view/material/audit_modal.ejs
  35. 28 19
      app/view/material/exponent.ejs
  36. 2 2
      app/view/material/index.ejs
  37. 22 13
      app/view/material/info.ejs
  38. 1 1
      app/view/profile/info.ejs
  39. 1 1
      app/view/revise/index.ejs
  40. 2 2
      app/view/revise/info.ejs
  41. 7 7
      app/view/tender/detail.ejs
  42. 156 0
      builder_report_index_define.js
  43. 7 8
      db_script/ledger_his.js
  44. 48 0
      db_script/recover_ledger_his.js
  45. 64 52
      sql/update.sql
  46. 58 0
      sql/update20220416.sql
  47. 58 0
      sql/update20220419.sql

+ 1 - 1
app/const/wechat_template.js

@@ -18,7 +18,7 @@ const template = {
 const templateId = {
     stage: '5vU3WmR90yDajbs4LWIWH4OQhunYlS1HXTiesIGxrsk',
     change: 'nSKl9u4DMvu7KtmFFS9bfVvznRDRpx6L7LlTnVFn4fs',
-    ledger: 'ia3ObPVe_0A1GLVpU-jcDe1P6zVriJ36eAijeQvbpFM', // 台账和台账修订共用模板
+    ledger: '9Ul3KFxvYQGfT6wbGGjqqnGR0zHtx0BHKs9sXj4Ii44', // 台账和台账修订共用模板
     revise: 'ia3ObPVe_0A1GLVpU-jcDe1P6zVriJ36eAijeQvbpFM',
     material: 'Y9ov80rev3LHcoM2hOQE6jrK_5xZsqE-PZ0Z6HS9VGA',
     advance: 'rgZHkyiLzrqaSGnQ2nSCCrOdUz2RJJZ_JA34L_MnQik',

+ 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, '获取决算数据错误');

+ 51 - 1
app/controller/material_controller.js

@@ -279,6 +279,10 @@ module.exports = app => {
             if (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) {
                 ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
             }
+
+            // 是否已验证手机短信
+            const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+            ctx.material.authMobile = pa.auth_mobile;
         }
 
         /**
@@ -975,7 +979,7 @@ module.exports = app => {
                         if (ctx.material.readOnly) {
                             throw '无权操作';
                         }
-                        await ctx.service.material.changeRate(data.rate);
+                        await ctx.service.material.changeExponentRate(data.rate);
                         break;
                     case 'paste':
                         [ex_tp, ex_expr] = await ctx.service.materialExponent.saveDatas(data.updateData);
@@ -1474,6 +1478,52 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 重新审批
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkAuditAgain(ctx) {
+            try {
+                if (ctx.query.confirm !== undefined && ctx.query.confirm !== '确认设置终审审批') {
+                    throw '请输入正确的文本信息';
+                }
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const code = ctx.query.code;
+                    const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!pa.auth_mobile) {
+                        throw '未绑定手机号';
+                    }
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+
+                if ((ctx.material.auditors[ctx.material.auditors.length - 1].aid === ctx.session.sessionUser.accountId || (ctx.query.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin)) && ctx.material.status === auditConst.status.checked && ctx.material.order === ctx.material.highOrder) {
+                    await ctx.service.materialAudit.checkAgain(ctx.material.id, ctx.material.times);
+                    // ctx.redirect(ctx.request.header.referer);
+                    ctx.body = {
+                        err: 0,
+                        url: ctx.request.header.referer,
+                        msg: '',
+                    };
+                } else {
+                    throw '您无权进行该操作';
+                }
+            } catch (err) {
+                this.log(err);
+                // ctx.session.postError = err.toString();
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
     }
 
     return MaterialController;

+ 3 - 3
app/controller/tender_controller.js

@@ -416,16 +416,16 @@ module.exports = app => {
                     tender.end_gather_tp = ctx.helper.add(tender.end_contract_tp, tender.end_qc_tp);
                     tender.pre_gather_tp = ctx.helper.add(lastStage.pre_contract_tp, lastStage.pre_qc_tp);
                     tender.yf_tp = lastStage.yf_tp;
+                    const change_tp = await ctx.service.change.getChangeTp(tender.id);
+                    tender.change_tp = change_tp;
                     tender.qc_ratio = ctx.helper.mul(ctx.helper.div(tender.end_qc_tp, ctx.tender.info.deal_param.contractPrice, 2), 100);
-                    tender.sum = ctx.helper.add(tender.total_price, tender.end_qc_tp);
+                    tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
                     tender.pre_ratio = ctx.helper.mul(ctx.helper.div(tender.pre_gather_tp, tender.sum, 2), 100);
                     tender.cur_ratio = ctx.helper.mul(ctx.helper.div(tender.gather_tp, tender.sum, 2), 100);
                     tender.other_tp = ctx.helper.sub(ctx.helper.sub(tender.sum, tender.pre_gather_tp), tender.gather_tp);
                     tender.other_ratio = Math.max(0, 100 - tender.pre_ratio - tender.cur_ratio);
                     tender.end_yf_tp = ctx.helper.add(lastStage.yf_tp, lastStage.pre_yf_tp);
                     tender.end_sf_tp = ctx.helper.add(lastStage.sf_tp, lastStage.pre_sf_tp);
-                    const change_tp = await ctx.service.change.getChangeTp(tender.id);
-                    tender.change_tp = change_tp;
                     tender.undone_tp = ctx.helper.sub(ctx.helper.sub(ctx.helper.add(tender.total_price, change_tp), tender.end_contract_tp), tender.end_qc_tp);
                     if (lastStage.status === auditConst.stage.status.uncheck) {
                         const status_name = await this.ctx.service.projectAccount.getAccountInfoById(lastStage.user_id);

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

+ 84 - 0
app/lib/rm/budget.js

@@ -0,0 +1,84 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const ledger = require('../ledger');
+
+class reportMemoryBudget {
+    constructor(ctx) {
+        this.ctx = ctx;
+        this.budget = null;
+        this.getBudget = false;
+    }
+
+    async budgetGai(bid) {
+        const gai = await this.ctx.service.budgetGai.getAllDataByCondition({ where: { bid } });
+        const tree = new ledger.billsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        tree.loadDatas(gai);
+        tree.calculateAll();
+        return tree.getDefaultDatas();
+    }
+    async budgetYu(bid) {
+        const yu = await this.ctx.service.budgetGai.getAllDataByCondition({ where: { bid } });
+        const tree = new ledger.billsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        tree.loadDatas(yu);
+        tree.calculateAll();
+        return tree.getDefaultDatas();
+    }
+    async budgetGu(bid) {
+        const yu = await this.ctx.service.budgetGai.getAllDataByCondition({ where: { bid } });
+        const tree = new ledger.billsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] });
+        tree.loadDatas(yu);
+        tree.calculateAll();
+        return tree.getDefaultDatas();
+    }
+    async budgetFinal(bid) {
+        const budget = this.ctx.budget && this.ctx.budget.id === bid
+            ? this.ctx.budget
+            : await this.ctx.service.budget.getDataById(bid);
+        if (!budget.final_id) return [];
+        const final = await this.ctx.service.budgetFinal.getAllDataByCondition({ where: { final_id: budget.final_id } });
+        const tree = new ledger.billsTree(this.ctx, { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        tree.loadDatas(final);
+        return tree.getDefaultDatas();
+    }
+
+    async _getTenderBudget(tid) {
+        if (this.getBudget) return;
+
+        const budgets = await this.ctx.service.budget.getBudget(true);
+        this.budget = budgets.find(x => {
+            const relaTender = x.rela_tender.split(',');
+            return relaTender.indexOf(tid + '') >= 0;
+        });
+        this.getBudget = true;
+    }
+
+    async tenderGai(tid) {
+        await this._getTenderBudget(tid);
+        return this.budget ? await this.budgetGai(this.budget.id) : [];
+    }
+
+    async tenderYu(tid) {
+        await this._getTenderBudget(tid);
+        return this.budget ? await this.budgetYu(this.budget.id) : [];
+    }
+
+    async tenderGu(tid) {
+        await this._getTenderBudget(tid);
+        return this.budget ? await this.budgetGu(this.budget.id) : [];
+    }
+
+    async tenderFinal(tid) {
+        await this._getTenderBudget(tid);
+        return this.budget ? await this.budgetFinal(this.budget.id) : [];
+    }
+}
+
+module.exports = reportMemoryBudget;

+ 0 - 1
app/middleware/auto_finish_logger.js

@@ -33,7 +33,6 @@ module.exports = options => {
         } else {
             logData = {
                 requestTime: ctx.logTime, responseTime, runTime,
-                data: ctx.body,
             };
         }
         const bLogger = runTime > 500 ? ctx.getLogger('warning') : ctx.getLogger('finish');

BIN
app/public/images/juecedaping01.png


BIN
app/public/images/juecedaping02.png


+ 119 - 106
app/public/js/budget_compare.js

@@ -39,13 +39,103 @@ $(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,
-    });
+    let sfSelect;
+    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, msg) {
+            if (msg) toastr.warning(msg);
+            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);
+            if (sfSelect) sfSelect.reloadSelect(this.finalInfo.tender);
+        }
+    };
 
     function compareCode(str1, str2, symbol = '-') {
         if (!str1) {
@@ -80,58 +170,13 @@ $(document).ready(() => {
         return aCodes.length - bCodes.length;
     }
 
-    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);
+    postData(window.location.pathname + '/load', {}, function (result, msg) {
+        if (result.final) {
+            compareObj.loadFinalData(result, msg);
+        } else {
+            compareObj.loadBudgetData(result);
+        }
+
     });
 
     $.subMenu({
@@ -186,6 +231,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,56 +279,20 @@ $(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, msg) {
+                    compareObj.loadFinalData(result, msg);
                     $('#select-final').modal('hide');
                 });
             });
         }
+        reloadSelect(select) {
+            if (compareObj.finalInfo) {
+                this.selectTree.datas.forEach(x => {
+                    x.selected = select.indexOf(x.tid + '') >= 0;
+                })
+            }
+            SpreadJsObj.reloadColData(this.sheet, 0);
+        }
         selectNode(node, select) {
             const posterity = this.selectTree.getPosterity(node);
             posterity.unshift(node);
@@ -295,7 +309,6 @@ $(document).ready(() => {
         }
     }
 
-    let sfSelect;
     $('#select-final').on('shown.bs.modal', () => {
         if (!sfSelect) sfSelect = new sfObject();
     });

+ 1 - 1
app/public/js/global.js

@@ -257,7 +257,7 @@ const postData = function (url, data, successCallback, errorCallBack, showWaitin
                     });
                 }
                 if (successCallback) {
-                    successCallback(result.data);
+                    successCallback(result.data, result.msg);
                 }
             } else if (result.err === 2) {
                 toastr.error('error: ' + result.msg);

+ 59 - 13
app/public/js/material.js

@@ -78,7 +78,7 @@ function resetTpTable() {
         $('#tax_rate_set').find('td').eq(1).text(ZhCalc.round(m_tax_tp, materialDecimal.tp));
         $('#tax_rate_set').find('td').eq(2).text(ZhCalc.round(ZhCalc.add(m_tax_pre_tp, m_tax_tp), materialDecimal.tp));
     } else {
-        const rate = $('#changeRate').val();
+        const rate = $('#rateInput').val();
         const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
         const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
         $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
@@ -1063,19 +1063,65 @@ $(document).ready(() => {
                 },
             }
         });
-        $('#changeRate').change(function () {
-            const rate = parseInt($(this).val());
-            postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
-                const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
-                const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
-                const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
-                const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
-                $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
-                $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
-                $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
-                $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
-            });
+        $('.changeRate').click(function () {
+            $('#rateInput').val(parseInt($(this).data('value')));
+            $('#rateInput').siblings('.dropdown-menu').hide();
+        });
+        $('#rateInput').click(function () {
+            $(this).siblings('.dropdown-menu').show();
+        })
+        // 回车提交
+        $('#rateInput').on('keypress', function () {
+            if(window.event.keyCode === 13) {
+                $(this).blur();
+            }
+        });
+        $('#rateInput').blur(function () {
+            const _self = $(this);
+            setTimeout(function () {
+                let rate = parseFloat(_self.val());
+                if (_.isNaN(rate)) {
+                    toastr.error('请输入0-100之前的整数值');
+                    $('#rateInput').val(materialRate);
+                    return;
+                }
+                rate = _.round(rate);
+                if(rate < 0 || rate > 100) {
+                    toastr.error('请输入0-100之前的整数值');
+                    $('#rateInput').val(materialRate);
+                    return;
+                }
+                $('#rateInput').siblings('.dropdown-menu').hide();
+                console.log(rate, materialRate);
+                if (rate !== materialRate) {
+                    postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
+                        const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
+                        // const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
+                        const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
+                        // const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
+                        $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
+                        $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+                        // $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                        // $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+                        materialRate = rate;
+                        $('#rateInput').val(rate);
+                    });
+                }
+            }, 500);
         });
+        // $('#changeRate').change(function () {
+        //     const rate = parseInt($(this).val());
+        //     postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
+        //         const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
+        //         const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
+        //         const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
+        //         const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
+        //         $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
+        //         $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+        //         $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+        //         $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+        //     });
+        // });
 
         $('#expr_select button').on('click', function () {
             const code = $(this).text();

+ 59 - 0
app/public/js/material_audit.js

@@ -180,6 +180,39 @@ $(document).ready(function () {
     $('#sp-list').on('hidden.bs.modal', function (e) {
         $(document.body).addClass('modal-open');
     });
+
+    // 重新审批获取手机验证码
+    // 获取验证码
+    let isPosting = false;
+    $("#get-code").click(function() {
+        if (isPosting) {
+            return false;
+        }
+        const btn = $(this);
+
+        $.ajax({
+            url: '/profile/code?_csrf_j=' + csrf,
+            type: 'post',
+            data: { mobile: authMobile, type: 'shenpi' },
+            dataTye: 'json',
+            error: function() {
+                isPosting = false;
+            },
+            beforeSend: function() {
+                isPosting = true;
+            },
+            success: function(response) {
+                isPosting = false;
+                if (response.err === 0) {
+                    codeSuccess(btn);
+                    $("input[name='code']").removeAttr('readonly');
+                    $("#re-shenpi-btn").removeAttr('disabled');
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    });
 });
 // 检查上报情况
 function checkAuditorFrom () {
@@ -210,3 +243,29 @@ function auditCheck(i) {
     }
     return true;
 }
+
+/**
+ * 获取成功后的操作
+ *
+ * @param {Object} btn - 点击的按钮
+ * @return {void}
+ */
+function codeSuccess(btn) {
+    let counter = 60;
+    btn.addClass('disabled').text('重新获取 ' + counter + 'S');
+    btn.parent().siblings('input').removeAttr('readonly').attr('placeholder', '输入短信中的6位验证码');
+    const bindBtn = $("#bind-btn");
+    bindBtn.removeClass('btn-secondary disabled').addClass('btn-primary');
+
+    const countDown = setInterval(function() {
+        const countString = counter - 1 <= 0 ? '' : ' ' + (counter - 1) + 'S';
+        // 倒数结束后
+        if (countString === '') {
+            clearInterval(countDown);
+            btn.removeClass('disabled');
+        }
+        const text = '重新获取' + countString;
+        btn.text(text);
+        counter -= 1;
+    }, 1000);
+}

+ 3 - 2
app/public/js/material_checklist.js

@@ -250,7 +250,8 @@ $(document).ready(() => {
     const materialCol = {
         readOnly: {
             isEdit: function (data) {
-                return !(!readOnly && materialBase.isEdit(data));
+                // return !(!readOnly && materialBase.isEdit(data));
+                return readOnly;
             },
         },
     };
@@ -287,7 +288,7 @@ $(document).ready(() => {
             //     return xmj && m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined);
             // });
             // 对清单调差工料table的单位数量进行改变
-            materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + gcl.unit + '数量 �';
+            materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + '每' + gcl.unit + '数量 �';
             SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
             SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialList);
         } else {

+ 62 - 15
app/public/js/material_exponent.js

@@ -13,7 +13,7 @@ function getPasteHint (str, row = '') {
     return returnObj;
 }
 function resetExTpTable() {
-    const rate = $('#changeRate').val();
+    const rate = $('#rateInput').val();
     const bqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
     const jzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, bqhs), materialDecimal.tp);
     $('#tp_set').find('td').eq(3).text(ZhCalc.round(ex_tp, materialDecimal.tp));
@@ -523,22 +523,69 @@ $(document).ready(() => {
             });
         });
 
-
-        $('#changeRate').change(function () {
-            const rate = parseInt($(this).val());
-            postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
-                const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
-                const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
-                if (!materialTax) {
-                    const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
-                    const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
-                    $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
-                    $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+        $('.changeRate').click(function () {
+            $('#rateInput').val(parseInt($(this).data('value')));
+            $('#rateInput').siblings('.dropdown-menu').hide();
+        });
+        $('#rateInput').click(function () {
+            $(this).siblings('.dropdown-menu').show();
+        })
+        // 回车提交
+        $('#rateInput').on('keypress', function () {
+            if(window.event.keyCode === 13) {
+                $(this).blur();
+            }
+        });
+        $('#rateInput').blur(function () {
+            const _self = $(this);
+            setTimeout(function () {
+                let rate = parseFloat(_self.val());
+                if (_.isNaN(rate)) {
+                    toastr.error('请输入0-100之前的整数值');
+                    $('#rateInput').val(materialRate);
+                    return;
                 }
-                $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
-                $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
-            });
+                rate = _.round(rate);
+                if(rate < 0 || rate > 100) {
+                    toastr.error('请输入0-100之前的整数值');
+                    $('#rateInput').val(materialRate);
+                    return;
+                }
+                $('#rateInput').siblings('.dropdown-menu').hide();
+                console.log(rate, materialRate);
+                if (rate !== materialRate) {
+                    postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
+                        const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
+                        const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
+                        // if (!materialTax) {
+                        //     const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
+                        //     const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
+                        //     $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
+                        //     $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+                        // }
+                        $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                        $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+                        materialRate = rate;
+                        $('#rateInput').val(rate);
+                    });
+                }
+            }, 500);
         });
+        // $('#changeRate').change(function () {
+        //     const rate = parseInt($(this).val());
+        //     postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
+        //         const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
+        //         const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
+        //         if (!materialTax) {
+        //             const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
+        //             const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
+        //             $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
+        //             $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+        //         }
+        //         $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+        //         $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+        //     });
+        // });
     }
 
     $.divResizer({

+ 9 - 4
app/public/js/material_list.js

@@ -169,7 +169,7 @@ $(document).ready(() => {
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, leafXmjs);
 
             // 对清单调差工料table的单位数量进行改变
-            materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + gcl.unit + '数量 �';
+            materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + '每' + gcl.unit + '数量 �';
             // SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
         } else {
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
@@ -331,9 +331,14 @@ $(document).ready(() => {
     };
 
     const materialBase = {
-        isEdit: function (data) {
+        isEdit: function (data, type = 'normal') {
             // 是否本期添加的工料
-            return data.order === stage_order && !openMaterialChecklist;
+            // return data.order === stage_order && !openMaterialChecklist;
+            let flag = true;
+            if (type === 'del') {
+                flag = data.order === stage_order;
+            }
+            return flag && !openMaterialChecklist;
         }
     };
 
@@ -1026,7 +1031,7 @@ $(document).ready(() => {
                             if (!select) {
                                 return true;
                             }
-                            if (!readOnly && select && materialBase.isEdit(select)) {
+                            if (!readOnly && select && materialBase.isEdit(select, 'del')) {
                                 return false;
                             } else {
                                 return true;

+ 2 - 0
app/public/js/measure_material.js

@@ -327,6 +327,8 @@ $(function () {
                                     gcl_id: xmj.gcl_id,
                                     quantity: bill.quantity,
                                     expr: bill.expr,
+                                    old_quantity: bill.quantity,
+                                    old_expr: bill.expr,
                                     mb_id: bill.mb_id,
                                     order: bill.order,
                                 });

+ 2 - 0
app/public/js/stage_gather.js

@@ -54,6 +54,8 @@ $(document).ready(function () {
     // 初始化工程量清单
     const gclSpread = SpreadJsObj.createNewSpread($('#gcl-spread')[0]);
     const gclSheet = gclSpread.getActiveSheet();
+    gclSheet.frozenColumnCount(8);
+    gclSheet.options.frozenlineColor = '#93b5e4';
     gclSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
         if (!data) return defaultColor;
         return data.overRange && hintOver ? '#f8d7da' : data.differ ? '#FFE699' : defaultColor;

+ 1 - 1
app/public/report/js/rpt_signature.js

@@ -617,7 +617,7 @@ let rptSignatureHelper = {
             domArr.push('</a>');
             //3. 显示名称
             let acc = rptSignatureHelper.getUserAccount(role.bind_acc_id);
-            domArr.push('<i class="fa fa-user"></i> ' + role.name + '<p>' + acc.name + '-<small class="text-muted">' + ((acc.role === '')?DFT_ROLE_NAME:acc.role) + '</small></p>');
+            if (acc) domArr.push('<i class="fa fa-user"></i> ' + role.name + '<p>' + acc.name + '-<small class="text-muted">' + ((acc.role === '')?DFT_ROLE_NAME:acc.role) + '</small></p>');
             ulDom.append(domArr.join(' '));
         }
     },

+ 1 - 1
app/router.js

@@ -171,7 +171,6 @@ module.exports = app => {
     app.post('/tender/:id/advance/:type/create', sessionAuth, tenderCheck, 'advanceController.create');
     app.post('/tender/:id/advance/:type/delete', sessionAuth, tenderCheck, 'advanceController.delete');
     app.get('/tender/:id/advance/:order/detail', sessionAuth, tenderCheck, advanceCheck, 'advanceController.detail');
-    app.post('/tender/:id/advance/:type/create', sessionAuth, tenderCheck, 'advanceController.create');
     app.post('/tender/:id/advance/:order/audit/add', sessionAuth, tenderCheck, advanceCheck, 'advanceController.addAudit');
     app.post('/tender/:id/advance/:order/audit/delete', sessionAuth, tenderCheck, advanceCheck, 'advanceController.deleteAudit');
     app.post('/tender/:id/advance/:order/audit/start', sessionAuth, tenderCheck, advanceCheck, 'advanceController.start');
@@ -520,6 +519,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/material/:order/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.deleteAudit');
     app.post('/tender/:id/measure/material/:order/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.startAudit');
     app.post('/tender/:id/measure/material/:order/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.checkAudit');
+    app.get('/tender/:id/measure/material/:order/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.checkAuditAgain');
     // 调差工料
     app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.info');
     app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveBillsData');

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

+ 5 - 3
app/service/ledger_audit.js

@@ -349,6 +349,8 @@ module.exports = app => {
                     tender_id: tenderId,
                     audit_order: 1,
                 });
+                const tenderMsg = await this.ctx.service.tender.getDataById(tenderId);
+                const users = this._.uniq(this._.concat(this._.map(auditList, 'audit_id'), tenderMsg.user_id));
                 if (checkType === auditConst.status.checked) {
                     const nextAudit = await this.getDataByCondition({
                         tender_id: tenderId,
@@ -377,7 +379,7 @@ module.exports = app => {
                             ledger_status: checkType,
                         });
                         await this.ctx.service.tenderTag.saveTenderTag(tenderId, {ledger_time: new Date()}, transaction);
-                        const users = this._.pull(this._.map(auditList, 'audit_id'), audit.id);
+                        // const users = this._.pull(this._.map(auditList, 'audit_id'), audit.id);
                         await this.ctx.helper.sendAliSms(users, smsTypeConst.const.TZ, smsTypeConst.judge.result.toString(), SmsAliConst.template.ledger_result, {
                             status: SmsAliConst.status.success,
                         });
@@ -407,7 +409,7 @@ module.exports = app => {
                     }
                     await transaction.insert(this.tableName, auditors);
 
-                    const users = this._.pull(this._.map(auditList, 'audit_id'), audit.id);
+                    // const users = this._.pull(this._.map(auditList, 'audit_id'), audit.id);
                     await this.ctx.helper.sendAliSms(users, smsTypeConst.const.TZ, smsTypeConst.judge.result.toString(), SmsAliConst.template.ledger_result, {
                         status: SmsAliConst.status.back,
                     });
@@ -417,7 +419,7 @@ module.exports = app => {
                         tips: wxConst.tips.back,
                         begin_time: Date.parse(begin_audit.begin_time),
                     };
-                    await this.ctx.helper.sendWechat(audit.audit_id, smsTypeConst.const.TZ, smsTypeConst.judge.result.toString(), wxConst.template.ledger, wechatData);
+                    await this.ctx.helper.sendWechat(users, smsTypeConst.const.TZ, smsTypeConst.judge.result.toString(), wxConst.template.ledger, wechatData);
                 }
 
                 await transaction.commit();

+ 22 - 1
app/service/material.js

@@ -154,6 +154,7 @@ module.exports = app => {
             try {
                 if (preMaterial) {
                     newMaterial.rate = preMaterial.rate;
+                    newMaterial.exponent_rate = preMaterial.exponent_rate;
                     newMaterial.pre_tp = this.ctx.helper.add(preMaterial.m_tp, preMaterial.pre_tp);
                     newMaterial.ex_pre_tp = this.ctx.helper.add(preMaterial.ex_tp, preMaterial.ex_pre_tp);
                     newMaterial.m_tax_pre_tp = preMaterial.material_tax ? this.ctx.helper.add(preMaterial.m_tax_tp, preMaterial.m_tax_pre_tp) : preMaterial.m_tax_pre_tp;
@@ -179,6 +180,8 @@ module.exports = app => {
                     await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, preNotJoinList, newMaterial);
                     // 新增或删除list_gcl表
                     await this.ctx.service.materialListGcl.insertOrDelGcl(transaction, data.insertGclList, data.removeGclList, newMaterial.id);
+                    // 设置list_gcl表old=>new更新
+                    await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id);
                     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
                     const [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal));
                     // 修改现行价格指数,并返回调差基数json
@@ -191,6 +194,8 @@ module.exports = app => {
                         ex_calc: JSON.stringify(ex_calc),
                     };
                     await transaction.update(this.tableName, updateMaterialData);
+                    // 删除material_list表冗余数据,减少表数据量
+                    await transaction.delete(this.ctx.service.materialList.tableName, { tid: this.ctx.tender.id, gather_qty: null });
                 }
 
                 await transaction.commit();
@@ -260,8 +265,11 @@ module.exports = app => {
                     const sqlParam2 = [this.ctx.tender.id, materialInfo.order - 1];
                     await transaction.query(sql2, sqlParam2);
                 }
+                // 设置list_gcl表old => new更新
+                await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id, 'old2new');
+                // 还要从material_list表更新gcl的old数据,更新方法
+                await this.ctx.service.materialListGcl.setOldFromLast(transaction, this.ctx.tender.id, materialInfo.order - 2);
                 await transaction.delete(this.tableName, { id });
-
                 // 记录删除日志
                 await this.ctx.service.projectLog.addProjectLog(transaction, projectLogConst.type.material, projectLogConst.status.delete, '第' + materialInfo.order + '期');
                 await transaction.commit();
@@ -306,6 +314,19 @@ module.exports = app => {
         }
 
         /**
+         * 修改增税税率
+         * @param {int} rate 税率
+         * @return {Promise<*>}
+         */
+        async changeExponentRate(rate) {
+            const updateData = {
+                id: this.ctx.material.id,
+                exponent_rate: rate,
+            };
+            return await this.db.update(this.tableName, updateData);
+        }
+
+        /**
          * 修改调差基数
          * @param {int} rate 税率
          * @return {Promise<*>}

+ 18 - 19
app/service/material_audit.js

@@ -242,7 +242,7 @@ module.exports = app => {
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(new Date()),
                     m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), material_decimal.tp)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
 
@@ -325,7 +325,7 @@ module.exports = app => {
                         tips: wxConst.tips.check,
                         begin_time: Date.parse(begin_audit.begin_time),
                         m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                        hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), material_decimal.tp)),
+                        hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                     };
                     await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
 
@@ -417,7 +417,7 @@ module.exports = app => {
                         tips: wxConst.tips.success,
                         begin_time: Date.parse(begin_audit.begin_time),
                         m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                        hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), material_decimal.tp)),
+                        hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                     };
                     await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
 
@@ -515,7 +515,7 @@ module.exports = app => {
                     tips: wxConst.tips.back,
                     begin_time: Date.parse(begin_audit.begin_time),
                     m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), material_decimal.tp)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
 
@@ -613,7 +613,7 @@ module.exports = app => {
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(begin_audit.begin_time),
                     m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), material_decimal.tp)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                 };
                 await this.ctx.helper.sendWechat(preAuditor.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
 
@@ -682,7 +682,7 @@ module.exports = app => {
             const time = new Date();
             // 整理当前流程审核人状态更新
             const audit = (await this.getAllDataByCondition({ where: { mid: materialId, times }, orders: [['order', 'desc']], limit: 1, offset: 0 }))[0];
-            if (!audit || audit.order <= 1) {
+            if (!audit || audit.order < 1) {
                 throw '审核数据错误';
             }
             const transaction = await this.db.beginTransaction();
@@ -713,19 +713,18 @@ module.exports = app => {
                     id: materialId, status: auditConst.status.checking,
                 });
 
-                // // 添加短信通知-需要审批提醒功能
-                // const smsUser = await this.ctx.service.projectAccount.getDataById(audit.aid);
-                // if (smsUser.auth_mobile !== undefined && smsUser.sms_type !== '') {
-                //     const smsType = JSON.parse(smsUser.sms_type);
-                //     if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
-                //         const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
-                //         const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
-                //         const sms = new SMS(this.ctx);
-                //         const tenderName = await sms.contentChange(tenderInfo.name);
-                //         const content = '【纵横计量支付】' + tenderName + '第' + stageInfo.order + '期,需要您审批。';
-                //         sms.send(smsUser.auth_mobile, content);
-                //     }
-                // }
+                const materialInfo = await this.ctx.service.material.getDataById(materialId);
+                const material_decimal = materialInfo && materialInfo.decimal ? JSON.parse(materialInfo.decimal) : materialConst.decimal;
+                // 微信模板通知
+                const wechatData = {
+                    qi: materialInfo.order,
+                    status: wxConst.status.check,
+                    tips: wxConst.tips.check,
+                    begin_time: Date.parse(new Date()),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
+                };
+                await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();

+ 2 - 2
app/service/material_list.js

@@ -494,7 +494,7 @@ module.exports = app => {
                             },
                             where: {
                                 tid: this.ctx.tender.id,
-                                mid: this.ctx.material.id,
+                                // mid: this.ctx.material.id,
                                 mb_id,
                                 gcl_id: xmj.gcl_id,
                             },
@@ -555,7 +555,7 @@ module.exports = app => {
                                 },
                                 where: {
                                     tid: this.ctx.tender.id,
-                                    mid: this.ctx.material.id,
+                                    // mid: this.ctx.material.id,
                                     mb_id: data.mb_id,
                                     gcl_id: xmj.gcl_id,
                                 },

+ 40 - 0
app/service/material_list_gcl.js

@@ -35,6 +35,8 @@ module.exports = app => {
                         mb_id: d.mb_id,
                         quantity: d.quantity,
                         expr: d.expr,
+                        old_quantity: d.quantity,
+                        old_expr: d.expr,
                     });
                 }
                 if (insertArray.length > 0) await transaction.insert(this.tableName, insertArray);
@@ -62,6 +64,44 @@ module.exports = app => {
                 }
             }
         }
+
+        async setNewOldData(transaction, tid, type = 'new2old') {
+            if (type === 'new2old') {
+                const sql = 'UPDATE ?? SET `old_quantity`=`quantity`, `old_expr`=`expr` WHERE tid=?';
+                const sqlParam = [this.tableName, this.ctx.tender.id];
+                return await transaction.query(sql, sqlParam);
+            }
+            const sql = 'UPDATE ?? SET `quantity`=`old_quantity`, `expr`=`old_expr` WHERE tid=?';
+            const sqlParam = [this.tableName, this.ctx.tender.id];
+            return await transaction.query(sql, sqlParam);
+        }
+
+        async setOldFromLast(transaction, tid, order) {
+            if (order >= 1) {
+                // 获取上一期的list值
+                const materialInfo = await this.ctx.service.material.getDataByCondition({ tid, order });
+                const materialList = await this.ctx.service.materialList.getAllDataByCondition({ where: { mid: materialInfo.id } });
+                const lastMaterialGclList = this._.unionWith(materialList, function(item1, item2) {
+                    return item1.gcl_id === item2.gcl_id && item1.mb_id === item2.mb_id;
+                });
+                const updateArray = [];
+                for (const lm of lastMaterialGclList) {
+                    const updateInfo = {
+                        row: {
+                            old_quantity: lm.quantity,
+                            old_expr: lm.expr,
+                        },
+                        where: {
+                            tid: this.ctx.tender.id,
+                            gcl_id: lm.gcl_id,
+                            mb_id: lm.mb_id,
+                        },
+                    };
+                    updateArray.push(updateInfo);
+                }
+                if (updateArray.length > 0) await transaction.updateRows(this.tableName, updateArray);
+            }
+        }
     }
     return MaterialListGcl;
 };

+ 18 - 0
app/service/report.js

@@ -8,6 +8,7 @@
  * @version
  */
 
+const BudgetSource = require('../lib/rm/budget');
 const rptCustomData = require('../lib/rptCustomData');
 
 module.exports = app => {
@@ -38,6 +39,7 @@ module.exports = app => {
             const rst = {};
             const runnableRst = [];
             const runnableKey = []; // 这个配合runnableRst用,未来考虑并行查询优化
+            const budgetSource = new BudgetSource(this.ctx);
             for (const filter of filters) {
                 if (runnableKey.indexOf(filter) < 0) {
                     switch (filter) {
@@ -246,6 +248,22 @@ module.exports = app => {
                             runnableRst.push(service.stageRelaImBills.getAllDataByCondition({ where: { tid: params.tender_id, sid: params.stage_id } }));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_budget_gu':
+                            runnableRst.push(params.budget_id ? budgetSource.budgetGu(params.budget_id) : budgetSource.tenderGu(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_budget_gai':
+                            runnableRst.push(params.budget_id ? budgetSource.budgetGai(params.budget_id) : budgetSource.tenderGai(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_budget_yu':
+                            runnableRst.push(params.budget_id ? budgetSource.budgetYu(params.budget_id) : budgetSource.tenderYu(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_budget_final':
+                            runnableRst.push(params.budget_id ? budgetSource.budgetFinal(params.budget_id) : budgetSource.tenderFinal(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
                         default:
                             break;
                     }

+ 2 - 1
app/service/rpt_stage_sum_memory.js

@@ -277,9 +277,10 @@ module.exports = app => {
                 const curMaterial = material.status === auditConst.material.status.checked
                     ? await this.ctx.service.materialBillsHistory.getAllDataByCondition({ where: { mid: material.id } })
                     : await this.ctx.service.materialBills.getAllDataByCondition({ where: { tid }});
+                const relaId = material.status === auditConst.material.status.checked ? 'mb_id' : 'id';
 
                 this.ctx.helper.assignRelaData(materialData, [
-                    {data: curMaterial, fields, prefix: prefix, relaId: 'mb_id', defaultData: defaultData}
+                    {data: curMaterial, fields, prefix: prefix, relaId, defaultData: defaultData}
                 ]);
             }
 

+ 5 - 4
app/service/stage.js

@@ -68,7 +68,7 @@ module.exports = app => {
             return result;
         }
 
-        async doCheckStage(stage, formDataCollect = false) {
+        async doCheckStage(stage, force = false) {
             const status = auditConst.status;
             stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
             stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
@@ -76,6 +76,7 @@ module.exports = app => {
             const accountId = this.ctx.session.sessionUser.accountId,
                 auditorIds = this._.map(stage.auditors, 'aid'),
                 shareIds = [];
+            const isTenderTourist = await this.service.tenderTourist.getDataByCondition({ tid: stage.tid, user_id: accountId });
             const permission = this.ctx.session.sessionUser.permission;
             if (accountId === stage.user_id) { // 原报
                 if (stage.curAuditor) {
@@ -91,7 +92,7 @@ module.exports = app => {
                 } else {
                     stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
                 }
-            } else if ((this.ctx.tender && this.ctx.tender.isTourist) || formDataCollect) { // 游客
+            } else if (!!isTenderTourist || force) { // 游客
                 stage.readOnly = true;
                 stage.curTimes = stage.times;
                 if (stage.status === status.uncheck || stage.status === status.checkNo) {
@@ -218,10 +219,10 @@ module.exports = app => {
             return await this.db.query(sql, sqlParam);
         }
 
-        async checkStageGatherData(stage, formDataCollect = false) {
+        async checkStageGatherData(stage, force = false) {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
             if (stage.status !== auditConst.status.checked) {
-                await this.doCheckStage(stage, formDataCollect);
+                await this.doCheckStage(stage, force);
                 if (!stage.readOnly && stage.check_calc) {
                     const tpData = await this.ctx.service.stageBills.getSumTotalPrice(stage);
                     stage.contract_tp = tpData.contract_tp;

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

+ 3 - 0
app/view/material/audit_btn.ejs

@@ -20,4 +20,7 @@
             <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm btn-block sp-list-btn">重新上报</a>
         <% } %>
     <% } %>
+    <% if (ctx.material.auditors !== undefined && ctx.material.auditors.length !== 0 && ctx.material.auditors[ctx.material.auditors.length-1].aid === ctx.session.sessionUser.accountId && ctx.material.status === auditConst.status.checked && ctx.material.order === ctx.material.highOrder) { %>
+        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
+    <% } %>
 </div>

File diff suppressed because it is too large
+ 630 - 537
app/view/material/audit_modal.ejs


+ 28 - 19
app/view/material/exponent.ejs

@@ -43,15 +43,23 @@
                             <div class="input-group-prepend">
                                 <span class="input-group-text" id="basic-addon1">增值税税率</span>
                             </div>
-                            <select class="form-control form-control-sm col-1" id="changeRate">
-                                <% if (!material.readOnly) { %>
-                                    <option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>
-                                    <option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>
-                                    <option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>
-                                <% } else { %>
-                                    <option value="<%= material.rate %>" selected><%= material.rate %>%</option>
-                                <% } %>
-                            </select>
+                            <input id="rateInput" class="form-control form-control-sm col-1" <% if (material.readOnly) { %>disabled<% } %> type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="100" value="<%- material.exponent_rate %>">
+                            <% if (!material.readOnly) { %>
+                                <div class="dropdown-menu">
+                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="9">9%</a>
+                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="10">10%</a>
+                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="11">11%</a>
+                                </div>
+                            <% } %>
+                            <!--<select class="form-control form-control-sm col-1" id="changeRate">-->
+                                <!--<% if (!material.readOnly) { %>-->
+                                    <!--<option value="9" <% if(material.exponent_rate === 9) { %>selected<% } %>>9%</option>-->
+                                    <!--<option value="10" <% if(material.exponent_rate === 10) { %>selected<% } %>>10%</option>-->
+                                    <!--<option value="11" <% if(material.exponent_rate === 11) { %>selected<% } %>>11%</option>-->
+                                <!--<% } else { %>-->
+                                    <!--<option value="<%= material.exponent_rate %>" selected><%= material.exponent_rate %>%</option>-->
+                                <!--<% } %>-->
+                            <!--</select>-->
                         </div>
                     </div>
                     <% if (!material.material_tax && !old_had_tax) { %>
@@ -72,8 +80,8 @@
                                     <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
                                         <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
                                         <td class="text-center"><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
+                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
+                                        <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
                                     </tr>
                                 </table>
                             </div>
@@ -93,17 +101,17 @@
                                         <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.decimal.tp) : null %></td>
                                         <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.ex_pre_tp, material.ex_tp), material.decimal.tp) : null %></td>
                                     </tr>
-                                    <tr><td>材料价差费用(含材料税)</td>
-                                        <td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null ? material.m_tax_tp : null) : '-' %></td>
-                                        <td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null || material.m_tax_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.m_tax_pre_tp, material.m_tax_tp), material.decimal.tp) : null) : material.m_tax_pre_tp %></td>
-                                        <td class="text-center">-</td>
-                                        <td class="text-center">-</td>
-                                    </tr>
+                                    <!--<tr><td>材料价差费用(含材料税)</td>-->
+                                        <!--<td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null ? material.m_tax_tp : null) : '-' %></td>-->
+                                        <!--<td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null || material.m_tax_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.m_tax_pre_tp, material.m_tax_tp), material.decimal.tp) : null) : material.m_tax_pre_tp %></td>-->
+                                        <!--<td class="text-center">-</td>-->
+                                        <!--<td class="text-center">-</td>-->
+                                    <!--</tr>-->
                                     <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
                                         <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp) : null) : '-' %></td>
                                         <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null) : pre_tp_hs !== null ? pre_tp_hs : '-' %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
+                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
+                                        <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
                                     </tr>
                                 </table>
                             </div>
@@ -164,5 +172,6 @@
     const materialType = JSON.parse('<%- JSON.stringify(materialType) %>');
     const ex_calc = JSON.parse('<%- JSON.stringify(ex_calc) %>');
     let ex_expr = '<%- material.ex_expr %>';
+    let materialRate = parseInt('<%- material.exponent_rate %>');
     let materialExponentData = JSON.parse(unescape('<%- escape(JSON.stringify(materialExponentData)) %>'));
 </script>

+ 2 - 2
app/view/material/index.ejs

@@ -59,10 +59,10 @@
                             <% if (openMaterialTax) { %><td class="text-right"><% if (m.material_tax) { %><% if (m.m_tax_tp) { %><%- m.m_tax_tp %><% } else { %><%- m.m_tp %><% } %><% } %></td><% } %>
                             <% if ((openMaterialTax && !allMaterialTax) || !openMaterialTax) { %><td class="text-right"><% if (!m.material_tax) { %><%= m.m_tp !== null ? ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), m.decimal.tp) : null %><% } %></td><% } %>
                             <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(m.ex_tp, m.decimal.tp) : null %></td>
-                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), m.decimal.tp) : null %></td>
+                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %></td>
                             <td class="text-right"><%= ctx.helper.add(ctx.helper.round(m.ex_tp, m.decimal.tp), ctx.helper.round(m.m_tp, m.decimal.tp)) %></td>
                             <% if (openMaterialTax) { %><td class="text-right"><% if (m.material_tax) { %><% if (m.m_tax_tp) { %><%- m.m_tax_tp %><% } else { %><%- m.m_tp %><% } %><% } %></td><% } %>
-                            <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), m.decimal.tp) : null %><% } else { %><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), m.decimal.tp), ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), m.decimal.tp)) %><% } %></td>
+                            <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %><% } else { %><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp), ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), m.decimal.tp)) %><% } %></td>
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
                                 <% if (m.curAuditor) { %>
                                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditor.name %><%if (m.curAuditor.role !== '' && m.curAuditor.role !== null) { %>-<%- m.curAuditor.role %><% } %></a>

+ 22 - 13
app/view/material/info.ejs

@@ -56,15 +56,23 @@
                             <div class="input-group-prepend">
                                 <span class="input-group-text" id="basic-addon1">建筑增值税</span>
                             </div>
-                            <select class="form-control form-control-sm col-1" id="changeRate" <% if (material.material_tax) { %>disabled<% } %>>
-                                <% if (!material.readOnly) { %>
-                                    <option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>
-                                    <option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>
-                                    <option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>
-                                <% } else { %>
-                                    <option value="<%= material.rate %>" selected><%= material.rate %>%</option>
-                                <% } %>
-                            </select>
+                            <input id="rateInput" class="form-control form-control-sm col-1" <% if (material.material_tax || material.readOnly) { %>disabled<% } %> type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="100" value="<%- material.rate %>">
+                            <% if (!material.readOnly || !material.material_tax) { %>
+                            <div class="dropdown-menu">
+                                <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="9">9%</a>
+                                <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="10">10%</a>
+                                <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="11">11%</a>
+                            </div>
+                            <% } %>
+                            <!--<select class="form-control form-control-sm col-1" id="changeRate" <% if (material.material_tax) { %>disabled<% } %>>-->
+                                <!--<% if (!material.readOnly) { %>-->
+                                    <!--<option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>-->
+                                    <!--<option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>-->
+                                    <!--<option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>-->
+                                <!--<% } else { %>-->
+                                    <!--<option value="<%= material.rate %>" selected><%= material.rate %>%</option>-->
+                                <!--<% } %>-->
+                            <!--</select>-->
                         </div>
                     </div>
                     <% if (!material.material_tax && !old_had_tax) { %>
@@ -85,8 +93,8 @@
                                 <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
                                     <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
                                     <td class="text-center"><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
-                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
-                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
                                 </tr>
                             </table>
                         </div>
@@ -115,8 +123,8 @@
                                 <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
                                     <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp) : null) : '-' %></td>
                                     <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null) : pre_tp_hs !== null ? pre_tp_hs : '-' %></td>
-                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
-                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)), material.decimal.tp) : null %></td>
                                 </tr>
                             </table>
                         </div>
@@ -166,6 +174,7 @@
     const readOnly = <%- material.readOnly %>;
     const materialID = <%- material.id %>;
     const materialTax = <%- material.material_tax %>;
+    let materialRate = parseInt('<%- material.rate %>');
     const materialDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.decimal)) %>'));
     let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
     let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;

+ 1 - 1
app/view/profile/info.ejs

@@ -14,7 +14,7 @@
                         <form action="/profile/save" method="post" id="base-form" onsubmit="return checkUserForm();">
                             <input-text label="账号" value="<%= accountData.account %>" readonly="readonly"></input-text>
                             <input-text label="姓名" value="<%= accountData.name %>" placeholder="请输入姓名" name="name"></input-text>
-                            <input-text label="单位" value="<%= accountData.company %>" name="company"></input-text>
+                            <input-text label="单位" value="<%= accountData.company %>" name="company" :readonly="true"></input-text>
                             <input-text label="角色/职称" value="<%= accountData.role %>" name="role"></input-text>
                             <input-text label="手机" value="<%= accountData.mobile %>" name="mobile" maxlength="11" :readonly="<%= accountData.bind === 1 %>" msg="已绑定第三方平台,无法修改"></input-text>
                             <input-text label="电话" value="<%= accountData.telephone %>" name="telephone"></input-text>

+ 1 - 1
app/view/revise/index.ejs

@@ -71,7 +71,7 @@
                             <% } else if (lr.status === auditConst.status.checkNo && lr.uid === ctx.session.sessionUser.accountId) { %>
                             <a href="<%- preUrl + '/revise/'  + lr.id + '/info' %>" class="btn btn-primary btn-sm">重新上报</a>
                             <a href="#remove" data-toggle="modal" data-target="#remove" class="btn btn-secondary btn-sm">作废</a>
-                            <% } else if (lr.status !== auditConst.status.checked) { %>
+                            <% } else if (lr.status !== auditConst.status.uncheck && lr.status !== auditConst.status.checked) { %>
                             <a href="<%- preUrl + '/revise/' + lr.id + '/info' %>">查看修订内容</a>
                             <% } %>
                             <% } %>

+ 2 - 2
app/view/revise/info.ejs

@@ -4,7 +4,6 @@
         <div class="title-main  d-flex">
             <% include ./sub_mini_menu.ejs %>
             <!--工具-->
-            <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && revise.uid === ctx.session.sessionUser.accountId) { %>
             <div>
                 <div class="d-inline-block">
                     <div class="dropdown">
@@ -22,6 +21,7 @@
                         </div>
                     </div>
                 </div>
+                <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && revise.uid === ctx.session.sessionUser.accountId) { %>
                 <div class="d-inline-block">
                     <a href="javascript: void(0);" name="base-opr" type="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
                     <a href="javascript: void(0);" name="base-opr" type="delete" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
@@ -41,8 +41,8 @@
                         <input type="text" class="form-control form-control-sm m-0" id="bills-expr" readonly="">
                     </div>
                 </div>
+                <% } %>
             </div>
-            <% } %>
             <div class="ml-auto">
                 <% if (revise.status !== audit.status.checked) { %>
                     <a class="btn btn-sm btn-primary mr-1" id="ledger-check2" href="javascript: void(0);">数据检查</a>

+ 7 - 7
app/view/tender/detail.ejs

@@ -94,7 +94,7 @@
                                     <div class="col-auto pl-0">
                                         <div class="card text-center <% if (!ctx.tender.info.display.dayMode) { %>bg-dark text-white<% } %>">
                                             <div class="card-body">
-                                                <h5 class="card-title card-big-title"><%- ctx.helper.formatMoney(ctx.helper.add(tender.total_price, tender.change_tp)) %></h5>
+                                                <h5 class="card-title card-big-title"><%- ctx.helper.formatMoney(tender.sum) %></h5>
                                                 <p class="card-text text-muted">变更后金额</p>
                                             </div>
                                         </div>
@@ -1175,7 +1175,7 @@
                         if (index === 0) {
                             historyHTML += `<li class="timeline-list-item pb-2">
                             <div class="timeline-item-date">
-                                ${formatDate(auditor.begin_time)}
+                                ${formatDate(auditor.begin_time, !dayMode)}
                             </div>
                             <div class="timeline-item-tail"></div>
                             <div class="timeline-item-icon bg-success text-light">
@@ -1197,7 +1197,7 @@
                         </li>
                         <li class="timeline-list-item pb-2">
                             <div class="timeline-item-date">
-                                ${formatDate(auditor.end_time)}
+                                ${formatDate(auditor.end_time, !dayMode)}
                             </div>`
 
                             if(index < auditors.length - 1) {
@@ -1239,7 +1239,7 @@
                         } else {
                             historyHTML += `<li class="timeline-list-item pb-2">
                         <div class="timeline-item-date">
-                            ${formatDate(auditor.end_time)}
+                            ${formatDate(auditor.end_time, !dayMode)}
                         </div>`
 
                             if(index < auditors.length - 1) {
@@ -1313,7 +1313,7 @@
             }
         });
 
-        function formatDate(date) {
+        function formatDate(date, dayMode) {
             if (!date) return '';
             date = new Date(date)
             const year = date.getFullYear();
@@ -1337,7 +1337,7 @@
             if (scond < 10) {
                 scond = '0' + scond.toString();
             }
-            return `${year}<span class="text-light">${mon}-${day}</span><span class="text-light">${hour}:${minute}:${scond}</span>`;
+            return `${year}<span class="${dayMode ? 'text-light' : ''}">${mon}-${day}</span><span class="${dayMode ? 'text-light' : ''}">${hour}:${minute}:${scond}</span>`;
         };
     })
 </script>
@@ -1377,7 +1377,7 @@
                     const center = JSON.parse(tm.center);
                     pointList.push(turf.point([center.lng, center.lat]));
                     if (tm.tips) {
-                        const centerResult = gcoord.transform([map_json[Math.ceil(map_json.length/2)].lng, map_json[Math.ceil(map_json.length/2)].lat], gcoord.WGS84, gcoord.BD09);
+                        const centerResult = gcoord.transform([map_json[Math.floor(map_json.length/2)].lng, map_json[Math.floor(map_json.length/2)].lat], gcoord.WGS84, gcoord.BD09);
                         centerList.push({ map: new BMapGL.Point(centerResult[0], centerResult[1]), color: tm.color, tips: tm.tips });
                     }
                 }

+ 156 - 0
builder_report_index_define.js

@@ -2461,6 +2461,161 @@ const custom_select = {
     ]
 };
 
+const budget = {
+    gu: {
+        name: '【动态决算】 估算 (mem_budget_gu)',
+        remark: '',
+        id: 90,
+        key: 'mem_budget_gu',
+        prefix: '【动态决算】 估算',
+        cols: [
+            { name: 'ID', field: 'id', type: dataType.int },
+            { name: '概算投资ID', field: 'bid', type: dataType.int },
+            { name: '树结构-ID', field: 'tree_id', type: dataType.int },
+            { name: '树结构-父项ID', field: 'tree_pid', type: dataType.int },
+            { name: '树结构-层级', field: 'level', type: dataType.int },
+            { name: '树结构-同层排序', field: 'order', type: dataType.int },
+            { name: '树结构-完整路径', field: 'full_path', type: dataType.str },
+            { name: '树结构-是否子项', field: 'is_leaf', type: dataType.int }, // 8
+
+            { name: '项目节编号', field: 'code', type: dataType.str },
+            { name: '清单编号', field: 'b_code', type: dataType.str },
+            { name: '名称', field: 'name', type: dataType.str },
+            { name: '单位', field: 'unit', type: dataType.str }, // 12
+            { name: '单价', field: 'unit_price', type: dataType.currency },
+
+            { name: '台账-数量', field: 'quantity', type: dataType.currency },
+            { name: '台账-金额', field: 'total_price', type: dataType.currency },
+
+            { name: '项目节-数量1', field: 'dgn_qty1', type: dataType.currency },
+            { name: '项目节-数量2', field: 'dgn_qty2', type: dataType.currency },
+
+            { name: '图册号', field: 'drawing_code', type: dataType.str },
+            { name: '备注', field: 'memo', type: dataType.str },
+            { name: '节点类型', field: 'node_type', type: dataType.int },
+        ],
+    },
+    gai: {
+        name: '【动态决算】 概算 (mem_budget_gai)',
+        remark: '',
+        id: 91,
+        key: 'mem_budget_gai',
+        prefix: '【动态决算】 概算',
+        cols: [
+            { name: 'ID', field: 'id', type: dataType.int },
+            { name: '概算投资ID', field: 'bid', type: dataType.int },
+            { name: '树结构-ID', field: 'tree_id', type: dataType.int },
+            { name: '树结构-父项ID', field: 'tree_pid', type: dataType.int },
+            { name: '树结构-层级', field: 'level', type: dataType.int },
+            { name: '树结构-同层排序', field: 'order', type: dataType.int },
+            { name: '树结构-完整路径', field: 'full_path', type: dataType.str },
+            { name: '树结构-是否子项', field: 'is_leaf', type: dataType.int }, // 8
+
+            { name: '项目节编号', field: 'code', type: dataType.str },
+            { name: '清单编号', field: 'b_code', type: dataType.str },
+            { name: '名称', field: 'name', type: dataType.str },
+            { name: '单位', field: 'unit', type: dataType.str }, // 12
+            { name: '单价', field: 'unit_price', type: dataType.currency },
+
+            { name: '台账-数量', field: 'quantity', type: dataType.currency },
+            { name: '台账-金额', field: 'total_price', type: dataType.currency },
+
+            { name: '项目节-数量1', field: 'dgn_qty1', type: dataType.currency },
+            { name: '项目节-数量2', field: 'dgn_qty2', type: dataType.currency },
+
+            { name: '图册号', field: 'drawing_code', type: dataType.str },
+            { name: '备注', field: 'memo', type: dataType.str },
+            { name: '节点类型', field: 'node_type', type: dataType.int },
+        ],
+    },
+    yu: {
+        name: '【动态决算】 预算 (mem_budget_yu)',
+        remark: '',
+        id: 92,
+        key: 'mem_budget_yu',
+        prefix: '【动态决算】 预算',
+        cols: [
+            { name: 'ID', field: 'id', type: dataType.int },
+            { name: '概算投资ID', field: 'bid', type: dataType.int },
+            { name: '树结构-ID', field: 'tree_id', type: dataType.int },
+            { name: '树结构-父项ID', field: 'tree_pid', type: dataType.int },
+            { name: '树结构-层级', field: 'level', type: dataType.int },
+            { name: '树结构-同层排序', field: 'order', type: dataType.int },
+            { name: '树结构-完整路径', field: 'full_path', type: dataType.str },
+            { name: '树结构-是否子项', field: 'is_leaf', type: dataType.int }, // 8
+
+            { name: '项目节编号', field: 'code', type: dataType.str },
+            { name: '清单编号', field: 'b_code', type: dataType.str },
+            { name: '名称', field: 'name', type: dataType.str },
+            { name: '单位', field: 'unit', type: dataType.str }, // 12
+            { name: '单价', field: 'unit_price', type: dataType.currency },
+
+            { name: '台账-数量', field: 'quantity', type: dataType.currency },
+            { name: '台账-金额', field: 'total_price', type: dataType.currency },
+
+            { name: '项目节-数量1', field: 'dgn_qty1', type: dataType.currency },
+            { name: '项目节-数量2', field: 'dgn_qty2', type: dataType.currency },
+
+            { name: '图册号', field: 'drawing_code', type: dataType.str },
+            { name: '备注', field: 'memo', type: dataType.str },
+            { name: '节点类型', field: 'node_type', type: dataType.int },
+        ],
+    },
+    final: {
+        name: '【动态决算】 决算对比 (mem_budget_final)',
+        remark: '',
+        id: 93,
+        key: 'mem_budget_final',
+        prefix: '【动态决算】 决算对比',
+        cols: [
+            { name: 'ID', field: 'id', type: dataType.int },
+            { name: '概算投资ID', field: 'bid', type: dataType.int },
+            { name: '决算ID', field: 'final_id', type: dataType.int },
+
+            { name: '树结构-ID', field: 'tree_id', type: dataType.int },
+            { name: '树结构-父项ID', field: 'tree_pid', type: dataType.int },
+            { name: '树结构-层级', field: 'level', type: dataType.int },
+            { name: '树结构-同层排序', field: 'order', type: dataType.int },
+            { name: '树结构-完整路径', field: 'full_path', type: dataType.str },
+            { name: '树结构-是否子项', field: 'is_leaf', type: dataType.int }, // 8
+
+            { name: '项目节编号', field: 'code', type: dataType.str },
+            { name: '名称', field: 'name', type: dataType.str },
+            { name: '单位', field: 'unit', type: dataType.str }, // 12
+            { name: '单价', field: 'unit_price', type: dataType.currency },
+
+            { name: '估算-项目节-数量1', field: 'gu_dgn_qty1', type: dataType.currency },
+            { name: '估算-项目节-数量2', field: 'gu_dgn_qty2', type: dataType.currency },
+            { name: '估算-金额', field: 'gu_tp', type: dataType.currency },
+
+            { name: '概算-项目节-数量1', field: 'gai_dgn_qty1', type: dataType.currency },
+            { name: '概算-项目节-数量2', field: 'gai_dgn_qty2', type: dataType.currency },
+            { name: '概算-金额', field: 'gai_tp', type: dataType.currency },
+
+            { name: '预算-项目节-数量1', field: 'yu_dgn_qty1', type: dataType.currency },
+            { name: '预算-项目节-数量2', field: 'yu_dgn_qty2', type: dataType.currency },
+            { name: '预算-金额', field: 'yu_tp', type: dataType.currency },
+
+            { name: '决算-台账-项目节-数量1', field: 'dgn_qty1', type: dataType.currency },
+            { name: '决算-台账-项目节-数量2', field: 'dgn_qty2', type: dataType.currency },
+            { name: '决算-台账-金额', field: 'total_price', type: dataType.currency },
+            { name: '决算-台账-经济指标', field: 'dgn_price', type: dataType.currency },
+            { name: '决算-台账-数量1/数量2', field: 'dgn_qty', type: dataType.currency },
+
+            { name: '决算-项目节-数量1', field: 'final_dgn_qty1', type: dataType.currency },
+            { name: '决算-项目节-数量2', field: 'final_dgn_qty2', type: dataType.currency },
+            { name: '决算-金额', field: 'final_tp', type: dataType.currency },
+            { name: '决算-经济指标', field: 'final_dgn_price', type: dataType.currency },
+            { name: '决算-数量1/数量2', field: 'final_dgn_qty', type: dataType.currency },
+
+            { name: '增减%-项目节-数量1', field: 'grow_dgn_qty1', type: dataType.currency },
+            { name: '增减%-项目节-数量2', field: 'grow_dgn_qty2', type: dataType.currency },
+            { name: '增减%-金额', field: 'grow_tp', type: dataType.currency },
+            { name: '增减%-数量1/数量2', field: 'grow_dgn_qty', type: dataType.currency },
+        ],
+    }
+};
+
 const recursiveMkdirSync = async function(pathName) {
     if (!fs.existsSync(pathName)) {
         const upperPath = path.dirname(pathName);
@@ -2580,6 +2735,7 @@ const defines = [
     jh_im_change, jh_gather_im_change, jh_gather_stage_bills_compare,
     custom_select,
     stage_change_info, stage_change_info_bills,
+    budget.gu, budget.gai, budget.yu, budget.final,
 ];
 for (const d of defines) {
     exportTableDefine(d);

+ 7 - 8
db_script/ledger_his.js

@@ -58,13 +58,12 @@ const querySql = async function(sql, sqlParam) {
 };
 
 const getTableName = function(tender, table) {
-    // switch(table) {
-    //     case 'ledger': return 'zh_ledger_' + (tender.id % 10);
-    //     case 'pos': return 'zh_pos_' + (tender.id % 20);
-    //     case 'revise_bills': return 'zh_revise_bills_' + (tender.id % 10);
-    //     case 'revise_pos': return 'zh_revise_pos_' + (tender.id % 20);
-    // }
-    return table.indexOf('pos') > 0 ? `zh_${table}_${tender.id % 20}` : `zh_${table}_${tender.id % 10}`;
+    switch(table) {
+        case 'ledger': return 'zh_ledger_' + (tender.id % 10);
+        case 'pos': return 'zh_pos_' + (tender.id % 20);
+        case 'revise_bills': return 'zh_revise_bills_' + (tender.id % 10);
+        case 'revise_pos': return 'zh_revise_pos_' + (tender.id % 20);
+    }
 };
 
 const saveLedgerHis = async function(tender) {
@@ -115,7 +114,7 @@ const doCompleteTender = async function(t) {
                 await querySql('Update zh_ledger_revise Set his_id = ? Where id = ?', [r.his_id, r.id]);
             } else {
                 if (r.status !== 1) withoutHisRevise.push(r);
-            }
+
         }
         if (!his_id || t.measure_type === measureType.gcl.value) his_id = await saveLedgerHis(t);
         await querySql('Update zh_tender Set his_id = ? Where id = ?', [his_id, t.id]);

+ 48 - 0
db_script/recover_ledger_his.js

@@ -0,0 +1,48 @@
+const fs = require('fs');
+const path = require('path');
+
+const measureType = require('../app/const/tender').measureType;
+
+const mysql = require('mysql');
+const oss = require('ali-oss');
+const config = process.argv.splice(2)[0];
+if (['local', 'uat', 'default'].indexOf(config) < 0) throw `参数错误: ${config}`;
+const options = require(`../config/config.${config}`)({ baseDir: __dirname + '/app', root: __dirname, name: 'calc' });
+
+const pool = mysql.createPool(options.mysql.client);
+const ossOption = {
+    bucket: options.oss.clients.his.bucket,
+    accessKeyId: options.oss.default.accessKeyId,
+    accessKeySecret: options.oss.default.accessKeySecret,
+    endpoint: options.oss.default.endpoint,
+    timeout: options.oss.default.timeout,
+};
+const ossClient = new oss(ossOption);
+
+const querySql = async function(sql, sqlParam) {
+    return new Promise(function(resolve, reject) {
+        pool.getConnection(function(err, conn) {
+            if (err) {
+                if (err) console.log(err);
+                reject(err);
+            } else {
+                conn.query(sql, sqlParam, function(err, rows, fields) {
+                    if (err) console.log(err);
+                    // 释放连接
+                    conn.release();
+                    // 传递Promise回调对象
+                    resolve(rows);
+                });
+            }
+        });
+    });
+};
+
+const getTableName = function(tender, table) {
+    switch(table) {
+        case 'ledger': return 'zh_ledger_' + (tender.id % 10);
+        case 'pos': return 'zh_pos_' + (tender.id % 20);
+        case 'revise_bills': return 'zh_revise_bills_' + (tender.id % 10);
+        case 'revise_pos': return 'zh_revise_pos_' + (tender.id % 20);
+    }
+};

+ 64 - 52
sql/update.sql

@@ -1,58 +1,70 @@
-ALTER TABLE `zh_material` ADD `is_new` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否是新建的调差,用于区分清单新建规则' AFTER `in_time`;
+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`;
 
-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`;
+CREATE TABLE `zh_budget_final_list` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `bid` int(11) unsigned NOT NULL COMMENT '动态决算id',
+  `uid` int(11) unsigned NOT NULL COMMENT '生成决算人id',
+  `u_name` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '生成决算人-名字(缓存)',
+  `u_role` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '生成决算人-角色(缓存)',
+  `u_company` varchar(60) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '生成决算人-单位(缓存)',
+  `u_mobile` varchar(15) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '生成决算人-电话',
+  `u_login` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' 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) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '关联标段id('',''分隔)',
+  `tender_info` text COLLATE utf8_unicode_ci COMMENT '关联标段信息(json)',
+  `status` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '0: 取消, 1: 等待, 2: 进行中, 3: 完成',
+  PRIMARY KEY (`id`),
+  KEY `idx_bid` (`bid`,`create_time`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-ALTER TABLE `zh_project_account` ADD `stamp_path` VARCHAR(255) NULL DEFAULT NULL COMMENT '用户签章oss地址' AFTER `sign_path`;
+CREATE TABLE `zh_budget_final` (
+  `id` varchar(36) COLLATE utf8_unicode_ci NOT NULL,
+  `bid` int(11) unsigned NOT NULL COMMENT '概算投资项目id',
+  `final_id` bigint(11) unsigned NOT NULL COMMENT '决算id',
+  `tree_id` int(11) unsigned NOT NULL COMMENT '树结构id',
+  `tree_pid` int(11) NOT NULL COMMENT '树结构父id',
+  `level` int(11) unsigned NOT NULL COMMENT '层级',
+  `order` int(11) unsigned NOT NULL COMMENT '同级排序',
+  `full_path` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '层级定位辅助字段parent.full_path.ledger_id',
+  `is_leaf` tinyint(1) NOT NULL COMMENT '是否叶子节点,界面显示辅助字段',
+  `code` varchar(50) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '节点编号',
+  `name` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '名称',
+  `unit` varchar(15) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '单位',
+  `gu_dgn_qty1` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '估算-设计数量1',
+  `gu_dgn_qty2` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '估算-设计数量1',
+  `gu_tp` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '估算-金额',
+  `gai_dgn_qty1` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '概算-设计数量1',
+  `gai_dgn_qty2` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '概算-设计数量1',
+  `gai_tp` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '概算-金额',
+  `yu_dgn_qty1` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '预算-设计数量1',
+  `yu_dgn_qty2` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '预算-设计数量1',
+  `yu_tp` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '预算-金额',
+  `dgn_qty1` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-台账-设计数量1',
+  `dgn_qty2` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-台账-设计数量1',
+  `total_price` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-台账-经济指标',
+  `dgn_price` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-台账-经济指标',
+  `dgn_qty` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '决算-台账-设计数量1/设计数量2',
+  `final_dgn_qty1` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-设计数量1',
+  `final_dgn_qty2` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-设计数量1',
+  `final_tp` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-金额',
+  `final_dgn_price` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '决算-经济指标',
+  `final_dgn_qty` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '决算-设计数量1/设计数量2',
+  `grow_dgn_qty1` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '增幅%-设计数量1',
+  `grow_dgn_qty2` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '增幅%-设计数量1',
+  `grow_tp` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '增幅%-金额',
+  `grow_dgn_qty` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '增幅%-设计数量1/设计数量2',
+  PRIMARY KEY (`id`),
+  KEY `idx_fid` (`final_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-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`;
+ALTER TABLE `zh_material_list_gcl` ADD `old_quantity` DECIMAL(30,8) NULL DEFAULT NULL COMMENT '数量,用于复原数据' AFTER `expr`,
+ADD `old_expr` VARCHAR(255) NULL DEFAULT NULL COMMENT '公式,用于复原数据' AFTER `old_quantity`;
 
-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 = '用于新建期时清单关联已使用过的工程量,创建新的工料与清单的联系表';
+UPDATE `zh_material_list_gcl` SET `old_quantity`=`quantity`,`old_expr`=`expr`;
 
-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 = '参建单位表';
+ALTER TABLE `zh_material` ADD `exponent_rate` TINYINT(3) NULL DEFAULT '0' COMMENT '材料税税率' AFTER `rate`;
 
-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;
+UPDATE `zh_material` SET `exponent_rate`=`rate`;

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

+ 58 - 0
sql/update20220419.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;