MaiXinRong il y a 2 ans
Parent
commit
b9c6d5601b

+ 0 - 18
app/const/external_data.js

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

+ 12 - 1
app/const/spread.js

@@ -13,6 +13,7 @@ const dgnCols = ['dgn_qty1', 'dgn_qty2', 'dgn_price'];
 const clCols = ['sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price'];
 const stageDgnCols = ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2', 'final_dgn_price'];
 const realCompleteCols = ['real_qty', 'estimate_qty'];
+const priceDiffCols = ['org_price', 'pc_tp'];
 const thirdPartyCols = {
     gxby: ['gxby'],
     dagl: ['dagl']
@@ -203,6 +204,8 @@ const stageTz = {
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', getValue(data){ return data.contract_pc_tp || data.qc_pc_tp ? data.org_price : null; }, },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
@@ -284,6 +287,8 @@ const stageCl = {
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number',},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', getValue(data){ return data.contract_pc_tp || data.qc_pc_tp ? data.org_price : null; }, },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
@@ -367,6 +372,8 @@ const stageNoCl = {
             {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', getValue(data){ return data.contract_pc_tp || data.qc_pc_tp ? data.org_price : null; }, },
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
@@ -446,6 +453,8 @@ const stageGather = {
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', getValue(data){ return data.contract_pc_tp || data.qc_pc_tp ? data.org_price : null; }, },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
@@ -503,6 +512,8 @@ const stageCompare = {
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', getValue(data){ return data.contract_pc_tp || data.qc_pc_tp ? data.org_price : null; }, },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
         ],
         extraCols: [
             {title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty%s', hAlign: 2, width: 60, type: 'Number'},
@@ -603,7 +614,7 @@ module.exports = {
     stageNoCl,
     stageGather,
     stageCompare,
-    filterCols: { dealCols, dgnCols, clCols, stageDgnCols, realCompleteCols, thirdPartyCols, minusNoValueCols},
+    filterCols: { dealCols, dgnCols, clCols, stageDgnCols, realCompleteCols, thirdPartyCols, minusNoValueCols, priceDiffCols },
     measure,
     blank,
 };

+ 1 - 0
app/const/tender_info.js

@@ -115,6 +115,7 @@ const defaultInfo = {
         stage: {
             realComplete: false,
             correct: true,
+            priceDiff: false,
         },
         dayMode: false,
     },

+ 0 - 1
app/controller/ledger_controller.js

@@ -19,7 +19,6 @@ const auditConst = audit.ledger;
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
 const shenpiConst = require('../const/shenpi');
-const externalDataConst = require('../const/external_data.js');
 const fs = require('fs');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;

+ 35 - 0
app/controller/measure_controller.js

@@ -199,6 +199,37 @@ module.exports = app => {
             }
         }
 
+        joinData(main, idField, rela) {
+            const index = {},
+                indexPre = 'id_';
+            const loadFields = function(datas, fields, prefix, relaId) {
+                for (const d of datas) {
+                    const key = indexPre + d[relaId];
+                    let m = index[key];
+                    if (!m) {
+                        m = {};
+                        m[idField] = d[relaId];
+                        main.push(m);
+                        index[indexPre + d[relaId]] = m;
+                    }
+                    for (const f of fields) {
+                        if (d[f] !== undefined) {
+                            m[prefix + f] = d[f];
+                        }
+                    }
+                }
+            };
+            for (const m of main) {
+                index[indexPre + m[idField]] = m;
+                for (const r of rela) {
+                    if (r.defaultData) _.assignIn(m, r.defaultData);
+                }
+            }
+            for (const r of rela) {
+                loadFields(r.data, r.fields, r.prefix, r.relaId);
+            }
+        }
+
         /**
          * 多期比较 - 获取数据(Ajax)
          * @param ctx
@@ -221,6 +252,10 @@ module.exports = app => {
                         const data = { order, bills: [], pos: [] };
                         const stage = await this.ctx.service.stage.getDataByCondition({ tid: ctx.tender.id, order });
                         data.bills = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, stage.id);
+                        const bpcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
+                        this.joinData(data.bills, 'lid', [
+                            { data: bpcData, fields: ['pc_tp'], prefix: '', relaId: 'lid' },
+                        ]);
                         data.pos = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, stage.id);
                         result.stages.push(data);
                     }

+ 31 - 4
app/controller/revise_controller.js

@@ -18,7 +18,6 @@ const accountGroup = require('../const/account_group').group;
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
 const shenpiConst = require('../const/shenpi');
-const fs = require('fs');
 const LzString = require('lz-string');
 const stdConst = require('../const/standard');
 const spreadSetting = require('../lib/spread_setting');
@@ -417,7 +416,8 @@ module.exports = app => {
                     }
                 }
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
-                ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags } };
+                const price = !revise.readOnly ? await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } }) : [];
+                ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags, price } };
             } catch (err) {
                 ctx.helper.log(err);
                 this.ajaxErrorBody(err, '加载台账修订数据错误');
@@ -813,7 +813,7 @@ module.exports = app => {
          */
         async check(ctx) {
             try {
-                const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
+                const revise = ctx.revise;
                 if (!revise || revise.status !== audit.revise.status.checking) throw '台账修订数据有误';
 
                 const curAudit = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times);
@@ -822,7 +822,7 @@ module.exports = app => {
                 const checkType = parseInt(ctx.request.body.checkType);
                 if (!checkType || isNaN(checkType)) throw '提交数据错误';
 
-                await ctx.service.reviseAudit.check(revise, checkType, ctx.request.body.opinion, revise.times);
+                await ctx.service.reviseAudit.check(ctx.revise, checkType, ctx.request.body.opinion, revise.times);
 
                 ctx.redirect(ctx.request.headers.referer);
             } catch (err) {
@@ -948,6 +948,9 @@ module.exports = app => {
                     return spec;
                 case 'tags':
                     return await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
+                case 'price':
+                    return await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: ctx.revise.id } });
+                default: throw '请求的数据不存在';
             }
         }
 
@@ -983,6 +986,30 @@ module.exports = app => {
             };
             await this.layout('revise/gcl_compare.ejs', renderData);
         }
+
+
+        async price(ctx) {
+            if (!ctx.revise) throw '台账修订数据有误';
+            const renderData = {
+                preUrl: ctx.url.replace('/price', ''),
+                revise: ctx.revise,
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.price),
+            };
+            await this.layout('revise/price.ejs', renderData);
+        }
+
+        async priceUpdate(ctx) {
+            try {
+                if (!ctx.revise) throw '台账修订数据有误';
+
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.revisePrice.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存数据失败');
+            }
+        }
     }
 
     return ReviseController;

+ 10 - 1
app/controller/stage_controller.js

@@ -15,7 +15,6 @@ const spreadConst = require('../const/spread');
 const tenderConst = require('../const/tender');
 const shenpiConst = require('../const/shenpi');
 const payConst = require('../const/deal_pay.js');
-const externalDataConst = require('../const/external_data.js');
 const changeConst = require('../const/change');
 const measureType = tenderConst.measureType;
 const path = require('path');
@@ -221,6 +220,7 @@ module.exports = app => {
                 : await ctx.service.ledger.getAllDataByCondition({ columns: this.ledgerColumn, where: { tender_id: ctx.tender.id } });
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
             const extraData = await ctx.service.ledgerExtra.getData(ctx.tender.id, this.ledgerExtraColumn);
+            const pcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: ctx.stage.id } });
             const importData = await ctx.service.stageImportChange.getImportLid(ctx.stage.id);
             let curStageData;
             // 当前操作人查看最新数据,其他人查看历史数据
@@ -241,6 +241,7 @@ module.exports = app => {
                 { data: importData, fields: ['is_import'], prefix: '', relaId: 'lid' },
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used'], prefix: 'pre_', relaId: 'lid' },
+                { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
             ]);
             return ledgerData;
         }
@@ -386,6 +387,7 @@ module.exports = app => {
                 checkData.checkBillsTp([
                     { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' },
                 ], this.ctx.tender.info.decimal, x => { return x.is_tp; });
+                checkData.checkBillsQty(['contract_qty', 'qc_qty']);
                 ctx.body = { err: 0, msg: '', data: checkData.checkResult };
             } catch (err) {
                 this.log(err);
@@ -1370,6 +1372,13 @@ module.exports = app => {
                     result.main.ledger = ctx.stage.ledgerHis
                         ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file)
                         : await ctx.service.ledger.getData(ctx.tender.id);
+                    const bpcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: ctx.stage.id } });
+                    if (bpcData.length > 0) {
+                        this.ctx.helper.assignRelaData(result.main.ledger, [
+                            { data: bpcData, fields: ['pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
+                        ]);
+                    }
+
                     result.main.pos = ctx.stage.ledgerHis
                         ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.pos_file)
                         : await ctx.service.pos.getPosData({ tid: ctx.tender.id });

+ 1 - 1
app/extend/helper.js

@@ -409,7 +409,7 @@ module.exports = {
      * @param {Number} value2
      * @return {boolean}
      */
-    checkNumberEqual(value1, value2) {
+    numEqual(value1, value2) {
         if (value1 && value2) {
             return Math.abs(value2 - value1) > zeroRange;
         }

+ 71 - 0
app/lib/ledger.js

@@ -944,6 +944,76 @@ class checkData {
     }
 }
 
+class reviseTree extends billsTree {
+    constructor (ctx, setting) {
+        super(ctx, setting);
+        this.price = [];
+    }
+    loadRevisePrice(price, decimal) {
+        this.decimal = decimal;
+        this.price = price || [];
+    }
+    checkRevisePrice(d) {
+        const helper = this.ctx.helper;
+        const p = this.price.find(x => {
+            return x.b_code === d.b_code &&
+                ((!x.name && !d.name) || x.name === d.name) &&
+                ((!x.unit && !d.unit) || x.unit === d.unit) &&
+                helper.checkZero(x.org_price - d.unit_price);
+        });
+        if (!p) return false;
+        d.org_price = p.org_price;
+        d.unit_price = p.new_price;
+        d.deal_tp = helper.mul(d.deal_qty, d.unit_price, this.decimal.tp);
+        d.sgfh_tp = helper.mul(d.sgfh_qty, d.unit_price, this.decimal.tp);
+        d.sjcl_tp = helper.mul(d.sjcl_qty, d.unit_price, this.decimal.tp);
+        d.qtcl_tp = helper.mul(d.qtcl_qty, d.unit_price, this.decimal.tp);
+        d.total_price = helper.mul(d.quantity, d.unit_price, this.decimal.tp);
+        return true;
+    }
+    loadDatas(datas) {
+        super.loadDatas(datas);
+        if (this.price.length > 0) {
+            for (const d of this.datas) {
+                if (d.children && d.children.length > 0) continue;
+                if (!d.b_code) continue;
+                this.checkRevisePrice(d);
+            }
+        }
+    }
+    getUpdateReviseData() {
+        return this.datas.map(x => {
+            if (x.children && x.children.length > 0) {
+                return {
+                    id: x.id, tender_id: x.tender_id, crid: x.crid,
+                    ledger_id: x.ledger_id, ledger_pid: x.ledger_pid, full_path: x.full_path, order: x.order, level: x.level, is_leaf: x.is_leaf,
+                    node_type: x.node_type, check_calc: x.check_calc,
+                    code: x.code, b_code: x.b_code, name: x.name, unit: x.unit, position: x.position,
+                    drawing_code: x.drawing_code, memo: x.memo, add_user: x.add_user, in_time: x.in_time,
+                    unit_price: 0, dgn_qty1: x.dgn_qty1, dgn_qty2: x.dgn_qty2,
+                    quantity: 0, total_price: 0,
+                    sgfh_qty: 0, sgfh_tp: 0, sgfh_expr: '',
+                    sjcl_qty: 0, sjcl_tp: 0, sjcl_expr: '',
+                    qtcl_qty: 0, qtcl_tp: 0, qtcl_expr: '',
+                };
+            } else {
+                return {
+                    id: x.id, tender_id: x.tender_id, crid: x.crid,
+                    ledger_id: x.ledger_id, ledger_pid: x.ledger_pid, full_path: x.full_path, order: x.order, level: x.level, is_leaf: x.is_leaf,
+                    node_type: x.node_type, check_calc: x.check_calc,
+                    code: x.code, b_code: x.b_code, name: x.name, unit: x.unit, position: x.position,
+                    drawing_code: x.drawing_code, memo: x.memo, add_user: x.add_user, in_time: x.in_time,
+                    unit_price: x.unit_price, dgn_qty1: x.dgn_qty1, dgn_qty2: x.dgn_qty2,
+                    quantity: x.quantity, total_price: x.total_price,
+                    sgfh_qty: x.sgfh_qty, sgfh_tp: x.sgfh_tp, sgfh_expr: x.sgfh_expr,
+                    sjcl_qty: x.sjcl_qty, sjcl_tp: x.sjcl_tp, sjcl_expr: x.sjcl_expr,
+                    qtcl_qty: x.qtcl_qty, qtcl_tp: x.qtcl_tp, qtcl_expr: x.qtcl_expr,
+                };
+            }
+        });
+    }
+}
+
 module.exports = {
     billsTree,
     pos,
@@ -951,4 +1021,5 @@ module.exports = {
     filterGatherTree,
     gatherTree,
     checkData,
+    reviseTree,
 };

+ 298 - 0
app/lib/revise_price.js

@@ -0,0 +1,298 @@
+'use strict';
+
+/**
+ *
+ * 单价调整计算:
+ * 1. 台账修订上报,保存台账修订历史时,使用当前单价调整计算一次
+ * 2. 台账修订完成:计算台账、应用到未审完成期、应用到所有工程变更
+ * 3. 新增期:检查是否存在未应用的单价变更
+ * 4. 期审批完成:所有未应用的单价调整,记录应用到该期(记录revise_price.use_stage/use_stage_order) - 一条sql即可
+ * 5. 期重现审批:上一次应用的所有单价调整,回到未应用状态(revise_price.use_stage/use_stage_order均回到0) - 一条sql即可
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Ledger = require('./ledger');
+const audit = require('../const/audit');
+
+class revisePriceCalc {
+    constructor(ctx) {
+        this.ctx = ctx;
+    }
+
+    findPrice(b_code, name, unit, unit_price) {
+        const helper = this.ctx.helper;
+        return this.price.find(x => {
+            return b_code === x.b_code && name === x.name && unit === x.unit && helper.numEqual(unit_price, x.org_price);
+        });
+    }
+
+    /**
+     * 新增一期计量,检查单价调整
+     * @param {Object} newStage - 新计量期
+     * @param {Object} preStage - 上一计量期
+     * @return { inseretPosData, insertBillsData }
+     */
+    async newStagePriceChange(newStage, preStage, transaction) {
+        // 获取未执行的单价变更,无单价变更不执行
+        const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { tid: newStage.tid, valid: 1, use_stage: 0 } });
+        if (price.length === 0) return;
+        // 无截止上期数据不执行
+        const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { sid: preStage.id } });
+        if (preBillsData.length === 0) return;
+
+        // 加载树结构
+        const bills = await this.ctx.service.ledger.getData(newStage.tid);
+        this.ctx.helper.assignRelaData(bills, [
+            { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price'], prefix: 'pre_', relaId: 'lid' },
+        ]);
+        const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        billsTree.loadDatas(bills);
+
+        // 计算
+        const result = { ibData: [] };
+        const helper = this.ctx.helper;
+        const decimal = this.ctx.tender.info.decimal;
+        billsTree.calculateAll(node => {
+            if (!node.pre_id) return;
+            if (node.children && node.children.length > 0) return;
+            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            if (!priceDiff) return;
+            node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+            node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+            node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
+            result.ibData.push({
+                tid: newStage.tid, sid: newStage.id, sorder: newStage.order, lid: node.id, org_price: node.pre_unit_price,
+                contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
+            });
+        });
+        if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.ibData);
+
+        let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0;
+        for (const ibc of result.ibData) {
+            contract_pc_tp = helper.add(contract_pc_tp, ibc.contract_pc_tp);
+            qc_pc_tp = helper.add(qc_pc_tp, ibc.qc_pc_tp);
+            pc_tp = helper.add(pc_tp, ibc.pc_tp);
+        }
+        await transaction.update(this.ctx.service.stage.tableName, { id: newStage.id, contract_pc_tp, qc_pc_tp, pc_tp, check_calc: true, cache_time_l: new Date() });
+    }
+
+    /**
+     * 期,重新审批,检查单价调整
+     * @param {Object} stage - 期
+     * @param {Number} auditOrder - 重新审批,最新审批人序号
+     * @param {Object} transaction - 事务
+     */
+    async stageCheckAgainPriceChange(stage, auditOrder, transaction) {
+        // 获取未执行的单价变更,无单价变更不执行
+        const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { tid: stage.tid, valid: 1, use_stage: 0 } });
+        if (price.length === 0) return;
+
+        const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
+        const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: stage.tid, sorder: stage.order - 1 } });
+
+        // 加载树结构
+        const bills = await this.ctx.service.ledger.getData(stage.tid);
+        this.ctx.helper.assignRelaData(bills, [
+            { data: curBillsData, fields: ['id', 'contract_qty', 'qc_qty', 'postil', 'times', 'order'], prefix: 'cur_', relaId: 'lid' },
+            { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price'], prefix: 'pre_', relaId: 'lid' },
+        ]);
+        const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        billsTree.loadDatas(bills);
+
+
+        // 计算 insertBills billsPriceChange reCalcBillsCur/reCalcBillsPrice
+        const result = { ibData: [], bpcData: [] };
+        const helper = this.ctx.helper;
+        const decimal = this.ctx.tender.info.decimal;
+        const said = this.ctx.session.sessionUser.accountId;
+        billsTree.calculateAll(node => {
+            if (node.children && node.children.length > 0) return;
+            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            if (!priceDiff) return;
+            if (node.cur_id) {
+                node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp);
+                node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp);
+                result.ibData.push({
+                    tid: stage.tid, sid: stage.id, said,
+                    lid: node.lid, pid: node.id,
+                    times: stage.times, order: auditOrder,
+                    contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp, qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp,
+                    postil: node.postil,
+                });
+            }
+            if (node.pre_id) {
+                node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+                node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+                node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
+                result.bpcData.push({
+                    tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.lid, org_price: node.pre_unit_price,
+                    contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
+                });
+            }
+        });
+        if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, result.ibData);
+        await transaction.delete(this.ctx.service.stageBillsPc.tableName, { sid: stage.id });
+        if (result.bpcData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.bpcData);
+
+        let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0;
+        for (const bpc of result.bpcData) {
+            contract_pc_tp = helper.add(contract_pc_tp, bpc.contract_pc_tp);
+            qc_pc_tp = helper.add(qc_pc_tp, bpc.qc_pc_tp);
+            pc_tp = helper.add(pc_tp, bpc.pc_tp);
+        }
+        await transaction.update(this.ctx.service.stage.tableName, { id: stage.id, contract_pc_tp, qc_pc_tp, pc_tp, check_calc: true, cache_time_l: new Date() });
+    }
+
+    /**
+     * 重算工程变更(调整单价)
+     * @param {Object} change - 工程变更
+     * @param {Object} transaction - 事务 (无则非事务提交)
+     */
+    async calcChange(change, transaction) {
+        const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: change.cid } });
+        const updateBills = [];
+        let total_price = 0;
+        for (const b of changeBills) {
+            const p = this.findPrice(b.code, b.name, b.unit, b.unit_price);
+            if (p) {
+                updateBills.push({ id: b.id, unit_price: p.new_price });
+                total_price = this.ctx.helper.add(total_price, this.ctx.helper.mul(p.new_price, b.spamount, change.tp_decimal));
+            } else {
+                total_price = this.ctx.helper.add(total_price, this.ctx.helper.mul(b.unit_price, b.spamount, change.tp_decimal));
+            }
+        }
+        if (updateBills.length > 0) {
+            const conn = transaction || this.ctx.sub_db;
+            await conn.updateRows(this.ctx.service.changeAuditList.tableName, updateBills);
+            await conn.update(this.ctx.service.change.tableName, { total_price }, { where: { cid: change.cid } });
+        }
+    }
+    /**
+     * 重算标段下所有工程变更(调整单价)
+     * @param {Number} tid - 标段id
+     * @param {Object} transaction - 事务 (无则非事务提交)
+     */
+    async calcAllChanges(tid, transaction) {
+        const change = await this.ctx.service.change.getAllDataByCondition({ where: { tid, valid: 1 } });
+        if (change.length === 0) return;
+        for (const c of change) {
+            await this.calcChange(c, transaction);
+        }
+    }
+    async _calcStage(stage, bills, transaction) {
+        // 无单价变更不执行
+        if (this.price.length === 0) return;
+
+        const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
+        const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: stage.tid, sorder: stage.order - 1 } });
+
+        // 加载树结构
+        this.ctx.helper.assignRelaData(bills, [
+            { data: curBillsData, fields: ['id', 'contract_qty', 'qc_qty', 'times', 'order', 'postil'], prefix: 'cur_', relaId: 'lid' },
+            { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price'], prefix: 'pre_', relaId: 'lid' },
+        ]);
+        const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        billsTree.loadDatas(bills);
+
+        // 计算 insertBills/updateBills billsPriceChange
+        const result = { ibData: [], ubData: [], bpcData: [] };
+        const helper = this.ctx.helper;
+        const decimal = this.ctx.tender.info.decimal;
+        const said = this.ctx.session.sessionUser.accountId;
+        billsTree.calculateAll(node => {
+            if (node.children && node.children.length > 0) return;
+            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            if (!priceDiff) return;
+            node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+            node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+            node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
+            if (node.cur_id) {
+                node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp);
+                node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp);
+                if (node.cur_times === stage.times && node.cur_order === 0) {
+                    result.ubData.push({
+                        id: node.cur_id,
+                        contract_tp: node.cur_contract_tp, qc_tp: node.cur_qc_tp,
+                    });
+                } else {
+                    result.ibData.push({
+                        tid: stage.tid, sid: stage.id, said,
+                        lid: node.lid, pid: node.id, times: stage.times, order: 0,
+                        contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp, qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp,
+                        postil: node.cur_postil,
+                    });
+                }
+            }
+            if (node.pre_id) {
+                result.bpcData.push({
+                    tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.lid, pid: node.id, org_price: node.pre_unit_price,
+                    contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
+                });
+            }
+        });
+        if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, result.ibData);
+        if (result.ubData.length > 0) await transaction.updateRows(this.ctx.service.stageBills.tableName, result.ubData);
+        await transaction.delete(this.ctx.service.stageBillsPc.tableName, { sid: stage.id });
+        if (result.bpcData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.bpcData);
+
+        let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0;
+        for (const bpc of result.bpcData) {
+            contract_pc_tp = helper.add(contract_pc_tp, bpc.contract_pc_tp);
+            qc_pc_tp = helper.add(qc_pc_tp, bpc.qc_pc_tp);
+            pc_tp = helper.add(pc_tp, bpc.pc_tp);
+        }
+        await transaction.update(this.ctx.service.stage.tableName, { id: stage.id, contract_pc_tp, qc_pc_tp, pc_tp, check_calc: true, cache_time_l: new Date() });
+    }
+    /**
+     * 计算修订台账
+     * @param {Object}revise - 最新一次台账修订(此处不检查)
+     * @param {Object} transaction - 事务 (无则非事务提交)
+     */
+    async calcReviseLedger(revise, transaction) {
+        const xmj = await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.bills_file);
+        xmj.forEach(x => {
+            delete x.is_tp;
+            delete x.gxby_status;
+            delete x.gxby_limit;
+            delete x.gxby_ratio;
+            delete x.gxby_url;
+            delete x.dagl_status;
+            delete x.dagl_limit;
+            delete x.dagl_ratio;
+            delete x.dagl_url;
+        });
+        const pos = await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.pos_file);
+        pos.forEach(p => {
+            p.in_time = new Date(p.in_time);
+            delete p.gxby_status;
+            delete p.gxby_limit;
+            delete p.gxby_ratio;
+            delete p.gxby_url;
+            delete p.dagl_status;
+            delete p.dagl_limit;
+            delete p.dagl_ratio;
+            delete p.dagl_url;
+        });
+        await transaction.delete(this.ctx.service.ledger.tableName, { tender_id: revise.tid });
+        if (xmj.length > 0) await transaction.insert(this.ctx.service.ledger.tableName, xmj);
+        await transaction.delete(this.ctx.service.pos.tableName, { tid: revise.tid });
+        if (pos.length > 0) await transaction.insert(this.ctx.service.pos.tableName, pos);
+
+        // 应用到未审完成期
+        const latestStage = await this.ctx.service.stage.getLastestStage(revise.tid, true);
+        if (latestStage && latestStage.status !== audit.stage.status.checked) await this._calcStage(latestStage, xmj, transaction);
+    }
+    async calcRevise(revise, transaction) {
+        if (revise.tid !== this.ctx.tender.id) throw '数据错误';
+
+        this.price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } });
+        await this.calcReviseLedger(revise, transaction);
+        // 引用到所有工程变更
+        await this.calcAllChanges(revise.tid, transaction);
+    }
+}
+
+module.exports = revisePriceCalc;

+ 1 - 0
app/lib/spread_setting.js

@@ -117,6 +117,7 @@ const getStageGatherSpreadSetting = async function (ctx, tid) {
 
     // if (tender.data.measure_type === measureType.tz.value && !tender.info.display.ledger.deal)
     //     removeFieldCols(gcl, spreadConst.filterCols.dealCols);
+    if (tender.info.display.stage.priceDiff) removeFieldCols(leafXmj, spreadConst.filterCols.priceDiffCols);
 
     if (tender.data.measure_type === measureType.gcl.value) removeFieldCols(leafXmj, ['quantity']);
     return [gcl, leafXmj];

+ 6 - 4
app/lib/stage_im.js

@@ -27,7 +27,7 @@ class StageIm {
             rootId: -1,
             keys: ['id', 'tender_id', 'ledger_id'],
             stageId: 'id',
-            calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp'],
+            calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_tp'],
             calc(node) {
                 if (node.children && node.children.length === 0) {
                     node.pre_gather_qty = self.ctx.helper.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -38,9 +38,9 @@ class StageIm {
                     node.end_gather_qty = self.ctx.helper.add(node.pre_gather_qty, node.gather_qty);
                 }
                 node.pre_gather_tp = self.ctx.helper.add(node.pre_contract_tp, node.pre_qc_tp);
-                node.gather_tp = self.ctx.helper.add(node.contract_tp, node.qc_tp);
-                node.end_contract_tp = self.ctx.helper.add(node.pre_contract_tp, node.contract_tp);
-                node.end_qc_tp = self.ctx.helper.add(node.pre_qc_tp, node.qc_tp);
+                node.gather_tp = self.ctx.helper.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+                node.end_contract_tp = self.ctx.helper.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+                node.end_qc_tp = self.ctx.helper.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
                 node.end_gather_tp = self.ctx.helper.add(node.pre_gather_tp, node.gather_tp);
             },
         });
@@ -76,10 +76,12 @@ class StageIm {
                 this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder)
             : await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
         const preStage = this.ctx.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
+        const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.ctx.stage.id } });
 
         this.ctx.helper.assignRelaData(billsData, [
             { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: '', relaId: 'lid' },
             { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid' },
+            { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
         ]);
         this.billsTree.loadDatas(billsData);
         this.billsTree.calculateAll();

+ 2 - 0
app/lib/tender_info.js

@@ -46,9 +46,11 @@ class TenderInfo {
                 : await this.ctx.service.stageBills.getLastestStageData2(this.tender.id, this.stage.id);
 
             const preStage = this.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.tender, this.stage.order - 1) : [];
+            const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.stage.id } });
             this.ctx.helper.assignRelaData(billsData, [
                 { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
                 { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
+                { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
             ]);
         }
         return billsData;

+ 1 - 0
app/middleware/revise_check.js

@@ -37,6 +37,7 @@ module.exports = options => {
             }
             revise.readOnly = revise.uid !== this.session.sessionUser.accountId ||
                 revise.status === auditConst.status.checking || revise.status === auditConst.status.checked;
+            revise.priceCount = yield this.service.revisePrice.count({ rid: revise.id });
             this.revise = revise;
             yield next;
         } catch (err) {

+ 2 - 1
app/public/js/component/menu.js

@@ -1,7 +1,7 @@
 'use strict';
 // 导航栏
 Vue.component('nav-menu', {
-    props: ['title', 'url', 'active', 'tclass', 'icon', 'ml', 'hint'],
+    props: ['title', 'url', 'active', 'tclass', 'icon', 'ml', 'hint', 'hinticon'],
     template: '' +
     '<div class="nav-box" data-toggle="tooltip" data-placement="right" :data-original-title="[ hint ]">' +
         '<ul class="nav-list list-unstyled">' +
@@ -9,6 +9,7 @@ Vue.component('nav-menu', {
                 '<a :href="url" :class="[ tclass ]">' +
                     '<i v-if="icon" class="fa" :class="icon"></i>' +
                     '<span :class="[ \'ml-\' + ml ]">{{ title }}</span>' +
+                    '<i v-if="hinticon" class="fa text-danger ml-2" :class="hinticon"></i>' +
                 '</a>' +
             '</li>' +
         '</ul>' +

+ 71 - 0
app/public/js/path_tree.js

@@ -1054,6 +1054,7 @@ const createNewPathTree = function (type, setting) {
     }
 
     class LedgerTree extends FxTree {
+
         /**
          *
          * @param parent
@@ -1128,6 +1129,10 @@ const createNewPathTree = function (type, setting) {
     }
 
     class ReviseTree extends LedgerTree {
+        constructor (setting) {
+            super(setting);
+            this.price = [];
+        }
         checkNodeUsed(node, pos) {
             if (node.children && node.children.length > 0) {
                 for (const child of node.children) {
@@ -1145,6 +1150,72 @@ const createNewPathTree = function (type, setting) {
             }
             return false;
         }
+        loadRevisePrice(price, decimal) {
+            this.decimal = decimal;
+            this.price = price || [];
+        }
+        checkRevisePrice(d) {
+            const p = this.price.find(x => {
+                return x.b_code === d.b_code &&
+                    ((!x.name && !d.name) || x.name === d.name) &&
+                    ((!x.unit && !d.unit) || x.unit === d.unit) &&
+                    checkZero(x.org_price - d.unit_price);
+            });
+            if (!p) return false;
+            d.org_price = p.org_price;
+            d.unit_price = p.new_price;
+            d.deal_tp = ZhCalc.mul(d.deal_qty, d.unit_price, this.decimal.tp);
+            d.sgfh_tp = ZhCalc.mul(d.sgfh_qty, d.unit_price, this.decimal.tp);
+            d.sjcl_tp = ZhCalc.mul(d.sjcl_qty, d.unit_price, this.decimal.tp);
+            d.qtcl_tp = ZhCalc.mul(d.qtcl_qty, d.unit_price, this.decimal.tp);
+            d.total_price = ZhCalc.mul(d.quantity, d.unit_price, this.decimal.tp);
+            return true;
+        }
+        loadDatas(datas) {
+            super.loadDatas(datas);
+            if (this.price.length > 0) {
+                for (const d of this.datas) {
+                    if (d.children && d.children.length > 0) continue;
+                    if (!d.b_code) continue;
+                    this.checkRevisePrice(d);
+                }
+            }
+        }
+        loadPostReivsePrice(data) {
+            let result = false;
+            if (!this.price) return result;
+            for (const d of data) {
+                if (!d.is_leaf || !d.b_code) continue;
+                if (this.checkRevisePrice(d)) result = true;
+            }
+            return result;
+        }
+        loadPostData(data) {
+            const result = {}, reCalcNodes = [];
+            if (!data) return result;
+            if (data.delete) {
+                result.delete = this._freeData(data.delete);
+                this._getReCalcNodes(reCalcNodes, result.delete);
+            }
+            if (data.create) {
+                result.create = this._loadData(data.create);
+                this.loadPostReivsePrice(result.create);
+                this._getReCalcNodes(reCalcNodes, result.create);
+            }
+            if (data.update) {
+                result.update = this._updateData(data.update);
+                this.loadPostReivsePrice(result.update);
+                this._getReCalcNodes(reCalcNodes, result.update);
+            }
+            reCalcNodes.sort((a, b) => {
+                return b.level - a.level;
+            });
+            for (const node of reCalcNodes) {
+                treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun);
+            }
+            result.update = result.update ? result.update.concat(reCalcNodes) : reCalcNodes;
+            return result;
+        }
     }
 
     class StageTree extends FxTree {

+ 1 - 0
app/public/js/revise.js

@@ -1942,6 +1942,7 @@ $(document).ready(() => {
 
     // 加载清单&计量单元数据
     postData(window.location.pathname + '/load', {}, function (result) {
+        billsTree.loadRevisePrice(result.price, decimal);
         billsTree.loadDatas(result.bills);
         treeCalc.calculateAll(billsTree);
         for (const t of result.tags) {

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

@@ -247,7 +247,7 @@ $(document).ready(() => {
     };
 
     // 加载清单&计量单元数据
-    postData('load', {filter: 'bills;pos;reviseBills;revisePos'}, function (result) {
+    postData('load', {filter: 'bills;pos;reviseBills;revisePos;price'}, function (result) {
         const tenderTreeSetting = {
             id: 'ledger_id',
             pid: 'ledger_pid',
@@ -258,9 +258,10 @@ $(document).ready(() => {
             calcFields: ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
         };
         const reviseLedger = {
-            billsTree: createNewPathTree('ledger', tenderTreeSetting),
+            billsTree: createNewPathTree('revise', tenderTreeSetting),
             pos: new PosData({ id: 'id', ledgerId: 'lid', }),
         };
+        reviseLedger.billsTree.loadRevisePrice(result.price, decimal);
         reviseLedger.billsTree.loadDatas(result.reviseBills);
         reviseLedger.pos.loadDatas(result.revisePos);
         const orgLedger = {

+ 436 - 0
app/public/js/revise_price.js

@@ -0,0 +1,436 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const showSideTools = function (show) {
+    const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+    if (show) {
+        right.show();
+        autoFlashHeight();
+        const percent = 100 - right.outerWidth() /parent.width() * 100;
+        left.css('width', percent + '%');
+    } else {
+        left.width(parent.width());
+        right.hide();
+    }
+};
+
+const setPriceHint = function (show) {
+    const hinticon = show ? 'fa-bell' : undefined;
+    subMiniMenu.$children[2].hinticon = hinticon;
+    subMenu.$children[2].hinticon = hinticon;
+};
+
+$(document).ready(() => {
+    const ledgerGclSpreadSetting = {
+        cols: [
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@' },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 200, formatter: '@' },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@' },
+            { title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number' },
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+    };
+    const priceSpreadSetting = {
+        cols: [
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 100, formatter: '@', readOnly: true },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 210, formatter: '@', readOnly: true },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true },
+            { title: '当前单价', colSpan: '1', rowSpan: '2', field: 'org_price', hAlign: 2, width: 80, type: 'Number', readOnly: true },
+            { title: '调整后单价', colSpan: '1', rowSpan: '2', field: 'new_price', hAlign: 2, width: 80, type: 'Number' },
+            { title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 2, width: 150, formatter: '@' },
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+    };
+    autoFlashHeight();
+    const priceSpread = SpreadJsObj.createNewSpread($('#price-spread')[0]);
+    const priceSheet = priceSpread.getActiveSheet();
+
+    SpreadJsObj.initSheet(priceSheet, priceSpreadSetting);
+
+    class RevisePrice {
+        constructor () {
+            this.data = [];
+        }
+        resortData() {
+            this.data.sort(function (a, b) {
+                return a.order - b.order;
+            });
+        }
+        loadDatas(datas) {
+            this.data = datas;
+            this.resortData();
+        }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+            this.resortData();
+        }
+    }
+    const revisePrice = new RevisePrice();
+    const priceOprObj = {
+        addRevisePrice(data) {
+            const op = revisePrice.data.find(x => {
+                return x.b_code === data.b_code && x.name === x.name && x.unit === x.unit && checkZero(ZhCalc.sub(x.org_price, data.unit_price));
+            });
+            if (op) {
+                toastr.warning('已存在该单价调整');
+                SpreadJsObj.locateData(priceSheet, op);
+                return;
+            }
+            postData(window.location.pathname + '/update', { add: { b_code: data.b_code, name: data.name, unit: data.unit, unit_price: data.unit_price } }, result => {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadSheetData(priceSheet);
+                setPriceHint(revisePrice.data.length > 0);
+            });
+        },
+        /**
+         * 删除按钮响应事件
+         * @param sheet
+         */
+        deletePress: function (sheet) {
+            if (!sheet.zh_setting || readOnly) return;
+
+            const sortData = sheet.zh_data;
+            const datas = [];
+            const sels = sheet.getSelections();
+            if (!sels || !sels[0]) return;
+
+            for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                let bDel = false;
+                const node = sortData[iRow];
+                if (node) {
+                    const data = {id: node.id};
+                    for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].colCount; iCol++) {
+                        const style = sheet.getStyle(iRow, iCol);
+                        if (!style.locked) {
+                            const colSetting = sheet.zh_setting.cols[iCol];
+                            data[colSetting.field] = null;
+                            bDel = true;
+                        }
+                    }
+                    if (bDel) {
+                        datas.push(data);
+                    }
+                }
+            }
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/update', {update: datas}, function (result) {
+                    revisePrice.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                });
+            }
+        },
+        delete: function (sheet) {
+            if (!sheet.zh_setting || readOnly) return;
+
+            const sortData = sheet.zh_data;
+            const datas = [];
+            const sels = sheet.getSelections();
+            if (!sels || !sels[0]) return;
+
+            for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                const node = sortData[iRow];
+                datas.push(node.id);
+            }
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/update', {del: datas}, function (result) {
+                    revisePrice.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                    setPriceHint(revisePrice.data.length > 0);
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                });
+            }
+        },
+        editEnded: function (e, info) {
+            if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
+
+            const node = info.sheet.zh_data[info.row];
+            if (!node) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            const data = { update: { id: node.id, org_price: node.org_price } };
+            const oldValue = node ? node[col.field] : null;
+            const newValue = trimInvalidChar(info.editingText);
+            if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if (col.type === 'Number') {
+                const num = _.toNumber(newValue);
+                if (num) data.update[col.field] = num;
+            } else {
+                data.update[col.field] = newValue;
+            }
+
+            postData(window.location.pathname + '/update', data, function (result) {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadSheetData(info.sheet);
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            });
+        },
+        clipboardPasting(e, info) {
+            const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+            info.cancel = true;
+
+            if (!setting || !sortData) return;
+            const pasteData = info.pasteData.html
+                ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                : (info.pasteData.text === ''
+                    ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                    : SpreadJsObj.analysisPasteText(info.pasteData.text));
+
+            const uDatas = [];
+            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                const curRow = info.cellRange.row + iRow;
+                const node = sortData[curRow];
+
+                let bPaste = false;
+                const data = {};
+                for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                    const curCol = info.cellRange.col + iCol;
+                    const colSetting = setting.cols[curCol];
+                    const value = trimInvalidChar(pasteData[iRow][iCol]);
+                    if (colSetting.type === 'Number') {
+                        const num = _.toNumber(value);
+                        if (num) {
+                            data[colSetting.field] = num;
+                            bPaste = true;
+                        }
+                    } else {
+                        data[colSetting.field] = value;
+                        bPaste = true;
+                    }
+                }
+                if (bPaste) {
+                    data.id = node.id;
+                    uDatas.push(data);
+                }
+            }
+            const updateData = {};
+            if (uDatas.length > 0) updateData.update = uDatas;
+            if (uDatas.length > 0) {
+                postData(window.location.pathname + '/update', updateData, function (result) {
+                    revisePrice.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                });
+            } else {
+                SpreadJsObj.reLoadSheetData(info.sheet);
+            }
+        },
+        upMove: function () {
+            const sels = priceSheet.getSelections(), sortData = priceSheet.zh_data;
+            const node = sortData[sels[0].row];
+            const preNode = sortData[sels[0].row - 1];
+            const data = [
+                {id: node.id, order: preNode.order},
+                {id: preNode.id, order: node.order}
+            ];
+            postData(window.location.pathname + '/update', {update: data}, function (result) {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadRowsData(priceSheet, [sels[0].row, sels[0].row - 1]);
+                priceSheet.setSelection(sels[0].row - 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+            });
+        },
+        downMove: function () {
+            const sels = priceSheet.getSelections(), sortData = priceSheet.zh_data;
+            const node = sortData[sels[0].row];
+            const nextNode = sortData[sels[0].row + 1];
+            const data = [
+                {id: node.id, order: nextNode.order},
+                {id: nextNode.id, order: node.order}
+            ];
+            postData(window.location.pathname + '/update', {update: data}, function (result) {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadRowsData(priceSheet, [sels[0].row, sels[0].row + 1]);
+                priceSheet.setSelection(sels[0].row + 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+            });
+        }
+    };
+    if (!readOnly) {
+        priceSheet.bind(spreadNS.Events.EditEnded, priceOprObj.editEnded);
+        priceSheet.bind(spreadNS.Events.ClipboardPasting, priceOprObj.clipboardPasting);
+        SpreadJsObj.addDeleteBind(priceSpread, priceOprObj.deletePress);
+        $.contextMenu({
+            selector: '#price-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, priceSpread);
+                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+            },
+            items: {
+                del: {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        priceOprObj.delete(priceSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(priceSheet);
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                sprDel: '------------',
+                upMove: {
+                    name: '上移',
+                    icon: 'fa-arrow-up',
+                    callback: function (key, opt) {
+                        priceOprObj.upMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = priceSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row === 0) return true;
+
+                        const row = sels[0].row;
+                        const node = revisePrice.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                downMove: {
+                    name: '下移',
+                    icon: 'fa-arrow-down',
+                    callback: function (key, opt) {
+                        priceOprObj.downMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = priceSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row >= revisePrice.data.length - 1) return true;
+
+                        const row = sels[0].row;
+                        const node = revisePrice.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                }
+            },
+        });
+    }
+
+    class LedgerGcl {
+        constructor(setting) {
+            this.setting = setting;
+            this.spread = SpreadJsObj.createNewSpread($(this.setting.selector)[0]);
+            this.sheet = this.spread.getActiveSheet();
+            SpreadJsObj.initSheet(this.sheet, this.setting.spreadSetting);
+            if (!readOnly) {
+                this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
+                    const gcl = SpreadJsObj.getSelectObject(info.sheet);
+                    priceOprObj.addRevisePrice(gcl);
+                });
+            }
+        }
+        loadData(bills, pos) {
+            gclGatherModel.loadLedgerData(bills);
+            gclGatherModel.loadPosData(pos);
+            this.gcl = gclGatherModel.gatherGclData();
+            this.sheet && SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, this.gcl);
+        }
+    }
+    const ledgerGcl = new LedgerGcl({
+        selector: '#ledger-gcl-spread',
+        spreadSetting: ledgerGclSpreadSetting
+    });
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            priceSpread.refresh();
+            ledgerGcl.spread.refresh();
+        }
+    });
+
+    $.divResizer({
+        select: '#revise-right-spr',
+        callback: function () {
+            priceSpread.refresh();
+            ledgerGcl.spread.refresh();
+        }
+    });
+
+    postData('load', { filter: 'bills;pos;price' }, result => {
+        revisePrice.loadDatas(result.price);
+        SpreadJsObj.loadSheetData(priceSheet, SpreadJsObj.DataType.Data, revisePrice.data);
+        ledgerGcl.loadData(result.bills, result.pos);
+        $("[content='#ledgerGcl']").click();
+    });
+
+    $('a', '#side-menu').bind('click', function (e) {
+        e.preventDefault();
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        // 展开工具栏、切换标签
+        if (!tab.hasClass('active')) {
+            $('a', '#side-menu').removeClass('active');
+            tab.addClass('active');
+            $('.tab-content .tab-pane').removeClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            ledgerGcl.spread.refresh();
+        } else {// 收起工具栏
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        priceSpread.refresh();
+    });
+});

+ 6 - 6
app/public/js/stage.js

@@ -155,8 +155,8 @@ function getNodeList(node) {
     }
     $('#nodelist-table').html(html);
     $('#nodelist-table').on('click', 'tr', function() {
-        $('#nodelist-table tr').removeClass('bg-light')
-        $(this).addClass('bg-light')
+        $('#nodelist-table tr').removeClass('bg-light');
+        $(this).addClass('bg-light');
     })
 }
 
@@ -268,7 +268,7 @@ $(document).ready(() => {
     };
     // 台账树结构计算相关设置
     stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil', 'used', 'contract_expr'];
-    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
         'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp', 'final_1_tp', 'end_final_1_tp'];
     stageTreeSetting.calcFun = function (node) {
         if (!node.children || node.children.length === 0) {
@@ -284,9 +284,9 @@ $(document).ready(() => {
             node.end_final_1_qty = ZhCalc.add(node.end_qc_qty, node.final_1_qty);
         }
         node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
-        node.gather_tp = ZhCalc.add(node.contract_tp, node.qc_tp);
-        node.end_contract_tp = ZhCalc.add(node.pre_contract_tp, node.contract_tp);
-        node.end_qc_tp = ZhCalc.add(node.pre_qc_tp, node.qc_tp);
+        node.gather_tp = ZhCalc.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+        node.end_contract_tp = ZhCalc.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+        node.end_qc_tp = ZhCalc.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
         node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
         node.end_final_tp = ZhCalc.add(node.end_qc_tp, node.total_price);
         node.end_final_1_tp = ZhCalc.add(node.end_qc_tp, node.final_1_tp);

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

@@ -133,7 +133,7 @@ $(document).ready(function () {
         keys: ['id', 'tender_id', 'ledger_id'],
         masterId: 'id',
         minorId: 'lid',
-        calcFields: [],
+        calcFields: ['pc_tp'],
         autoExpand: 3,
     };
     const scTree = createNewPathTree('master', scTreeSetting);

+ 5 - 2
app/router.js

@@ -239,6 +239,9 @@ module.exports = app => {
     app.get('/tender/:id/revise/:rid/compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.compare');
     app.get('/tender/:id/revise/:rid/gcl-compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.gclCompare');
     app.post('/tender/:id/revise/:rid/load', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.loadData');
+    // 单价调整
+    app.get('/tender/:id/revise/:rid/price', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.price');
+    app.post('/tender/:id/revise/:rid/price/update', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.priceUpdate');
 
     // 查看修订数据
     app.get('/tender/:id/revise/history/:rid/info', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.history');
@@ -249,8 +252,8 @@ module.exports = app => {
     // 修订审批
     app.post('/tender/:id/revise/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.addAuditor');
     app.post('/tender/:id/revise/audit/remove', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.removeAuditor');
-    app.post('/tender/:id/revise/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.start');
-    app.post('/tender/:id/revise/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.check');
+    app.post('/tender/:id/revise/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.start');
+    app.post('/tender/:id/revise/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.check');
 
     // 签约清单
     app.post('/tender/:id/deal/get-data', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.getData');

+ 13 - 37
app/service/ledger_history.js

@@ -7,6 +7,7 @@
  * @date
  * @version
  */
+const Ledger = require('../lib/ledger');
 
 module.exports = app => {
 
@@ -107,13 +108,23 @@ module.exports = app => {
             const now = new Date();
             const timestamp = (now).getTime();
 
+            const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } });
+
             const billsHis = `${this.ctx.session.sessionProject.id}/${revise.tid}/ledger/bills${timestamp}-r.json`;
             const bills = await this.ctx.service.reviseBills.getData(revise.tid);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
 
             const posHis = `${this.ctx.session.sessionProject.id}/${revise.tid}/ledger/pos${timestamp}-r.json`;
             const pos = await this.ctx.service.revisePos.getData(revise.tid);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
+            if (price.length === 0) {
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
+            } else {
+                const reviseTree = new Ledger.reviseTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
+                reviseTree.loadRevisePrice(price, this.ctx.tender.info.decimal);
+                reviseTree.loadDatas(bills);
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(reviseTree.getUpdateReviseData()), 'utf8'));
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
+            }
 
             const result = await this.db.insert(this.tableName, {
                 pid: this.ctx.session.sessionProject.id, tid: revise.tid,
@@ -125,41 +136,6 @@ module.exports = app => {
 
             return result.insertId;
         }
-
-        /**
-         * 备份 (预留功能)
-         * @param {Object} transaction - 事务
-         * @param {Object} change - 工程变更
-         * @param {Array} newBillsNode - 新增项目节节点
-         * @param {Array} newPosNode - 新增计量单元节点
-         * @return {Promise<void>} - 新增备份id
-         * @private
-         */
-        async backupChangeHistory(transaction, change, newBillsNodes, newPosNodes) {
-            if ((newBillsNodes || newBillsNodes === 0) && (newPosNodes || newPosNodes.length === 0)) return;
-            const now = new Date();
-            const timestamp = (now).getTime();
-
-            const billsHis = `${this.ctx.session.sessionProject.id}/${change.tid}/ledger/bills${timestamp}-c.json`;
-            const bills = await this.ctx.service.ledger.getData(change.tid);
-            if (newBillsNodes.length > 0) bills.push(...newBillsNodes);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
-
-            const posHis = `${this.ctx.session.sessionProject.id}/${change.tid}/ledger/pos${timestamp}-c.json`;
-            const pos = await this.ctx.service.pos.getPosData({ tid: change.tid });
-            if (newPosNodes.length > 0) pos.push(...newPosNodes);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
-
-            const result = await transaction.insert(this.tableName, {
-                pid: this.ctx.session.sessionProject.id, tid: change.tid,
-                cid: change.cid,
-                in_time: now,
-                bills_file: billsHis, pos_file: posHis,
-                bills_count: bills.length, pos_count: pos.length,
-            });
-
-            return result.insertId;
-        }
     }
 
     return LedgerTag;

+ 2 - 0
app/service/ledger_revise.js

@@ -202,6 +202,8 @@ module.exports = app => {
                 if (revise.his_id > 0) await transaction.update(this.ctx.service.ledgerHistory.tableName, { id: revise.his_id, valid: 0 });
                 // 投资进度改变状态
                 await transaction.update(this.ctx.service.schedule.tableName, { revising: 0 }, { where: { tid: this.ctx.tender.id } });
+                // 作废单价调整
+                await transaction.update(this.ctx.service.revisePrice.tableName, { valid: 0 }, { where: { rid: revise.id } });
                 await transaction.commit();
                 return result.affectedRows === 1;
             } catch (err) {

+ 22 - 32
app/service/report_memory.js

@@ -107,7 +107,7 @@ module.exports = app => {
                 rootId: -1,
                 keys: ['id', 'tender_id', 'ledger_id'],
                 stageId: 'id',
-                calcFields: calcFields || ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'final_1_tp'],
+                calcFields: calcFields || ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'final_1_tp'],
                 calc: function (node, helper, decimal) {
                     if (node.children && node.children.length === 0) {
                         node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -120,9 +120,9 @@ module.exports = app => {
                         node.end_final_1_qty = helper.add(node.final_1_qty, node.end_qc_qty);
                     }
                     node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
-                    node.gather_tp = helper.add(node.contract_tp, node.qc_tp);
-                    node.end_contract_tp = helper.add(node.pre_contract_tp, node.contract_tp);
-                    node.end_qc_tp = helper.add(node.pre_qc_tp, node.qc_tp);
+                    node.gather_tp = helper.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+                    node.end_contract_tp = helper.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+                    node.end_qc_tp = helper.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
                     node.end_gather_tp = helper.add(node.pre_gather_tp, node.gather_tp);
 
                     node.final_tp = helper.add(node.total_price, node.end_qc_tp);
@@ -405,9 +405,13 @@ module.exports = app => {
                 if (this._checkFieldsExist(fields, billsFields.stageEnd)) {
                     preStage = this.ctx.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
                 }
+                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.ctx.stage.id } });
+                const endBpcStage = await this.ctx.service.stageBillsPc.getEndStageData(this.ctx.stage);
                 this.ctx.helper.assignRelaData(billsData, [
-                    {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid'},
-                    {data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid'}
+                    { data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' },
+                    { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid' },
+                    { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
+                    { data: endBpcStage, fields: ['end_contract_pc_tp', 'end_qc_pc_tp', 'end_pc_tp'], prefix: '', relaId: 'lid' },
                 ]);
 
                 const billsTree = this._getNewBillsTree();
@@ -549,12 +553,17 @@ module.exports = app => {
                     ]);
                 }
 
-                if (this._checkFieldsExist(fields, billsFields.stageEnd)) {
-                    const preStage = this.ctx.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
-                    this.ctx.helper.assignRelaData(billsData, [
-                        {data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid'}
-                    ]);
-                }
+                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.ctx.stage.id } });
+                const endBpcStage = await this.ctx.service.stageBillsPc.getEndStageData(this.ctx.stage);
+                const preStage = this._checkFieldsExist(fields, billsFields.stageEnd) && this.ctx.stage.order > 1
+                    ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1)
+                    : [];
+
+                this.ctx.helper.assignRelaData(billsData, [
+                    {data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid'},
+                    { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
+                    { data: endBpcStage, fields: ['end_contract_pc_tp', 'end_qc_pc_tp', 'end_pc_tp'], prefix: '', relaId: 'lid' },
+                ]);
                 const billsTree = this._getNewBillsTree();
                 billsTree.loadDatas(billsData);
 
@@ -575,7 +584,7 @@ module.exports = app => {
                     node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
                     for (const role of validRole) {
                         prefix = 'r' + role.flowOrder + '_';
-                        node[prefix + 'gather_tp'] = helper.add(node[prefix + 'contract_tp'], node[prefix + 'qc_tp']);
+                        node[prefix + 'gather_tp'] = helper.sum([node[prefix + 'contract_tp'], node[prefix + 'qc_tp'], node.pc_tp]);
                     }
                 });
 
@@ -584,25 +593,6 @@ module.exports = app => {
                 }
 
                 return billsTree.getDefaultDatas();
-                // return billsTree.getDatas([
-                //     'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',                 //8
-                //     'code', 'b_code', 'name', 'unit', 'unit_price',                                                         //5
-                //     'deal_qty', 'deal_tp', 'quantity', 'total_price', 'dgn_qty1', 'dgn_qty2',                               //6
-                //     'drawing_code', 'memo', 'node_type', 'is_tp',                                                           //4
-                //     'r0_contract_qty', 'r0_contract_tp', 'r0_qc_qty', 'r0_qc_tp', 'r0_gather_qty', 'r0_gather_tp',          //6
-                //     'r1_contract_qty', 'r1_contract_tp', 'r1_qc_qty', 'r1_qc_tp', 'r1_gather_qty', 'r1_gather_tp',
-                //     'r2_contract_qty', 'r2_contract_tp', 'r2_qc_qty', 'r2_qc_tp', 'r2_gather_qty', 'r2_gather_tp',
-                //     'r3_contract_qty', 'r3_contract_tp', 'r3_qc_qty', 'r3_qc_tp', 'r3_gather_qty', 'r3_gather_tp',
-                //     'r4_contract_qty', 'r4_contract_tp', 'r4_qc_qty', 'r4_qc_tp', 'r4_gather_qty', 'r4_gather_tp',
-                //     'r5_contract_qty', 'r5_contract_tp', 'r5_qc_qty', 'r5_qc_tp', 'r5_gather_qty', 'r5_gather_tp',
-                //     'r6_contract_qty', 'r6_contract_tp', 'r6_qc_qty', 'r6_qc_tp', 'r6_gather_qty', 'r6_gather_tp',
-                //     'r7_contract_qty', 'r7_contract_tp', 'r7_qc_qty', 'r7_qc_tp', 'r7_gather_qty', 'r7_gather_tp',
-                //     'r8_contract_qty', 'r8_contract_tp', 'r8_qc_qty', 'r8_qc_tp', 'r8_gather_qty', 'r8_gather_tp',
-                //     'r9_contract_qty', 'r9_contract_tp', 'r9_qc_qty', 'r9_qc_tp', 'r9_gather_qty', 'r9_gather_tp',
-                //     'r10_contract_qty', 'r10_contract_tp', 'r10_qc_qty', 'r10_qc_tp', 'r10_gather_qty', 'r10_gather_tp',
-                //     'pre_contract_qty', 'pre_contract_tp', 'pre_qc_qty', 'pre_qc_tp', 'pre_gather_qty', 'pre_gather_tp',
-                //     'chapter',                                                                                              //1
-                // ]);
             } catch (err) {
                 return [];
             }

+ 4 - 2
app/service/revise_audit.js

@@ -14,6 +14,7 @@ const SmsAliConst = require('../const/sms_alitemplate');
 const wxConst = require('../const/wechat_template');
 const shenpiConst = require('../const/shenpi');
 const pushType = require('../const/audit').pushType;
+const RevisePrice = require('../lib/revise_price');
 
 module.exports = app => {
     class ReviseAudit extends app.BaseService {
@@ -449,8 +450,9 @@ module.exports = app => {
                                 cache_time_r: cacheTime,
                             });
                         }
-                        // 拷贝修订数据至台账
-                        await this._replaceLedgerByRevise(transaction, revise);
+                        // 重算台账、计量、工程变更
+                        const reviseCalc = new RevisePrice(this.ctx);
+                        await reviseCalc.calcRevise(revise, transaction);
                         const sum = await this.ctx.service.reviseBills.addUp({
                             tender_id: revise.tid, /* , is_leaf: true*/
                         });

+ 114 - 0
app/service/revise_price.js

@@ -0,0 +1,114 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class RevisePrice extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'revise_price';
+        }
+
+        async _addDatas(data) {
+            const datas = data instanceof Array ? data : [data];
+            const insertData = [];
+            const count = await this.db.count(this.tableName, { rid: this.ctx.revise.id });
+            for (const [i, d] of datas.entries()) {
+                if (!d.b_code) throw '新增单价调整,提交的数据错误';
+                const nd = {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.tender.id,
+                    rid: this.ctx.revise.id,
+                    order: count + i + 1,
+                    b_code: d.b_code,
+                    name: d.name,
+                    unit: d.unit,
+                    org_price: d.unit_price,
+                    new_price: d.unit_price,
+                    valid: 1,
+                    memo: d.memo || '',
+                };
+                insertData.push(nd);
+            }
+            const result = await this.db.insert(this.tableName, insertData);
+            for (const [i, d] of insertData.entries()) {
+                d.id = result.insertId + i;
+            }
+            return insertData;
+        }
+
+        async _delDatas(data) {
+            const datas = data instanceof Array ? data : [data];
+            await this.db.delete(this.tableName, { id: datas });
+            return datas;
+        }
+
+        async _updateDatas(data) {
+            const datas = data instanceof Array ? data : [data];
+
+            const uDatas = [];
+            for (const d of datas) {
+                const nd = { id: d.id };
+                if (d.order !== undefined) nd.order = d.order;
+                if (d.new_price !== undefined) {
+                    nd.new_price = this.ctx.helper.round(d.new_price, this.ctx.tender.info.decimal.up);
+                    nd.valid = !this.ctx.helper.checkZero(this.ctx.helper.sub(nd.new_price, d.org_price));
+                }
+                if (d.memo !== undefined) nd.memo = d.memo;
+                uDatas.push(nd);
+            }
+            if (uDatas.length > 0) {
+                await this.db.updateRows(this.tableName, uDatas);
+                return uDatas;
+            } else {
+                return [];
+            }
+        }
+
+        async updateDatas(data) {
+            const result = { add: [], del: [], update: [] };
+            try {
+                if (data.add) {
+                    result.add = await this._addDatas(data.add);
+                }
+                if (data.update) {
+                    result.update = await this._updateDatas(data.update);
+                }
+                if (data.del) {
+                    result.del = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                throw err;
+            }
+        }
+
+        async doPriceUsed(stage, transaction) {
+            const sql = `Update ${this.tableName} Set use_stage = ${stage.id}, use_stage_order = ${stage.order}` +
+                ' Where tid = ? and use_stage = 0';
+            await transaction.query(sql, [stage.tid]);
+        }
+
+        async cancelPriceUsed(stage, transaction) {
+            const sql = `Update ${this.tableName} Set use_stage = 0, use_stage_order = 0` +
+                ' Where tid = ? and use_stage = ?';
+            await transaction.query(sql, [stage.tid, stage.id]);
+        }
+    }
+
+    return RevisePrice;
+};

+ 24 - 55
app/service/rpt_gather_memory.js

@@ -46,6 +46,7 @@ const gatherUtils = {
         gatherNode[prefix + "qc_tp"] = helper.add(gatherNode[prefix + "qc_tp"], sourceNode.qc_tp);
         gatherNode[prefix + "gather_qty"] = helper.add(gatherNode[prefix + "gather_qty"], sourceNode.gather_qty);
         gatherNode[prefix + "gather_tp"] = helper.add(gatherNode[prefix + "gather_tp"], sourceNode.gather_tp);
+        gatherNode[prefix + 'pc_tp'] = helper.add(gatherNode[prefix + 'pc_tp'], sourceNode.pc_tp);
 
         gatherNode[prefix + "pre_contract_qty"] = helper.add(gatherNode[prefix + "pre_contract_qty"], sourceNode.pre_contract_qty);
         gatherNode[prefix + "pre_contract_tp"] = helper.add(gatherNode[prefix + "pre_contract_tp"], sourceNode.pre_contract_tp);
@@ -115,6 +116,7 @@ const gatherUtils = {
         gatherNode[prefix + "qc_tp"] = helper.add(gatherNode[prefix + "qc_tp"], sourceNode.qc_tp);
         gatherNode[prefix + "gather_qty"] = helper.add(gatherNode[prefix + "gather_qty"], sourceNode.gather_qty);
         gatherNode[prefix + "gather_tp"] = helper.add(gatherNode[prefix + "gather_tp"], sourceNode.gather_tp);
+        gatherNode[prefix + 'pc_tp'] = helper.add(gatherNode[prefix + 'pc_tp'], sourceNode.pc_tp);
 
         gatherNode[prefix + "deal_dgn_qty1"] = helper.add(gatherNode[prefix + "deal_dgn_qty1"], sourceNode.deal_dgn_qty1);
         gatherNode[prefix + "deal_dgn_qty2"] = helper.add(gatherNode[prefix + "deal_dgn_qty2"], sourceNode.deal_dgn_qty2);
@@ -258,7 +260,7 @@ module.exports = app => {
                 rootId: -1,
                 keys: ['id', 'tender_id', 'ledger_id'],
                 stageId: 'id',
-                calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp'],
+                calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp'],
                 calc: function (node) {
                     if (node.children && node.children.length === 0) {
                         node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -268,9 +270,9 @@ module.exports = app => {
                         node.end_gather_qty = helper.add(node.pre_gather_qty, node.gather_qty);
                     }
                     node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
-                    node.gather_tp = helper.add(node.contract_tp, node.qc_tp);
-                    node.end_contract_tp = helper.add(node.pre_contract_tp, node.contract_tp);
-                    node.end_qc_tp = helper.add(node.pre_qc_tp, node.qc_tp);
+                    node.gather_tp = helper.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+                    node.end_contract_tp = helper.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+                    node.end_qc_tp = helper.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
                     node.end_gather_tp = helper.add(node.pre_gather_tp, node.gather_tp);
                 }
             });
@@ -284,39 +286,16 @@ module.exports = app => {
 
             if (stage) {
                 await this.ctx.service.stage.doCheckStage(stage);
-                if (stage.readOnly) {
-                    const curStage = await this.ctx.service.stageBills.getAuditorStageData2(tender.id,
-                        stage.id, stage.curTimes, stage.curOrder);
-                    this.ctx.helper.assignRelaData(billsData, [
-                        {
-                            data: curStage,
-                            fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
-                            prefix: '',
-                            relaId: 'lid'
-                        }
-                    ]);
-                } else {
-                    const curStage = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
-                    this.ctx.helper.assignRelaData(billsData, [
-                        {
-                            data: curStage,
-                            fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
-                            prefix: '',
-                            relaId: 'lid'
-                        }
-                    ]);
-                }
-                if (hasPre) {
-                    const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
-                    this.ctx.helper.assignRelaData(billsData, [
-                        {
-                            data: preStage,
-                            fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
-                            prefix: 'pre_',
-                            relaId: 'lid'
-                        }
-                    ]);
-                }
+                const curStage = stage.readOnly
+                    ? await this.ctx.service.stageBills.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
+                const preStage = hasPre && stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
+                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
+                this.ctx.helper.assignRelaData(billsData, [
+                    { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
+                    { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
+                    { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
+                ]);
             }
             billsTree.loadDatas(billsData);
             billsTree.calculateAll();
@@ -383,24 +362,14 @@ module.exports = app => {
 
             for (const stage of stages) {
                 await this.ctx.service.stage.doCheckStage(stage);
-                if (stage.readOnly) {
-                    const curStage = await this.ctx.service.stageBills.getAuditorStageData2(tender.id,
-                        stage.id, stage.curTimes, stage.curOrder);
-                    sumAssignRelaData(billsIndexData, [{
-                        data: curStage,
-                        fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
-                        prefix: '',
-                        relaId: 'lid'
-                    }]);
-                } else {
-                    const curStage = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
-                    sumAssignRelaData(billsIndexData, [{
-                        data: curStage,
-                        fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
-                        prefix: '',
-                        relaId: 'lid'
-                    }]);
-                }
+                const curStage = stage.readOnly
+                    ? await this.ctx.service.stageBills.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
+                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.ctx.stage.id } });
+                sumAssignRelaData(billsIndexData, [
+                    {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'},
+                    { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
+                ]);
             }
 
             billsTree.loadDatas(billsData);

+ 4 - 0
app/service/stage.js

@@ -16,6 +16,7 @@ const path = require('path');
 const _ = require('lodash');
 const projectLogConst = require('../const/project_log');
 const syncApiConst = require('../const/sync_api');
+const RevisePrice = require('../lib/revise_price');
 
 module.exports = app => {
     class Stage extends app.BaseService {
@@ -379,6 +380,9 @@ module.exports = app => {
                     if (!safeResult) throw '初始化其他台账数据失败';
                     const tempResult = await this.ctx.service.stageTempLand.addInitialStageData(newStage, preStage, transaction);
                     if (!tempResult) throw '初始化其他台账数据失败';
+
+                    const priceCalc = new RevisePrice(this.ctx);
+                    await priceCalc.newStagePriceChange(newStage, preStage, transaction);
                 }
                 // 新增期拷贝报表相关配置/签名角色 等
                 if (preStage) {

+ 6 - 0
app/service/stage_audit.js

@@ -18,6 +18,7 @@ const payConst = require('../const/deal_pay');
 const pushType = require('../const/audit').pushType;
 const syncApiConst = require('../const/sync_api');
 const measureType = require('../const/tender').measureType;
+const RevisePrice = require('../lib/revise_price');
 
 module.exports = app => {
     class StageAudit extends app.BaseService {
@@ -951,6 +952,11 @@ module.exports = app => {
                     cache_time_r: this.ctx.stage.cache_time_l,
                     tp_history: JSON.stringify(this.ctx.stage.tp_history),
                 });
+                // 已经引用到本期的单价变更,全部取消
+                await this.ctx.service.revisePrice.cancelPriceUsed(this.ctx.stage, transaction);
+                // 重算所有单价变更
+                const priceCalc = new RevisePrice(this.ctx);
+                await priceCalc.stageCheckAgainPriceChange(this.ctx.stage, audit.order + 2, transaction);
 
                 // 添加短信通知-需要审批提醒功能
                 // const smsUser = await this.ctx.service.projectAccount.getDataById(audit.aid);

+ 13 - 0
app/service/stage_bills_final.js

@@ -76,6 +76,7 @@ module.exports = app => {
                 throw '数据错误';
             }
             const cur = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
+            const curPc = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
             const pre = await this.getFinalData(tender, stage.order - 1);
             if ((!cur || cur.length === 0) && (!pre || pre.length === 0)) return;
             for (const c of cur) {
@@ -101,12 +102,24 @@ module.exports = app => {
                     c.used = !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty)
                         || !this.ctx.helper.checkZero(c.contract_tp) || ! this.ctx.helper.checkZero(c.qc_minus_qty);
                 }
+                const cp = curPc.find(x => { return x.lid === c.lid; });
+                if (cp) {
+                    c.contract_tp = this.ctx.helper.add(c.contract_tp, cp.contract_pc_tp);
+                    c.qc_tp = this.ctx.helper.add(c.qc_tp, cp.qc_pc_tp);
+                    curPc.splice(curPc.indexOf(cp), 1);
+                }
             }
 
             for (const p of pre) {
                 if (p.id !== undefined) delete p.id;
                 p.sid = stage.id;
                 p.sorder = stage.order;
+                const cp = curPc.find(x => { return x.lid === p.lid; });
+                if (cp) {
+                    p.contract_tp = this.ctx.helper.add(p.contract_tp, cp.contract_pc_tp);
+                    p.qc_tp = this.ctx.helper.add(p.qc_tp, cp.qc_pc_tp);
+                    curPc.splice(curPc.indexOf(cp), 1);
+                }
             }
             await transaction.delete(this.tableName, { tid: tender.id, sid: stage.id });
             await transaction.insert(this.tableName, cur ? cur.concat(pre) : pre);

+ 79 - 0
app/service/stage_bills_pc.js

@@ -0,0 +1,79 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class stageBillsPc extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_bills_pc';
+        }
+
+        async getSumTotalPrice(stage) {
+            const sql = 'SELECT Sum(`contract_pc_tp`) As `contract_pc_tp`, Sum(`qc_pc_tp`) As `qc_pc_tp`, Sum(`pc_tp`) As `pc_tp`' +
+                '  FROM ' + this.tableName + ' Where sid = ?';
+            const result = await this.db.queryOne(sql, [stage.id]);
+            return result;
+        }
+
+        async getSumTotalPriceByMaterial(stage_list) {
+            let contract_pc_tp = 0;
+            let qc_pc_tp = 0;
+            let pc_tp = 0;
+            for (const stage of stage_list) {
+                const result = await this.getSumTotalPrice(stage);
+                if (result) {
+                    contract_pc_tp = this.ctx.helper.add(contract_pc_tp, result.contract_pc_tp);
+                    qc_pc_tp = this.ctx.helper.add(qc_pc_tp, result.qc_pc_tp);
+                    pc_tp = this.ctx.helper.add(pc_tp, result.pc_tp);
+                }
+            }
+            return { contract_pc_tp, qc_pc_tp, pc_tp };
+        }
+
+        async getEndStageData(stage) {
+            const sql = 'SELECT lid, SUM(contract_pc_tp) AS end_contract_pc_tp, SUM(qc_pc_tp) AS end_qc_pc_tp, SUM(pc_tp) AS end_pc_tp' +
+                '  FROM ' + this.tableName + ' WHERE tid = ? and sorder <= ? GROUP BY lid';
+            const result = await this.db.query(sql, [stage.tid, stage.order]);
+            return result;
+        }
+
+        async getStagesData(tid, stage_id_list) {
+            const sids = stage_id_list.split(',');
+            const result = [];
+            for (const sid of sids) {
+                const pcData = await this.getAllDataByCondition({ where: { sid } });
+                for (const sp of pcData) {
+                    const rsp = result.find(x => { return x.lid === sp.lid; });
+                    if (rsp) {
+                        rsp.contract_pc_tp = this.ctx.helper.add(rsp.contract_pc_tp, sp.contract_pc_tp);
+                        rsp.pc_tp = this.ctx.helper.add(rsp.pc_tp, sp.pc_tp);
+                        rsp.qc_pc_tp = this.ctx.helper.add(rsp.qc_pc_tp, sp.qc_pc_tp);
+                    } else {
+                        result.push({
+                            id: sp.id, tid: sp.tid, lid: sp.lid,
+                            qc_pc_tp: sp.qc_pc_tp, contract_pc_tp: sp.contract_pc_tp, pc_tp: sp.pc_tp,
+                        });
+                    }
+                }
+            }
+            return result;
+        }
+    }
+
+    return stageBillsPc;
+};

+ 8 - 8
app/service/stage_change.js

@@ -177,9 +177,9 @@ module.exports = app => {
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid`, `cid`, `cbid`, `no_value` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And lid = ? And pid = ?' + filter +
-                '      GROUP By `lid`, `pid`, `cid`, `cbid`, `no_value`' +
+                '      GROUP By `lid`, `pid`, `cbid`, `no_value`' +
                 '  ) As m ' +
-                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cid` = m.`cid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid' +
                 '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' As ocb' +
@@ -208,9 +208,9 @@ module.exports = app => {
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid`, `cid`, `cbid`, `no_value` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?)) And lid = ? And pid = ?' + filter +
-                '      GROUP By `lid`, `pid`, cid, cbid, no_value' +
+                '      GROUP By `lid`, `pid`, cbid, no_value' +
                 '  ) As m ' +
-                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cid` = m.`cid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid' +
                 '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' As ocb' +
@@ -228,9 +228,9 @@ module.exports = app => {
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid`, `cid`, `cbid`, `no_value` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ?' +
-                '      GROUP By `lid`, `pid`, cid, cbid, no_value' +
+                '      GROUP By `lid`, `pid`, cbid, no_value' +
                 '  ) As m ' +
-                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cid` = m.`cid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid' +
                 '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' As ocb' +
@@ -247,9 +247,9 @@ module.exports = app => {
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid`, `cid`, `cbid`, `no_value` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?))' +
-                '      GROUP By `lid`, `pid`, cid, cbid, no_value' +
+                '      GROUP By `lid`, `pid`, cbid, no_value' +
                 '  ) As m ' +
-                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cid` = m.`cid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid` And c.`cbid` = m.`cbid` And c.`no_value` = m.`no_value`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid' +
                 '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' As ocb' +

+ 43 - 0
app/view/revise/price.ejs

@@ -0,0 +1,43 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main  d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            单价调整
+        </div>
+    </div>
+    <div class="content-wrap row pr-46">
+        <div class="c-header p-0 col-12"></div>
+        <!--核心内容(两栏)-->
+        <div class="row w-100 sub-content">
+            <!--左栏-->
+            <div class="c-body" id="left-view" style="width: 100%">
+                <!--0号台账模式-->
+                <div class="sjs-height-0" style="overflow: hidden" id="price-spread">
+                </div>
+            </div>
+            <!--右栏-->
+            <div class="c-body" id="right-view" style="display: none; width: 33%;">
+                <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <div id="ledgerGcl" class="tab-pane">
+                        <div id="ledger-gcl-spread" class="sjs-sh"></div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!--右侧菜单-->
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav" id="side-menu">
+                <li class="nav-item">
+                    <a class="nav-link" content="#ledgerGcl" href="javascript: void(0);">台账清单</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<script src="/public/js/moment/moment.min.js"></script>
+<script>
+    const readOnly = <%- ctx.revise.readOnly %>;
+</script>

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

@@ -5,7 +5,7 @@
         <div class="side-fold"><a href="javascript: void(0)" data-toggle="tooltip" data-placement="top" data-original-title="折叠侧栏" id="to-mini-menu"><i class="fa fa-upload fa-rotate-270"></i></a></div>
     </div>
     <script>
-        new Vue({
+        const subMenu = new Vue({
             el: '.scrollbar-auto',
         });
     </script>

+ 1 - 0
app/view/revise/sub_menu_list.ejs

@@ -1,4 +1,5 @@
 <nav-menu title="返回" url="/tender/<%= ctx.tender.id %>/revise" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
 <nav-menu title="台账修订" url="<%= preUrl %>/info" ml="3" active="<%= ctx.url.indexOf('/info') %>"></nav-menu>
+<nav-menu title="单价调整" url="<%= preUrl %>/price" ml="3" active="<%= ctx.url.indexOf('/price') %>" <% if (ctx.revise.priceCount > 0) { %>hinticon="fa-bell"<% } %>></nav-menu>
 <nav-menu title="台账对比" url="<%= preUrl %>/compare" ml="3" active="<%= ctx.url.indexOf('/compare') %>"></nav-menu>
 <nav-menu title="清单对比" url="<%= preUrl %>/gcl-compare" ml="3" active="<%= ctx.url.indexOf('/gcl-compare') %>"></nav-menu>

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

@@ -10,7 +10,7 @@
     </div>
 </div>
 <script>
-    new Vue({
+    const subMiniMenu = new Vue({
         el: '.side-menu',
     });
 </script>

+ 6 - 1
app/view/tender/detail_modal.ejs

@@ -715,6 +715,10 @@
                         <label class="custom-control-label" for="stage-rc">实际完成量与预计变更</label>
                     </div>
                     <div class="custom-control custom-checkbox mb-2">
+                        <input type="checkbox" class="custom-control-input" checked="" id="stage-priceDiff">
+                        <label class="custom-control-label" for="stage-priceDiff">显示“本期补差”列</label>
+                    </div>
+                    <div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" class="custom-control-input" checked="" id="stage-correct">
                         <label class="custom-control-label" for="stage-correct">使用数量纠正完成率</label>
                     </div>
@@ -1496,6 +1500,7 @@
         $('#ex-memo')[0].checked = property.display.exMemo;
         $('#thousandth')[0].checked = property.display.thousandth;
         $('#stage-rc')[0].checked = property.display.stage.realComplete;
+        $('#stage-priceDiff')[0].checked = property.display.stage.priceDiff;
         $('#stage-correct')[0].checked = property.display.stage.correct;
         $('#dayMode')[0].checked = property.display.dayMode;
     }
@@ -1508,7 +1513,7 @@
                 ledger: { deal: $('#ledger-deal')[0].checked, dgnQty: $('#ledger-dgn-qty')[0].checked, clQty: $('#ledger-cl-qty')[0].checked, },
                 exMemo: $('#ex-memo')[0].checked,
                 thousandth: $('#thousandth')[0].checked,
-                stage: { realComplete: $('#stage-rc')[0].checked, correct: $('#stage-correct')[0].checked },
+                stage: { realComplete: $('#stage-rc')[0].checked, correct: $('#stage-correct')[0].checked, priceDiff: $('#stage-priceDiff')[0].checked },
                 dayMode: $('#dayMode')[0].checked,
             },
         };

+ 18 - 0
config/web.js

@@ -252,6 +252,24 @@ const JsFiles = {
                 ],
                 mergeFile: 'revise',
             },
+            price: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/component/menu.js',
+                    '/public/js/decimal.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
+                    '/public/js/revise_price.js',
+                ],
+                mergeFile: 'revise_price',
+            },
             history: {
                 files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js', '/public/js/component/menu.js'],
                 mergeFiles: [

+ 179 - 0
sql/update.sql

@@ -61,3 +61,182 @@ ADD COLUMN `invalid_time`  varchar(50) NOT NULL DEFAULT '' AFTER `wx_name`;
 ALTER TABLE `zh_valuation_list` ADD `glj_lib_id` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '(0号台账模式)zh_glj_lib_list id列表,逗号分隔' AFTER `chapter_id`;
 ALTER TABLE `zh_valuation_list` ADD `list_glj_lib_id` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '(工程量清单模式)zh_glj_lib_list id列表,逗号分隔' AFTER `list_chapter_id`;
 
+CREATE TABLE `zh_revise_price` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `pid` bigint(20) unsigned NOT NULL COMMENT '项目id',
+  `tid` bigint(20) unsigned NOT NULL COMMENT '标段id',
+  `rid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '台账修订id',
+  `order` int(11) unsigned NOT NULL COMMENT '排序',
+  `b_code` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '清单编号',
+  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
+  `unit` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '单位',
+  `org_price` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '旧单价',
+  `new_price` decimal(24,8) NOT NULL DEFAULT '0.00000000' COMMENT '变更单价',
+  `memo` varchar(1000) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
+  `valid` tinyint(4) unsigned NOT NULL DEFAULT '1' COMMENT '是否有效',
+  `use_stage` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '使用期',
+  `use_stage_order` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '使用期序号',
+  PRIMARY KEY (`id`),
+  KEY `idx_rid` (`rid`) USING BTREE,
+  KEY `idx_tid_valid_stage` (`rid`,`valid`,`use_stage`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='台账修订-单价调整';
+
+CREATE TABLE `zh_stage_bills_pc` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) NOT NULL,
+  `lid` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '项目节id',
+  `contract_pc_tp` decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)',
+  `qc_pc_tp` decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)',
+  `pc_tp` decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差',
+  PRIMARY KEY (`id`),
+  KEY `idx_sid` (`sid`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='期-项目节-补差数据';
+
+ALTER TABLE `zh_stage_bills_final_0`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_1`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_2`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_3`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_4`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_5`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_6`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_7`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_8`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_bills_final_9`
+ADD COLUMN `unit_price`  decimal(24,8) UNSIGNED NOT NULL DEFAULT 0 COMMENT '本期单价' AFTER `contract_expr`,
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `used`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT  '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+
+ALTER TABLE `zh_stage_pos_final_0`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0  COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0  COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_1`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0  COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0  COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_2`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0  COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_3`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_4`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_5`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_6`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_7`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_8`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_9`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_10`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_11`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_12`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_13`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_14`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_15`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_16`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_17`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_18`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+ALTER TABLE `zh_stage_pos_final_19`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `unit_price`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;
+
+Update zh_stage_bills_final_0 sp LEFT JOIN zh_ledger_0 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_1 sp LEFT JOIN zh_ledger_1 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_2 sp LEFT JOIN zh_ledger_2 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_3 sp LEFT JOIN zh_ledger_3 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_4 sp LEFT JOIN zh_ledger_4 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_5 sp LEFT JOIN zh_ledger_5 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_6 sp LEFT JOIN zh_ledger_6 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_7 sp LEFT JOIN zh_ledger_7 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_8 sp LEFT JOIN zh_ledger_8 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+Update zh_stage_bills_final_9 sp LEFT JOIN zh_ledger_9 p on sp.lid = p.id Set sp.unit_price = IFNULL(p.unit_price, 0);
+
+ALTER TABLE `zh_stage`
+ADD COLUMN `contract_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(合同)' AFTER `qc_tp`,
+ADD COLUMN `qc_pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差(变更)' AFTER `contract_pc_tp`,
+ADD COLUMN `pc_tp`  decimal(24,8) NOT NULL DEFAULT 0 COMMENT '本期补差' AFTER `qc_pc_tp`;