ellisran 4 часов назад
Родитель
Сommit
392ebabbf6

+ 15 - 0
app/const/material.js

@@ -42,6 +42,11 @@ const decimal = {
     qty: 3, // 数量
     tp: 2, // 金额
 };
+const exponent_decimal = {
+    up: 3, // 单价, 基准价格指数,现行价格指数
+    tp: 0, // 金额
+    qty: 3, // 计算值
+};
 const ex_calc = [
     {
         code: 'bqht',
@@ -65,6 +70,7 @@ const ex_calc = [
         code: 'zdy',
         text: '自定义金额',
         value: '',
+        result: '',
         select: false,
     },
 ];
@@ -97,6 +103,13 @@ const qty_source_value = {
     gather_minus_qty: 3,
 };
 
+const exponent_status = {
+    shared_noNode: 1,
+    shared_node: 2,
+    self_noNode: 3,
+    self_node: 4,
+};
+
 
 module.exports = {
     t_type,
@@ -106,6 +119,8 @@ module.exports = {
     ex_calc,
     ex_basic_text,
     decimal,
+    exponent_decimal,
     qty_source,
     qty_source_value,
+    exponent_status,
 };

+ 171 - 27
app/controller/material_controller.js

@@ -111,10 +111,11 @@ module.exports = app => {
                         openMaterialTax = 1;
                     }
                     s.decimal = s.decimal ? JSON.parse(s.decimal) : materialConst.decimal;
+                    s.exponent_decimal = s.exponent_decimal ? JSON.parse(s.exponent_decimal) : materialConst.exponent_decimal;
                     materialTotalPrice.m_tp = ctx.helper.add(materialTotalPrice.m_tp, ctx.helper.round(s.m_tp, s.decimal.tp));
                     materialTotalPrice.tax_tp = !s.material_tax ? ctx.helper.add(materialTotalPrice.tax_tp, ctx.helper.round(ctx.helper.mul(s.m_tp, 1 + s.rate / 100), s.decimal.tp)) : materialTotalPrice.tax_tp;
                     materialTotalPrice.ex_tp = ctx.helper.add(materialTotalPrice.ex_tp, ctx.helper.round(s.ex_tp, s.decimal.tp));
-                    materialTotalPrice.ex_tax_tp = ctx.helper.add(materialTotalPrice.ex_tax_tp, ctx.helper.round(ctx.helper.mul(s.ex_tp, 1 + s.exponent_rate / 100), s.decimal.tp));
+                    materialTotalPrice.ex_tax_tp = ctx.helper.add(materialTotalPrice.ex_tax_tp, ctx.helper.round(s.ex_tax_tp, s.exponent_decimal.tp));
                     materialTotalPrice.m_tax_tp = s.material_tax ? ctx.helper.add(materialTotalPrice.m_tax_tp, ctx.helper.round(s.m_tax_tp, s.decimal.tp)) : materialTotalPrice.m_tax_tp;
                 }
                 materialTotalPrice.total_tp = ctx.helper.add(materialTotalPrice.m_tp, materialTotalPrice.ex_tp);
@@ -216,6 +217,12 @@ module.exports = app => {
                     case 'make_self_list':
                         responseData.data = await ctx.service.materialList.makeSelfList(data);
                         break;
+                    case 'make_stage_exponent':
+                        responseData.data = await ctx.service.materialExponentShard.makeExponentList(ctx.tender.id, data);
+                        break;
+                    case 'make_exponent_tp':
+                        responseData.data = await ctx.service.material.makeExponentTp(data);
+                        break;
                     default: throw '参数有误';
                 }
                 ctx.body = responseData;
@@ -500,7 +507,7 @@ module.exports = app => {
                 // 取当前期截止上期含税金额
                 renderData.material.m_tax_tp = renderData.material.m_tax_tp ? renderData.material.m_tax_tp : renderData.material.m_tp;
                 renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
-                renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
+                // renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
                 // renderData.tax_pre_tp_hs = await ctx.service.material.getTaxPreTpHs(ctx.tender.id, ctx.material.order);
 
                 renderData.months = ctx.material.months ? ctx.material.months.split(',') : [];
@@ -774,7 +781,40 @@ module.exports = app => {
                 const renderData = await this._getDefaultRenderData(ctx);
                 const stage_list = await ctx.service.stage.getStageMsgByStageId(ctx.material.stage_id);
                 renderData.ex_calc = materialConst.ex_calc;
-                if (!ctx.material.ex_calc) {
+                renderData.materialExponentShardData = ctx.material.showStageExponent || ctx.material.exponent_node ? await ctx.service.materialExponentShard.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
+                renderData.materialExponentNodeData = ctx.material.exponent_node ? await ctx.service.materialExponentNode.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
+                const materialExponentNodeSort = ctx.material.exponent_node ? ['-1'].concat(ctx.material.exponent_node.split(',')) : [];
+                const map = Object.fromEntries(materialExponentNodeSort.map((v, i) => [v, i]));
+                renderData.materialExponentNodeData = renderData.materialExponentNodeData.sort((a, b) => map[a.node] - map[b.node]);
+                if (ctx.material.showStageExponent) {
+                    const ex_calcList = [];
+                    for (const ms of renderData.materialStageData) {
+                        if (ctx.material.exponent_node) {
+                            for (const node of renderData.materialExponentNodeData) {
+                                ex_calcList.push({
+                                    ms_id: ms.id,
+                                    mn_id: node.id,
+                                    ex_calc: node.ex_calc ? JSON.parse(node.ex_calc) : materialConst.ex_calc,
+                                });
+                            }
+                        } else {
+                            ex_calcList.push({
+                                ms_id: ms.id,
+                                ex_calc: ms.ex_calc ? JSON.parse(ms.ex_calc) : materialConst.ex_calc,
+                            });
+                        }
+                    }
+                    renderData.ex_calc = ex_calcList;
+                } else if (!ctx.material.showStageExponent && ctx.material.exponent_node) {
+                    const ex_calcList = [];
+                    for (const node of renderData.materialExponentNodeData) {
+                        ex_calcList.push({
+                            mn_id: node.id,
+                            ex_calc: node.ex_calc ? JSON.parse(node.ex_calc) : materialConst.ex_calc,
+                        });
+                    }
+                    renderData.ex_calc = ex_calcList;
+                } else if (!ctx.material.showStageExponent && !ctx.material.exponent_node && !ctx.material.ex_calc) {
                     const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
                     for (const bq of renderData.ex_calc) {
                         const calc = _.find(calcBase, { code: bq.code }) || _.find(calcBase, { code: 'bqwc' });
@@ -800,7 +840,7 @@ module.exports = app => {
                     }
                     renderData.ex_calc = ex_calc;
                 }
-                renderData.materialBillsData = await this._getMaterialBillsData(ctx);
+                // renderData.materialBillsData = await this._getMaterialBillsData(ctx);
                 renderData.materialExponentData = await this._getMaterialExponentData(ctx);
                 // 取对应期的截取上期的调差金额和应耗数量
                 if (ctx.material.highOrder !== ctx.material.order) {
@@ -813,7 +853,6 @@ module.exports = app => {
                 }
 
                 // 取当前期截止上期含税金额
-                renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
                 renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
                 renderData.materialType = materialConst;
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.exponent);
@@ -867,21 +906,41 @@ module.exports = app => {
                 const newDecimalUp = parseInt(data.up);
                 const newDecimalTp = parseInt(data.tp);
                 const newDecimalQty = parseInt(data.qty);
+                const newExponentDecimalUp = parseInt(data.exponent_up);
+                const newExponentDecimalTp = parseInt(data.exponent_tp);
+                const newExponentDecimalQty = parseInt(data.exponent_qty);
                 const newQtySource = parseInt(data.qty_source);
                 if (ctx.app._.isNaN(newDecimalUp) || newDecimalUp > 6 || newDecimalUp < 0) {
-                    throw '单价小数位数设置不能大于6或小于0';
+                    throw '信息价单价小数位数设置不能大于6或小于0';
                 }
                 if (ctx.app._.isNaN(newDecimalTp) || newDecimalTp > 6 || newDecimalTp < 0) {
-                    throw '金额小数位数设置不能大于6或小于0';
+                    throw '信息价金额小数位数设置不能大于6或小于0';
                 }
                 if (ctx.app._.isNaN(newDecimalQty) || newDecimalQty > 6 || newDecimalQty < 0) {
-                    throw '数量小数位数设置不能大于6或小于0';
+                    throw '信息价数量小数位数设置不能大于6或小于0';
+                }
+                if (ctx.app._.isNaN(newExponentDecimalUp) || newExponentDecimalUp > 6 || newExponentDecimalUp < 0) {
+                    throw '指数法单价小数位数设置不能大于6或小于0';
+                }
+                if (ctx.app._.isNaN(newExponentDecimalTp) || newExponentDecimalTp > 6 || newExponentDecimalTp < 0) {
+                    throw '指数法金额小数位数设置不能大于6或小于0';
+                }
+                if (ctx.app._.isNaN(newExponentDecimalQty) || newExponentDecimalQty > 6 || newExponentDecimalQty < 0) {
+                    throw '指数法计算值小数位数设置不能大于6或小于0';
+                }
+                let need = true;
+                let needExponent = true;
+                if (ctx.material.decimal.up === newDecimalUp && ctx.material.decimal.tp === newDecimalTp && ctx.material.decimal.qty === newDecimalQty) {
+                    need = false;
                 }
-                if (ctx.material.decimal.up === newDecimalUp && ctx.material.decimal.tp === newDecimalTp && ctx.material.decimal.qty === newDecimalQty && newQtySource === ctx.material.qty_source) {
+                if (ctx.material.exponent_decimal.up === newExponentDecimalUp && ctx.material.exponent_decimal.tp === newExponentDecimalTp && ctx.material.exponent_decimal.qty === newExponentDecimalQty) {
+                    needExponent = false;
+                }
+                if (!need && !needExponent && newQtySource === ctx.material.qty_source) {
                     throw '设置内容未发生变化';
                 }
-                if (ctx.material.decimal.up !== newDecimalUp || ctx.material.decimal.tp !== newDecimalTp || ctx.material.decimal.qty !== newDecimalQty) {
-                    const result = await ctx.service.material.saveDecimal(newDecimalUp, newDecimalTp, newDecimalQty);
+                if (need || needExponent) {
+                    const result = await ctx.service.material.saveDecimal(need, newDecimalUp, newDecimalTp, newDecimalQty, needExponent, newExponentDecimalUp, newExponentDecimalTp, newExponentDecimalQty);
                     if (!result) {
                         throw '小数位数设置失败';
                     }
@@ -1162,22 +1221,20 @@ module.exports = app => {
                     err: 0,
                     msg: '',
                     data: {},
+                    hpack: [],
                 };
-                let ex_tp = ctx.material.ex_tp;
-                let ex_expr = ctx.material.ex_expr;
+                // 转换为整型,不然会无法获取
+                if (data.ms_id) data.ms_id = parseInt(data.ms_id);
+                if (data.mn_id) data.mn_id = parseInt(data.mn_id);
                 switch (data.type) {
                     case 'add':
                         responseData.data = await ctx.service.materialExponent.add();
                         break;
                     case 'del':
-                        [ex_tp, ex_expr] = await ctx.service.materialExponent.del(data.id);
-                        responseData.data.ex_tp = ex_tp;
-                        responseData.data.ex_expr = ex_expr;
+                        responseData.data = await ctx.service.materialExponent.del(data.id);
                         break;
                     case 'update':
-                        [ex_tp, ex_expr] = await ctx.service.materialExponent.save(data.updateData);
-                        responseData.data.ex_tp = ex_tp;
-                        responseData.data.ex_expr = ex_expr;
+                        responseData.data = await ctx.service.materialExponent.save(data.updateData, data.ms_id, data.mn_id);
                         break;
                     case 'rate':
                         // 判断数量是否为数字
@@ -1187,20 +1244,45 @@ module.exports = app => {
                         if (ctx.material.readOnly) {
                             throw '无权操作';
                         }
-                        await ctx.service.material.changeExponentRate(data.rate);
+                        responseData.data = await ctx.service.material.changeExponentRate(data.rate);
                         break;
                     case 'paste':
-                        [ex_tp, ex_expr] = await ctx.service.materialExponent.saveDatas(data.updateData);
-                        responseData.data.ex_tp = ex_tp;
-                        responseData.data.ex_expr = ex_expr;
+                        responseData.data = await ctx.service.materialExponent.saveDatas(data.updateData, data.ms_id, data.mn_id);
                         // 取所有指数清单
-                        responseData.data.info = await this._getMaterialExponentData(ctx);
+                        // responseData.data.info = await this._getMaterialExponentData(ctx);
                         break;
                     case 'ex_calc':
                         // 判断数量是否为数字
-                        [ex_tp, ex_expr] = await ctx.service.material.changeExCalc(data.updateData);
-                        responseData.data.ex_tp = ex_tp;
-                        responseData.data.ex_expr = ex_expr;
+                        responseData.data = await ctx.service.material.changeExCalc(data.updateData, data.ms_id, data.mn_id);
+                        break;
+                    case 'load':
+                        const hpack = true;
+                        const filter = data.filter.split(';');
+                        for (const f of filter) {
+                            switch (f) {
+                                case 'ledger':
+                                    if (hpack) {
+                                        responseData.hpack.push('ledgerData');
+                                        responseData.data.ledgerData = this.ctx.helper.hpackArr(await this._getStageLedgerData(ctx));
+                                    } else {
+                                        responseData.data.ledgerData = await this._getStageLedgerData(ctx);
+                                    }
+                                    break;
+                                case 'stage':
+                                    responseData.data.materialStageData = await ctx.service.materialStage.getAllDataByCondition({ where: { mid: ctx.material.id } });
+                                    break;
+                                case 'nodes':
+                                    responseData.data.exponent_nodes = ctx.material.pre_exponent_node;
+                                    break;
+                                case 'pre_nodes':
+                                    responseData.data.materialPreNodes = await ctx.service.materialExponentNode.getPreNodeData(ctx.material.id, ctx.tender.id);
+                                    break;
+                                default: throw '参数有误';
+                            }
+                        }
+                        break;
+                    case 'update_node':
+                        await ctx.service.materialExponentNode.updateNode(data.updateData);
                         break;
                     default: throw '参数有误';
                 }
@@ -1212,6 +1294,68 @@ module.exports = app => {
             }
         }
 
+        async _getStageLedgerData(ctx) {
+            const curLedgerListData = [];
+            // 获取所选期数据并合并相加同类清单项
+            if (ctx.material.is_stage_self) {
+                for (const s of ctx.material.stage_id.split(',')) {
+                    const curLedgerPcData = await ctx.service.stageBillsPc.getStagesData(ctx.tender.id, s.toString());
+                    const curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, s.toString());
+                    for (const cl of curLedgerData) {
+                        const clpc = ctx.helper._.find(curLedgerPcData, { lid: cl.lid });
+                        ctx.helper._.assign(cl, clpc);
+                    }
+                    curLedgerListData.push({
+                        stage_id: s,
+                        curLedgerData,
+                    });
+                }
+            } else {
+                const curLedgerPcData = await ctx.service.stageBillsPc.getStagesData(ctx.tender.id, ctx.material.stage_id);
+                const curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, ctx.material.stage_id);
+                for (const cl of curLedgerData) {
+                    const clpc = ctx.helper._.find(curLedgerPcData, { lid: cl.lid });
+                    ctx.helper._.assign(cl, clpc);
+                }
+                curLedgerListData.push(...curLedgerData);
+            }
+            const sids = ctx.material.stage_id.split(',');
+            const lastSid = sids[sids.length - 1];
+            const stage = await ctx.service.stage.getDataById(lastSid);
+            const ledgerHis = await ctx.service.ledgerHistory.getDataById(stage.his_id);
+            const ledgerData = await ctx.helper.loadLedgerDataFromOss(ledgerHis.bills_file);
+            const resultData = [];
+            if (ctx.material.is_stage_self) {
+                for (const cl of curLedgerListData) {
+                    resultData.push({ data: cl.curLedgerData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: cl.stage_id + '_', relaId: 'lid' });
+                }
+            } else {
+                resultData.push({ data: curLedgerListData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' });
+            }
+            this.ctx.helper.assignRelaData(ledgerData, resultData);
+            // const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
+            // const pcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
+            // const importData = await ctx.service.stageImportChange.getImportLid(stage.id);
+            // const curStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, stage.id);
+            // // 结算状态
+            // const settleStatus = stage.readySettle ? await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.stage.readySettle.id }}) : [];
+            // // 查询截止上期数据
+            // const preStageData = ctx.stage.preCheckedStage ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.preCheckedStage.order) : [];
+            // const exprData = await ctx.service.expr.getAllDataByCondition({ where: { tid: ctx.stage.tid, calc_module: 'stage' } });
+            // this.ctx.helper.assignRelaData(ledgerData, [
+            //     { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
+            //     { data: memoData, fields: this.ledgerMemoColumn, prefix: '', relaId: 'id' },
+            //     { data: extraData, fields: this.ledgerExtraColumn, prefix: '', relaId: 'id' },
+            //     { 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', 'ex_stage_qty1', 'ex_stage_tp1'], prefix: '', relaId: 'lid' },
+            //     { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used', 'ex_stage_qty1', 'ex_stage_tp1', 'used_time', 'update_time'], prefix: 'pre_', relaId: 'lid' },
+            //     { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
+            //     { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+            //     { data: exprData, fields: ['expr'], prefix: 'calc_', relaId: 'calc_id' },
+            // ]);
+            return ledgerData;
+        }
+
         // 审批相关
         /**
          * 添加审批人

+ 16 - 0
app/middleware/material_check.js

@@ -98,6 +98,22 @@ module.exports = options => {
                 || (this.subProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditorIds.indexOf(accountId) !== -1));
             material.editForAudit = this.subProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditorIds.indexOf(accountId) !== -1;
             material.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
+            material.exponent_decimal = material.exponent_decimal ? JSON.parse(material.exponent_decimal) : materialConst.exponent_decimal;
+            material.needStageExponent = !material.is_new_exponent && material.is_stage_self && !material.readOnly;
+            material.showStageExponent = material.is_stage_self && material.is_new_exponent;
+            material.needUpdateExponent = yield this.service.material.checkExponentUpdate(material);
+            // 指数调差5个状态,第一个:共用期无分项,第二个:共用期有分项,第三个:独立期无分项,第四个:独立期有分项, 第五个:旧数据,非上报状态下是独立期但指数调差是共用期
+            if (material.readOnly && material.is_stage_self && !material.is_new_exponent) {
+                material.exponentStatus = 5;
+            } else if (!material.is_stage_self && !material.exponent_node) {
+                material.exponentStatus = materialConst.exponent_status.shared_noNode;// 共用期无分项
+            } else if (!material.is_stage_self && material.exponent_node) {
+                material.exponentStatus = materialConst.exponent_status.shared_node;// 共用期有分项
+            } else if (material.is_stage_self && !material.exponent_node) {
+                material.exponentStatus = materialConst.exponent_status.self_noNode;// 独立期无分项
+            } else if (material.is_stage_self && material.exponent_node) {
+                material.exponentStatus = materialConst.exponent_status.self_node;// 独立期有分项
+            }
             // 判断stage流程可否撤回,是哪一种撤回
             // yield this.service.material.doCheckMaterialCanCancel(material);
             this.material = material;

+ 14 - 0
app/public/css/main.css

@@ -2284,3 +2284,17 @@ animation:shake 1s .2s ease both;}
     color: rgb(253, 172, 33);
     background-color: rgb(255, 228, 186);
 }
+.nav-tabs-wrapper {
+    overflow-x: auto;
+    overflow-y: hidden;
+    white-space: nowrap;
+}
+
+/* 去掉滚动条(可选) */
+.nav-tabs-wrapper::-webkit-scrollbar {
+    height: 6px;
+}
+.nav-tabs-wrapper::-webkit-scrollbar-thumb {
+    background: #ccc;
+    border-radius: 3px;
+}

+ 498 - 85
app/public/js/material_exponent.js

@@ -12,16 +12,70 @@ function getPasteHint (str, row = '') {
     }
     return returnObj;
 }
+let show_node = false;
 function resetExTpTable() {
-    const rate = $('#rateInput').val();
-    const bqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
-    const jzbqhs = ZhCalc.add(ex_pre_tp_hs, bqhs);
-    $('#tp_set').find('td').eq(3).text(ZhCalc.round(ex_tp, materialDecimal.tp));
-    $('#tp_set').find('td').eq(4).text(ZhCalc.add(ex_pre_tp, ZhCalc.round(ex_tp, materialDecimal.tp)));
-    $('#rate_set').find('td').eq(3).text(bqhs !== 0 ? bqhs : '');
-    $('#rate_set').find('td').eq(4).text(jzbqhs !== 0 ? jzbqhs : '');
-    // $('#ex_expr').html(ex_expr);
-    $('#ex_expr').attr('data-original-title', '本期价差:' + (ex_expr ? ex_expr : ''));
+    $('#tj_ledger').children('td').eq(1).text(ex_tp ? ZhCalc.round(ex_tp, materialExponentDecimal.tp) : '');
+    $('#tj_ledger').children('td').eq(2).text(ex_tax_tp ? ZhCalc.round(ex_tax_tp, materialExponentDecimal.tp) : '');
+    $('#tj_ledger').children('td').eq(3).text(ZhCalc.add(ex_pre_tp, ZhCalc.round(ex_tp, materialExponentDecimal.tp)));
+    $('#tj_ledger').children('td').eq(4).text(ZhCalc.add(ex_pre_tp_hs, ZhCalc.round(ex_tax_tp, materialExponentDecimal.tp)));
+    $('#total_set').children('td').eq(1).text(ex_tp ? ZhCalc.round(ex_tp, materialExponentDecimal.tp) : '');
+    $('#total_set').children('td').eq(2).text(ex_tax_tp ? ZhCalc.round(ex_tax_tp, materialExponentDecimal.tp) : '');
+    $('#total_set').children('td').eq(3).text(ZhCalc.add(ex_pre_tp, ZhCalc.round(ex_tp, materialExponentDecimal.tp)));
+    $('#total_set').children('td').eq(4).text(ZhCalc.add(ex_pre_tp_hs, ZhCalc.round(ex_tax_tp, materialExponentDecimal.tp)));
+    if (exponent_nodes) {
+        const materialExponentNodeSort = exponent_nodes ? ['-1'].concat(exponent_nodes.split(',')) : [];
+        const map = Object.fromEntries(materialExponentNodeSort.map((v, i) => [v, i]));
+        materialExponentNodeData = materialExponentNodeData.sort((a, b) => map[a.node] - map[b.node]);
+        let html = '';
+        for (const node of materialExponentNodeSort) {
+            const mnList = _.filter(materialExponentNodeData, { node });
+            const one_mn = mnList[0];
+            let total_tp = 0;
+            let total_tax_tp = 0;
+            let total_pre_tp = one_mn.ex_pre_tp || 0;
+            let total_pre_tp_hs = one_mn.ex_tax_pre_tp || 0;
+            // 汇总
+            for (const mn of mnList) {
+                total_tp = ZhCalc.add(total_tp, mn.ex_tp ? mn.ex_tp : 0);
+                total_tax_tp = ZhCalc.add(total_tax_tp, mn.ex_tax_tp ? mn.ex_tax_tp : 0);
+                total_pre_tp = ZhCalc.add(total_pre_tp, mn.ex_tp ? mn.ex_tp : 0);
+                total_pre_tp_hs = ZhCalc.add(total_pre_tp_hs, mn.ex_tax_tp ? mn.ex_tax_tp : 0);
+            }
+            const exprHtml = isStageSelf ? '' : `<a href="javascript:void(0);" data-toggle="tooltip" data-placement="bottom" title="${one_mn.name}(${one_mn.code})价差:${one_mn.ex_expr ? one_mn.ex_expr : ''}"><i class="fa fa-question-circle-o"></i></a>`;
+            html += `<tr id="tj_node_${node}">
+                                                <td class="text-center">${one_mn.name}(${one_mn.code}) ${exprHtml}</td>
+                                                <td class="text-center">${total_tp}</td>
+                                                <td class="text-center">${total_tax_tp}</td>
+                                                <td class="text-center">${total_pre_tp}</td>
+                                                <td class="text-center">${total_pre_tp_hs}</td>
+                                            </tr>`;
+            // if (!show_node) {
+            //     show_node = true;
+            // }
+        }
+        $('#tj_node').html(html);
+        $('[data-toggle="tooltip"]').tooltip();
+    }
+    if (showStageExponent) {
+        $('#total_stage').children('td').eq(1).text(ex_tp ? ZhCalc.round(ex_tp, materialExponentDecimal.tp) : '');
+        $('#total_stage').children('td').eq(2).text(ex_tax_tp ? ZhCalc.round(ex_tax_tp, materialExponentDecimal.tp) : '');
+        for (const ms of materialStageData) {
+            if (!exponent_nodes) {
+                $('.ex_expr_' + ms.order).attr('data-original-title', '第' + ms.order + '期价差:' + (ms.ex_expr ? ms.ex_expr : ''));
+            }
+            $('#tj_stage_' + ms.order).children('td').eq(1).text(ms.ex_tp ? ZhCalc.round(ms.ex_tp, materialExponentDecimal.tp) : '');
+            $('#tj_stage_' + ms.order).children('td').eq(2).text(ms.ex_tax_tp ? ZhCalc.round(ms.ex_tax_tp, materialExponentDecimal.tp) : '');
+        }
+        if (exponent_nodes) {
+            for (const mn of materialExponentNodeData) {
+                $('.ex_expr_node_' + mn.id).attr('data-original-title', mn.name + '价差:' + (mn.ex_expr ? mn.ex_expr : ''));
+                $('#tj_node_' + mn.id).children('td').eq(1).text(mn.ex_tp ? ZhCalc.round(mn.ex_tp, materialExponentDecimal.tp) : '');
+                $('#tj_node_' + mn.id).children('td').eq(2).text(mn.ex_tax_tp ? ZhCalc.round(mn.ex_tax_tp, materialExponentDecimal.tp) : '');
+            }
+        }
+    } else {
+        $('#ex_expr').attr('data-original-title', '本期价差:' + (ex_expr ? ex_expr : ''));
+    }
 }
 $(document).ready(() => {
     autoFlashHeight();
@@ -32,11 +86,11 @@ $(document).ready(() => {
             {title: '符号', colSpan: '1', rowSpan: '2', field: 'symbol', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '符号说明', colSpan: '1', rowSpan: '2', field: 'symbol_desc', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '加权系数', colSpan: '1', rowSpan: '2', field: 'weight_num', hAlign: 2, width: 120, formatter: '@', readOnly: 'readOnly.isConstant'},
-            {title: '基本价格指数', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 120, readOnly: 'readOnly.isEdit'},
+            {title: '基本价格指数(F0)', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 120, readOnly: 'readOnly.isEdit'},
             {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '现行价格指数', colSpan: '1', rowSpan: '2', field: 'm_price', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.remark'},
-            {title: '计算值', colSpan: '1', rowSpan: '2', field: 'calc_num', hAlign: 2, width: 80, formatter: '@', readOnly: true},
+            {title: '现行价格指数(Ft)', colSpan: '1', rowSpan: '2', field: 'm_price', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.remark'},
+            {title: '加权系数(B)', colSpan: '1', rowSpan: '2', field: 'weight_num', hAlign: 2, width: 120, formatter: '@', readOnly: 'readOnly.isConstant'},
+            {title: '计算值(Ft/F0*B)', colSpan: '1', rowSpan: '2', field: 'calc_num', hAlign: 2, width: 100, formatter: '@', readOnly: true, getValue: 'getValue.calc_num'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.remark'},
             {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
         ],
@@ -66,8 +120,9 @@ $(document).ready(() => {
     const materialExponentCol = {
         getValue: {
             calc_num : function (data) {
+                if (data.type === 1) return null;
                 const calc_num = data.basic_price > 0 ? ZhCalc.mul(data.weight_num, ZhCalc.div(data.m_price, data.basic_price)) : 0;
-                return calc_num > 0 ? ZhCalc.round(calc_num, 3) : 0;
+                return calc_num > 0 ? ZhCalc.round(calc_num, materialExponentDecimal.qty) : 0;
             },
         },
         readOnly: {
@@ -86,11 +141,135 @@ $(document).ready(() => {
             }
         },
     };
-    SpreadJsObj.initSpreadSettingEvents(materialExponentSpreadSetting, materialExponentCol);
-    SpreadJsObj.initSheet(materialExponentSpread.getActiveSheet(), materialExponentSpreadSetting);
-    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+
+    const needUpdateArray = ['m_price', 'remark'];
 
     const materialExponentSpreadObj = {
+        getMaterialExponentData: function (msid = null, mnid = null) {
+            const ms_id = msid || $('#myTab').find('.active').attr('data-msid') || null;
+            const mn_id = mnid || $('#myTab2').find('.active').attr('data-mnid') || null;
+            if (ms_id || mn_id) {
+                const condition = {};
+                if (ms_id) condition.ms_id = parseInt(ms_id);
+                if (mn_id) condition.mn_id = parseInt(mn_id);
+                const mseList = _.filter(materialExponentShardData, condition);
+                console.log(mseList);
+                for (const me of materialExponentData) {
+                    me.ms_id = ms_id ? parseInt(ms_id) : null;
+                    me.mn_id = mn_id ? parseInt(mn_id) : null;
+                    const mseInfo = _.find(mseList, { me_id: me.me_id ? me.me_id : me.id });
+                    console.log(mseInfo);
+                    if (mseInfo) {
+                        for (const nu of needUpdateArray) {
+                            me[nu] = mseInfo[nu];
+                        }
+                    }
+                }
+            }
+            console.log(materialExponentData, materialExponentShardData);
+            return materialExponentData;
+        },
+        updateMaterialNodeTab: function () {
+            const nodeList = $('#myTab2').find('li');
+            const nowli = $('#myTab2').find('.active');
+            const materialExponentNodeSort = exponent_nodes ? ['-1'].concat(exponent_nodes.split(',')) : [];
+            const map = Object.fromEntries(materialExponentNodeSort.map((v, i) => [v, i]));
+            materialExponentNodeData = materialExponentNodeData.sort((a, b) => map[a.node] - map[b.node]);
+            const newNodeList = _.filter(materialExponentNodeData, { ms_id: parseInt($('#myTab').find('.active').attr('data-msid')) });
+            for (const n of nodeList) {
+                const node = $(n).children('a').attr('data-node');
+                const nl = _.find(newNodeList, { node });
+                $(n).children('a').attr('data-mnid', nl.id);
+            }
+            const newMnid = parseInt(nowli.attr('data-mnid'));
+            materialExponentSpreadObj.getMaterialExponentData($('#myTab').find('.active').attr('data-msid'), newMnid);
+            materialExponentSpreadObj.materialSheetReset(true);
+            setExCalc($('#myTab').find('.active').attr('data-msid'), newMnid);
+        },
+        updateOneMaterialExponent: function (data) {
+            const ms_id = $('#myTab').find('.active').attr('data-msid') || null;
+            const mn_id = $('#myTab2').find('.active').attr('data-mnid') || null;
+            const condition = { me_id: data.id };
+            if (ms_id) condition.ms_id = parseInt(ms_id);
+            if (mn_id) condition.mn_id = parseInt(mn_id);
+            const mseInfo = _.find(materialExponentShardData, condition);
+            console.log(mseInfo);
+            data.ms_id = ms_id ? parseInt(ms_id) : null;
+            data.mn_id = mn_id ? parseInt(mn_id) : null;
+            for (const nu of needUpdateArray) {
+                data[nu] = mseInfo[nu];
+            }
+            return data;
+        },
+        updateMaterialData: function (datas) {
+            if (datas.stageData && datas.stageData.length > 0) {
+                if (datas.stageData.length === materialStageData.length) {
+                    // 全体替换
+                    materialStageData = datas.stageData;
+                } else {
+                    // 替换对应的stage数据
+                    for (const s of datas.stageData) {
+                        const index = _.findIndex(materialStageData, { id: s.id });
+                        materialStageData.splice(index, 1, s);
+                    }
+                }
+            }
+            if (datas.nodeData && datas.nodeData.length > 0) {
+                if (datas.nodeData.length === materialExponentNodeData.length) {
+                    // 全体替换
+                    materialExponentNodeData = datas.nodeData;
+                } else {
+                    // 替换对应的stage数据
+                    for (const s of datas.nodeData) {
+                        const index = _.findIndex(materialExponentNodeData, { id: s.id });
+                        materialExponentNodeData.splice(index, 1, s);
+                    }
+                }
+            }
+            if (datas.pushExponentShardData && datas.pushExponentShardData.length > 0) {
+                materialExponentShardData = _.concat(materialExponentShardData, datas.pushExponentShardData);
+            }
+            if (datas.removeExponentShardData && datas.removeExponentShardData.length > 0) {
+                materialExponentShardData = _.remove(materialExponentShardData, function (item) {
+                    return _.findIndex(datas.removeExponentShardData, { id: item.me_id }) === -1
+                })
+            }
+            if (datas.exponentShardData && datas.exponentShardData.length > 0) {
+                if (datas.exponentShardData.length === materialExponentShardData.length) {
+                    // 全体替换
+                    materialExponentShardData = datas.exponentShardData;
+                } else {
+                    // 替换对应的stage数据
+                    for (const s of datas.exponentShardData) {
+                        const index = _.findIndex(materialExponentShardData, { id: s.id });
+                        materialExponentShardData.splice(index, 1, s);
+                    }
+                }
+            }
+            if (datas.exponentData && datas.exponentData.length > 0) {
+                if (datas.exponentData.length === materialExponentData.length) {
+                    // 全体替换
+                    materialExponentData = datas.exponentData;
+                    materialExponentSpreadObj.getMaterialExponentData();
+                } else {
+                    for (const b of datas.exponentData) {
+                        const index = _.findIndex(materialExponentData, {id: b.id});
+                        materialExponentData.splice(index, 1, b);
+                        materialExponentSpreadObj.updateOneMaterialExponent(materialExponentData[index]);
+                    }
+                }
+            }
+        },
+        materialSheetReset: function (redo = false) {
+            let newMaterialExponentData = _.cloneDeep(materialExponentData);
+            if (redo) {
+                materialExponentSpread.getActiveSheet().reset();
+                SpreadJsObj.initSpreadSettingEvents(materialExponentSpreadSetting, materialExponentCol);
+                SpreadJsObj.initSheet(materialExponentSpread.getActiveSheet(), materialExponentSpreadSetting);
+            }
+            SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, newMaterialExponentData);
+            materialExponentSpreadObj.refreshActn();
+        },
         refreshActn: function (rowCount = 1) {
             const setObjEnable = function (obj, enable) {
                 if (enable) {
@@ -108,9 +287,12 @@ $(document).ready(() => {
             const sheet = materialExponentSpread.getActiveSheet();
             postData(window.location.pathname + '/save', {type: 'add'}, function (result) {
                 if (result) {
-                    materialExponentData.push(result);
-                    sheet.addRows(materialExponentData.length - 1, 1);
-                    SpreadJsObj.reLoadRowData(sheet, materialExponentData.length - 1);
+                    materialExponentData.push(result.info);
+                    if (showStageExponent || exponent_nodes) {
+                        materialExponentSpreadObj.updateMaterialData(result);
+                        materialExponentSpreadObj.getMaterialExponentData();
+                    }
+                    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
                     sheet.setSelection(materialExponentData.length - 1, 0, 1, 1);
                     materialExponentSpreadObj.refreshActn();
                 }
@@ -119,14 +301,20 @@ $(document).ready(() => {
         del: function () {
             const sheet = materialExponentSpread.getActiveSheet();
             const select = SpreadJsObj.getSelectObject(sheet);
+            const sel = sheet.getSelections();
             postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
                 ex_tp = result.ex_tp;
-                ex_expr = result.ex_expr;
-                resetExTpTable();
-                const index = materialExponentData.indexOf(select);
+                ex_tax_tp = result.ex_tax_tp;
+                ex_expr = result.ex_expr || null;
+                const index = _.findIndex(materialExponentData, { id: select.id });
                 materialExponentData.splice(index, 1);
-                sheet.deleteRows(index, 1);
-                const sel = sheet.getSelections();
+                if (showStageExponent || exponent_nodes) {
+                    result.removeExponentShardData = [{ id: select.id }];
+                    materialExponentSpreadObj.updateMaterialData(result);
+                    materialExponentSpreadObj.getMaterialExponentData();
+                }
+                materialExponentSpreadObj.materialSheetReset(true);
+                resetExTpTable();
                 sheet.setSelection(index > 0 ? index - 1 : 0, sel.length > 0 ? sel[0].col : 0, 1, 1);
                 materialExponentSpreadObj.refreshActn();
             });
@@ -204,12 +392,16 @@ $(document).ready(() => {
                 // console.log(select);
 
                 // 更新至服务器
-                postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                postData(window.location.pathname + '/save', { type:'update', updateData: select, ms_id: $('#myTab').find('.active').attr('data-msid') || null, mn_id: $('#myTab2').find('.active').attr('data-mnid') || null }, function (result) {
                     ex_tp = result.ex_tp;
-                    ex_expr = result.ex_expr;
+                    ex_tax_tp = result.ex_tax_tp;
+                    ex_expr = result.ex_expr || null;
+                    if (showStageExponent || exponent_nodes) {
+                        materialExponentSpreadObj.updateMaterialData(result);
+                    }
                     resetExTpTable();
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    materialExponentData.splice(info.row, 1, select);
+                    // materialExponentData.splice(info.row, 1, select);
                 }, function () {
                     select[col.field] = orgValue;
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -236,9 +428,13 @@ $(document).ready(() => {
                     select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
                     delete select.waitingLoading;
                     // 更新至服务器
-                    postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                    postData(window.location.pathname + '/save', { type:'update', updateData: select, ms_id: $('#myTab').find('.active').attr('data-msid') || null, mn_id: $('#myTab2').find('.active').attr('data-mnid') || null }, function (result) {
                         ex_tp = result.ex_tp;
-                        ex_expr = result.ex_expr;
+                        ex_tax_tp = result.ex_tax_tp;
+                        ex_expr = result.ex_expr || null;
+                        if (showStageExponent || exponent_nodes) {
+                            materialExponentSpreadObj.updateMaterialData(result);
+                        }
                         resetExTpTable();
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     }, function () {
@@ -292,11 +488,15 @@ $(document).ready(() => {
                     return;
                 }
                 // 更新至服务器
-                postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
-                    materialExponentData = result.info;
-                    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+                postData(window.location.pathname + '/save', { type:'paste', updateData: data, ms_id: $('#myTab').find('.active').attr('data-msid') || null, mn_id: $('#myTab2').find('.active').attr('data-mnid') || null }, function (result) {
                     ex_tp = result.ex_tp;
-                    ex_expr = result.ex_expr;
+                    ex_tax_tp = result.ex_tax_tp;
+                    ex_expr = result.ex_expr || null;
+                    materialExponentSpreadObj.updateMaterialData(result);
+                    if (showStageExponent || exponent_nodes) {
+                        materialExponentSpreadObj.getMaterialExponentData();
+                    }
+                    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
                     resetExTpTable();
                 }, function () {
                     SpreadJsObj.reLoadSheetData(sheet);
@@ -415,11 +615,15 @@ $(document).ready(() => {
             }
             // console.log(data);
             // 更新至服务器
-            postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
-                materialExponentData = result.info;
-                SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+            postData(window.location.pathname + '/save', { type:'paste', updateData: data, ms_id: $('#myTab').find('.active').attr('data-msid') || null, mn_id: $('#myTab2').find('.active').attr('data-mnid') || null }, function (result) {
                 ex_tp = result.ex_tp;
-                ex_expr = result.ex_expr;
+                ex_tax_tp = result.ex_tax_tp;
+                ex_expr = result.ex_expr || null;
+                materialExponentSpreadObj.updateMaterialData(result);
+                if (showStageExponent || exponent_nodes) {
+                    materialExponentSpreadObj.getMaterialExponentData();
+                }
+                SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
                 resetExTpTable();
             }, function () {
                 SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
@@ -428,10 +632,185 @@ $(document).ready(() => {
         },
         setReadOnly: function(readOnly) {
             // SpreadJsObj.resetFieldReadOnly(materialSpread.getActiveSheet(), 'msg_spread', 'm_spread', 'm_tp', 'pre_tp', readOnly);
-        }
+        },
+        _checkExprValid(expr) {
+            if (!expr) return [true, null];
+            const param = [];
+            let num = '', base = '';
+            for (let i = 0, iLen = expr.length; i < iLen; i++) {
+                if (/^[\d\.%]+/.test(expr[i])) {
+                    if (base !== '') {
+                        param.push({type: 'base', value: base});
+                        base = '';
+                    }
+                    num = num + expr[i];
+                } else if (expr[i] === '(') {
+                    if (num !== '') {
+                        param.push({type: 'num', value: num});
+                        num = '';
+                    }
+                    if (base !== '') {
+                        param.push({type: 'base', value: base});
+                        base = '';
+                    }
+                    param.push({type: 'left', value: '('});
+                } else if (expr[i] === ')') {
+                    if (num !== '') {
+                        param.push({type: 'num', value: num});
+                        num = '';
+                    }
+                    if (base !== '') {
+                        param.push({type: 'base', value: base});
+                        base = '';
+                    }
+                    param.push({type: 'right', value: ')'});
+                } else if (/^[\+\-*\/]/.test(expr[i])) {
+                    if (num !== '') {
+                        param.push({type: 'num', value: num});
+                        num = '';
+                    }
+                    if (base !== '') {
+                        param.push({type: 'base', value: base});
+                        base = '';
+                    }
+                    param.push({type: 'calc', value: expr[i]});
+                } else {
+                    return [false, '输入的表达式含有非法字符: ' + expr[i]];
+                }
+            }
+            if (num !== '') {
+                param.push({type: 'num', value: num});
+                num = '';
+            }
+            if (base !== '') {
+                param.push({type: 'base', value: base});
+                base = '';
+            }
+            if (param.length === 0) return true;
+            if (param.length > 1) {
+                if (param[0].value === '-' && param[1].type === 'num') {
+                    param[1].value = '-' + param[1].value;
+                    param.shift();
+                }
+            }
+            const iLen = param.length;
+            let iLeftCount = 0, iRightCount = 0;
+            for (const [i, p] of param.entries()) {
+                if (p.type === 'calc') {
+                    if (i === 0 || i === iLen - 1)
+                        return [false, '输入的表达式非法:计算符号' + p.value + '前后应有数字'];
+                }
+                if (p.type === 'num') {
+                    num = p.value.replace('%', '');
+                    if (p.value.length - num.length > 1)
+                        return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
+                    num = _.toNumber(num);
+                    if (num === undefined || num === null || _.isNaN(num))
+                        return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
+                    if (i > 0) {
+                        if (param[i - 1].type !== 'calc' && param[i - 1].type !== 'left') {
+                            return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
+                        } else if (param[i - 1].value === '/' && num === 0) {
+                            return [false, '输入的表达式非法:请勿除0'];
+                        }
+                    }
+                }
+                if (p.type === 'base') {
+                    if (i > 0 && (param[i - 1].type === 'num' || param[i - 1].type === 'right'))
+                        return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
+                }
+                if (p.type === 'left') {
+                    iLeftCount += 1;
+                    if (i !== 0 && param[i-1].type !== 'calc')
+                        return [false, '输入的表达式非法:(前应有运算符'];
+                }
+                if (p.type === 'right') {
+                    iRightCount += 1;
+                    if (i !== iLen - 1 && param[i+1].type !== 'calc')
+                        return [false, '输入的表达式非法:)后应有运算符'];
+                    if (iRightCount > iLeftCount)
+                        return [false, '输入的表达式非法:")"前无对应的"("'];
+                }
+            }
+            if (iLeftCount > iRightCount)
+                return [false, '输入的表达式非法:"("后无对应的")"'];
+            return [true, ''];
+        },
+        _checkExpr: function (text, data) {
+            if (text) {
+                const num = _.toNumber(text);
+                if (num) {
+                    data.quantity = num;
+                    data.expr = '';
+                } else {
+                    const expr = $.trim(text).replace('\t', '').replace('=', '').toLowerCase();
+                    const [valid, msg] = this._checkExprValid(expr);
+                    if (!valid) return [valid, msg];
+                    data.expr = expr;
+                    data.quantity = ZhCalc.calcExpr.calcExprStrRpn(expr.replace(new RegExp('%', 'gm'), '/100'));
+                    // const ce = new CalcEvalMin();
+                    // data.quantity = ce.eval(expr);
+                    // console.log(data.quantity);
+                }
+            } else {
+                data.quantity = 0;
+                data.expr = '';
+            }
+            return [true, ''];
+        },
     };
+
+    SpreadJsObj.initSpreadSettingEvents(materialExponentSpreadSetting, materialExponentCol);
+    SpreadJsObj.initSheet(materialExponentSpread.getActiveSheet(), materialExponentSpreadSetting);
+    materialExponentSpreadObj.getMaterialExponentData();
+    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+    if (showStageExponent || exponent_nodes) setExCalc();
+    if (exponent_nodes) resetExTpTable();
     materialExponentSpreadObj.refreshActn();
 
+    // 期切换
+    $('#myTab a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+        e.preventDefault();
+        showWaitingView();
+        setTimeout(function () {
+            if (exponent_nodes) {
+                materialExponentSpreadObj.updateMaterialNodeTab();
+            } else {
+                materialExponentSpreadObj.getMaterialExponentData();
+                materialExponentSpreadObj.materialSheetReset(true);
+                setExCalc();
+            }
+            closeWaitingView();
+        }, 1000);
+    });
+    // 分项切换
+    $('#myTab2 a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+        e.preventDefault();
+        showWaitingView();
+        setTimeout(function () {
+            materialExponentSpreadObj.getMaterialExponentData();
+            materialExponentSpreadObj.materialSheetReset(true);
+            setExCalc();
+            closeWaitingView();
+        }, 1000);
+    });
+
+    function setExCalc(msid = null, mnid = null) {
+        let html = '';
+        const mn_id = mnid || $('#myTab2').find('.active').attr('data-mnid') || null;
+        const ms_id = msid || $('#myTab').find('.active').attr('data-msid') || null;
+        const condition = {};
+        if (mn_id) condition.mn_id = parseInt(mn_id);
+        if (ms_id) condition.ms_id = parseInt(ms_id);
+        ex_calc = _.find(ex_calcList, condition).ex_calc;
+        for (const bq of ex_calc) {
+            html += `<tr>
+                                        <td><input type="checkbox" value="${bq.code}" ${readOnly ? 'disabled' : ''} class="calc_select" ${bq.select ? 'checked' : ''}></td>
+                                        <td>${materialType.ex_basic_text[bq.code]}</td><td>${bq.code === 'zdy' && !readOnly ? `<input type="text" id="calc_zdy" class="form-control form-control-sm" value="${bq.result ? bq.result : bq.value}">` : bq.result ? bq.result : bq.value }</td>
+                                    </tr>`;
+        }
+        $('#calc_basic_select').html(html);
+    }
 
     if (!readOnly) {
         $('#add').click(materialExponentSpreadObj.add);
@@ -480,7 +859,7 @@ $(document).ready(() => {
         }
 
         // 调差基数选中
-        $('.calc_select').on('click', function () {
+        $('body').on('click', '.calc_select', function () {
             // 如果是选中则清除其余的选中
             const code = $(this).val();
             for (const calc of ex_calc) {
@@ -489,37 +868,71 @@ $(document).ready(() => {
                     $('.calc_select[value="'+ calc.code +'"]').prop('checked', false);
                 }
             }
-            postData(window.location.pathname + '/save', { type:'ex_calc', updateData: ex_calc }, function (result) {
+            postData(window.location.pathname + '/save', { type:'ex_calc', updateData: ex_calc, ms_id: $('#myTab').find('.active').attr('data-msid') || null, mn_id: $('#myTab2').find('.active').attr('data-mnid') || null }, function (result) {
                 ex_tp = result.ex_tp;
-                ex_expr = result.ex_expr;
+                ex_tax_tp = result.ex_tax_tp;
+                ex_expr = result.ex_expr || null;
+                if (showStageExponent || exponent_nodes) {
+                    materialExponentSpreadObj.updateMaterialData(result);
+                }
                 resetExTpTable();
             });
         });
         // 回车提交
-        $('#calc_zdy').on('keypress', function () {
+        $('body').on('keypress', '#calc_zdy', function () {
             if(window.event.keyCode === 13) {
                 $(this).blur();
             }
         });
+        $('body').on('click', '#calc_zdy', function (e) {
+            e.stopPropagation();
+            const zdy = _.find(ex_calc, { code: 'zdy' });
+            $(this).val(zdy.value);
+        });
         // 自定义金额变更并提交
-        $('#calc_zdy').on('blur', function () {
-            let newValue = parseFloat($(this).val());
-            // 判断输入位数,并自动四舍五入
-            newValue = ZhCalc.round(newValue, materialDecimal.tp);
-            $('#calc_zdy').val(newValue);
-            if (isNaN(newValue)) {
-                toastr.error('请输入正确的金额');
-                return false;
-            }
+        $('body').on('blur', '#calc_zdy', function () {
+            let validText = $(this).val();
+            let newValue = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : '');
             const zdy = _.find(ex_calc, { code: 'zdy' });
             if (zdy.value === newValue) {
+                $('#calc_zdy').val(zdy.result ? zdy.result : zdy.value);
+                return;
+            }
+            const exprQuantity = {
+                expr: '',
+                quantity: 0,
+            };
+            const [valid, msg] = materialExponentSpreadObj._checkExpr(newValue, exprQuantity);
+            console.log(exprQuantity, validText, valid, msg);
+            if (!valid) {
+                toastr.error(msg);
+                $('#calc_zdy').val(zdy.result ? zdy.result : zdy.value);
+                return;
+            }
+            if (isNaN(exprQuantity.quantity)) {
+                toastr.error('不能输入其它非数字类型字符');
+                $('#calc_zdy').val(zdy.result ? zdy.result : zdy.value);
                 return;
+            }
+            validText = parseFloat(exprQuantity.quantity);
+            newValue = ZhCalc.round(validText, 6);
+            const gather = _.find(ex_calc, { code: 'bqwc' });
+            if (gather.value === newValue) {
+                zdy.value = gather.value;
+                zdy.result = '';
             } else {
-                zdy.value = newValue;
+                zdy.value = exprQuantity.expr ? exprQuantity.expr : newValue;
+                zdy.result = newValue;
             }
-            postData(window.location.pathname + '/save', { type:'ex_calc', updateData: ex_calc }, function (result) {
+            console.log(zdy, ex_calcList, ex_calc);
+            $('#calc_zdy').val(zdy.result ? zdy.result : zdy.value);
+            postData(window.location.pathname + '/save', { type:'ex_calc', updateData: ex_calc, ms_id: $('#myTab').find('.active').attr('data-msid') || null, mn_id: $('#myTab2').find('.active').attr('data-mnid') || null }, function (result) {
                 ex_tp = result.ex_tp;
-                ex_expr = result.ex_expr;
+                ex_tax_tp = result.ex_tax_tp;
+                ex_expr = result.ex_expr || null;
+                if (showStageExponent || exponent_nodes) {
+                    materialExponentSpreadObj.updateMaterialData(result);
+                }
                 resetExTpTable();
             });
         });
@@ -556,39 +969,23 @@ $(document).ready(() => {
                 console.log(rate, materialRate);
                 if (rate !== materialRate) {
                     postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
-                        const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
-                        const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
-                        // if (!materialTax) {
-                        //     const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
-                        //     const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
-                        //     $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
-                        //     $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
-                        // }
-                        $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
-                        $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+                        // const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
+                        // const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
+                        // $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                        // $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
                         materialRate = rate;
+                        ex_tax_tp = result.ex_tax_tp;
                         $('#rateInput').val(rate);
+                        if (showStageExponent || exponent_nodes) {
+                            materialExponentSpreadObj.updateMaterialData(result);
+                        }
+                        resetExTpTable();
                     });
                 } else {
                     $('#rateInput').val(rate);
                 }
             }, 500);
         });
-        // $('#changeRate').change(function () {
-        //     const rate = parseInt($(this).val());
-        //     postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
-        //         const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
-        //         const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), materialDecimal.tp);
-        //         if (!materialTax) {
-        //             const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), materialDecimal.tp);
-        //             const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), materialDecimal.tp);
-        //             $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
-        //             $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
-        //         }
-        //         $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
-        //         $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
-        //     });
-        // });
     }
 
     $.divResizer({
@@ -603,8 +1000,14 @@ $(document).ready(() => {
     $.divResizer({
         select: '#main-resize',
         callback: function () {
+            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent-30);
+            const height = $('.bcontent-wrap').height();
+            const cutHeight = isStageSelf ? getObjHeight($('#myTab')) : 0;
+            const cutHeight2 = exponent_nodes ? getObjHeight($('#myTab2')) : 0;
+            $('.sjs-height-material').height($('.sjs-height-1').height() - cutHeight - cutHeight2);
             materialExponentSpread.refresh();
-            const height = $('#material-exponent-spread').height();
+            // const height = $('#material-exponent-spread').height();
             setLocalCache('material_exponent2_' + materialID, height);
         }
     });
@@ -632,6 +1035,8 @@ $(document).ready(() => {
         materialExponentSpread.refresh();
     });
     // 根据浏览器记录展开收起
+    const cutHeight = isStageSelf ? getObjHeight($('#myTab')) : 0;
+    const cutHeight2 = exponent_nodes ? getObjHeight($('#myTab2')) : 0;
     if (getLocalCache('material_exponent_' + materialID)) {
         const tab = $('.right-nav a[content="#base-tab"]'), tabPanel = $(tab.attr('content'));
         $('a', '.side-menu').removeClass('active');
@@ -643,14 +1048,22 @@ $(document).ready(() => {
         materialExponentSpread.refresh();
     }
     if (getLocalCache('material_exponent2_' + materialID)) {
-        $('#material-exponent-spread').height(getLocalCache('material_exponent2_' + materialID));
+        // $('#material-exponent-spread').height(getLocalCache('material_exponent2_' + materialID));
+        $('.bcontent-wrap').height(getLocalCache('material_exponent2_' + materialID));
         var cHeader = getObjHeight($(".c-header"));
-        var sjs1 = getObjHeight($('.sjs-height-1'));
-        $(".bcontent-wrap").height($(window).height()-cHeader-sjs1-90+53);
+        const bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
+        $(".sp-wrap").height(bcontent-30);
+        $('.sjs-height-1').height($(window).height()-cHeader-bcontent-90+53);
+        $('.sjs-height-material').height($('.sjs-height-1').height() - cutHeight - cutHeight2);
+        // var sjs1 = getObjHeight($('.sjs-height-1'));
+        // $(".bcontent-wrap").height($(window).height()-cHeader-sjs1-90+53);
+        materialExponentSpread.refresh();
+    } else {
+        $('.sjs-height-material').height($('.sjs-height-1').height() - cutHeight - cutHeight2);
         materialExponentSpread.refresh();
     }
     function getObjHeight(select) {
-        return select.length > 0 ? select.height() : 0;
+        return select.length > 0 ? select.outerHeight(true) : 0;
     }
 
     $.subMenu({

+ 279 - 59
app/service/material.js

@@ -71,6 +71,15 @@ module.exports = app => {
             }
         }
 
+        async checkExponentUpdate(material) {
+            if (!material.pre_exponent_node) return false; // 空则直接通过
+
+            const need = material.pre_exponent_node.split(',');// 要求的节点数组
+            const got = material.exponent_node ? material.exponent_node.split(',') : [];// 实际的节点数组
+            // every 表示:need 中每个元素都必须包含在 got 中
+            return !need.every(n => got.includes(n));
+        }
+
         /**
          * 获取 最新一期 材料调差期计量
          * @param tenderId
@@ -200,10 +209,12 @@ module.exports = app => {
                 s_order: data.s_order,
                 material_tax: this.ctx.subProject.page_show.openMaterialTax,
                 decimal: preMaterial && preMaterial.decimal ? preMaterial.decimal : JSON.stringify(materialConst.decimal),
+                exponent_decimal: preMaterial && preMaterial.exponent_decimal ? preMaterial.exponent_decimal : JSON.stringify(materialConst.exponent_decimal),
                 is_new: 1,
                 is_stage_self: data.is_stage_self,
                 qty_source: data.qty_source,
                 is_new_qty: 1,
+                is_new_exponent: data.is_stage_self ? 0 : 1,
                 rate: 9,
                 calc_stage: data.stage_id.join(','),
                 calc_tp: 1,
@@ -215,9 +226,13 @@ module.exports = app => {
                     newMaterial.exponent_rate = preMaterial.exponent_rate;
                     newMaterial.pre_tp = this.ctx.helper.add(preMaterial.m_tp, preMaterial.pre_tp);
                     newMaterial.ex_pre_tp = this.ctx.helper.add(preMaterial.ex_tp, preMaterial.ex_pre_tp);
+                    newMaterial.ex_tax_pre_tp = this.ctx.helper.add(preMaterial.ex_tax_tp, preMaterial.ex_tax_pre_tp);
                     newMaterial.m_tax_pre_tp = preMaterial.material_tax ? this.ctx.helper.add(preMaterial.m_tax_tp, preMaterial.m_tax_pre_tp) : preMaterial.m_tax_pre_tp;
                     newMaterial.calc_stage = '';
                     newMaterial.calc_tp = 0;
+                    newMaterial.is_new_exponent = 1;
+                    // newMaterial.exponent_node = preMaterial.exponent_node;
+                    newMaterial.pre_exponent_node = preMaterial.exponent_node;
                 }
                 // 新增期记录
                 const result = await transaction.insert(this.tableName, newMaterial);
@@ -257,36 +272,39 @@ module.exports = app => {
                     // 复制单独计量调差清单
                     const preSelfList = await this.ctx.service.materialListSelf.getAllDataByCondition({ where: { tid: this.ctx.tender.id, mid: preMaterial.id } });
                     await this.ctx.service.materialListSelf.copyNewStageSelfList(transaction, preSelfList, newMaterial.id);
-                //     // 复制调差清单工料关联表
-                //     // await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
-                //     await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, data.material_self_list, preNotJoinList, newMaterial, insertMaterialStage);
-                //     // 新增或删除list_gcl表
-                //     await this.ctx.service.materialListGcl.insertOrDelGcl(transaction, data.insertGclList, data.removeGclList, newMaterial.id);
-                //     // 设置list_gcl表old=>new更新
-                //     await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id);
-                //     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
-                //     let m_tp = null;
-                //     let m_tax_tp = null;
-                //     let rate_tp = null;
-                //     if (data.is_stage_self) {
-                //         [m_tp, m_tax_tp, rate_tp] = await this.ctx.service.materialStageBills.insertBills(transaction, this.ctx.tender.id, newMaterial.id, newMaterial.stage_id, insertMaterialStage, JSON.parse(newMaterial.decimal), preMaterial.is_stage_self, data.qty_source, newMaterial.rate);
-                //     } else {
-                //         [m_tp, m_tax_tp, rate_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal), preMaterial.is_stage_self, data.qty_source, newMaterial.rate);
-                //     }
-                    // 修改现行价格指数,并返回调差基数json
-                    const ex_calc = await this.ctx.service.materialExponent.updateNewMaterial(transaction, newMaterial.id, this.ctx, newMaterial.stage_id, preMaterial.ex_calc, JSON.parse(newMaterial.decimal));
-                //     // 计算得出本期总金额
-                //     // 找出当前人并更新tp_data
-                //     const tp_data = await this.ctx.service.materialAudit.getTpData(transaction, newMaterial.id, JSON.parse(newMaterial.decimal));
-                    const updateMaterialData = {
-                        id: newMaterial.id,
-                        // m_tp,
-                        // m_tax_tp,
-                        // rate_tp,
-                        ex_calc: JSON.stringify(ex_calc),
-                        // tp_data: JSON.stringify(tp_data),
-                    };
-                    await transaction.update(this.tableName, updateMaterialData);
+                    // // 复制调差清单工料关联表
+                    // // await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
+                    // await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, data.material_self_list, preNotJoinList, newMaterial, insertMaterialStage);
+                    // // 新增或删除list_gcl表
+                    // await this.ctx.service.materialListGcl.insertOrDelGcl(transaction, data.insertGclList, data.removeGclList, newMaterial.id);
+                    // // 设置list_gcl表old=>new更新
+                    // await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id);
+                    // // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
+                    // let m_tp = null;
+                    // let m_tax_tp = null;
+                    // let rate_tp = null;
+                    // if (data.is_stage_self) {
+                    //     [m_tp, m_tax_tp, rate_tp] = await this.ctx.service.materialStageBills.insertBills(transaction, this.ctx.tender.id, newMaterial.id, newMaterial.stage_id, insertMaterialStage, JSON.parse(newMaterial.decimal), preMaterial.is_stage_self, data.qty_source, newMaterial.rate);
+                    // } else {
+                    //     [m_tp, m_tax_tp, rate_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal), preMaterial.is_stage_self, data.qty_source, newMaterial.rate);
+                    // }
+                    // // 计算得出本期总金额
+                    // // 找出当前人并更新tp_data
+                    // const tp_data = await this.ctx.service.materialAudit.getTpData(transaction, newMaterial.id, JSON.parse(newMaterial.decimal));
+                    // const updateMaterialData = {
+                    //     id: newMaterial.id,
+                    //     // m_tp,
+                    //     // m_tax_tp,
+                    //     // rate_tp,
+                    //     // tp_data: JSON.stringify(tp_data),
+                    // };
+                    // // 修改现行价格指数,并返回调差基数json,
+                    if (!data.is_stage_self && !newMaterial.pre_exponent_node) {
+                    //     const ex_calc = await this.ctx.service.materialExponent.updateNewMaterial(transaction, newMaterial.id, this.ctx, newMaterial.stage_id, preMaterial.ex_calc, JSON.parse(newMaterial.decimal), newMaterial.exponent_rate);
+                        await this.ctx.service.materialExponent.updateNewMaterial(transaction, newMaterial.id, this.ctx, newMaterial.stage_id, preMaterial.ex_calc, JSON.parse(newMaterial.exponent_decimal), newMaterial.exponent_rate);
+                    //     updateMaterialData.ex_calc = JSON.stringify(ex_calc);
+                    }
+                    // await transaction.update(this.tableName, updateMaterialData);
                 //     // 删除material_list表冗余数据,减少表数据量
                 //     await transaction.delete(this.ctx.service.materialList.tableName, { tid: this.ctx.tender.id, gather_qty: null, is_self: 0 });
                 }
@@ -363,9 +381,11 @@ module.exports = app => {
                 await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id, 'old2new');
                 // 还要从material_list表更新gcl的old数据,更新方法
                 await this.ctx.service.materialListGcl.setOldFromLast(transaction, this.ctx.tender.id, materialInfo.order - 2);
-                if (materialInfo.is_stage_self) {
+                if (materialInfo.is_stage_self || materialInfo.exponent_node) {
                     await transaction.delete(this.ctx.service.materialStage.tableName, { mid: id });
                     await transaction.delete(this.ctx.service.materialStageBills.tableName, { mid: id });
+                    await transaction.delete(this.ctx.service.materialExponentShard.tableName, { mid: id });
+                    await transaction.delete(this.ctx.service.materialExponentNode.tableName, { mid: id });
                 }
                 await transaction.delete(this.tableName, { id });
                 // 记录删除日志
@@ -417,11 +437,113 @@ module.exports = app => {
          * @return {Promise<*>}
          */
         async changeExponentRate(rate) {
-            const updateData = {
-                id: this.ctx.material.id,
-                exponent_rate: rate,
-            };
-            return await this.db.update(this.tableName, updateData);
+            const transaction = await this.db.beginTransaction();
+            try {
+                const result = {};
+                const info = {};
+                const decimal = this.ctx.material.exponent_decimal;
+                switch (this.ctx.material.exponentStatus) {
+                    case materialConst.exponent_status.shared_noNode:
+                        const updateData = {
+                            id: this.ctx.material.id,
+                            exponent_rate: rate,
+                            ex_tax_tp: this.ctx.material.ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.material.ex_tp, 1 + rate / 100), decimal.tp) : null,
+                        };
+                        await transaction.update(this.tableName, updateData);
+                        result.ex_tax_tp = updateData.ex_tax_tp;
+                        break;
+                    case materialConst.exponent_status.shared_node:
+                        // 汇总到material上
+                        info.materialNodes = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                        info.ex_tax_tp = 0;
+                        info.updateTaxTps = [];
+                        for (const mn of info.materialNodes) {
+                            const newExTaxTp = mn.ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(mn.ex_tp, 1 + rate / 100), decimal.tp) : null;
+                            info.updateTaxTps.push({
+                                id: mn.id,
+                                ex_tax_tp: newExTaxTp,
+                            });
+                            info.ex_tax_tp = this.ctx.helper.add(info.ex_tax_tp, newExTaxTp);
+                        }
+                        if (info.updateTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialExponentNode.tableName, info.updateTaxTps);
+                        await transaction.update(this.tableName, {
+                            id: this.ctx.material.id,
+                            exponent_rate: rate,
+                            ex_tax_tp: info.ex_tax_tp,
+                        });
+                        result.ex_tax_tp = info.ex_tax_tp;
+                        result.nodeData = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                        break;
+                    case materialConst.exponent_status.self_noNode:
+                        // 汇总到material上
+                        info.materialStages = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                        info.ex_tax_tp = 0;
+                        info.updateTaxTps = [];
+                        for (const ms of info.materialStages) {
+                            const newExTaxTp = ms.ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(ms.ex_tp, 1 + rate / 100), decimal.tp) : null;
+                            info.updateTaxTps.push({
+                                id: ms.id,
+                                ex_tax_tp: newExTaxTp,
+                            });
+                            info.ex_tax_tp = this.ctx.helper.add(info.ex_tax_tp, newExTaxTp);
+                        }
+                        if (info.updateTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, info.updateTaxTps);
+                        await transaction.update(this.tableName, {
+                            id: this.ctx.material.id,
+                            exponent_rate: rate,
+                            ex_tax_tp: info.ex_tax_tp,
+                        });
+                        result.ex_tax_tp = info.ex_tax_tp;
+                        result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                        break;
+                    case materialConst.exponent_status.self_node:
+                        // 汇总到material上
+                        info.materialStages = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                        info.materialNodes = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                        info.updateTaxTps = [];
+                        for (const mn of info.materialNodes) {
+                            const newExTaxTp = mn.ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(mn.ex_tp, 1 + rate / 100), decimal.tp) : null;
+                            info.updateTaxTps.push({
+                                id: mn.id,
+                                ex_tax_tp: newExTaxTp,
+                            });
+                            mn.ex_tax_tp = newExTaxTp;
+                            // info.ex_tax_tp = this.ctx.helper.add(info.ex_tax_tp, newExTaxTp);
+                        }
+                        if (info.updateTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialExponentNode.tableName, info.updateTaxTps);
+                        info.ex_tax_tp = 0;
+                        info.updateStageTaxTps = [];
+                        for (const ms of info.materialStages) {
+                            const mnList = this._.filter(info.materialNodes, { ms_id: ms.id });
+                            const taxTpList = this._.map(mnList, 'ex_tax_tp');
+                            // 这里的this.ctx为undefined,所以用this._.reduce时需要传入this.ctx.helper
+                            const that = this;
+                            const newExTaxTp = taxTpList.length > 0 ? that._.reduce(taxTpList, function(sum, n) { return that.ctx.helper.add(sum, n); }, 0) : null;
+                            // const newExTaxTp = taxTpList.length > 0 ? this._.reduce(taxTpList, function(sum, n) { return this.ctx.helper.add(sum, n); }, 0) : null;
+                            info.updateStageTaxTps.push({
+                                id: ms.id,
+                                ex_tax_tp: newExTaxTp,
+                            });
+                            info.ex_tax_tp = this.ctx.helper.add(info.ex_tax_tp, newExTaxTp);
+                        }
+                        if (info.updateStageTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, info.updateStageTaxTps);
+                        await transaction.update(this.tableName, {
+                            id: this.ctx.material.id,
+                            exponent_rate: rate,
+                            ex_tax_tp: info.ex_tax_tp,
+                        });
+                        result.ex_tax_tp = info.ex_tax_tp;
+                        result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                        result.nodeData = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                        break;
+                    default: break;
+                }
+                await transaction.commit();
+                return result;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
         }
 
         /**
@@ -429,17 +551,54 @@ module.exports = app => {
          * @param {int} rate 税率
          * @return {Promise<*>}
          */
-        async changeExCalc(ex_calc) {
+        async changeExCalc(ex_calc, ms_id = null, mn_id = null) {
             const transaction = await this.db.beginTransaction();
             try {
-                const updateData = {
-                    id: this.ctx.material.id,
-                    ex_calc: JSON.stringify(ex_calc),
-                };
-                await transaction.update(this.tableName, updateData);
-                const [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction, ex_calc);
+                const result = {};
+                const info = {};
+                switch (this.ctx.material.exponentStatus) {
+                    case materialConst.exponent_status.shared_noNode:
+                        [result.ex_tp, result.ex_tax_tp, result.ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction, ex_calc);
+                        break;
+                    case materialConst.exponent_status.shared_node:
+                        if (!mn_id) throw '参数有误';
+                        info.mnInfo = await transaction.get(this.ctx.service.materialExponentNode.tableName, { mid: this.ctx.material.id, id: mn_id });
+                        if (!info.mnInfo) {
+                            throw '调差新增分项不存在';
+                        }
+                        await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, ex_calc, this.ctx.material.id, null, mn_id);
+                        [result.ex_tp, result.ex_tax_tp] = await this.ctx.service.materialExponent.calcTpByMaterialExponentNode(transaction, this.ctx.material.id);
+                        result.nodeData = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id, id: mn_id } });
+                        break;
+                    case materialConst.exponent_status.self_noNode:
+                        if (!ms_id) throw '参数有误';
+                        info.msInfo = await transaction.get(this.ctx.service.materialStage.tableName, { mid: this.ctx.material.id, id: ms_id });
+                        if (!info.msInfo) {
+                            throw '调差独立期不存在';
+                        }
+                        await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, ex_calc, this.ctx.material.id, ms_id);
+                        [result.ex_tp, result.ex_tax_tp] = await this.ctx.service.materialExponent.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                        result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                        break;
+                    case materialConst.exponent_status.self_node:
+                        if (!ms_id || !mn_id) throw '参数有误';
+                        info.msInfo = await transaction.get(this.ctx.service.materialStage.tableName, { mid: this.ctx.material.id, id: ms_id });
+                        if (!info.msInfo) {
+                            throw '调差独立期不存在';
+                        }
+                        info.mnInfo = await transaction.get(this.ctx.service.materialExponentNode.tableName, { mid: this.ctx.material.id, id: mn_id });
+                        if (!info.mnInfo) {
+                            throw '调差新增分项不存在';
+                        }
+                        await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, ex_calc, this.ctx.material.id, ms_id, mn_id);
+                        [result.ex_tp, result.ex_tax_tp] = await this.ctx.service.materialExponent.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                        result.nodeData = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id, id: mn_id } });
+                        result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                        break;
+                    default: break;
+                }
                 await transaction.commit();
-                return [ex_tp, ex_expr];
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -511,28 +670,47 @@ module.exports = app => {
             return result && result.count !== 0;
         }
 
-        async saveDecimal(newUp, newTp, newQty) {
+        async saveDecimal(need, newUp, newTp, newQty, needExponent, newExponentUp, newExponentTp, newExponentQty) {
             const transaction = await this.db.beginTransaction();
             try {
-                await this.ctx.service.materialBills.resetDecimal(transaction, newUp, newTp, newQty);
-                this.ctx.material.decimal.up = newUp;
-                this.ctx.material.decimal.tp = newTp;
-                this.ctx.material.decimal.qty = newQty;
-                const m_tp = await this.ctx.service.materialBills.calcMaterialMTp(transaction);
-                let update_calc = false;
-                if (this.ctx.material.ex_calc) {
-                    const ex_calc = JSON.parse(this.ctx.material.ex_calc);
-                    const zdy = this._.find(ex_calc, { code: 'zdy' });
-                    zdy.value = this.ctx.helper.round(zdy.value, newTp);
-                    this.ctx.material.ex_calc = JSON.stringify(ex_calc);
-                    update_calc = true;
+                if (need) {
+                    await this.ctx.service.materialBills.resetDecimal(transaction, newUp, newTp, newQty);
+                    this.ctx.material.decimal.up = newUp;
+                    this.ctx.material.decimal.tp = newTp;
+                    this.ctx.material.decimal.qty = newQty;
+                    await this.ctx.service.materialBills.calcMaterialMTp(transaction);
+                }
+                // let update_calc = false;
+                if (needExponent) {
+                    this.ctx.material.exponent_decimal.up = newExponentUp;
+                    this.ctx.material.exponent_decimal.tp = newExponentTp;
+                    this.ctx.material.exponent_decimal.qty = newExponentQty;
+                    await this.ctx.service.materialExponent.resetDecimal(transaction, this.ctx.material.exponent_decimal);
+                    switch (this.ctx.material.exponentStatus) {
+                        case materialConst.exponent_status.shared_noNode:
+                            await this.ctx.service.materialExponent.calcMaterialExTp(transaction);
+                            break;
+                        case materialConst.exponent_status.shared_node:
+                            await this.ctx.service.materialExponentShard.calcMaterialAllExTpByNode(transaction, this.ctx.material.id, null, false);
+                            await this.ctx.service.materialExponent.calcTpByMaterialExponentNode(transaction, this.ctx.material.id);
+                            break;
+                        case materialConst.exponent_status.self_noNode:
+                            await this.ctx.service.materialExponentShard.calcMaterialAllExTpByStage(transaction, this.ctx.material.id);
+                            await this.ctx.service.materialExponent.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                            break;
+                        case materialConst.exponent_status.self_node:
+                            await this.ctx.service.materialExponentShard.calcMaterialAllExTpByNode(transaction, this.ctx.material.id);
+                            await this.ctx.service.materialExponent.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                            break;
+                        default: break;
+                    }
                 }
-                const [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction);
                 const updateData = {
                     id: this.ctx.material.id,
                     decimal: JSON.stringify(this.ctx.material.decimal),
+                    exponent_decimal: JSON.stringify(this.ctx.material.exponent_decimal),
                 };
-                if (update_calc) updateData.ex_calc = this.ctx.material.ex_calc;
+                // if (update_calc) updateData.ex_calc = this.ctx.material.ex_calc;
                 await transaction.update(this.tableName, updateData);
                 await transaction.commit();
                 return true;
@@ -572,6 +750,48 @@ module.exports = app => {
             const list = await this.db.query(sql, params);
             return list;
         }
+
+        async makeExponentTp(data) {
+            if (!data.mid) {
+                throw '参数错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const material = await this.getDataById(data.mid);
+                if (!material) {
+                    throw '调差不存在';
+                }
+                material.decimal = JSON.parse(material.decimal);
+                if (!material.is_stage_self || material.is_new_exponent) {
+                    throw '独立期指数调差工料已生成或非独立期调差';
+                }
+                const materialStages = await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid: data.mid } });
+                let ex_tax_tp = 0;
+                let total_ex_tp = 0;
+                // const updateTaxTps = [];
+                for (const ms of materialStages) {
+                    // const tax_tp = this.ctx.helper.round(this.ctx.helper.mul(ms.ex_tp, 1 + material.exponent_rate / 100), material.decimal.tp);
+                    // updateTaxTps.push({ id: ms.id, ex_tax_tp: tax_tp });
+                    ex_tax_tp = this.ctx.helper.add(ex_tax_tp, ms.ex_tax_tp);
+                    total_ex_tp = this.ctx.helper.add(total_ex_tp, ms.ex_tp || 0);
+                }
+                // if (updateTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, updateTaxTps);
+                await transaction.update(this.tableName, {
+                    id: material.id,
+                    ex_tp: total_ex_tp,
+                    ex_tax_tp,
+                    ex_expr: null,
+                    // ex_calc: null,
+                    is_new_exponent: 1,
+                    // exponent_decimal: JSON.stringify(materialConst.exponent_decimal),
+                });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
 
     return Material;

+ 17 - 16
app/service/material_audit.js

@@ -236,14 +236,14 @@ module.exports = app => {
             return true;
         }
 
-        async getTpData(transaction, materialId, decimal = (this.ctx.material.decimal ? this.ctx.material.decimal : materialConst.decimal)) {
+        async getTpData(transaction, materialId, decimal = (this.ctx.material.decimal ? this.ctx.material.decimal : materialConst.decimal), exponent_decimal = (this.ctx.material.exponent_decimal ? this.ctx.material.exponent_decimal : materialConst.exponent_decimal)) {
             const materialInfo = await transaction.get(this.ctx.service.material.tableName, { id: materialId });
             const tp_data = {
                 m_tp: materialInfo.m_tp !== null ? this.ctx.helper.round(materialInfo.m_tp, decimal.tp) : null,
                 tax_tp: materialInfo.material_tax ? (materialInfo.m_tax_tp ? materialInfo.m_tax_tp : materialInfo.m_tp) :
                     (materialInfo.m_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), decimal.tp) : null),
-                ex_tp: materialInfo.ex_tp !== null ? this.ctx.helper.round(materialInfo.ex_tp, decimal.tp) : null,
-                ex_tax_tp: materialInfo.ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), decimal.tp) : null,
+                ex_tp: materialInfo.ex_tp !== null ? this.ctx.helper.round(materialInfo.ex_tp, exponent_decimal.tp) : null,
+                ex_tax_tp: materialInfo.ex_tax_tp !== null ? this.ctx.helper.round(materialInfo.ex_tax_tp, exponent_decimal.tp) : null,
             }
             tp_data.total_tp = this.ctx.helper.add(tp_data.m_tp, tp_data.ex_tp);
             tp_data.total_tax_tp = !materialInfo.material_tax ? this.ctx.helper.add(tp_data.tax_tp, tp_data.ex_tax_tp) : tp_data.ex_tax_tp;
@@ -306,8 +306,8 @@ module.exports = app => {
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(new Date()),
-                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), materialInfo.ex_tp),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), materialInfo.ex_tax_tp),
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
                 for (const audit of audits) {
@@ -418,8 +418,8 @@ module.exports = app => {
                             status: wxConst.status.check,
                             tips: wxConst.tips.check,
                             begin_time: Date.parse(begin_audit.begin_time),
-                            m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                            hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1 + materialInfo.rate / 100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1 + materialInfo.exponent_rate / 100), material_decimal.tp)),
+                            m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), materialInfo.ex_tp),
+                            hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1 + materialInfo.rate / 100), material_decimal.tp), materialInfo.ex_tax_tp),
                         };
                         await this.ctx.helper.sendWechat(this._.map(nextAudits, 'aid'), smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
                         // 重新发送配置
@@ -517,8 +517,8 @@ module.exports = app => {
                             status: wxConst.status.success,
                             tips: wxConst.tips.success,
                             begin_time: Date.parse(begin_audit.begin_time),
-                            m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                            hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1 + materialInfo.rate / 100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1 + materialInfo.exponent_rate / 100), material_decimal.tp)),
+                            m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), materialInfo.ex_tp),
+                            hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1 + materialInfo.rate / 100), material_decimal.tp), materialInfo.ex_tax_tp),
                         };
                         await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
                         // 检查三方特殊推送
@@ -603,8 +603,8 @@ module.exports = app => {
                     status: wxConst.status.back,
                     tips: wxConst.tips.back,
                     begin_time: Date.parse(begin_audit.begin_time),
-                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), materialInfo.ex_tp),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), materialInfo.ex_tax_tp),
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
                 // 检查三方特殊推送
@@ -696,8 +696,8 @@ module.exports = app => {
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(begin_audit.begin_time),
-                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), materialInfo.ex_tp),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), materialInfo.ex_tax_tp),
                 };
                 await this.ctx.helper.sendWechat(this._.map(preAuditors, 'aid'), smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
                 for (const a of newAuditors) {
@@ -795,7 +795,8 @@ module.exports = app => {
             try {
                 const materialInfo = await this.ctx.service.material.getDataById(materialId);
                 const material_decimal = materialInfo && materialInfo.decimal ? JSON.parse(materialInfo.decimal) : materialConst.decimal;
-                const tp_data = await this.getTpData(transaction, materialId, material_decimal);
+                const material_exponent_decimal = materialInfo && materialInfo.exponent_decimal ? JSON.parse(materialInfo.exponent_decimal) : materialConst.exponent_decimal;
+                const tp_data = await this.getTpData(transaction, materialId, material_decimal, material_exponent_decimal);
                 const checkAgainAuditors = [], checkingAuditors = [];
                 audits.forEach(x => {
                     checkAgainAuditors.push({
@@ -838,8 +839,8 @@ module.exports = app => {
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(new Date()),
-                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), this.ctx.helper.round(materialInfo.ex_tp, material_decimal.tp)),
-                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, material_decimal.tp), materialInfo.ex_tp),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), materialInfo.ex_tax_tp),
                 };
                 await this.ctx.helper.sendWechat(this._.map(checkingAuditors, 'aid'), smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
                 for (const a of checkingAuditors) {

+ 417 - 37
app/service/material_exponent.js

@@ -35,6 +35,7 @@ module.exports = app => {
             }
             const transaction = await this.db.beginTransaction();
             try {
+                const resultData = {};
                 const newExponent = {
                     tid: this.ctx.tender.id,
                     mid: this.ctx.material.id,
@@ -45,8 +46,20 @@ module.exports = app => {
                 if (result.affectedRows !== 1) {
                     throw '新增指数数据失败';
                 }
+                switch (this.ctx.material.exponentStatus) {
+                    case materialConst.exponent_status.shared_noNode:
+                        break;
+                    case materialConst.exponent_status.shared_node:
+                    case materialConst.exponent_status.self_noNode:
+                    case materialConst.exponent_status.self_node:
+                        await this.ctx.service.materialExponentShard.add(transaction, this.ctx.material, result.insertId);
+                        resultData.pushExponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id, me_id: result.insertId } });
+                        break;
+                    default: break;
+                }
                 await transaction.commit();
-                return await this.getDataById(result.insertId);
+                resultData.info = await this.getDataById(result.insertId);
+                return resultData;
             } catch (error) {
                 console.log(error);
                 await transaction.rollback();
@@ -66,19 +79,68 @@ module.exports = app => {
             // 判断t_type是否为费用,且存在quantity,m_spread值
             const transaction = await this.db.beginTransaction();
             try {
+                const result = {};
+                const info = {};
                 const meInfo = await this.getDataById(id);
                 await transaction.delete(this.tableName, { id });
                 if (meInfo.weight_num === null) {
-                    await transaction.delete(this.ctx.service.materialExponentHistory.tableName, { mid: id });
+                    await transaction.delete(this.ctx.service.materialExponentHistory.tableName, { mid: this.ctx.material.id, me_id: id });
                 }
-                let ex_tp = this.ctx.material.ex_tp;
-                let ex_expr = this.ctx.material.ex_expr;
-                if (meInfo.is_summary === materialConst.is_summary.yes) {
-                    // 金额发生变化,则重新计算本期金额
-                    [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction);
+                switch (this.ctx.material.exponentStatus) {
+                    case materialConst.exponent_status.shared_noNode:
+                        result.ex_tp = this.ctx.material.ex_tp;
+                        result.ex_expr = this.ctx.material.ex_expr;
+                        if (meInfo.is_summary === materialConst.is_summary.yes) {
+                            // 金额发生变化,则重新计算本期金额
+                            [result.ex_tp, result.ex_tax_tp, result.ex_expr] = await this.calcMaterialExTp(transaction);
+                        }
+                        break;
+                    case materialConst.exponent_status.shared_node:
+                        await transaction.delete(this.ctx.service.materialExponentShard.tableName, { me_id: id });
+                        if (meInfo.is_summary === materialConst.is_summary.yes) {
+                            info.nodeList = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                            for (const node of info.nodeList) {
+                                await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, node.ex_calc ? JSON.parse(node.ex_calc) : null, this.ctx.material.id, null, node.id);
+                            }
+                            [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialExponentNode(transaction, this.ctx.material.id);
+                            result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, {where: {mid: this.ctx.material.id}});
+                            result.nodeData = await transaction.select(this.ctx.service.materialExponentNode.tableName, {where: {mid: this.ctx.material.id}});
+                        }
+                        break;
+                    case materialConst.exponent_status.self_noNode:
+                        await transaction.delete(this.ctx.service.materialExponentShard.tableName, { me_id: id });
+                        if (meInfo.is_summary === materialConst.is_summary.yes) {
+                            info.materialStages = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                            for (const ms of info.materialStages) {
+                                await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, ms.ex_calc ? JSON.parse(ms.ex_calc) : null, this.ctx.material.id, ms.id);
+                            }
+                            [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                            result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id } });
+                            result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                        }
+                        break;
+                    case materialConst.exponent_status.self_node:
+                        await transaction.delete(this.ctx.service.materialExponentShard.tableName, { me_id: id });
+                        if (meInfo.is_summary === materialConst.is_summary.yes) {
+                            info.materialStages = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                            info.materialNodes = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                            for (const ms of info.materialStages) {
+                                const nodeList = this._.filter(info.materialNodes, { ms_id: ms.id });
+                                for (const node of nodeList) {
+                                    await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, node.ex_calc ? JSON.parse(node.ex_calc) : null, this.ctx.material.id, null, node.id);
+                                }
+                                await this.ctx.service.materialExponentNode.calcStageExTpByNode(transaction, this.ctx.material.id, ms.id);
+                            }
+                            [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                            result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id } });
+                            result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                            result.nodeData = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } });
+                        }
+                        break;
+                    default: break;
                 }
                 await transaction.commit();
-                return [ex_tp, ex_expr];
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -90,7 +152,7 @@ module.exports = app => {
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async save(data) {
+        async save(data, ms_id = null, mn_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -100,22 +162,108 @@ module.exports = app => {
             // 判断t_type是否为费用
             const transaction = await this.db.beginTransaction();
             try {
-                await transaction.update(this.tableName, data);
-                const [ex_tp, ex_expr] = await this.calcMaterialExTp(transaction);
+                const result = {};
+                const info = {};
+                switch (this.ctx.material.exponentStatus) {
+                    case materialConst.exponent_status.shared_noNode:
+                        await transaction.update(this.tableName, data);
+                        [result.ex_tp, result.ex_tax_tp, result.ex_expr] = await this.calcMaterialExTp(transaction);
+                        break;
+                    case materialConst.exponent_status.shared_node:
+                        if (!mn_id) {
+                            throw '分项参数有误';
+                        }
+                        [info.needUp, info.needCalc] = await this._updateOneExponentData(transaction, data, null, mn_id);
+                        await this.ctx.service.materialExponentShard.update(transaction, data, null, mn_id, info.needCalc);
+                        [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialExponentNode(transaction, this.ctx.material.id);
+                        if (info.needUp) {
+                            result.exponentData = await transaction.select(this.tableName, { where: { id: data.id } });
+                        }
+                        result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id, mn_id, me_id: data.id } });
+                        result.nodeData = info.needCalc ? await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id, id: mn_id } });
+                        break;
+                    case materialConst.exponent_status.self_noNode:
+                        if (!ms_id) {
+                            throw '期参数有误';
+                        }
+                        [info.needUp, info.needCalc] = await this._updateOneExponentData(transaction, data, ms_id);
+                        await this.ctx.service.materialExponentShard.update(transaction, data, ms_id, null, info.needCalc);
+                        [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                        if (info.needUp) {
+                            result.exponentData = await transaction.select(this.tableName, { where: { id: data.id } });
+                        }
+                        result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id, ms_id, me_id: data.id } });
+                        result.stageData = info.needCalc ? await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                        break;
+                    case materialConst.exponent_status.self_node:
+                        if (!ms_id && !mn_id) {
+                            throw '参数有误';
+                        }
+                        [info.needUp, info.needCalc] = await this._updateOneExponentData(transaction, data, ms_id, mn_id);
+                        await this.ctx.service.materialExponentShard.update(transaction, data, ms_id, mn_id, info.needCalc);
+                        [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                        if (info.needUp) {
+                            result.exponentData = await transaction.select(this.tableName, { where: { id: data.id } });
+                        }
+                        result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id, ms_id, mn_id, me_id: data.id } });
+                        result.stageData = info.needCalc ? await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                        result.nodeData = info.needCalc ? await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id, id: mn_id } });
+                        break;
+                    default: break;
+                }
                 await transaction.commit();
-                return [ex_tp, ex_expr];
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
             }
         }
 
+        async _updateOneExponentData(transaction, data, ms_id = null, mn_id = null) {
+            // 当以下值和exponent值不相同时,需要同步更新最新的计算表达式和金额到stage表里,无需更新到exponentShard表
+            const updateColsArray = ['symbol', 'symbol_desc', 'code', 'weight_num', 'basic_price', 'is_summary', 'basic_times'];
+            const updateCalcArray = ['weight_num', 'basic_price', 'is_summary'];
+            let needCalc = false;
+            let needUp = false;
+            const meInfo = await this.getDataById(data.id);
+            if (!meInfo) {
+                throw '指数工料数据不存在';
+            }
+            const updateData = {};
+            for (const uc of updateColsArray) {
+                if (data[uc] !== undefined && data[uc] !== meInfo[uc]) {
+                    if (updateCalcArray.includes(uc)) {
+                        needCalc = true;
+                    }
+                    needUp = true;
+                    updateData[uc] = data[uc];
+                }
+            }
+            // 判断updateData不为空时,不需要更新
+            if (needUp) {
+                updateData.id = data.id;
+                await transaction.update(this.tableName, updateData);
+            }
+            if (needCalc) {
+                if (mn_id) {
+                    await this.ctx.service.materialExponentShard.calcMaterialAllExTpByNode(transaction, this.ctx.material.id, mn_id);
+                } else if (ms_id && !mn_id) {
+                    await this.ctx.service.materialExponentShard.calcMaterialAllExTpByStage(transaction, this.ctx.material.id, ms_id);
+                }
+            }
+            return [needUp, needCalc];
+        }
+
         /**
          * 复制粘贴指数清单
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async saveDatas(datas) {
+        async saveDatas(datas, ms_id = null, mn_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -123,16 +271,119 @@ module.exports = app => {
             // 判断t_type是否为费用
             const transaction = await this.db.beginTransaction();
             try {
-                await transaction.updateRows(this.tableName, datas);
-                const [ex_tp, ex_expr] = await this.calcMaterialExTp(transaction);
+                const result = { ex_tp: this.ctx.material.ex_tp };
+                const info = {};
+                if (this.ctx.material.exponentStatus !== materialConst.exponent_status.shared_noNode) {
+                    info.meList = await transaction.select(this.tableName, { where: { id: this._.map(datas, 'id') } });
+                }
+                switch (this.ctx.material.exponentStatus) {
+                    case materialConst.exponent_status.shared_noNode:
+                        await transaction.updateRows(this.tableName, datas);
+                        [result.ex_tp, result.ex_tax_tp, result.ex_expr] = await this.calcMaterialExTp(transaction);
+                        result.exponentData = await transaction.select(this.tableName, { where: { id: this._.map(datas, 'id') } });
+                        break;
+                    case materialConst.exponent_status.shared_node:
+                        if (!mn_id) {
+                            throw '参数有误';
+                        }
+                        info.needCalc = await this._updateMoreExponentData(transaction, datas, result, info.meList, null, mn_id);
+                        if (info.needCalc) await this.ctx.service.materialExponentShard.calcMaterialAllExTpByNode(transaction, this.ctx.material.id);
+                        [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialExponentNode(transaction, this.ctx.material.id);
+                        result.nodeData = info.needCalc ? await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id, id: mn_id } });
+                        break;
+                    case materialConst.exponent_status.self_noNode:
+                        if (!ms_id) {
+                            throw '参数有误';
+                        }
+                        info.needCalc = await this._updateMoreExponentData(transaction, datas, result, info.meList, ms_id, null);
+                        if (info.needCalc) await this.ctx.service.materialExponentShard.calcMaterialAllExTpByStage(transaction, this.ctx.material.id);
+                        [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                        result.stageData = info.needCalc ? await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                        break;
+                    case materialConst.exponent_status.self_node:
+                        if (!mn_id && !ms_id) {
+                            throw '参数有误';
+                        }
+                        info.needCalc = await this._updateMoreExponentData(transaction, datas, result, info.meList, null, mn_id);
+                        if (info.needCalc) await this.ctx.service.materialExponentShard.calcMaterialAllExTpByNode(transaction, this.ctx.material.id);
+                        [result.ex_tp, result.ex_tax_tp] = await this.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                        result.nodeData = info.needCalc ? await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: this.ctx.material.id, id: mn_id } });
+                        result.stageData = info.needCalc ? await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } }) :
+                            await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                        break;
+                    default: break;
+                }
                 await transaction.commit();
-                return [ex_tp, ex_expr];
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
             }
         }
 
+        async _updateMoreExponentData(transaction, datas, result, meList, ms_id = null, mn_id = null) {
+            // 判断data值是否需要更新materialExponent和materialExponentShard
+            const mesCondition = { me_id: this._.map(datas, 'id') };
+            if (ms_id !== null) mesCondition.ms_id = ms_id;
+            if (mn_id !== null) mesCondition.mn_id = mn_id;
+            const mesList = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: mesCondition });
+            console.log(mesList);
+            const materialExponentColArray = ['symbol', 'symbol_desc', 'code', 'weight_num', 'basic_price', 'is_summary', 'basic_times'];
+            const materialExponentShardColArray = ['m_price', 'remark'];
+            const updateCalcArray = ['weight_num', 'basic_price', 'm_price', 'is_summary'];
+            const updateMeArray = [];
+            const udpateMseArray = [];
+            let needCalc = false;
+            for (const data of datas) {
+                const meInfo = this._.find(meList, { id: data.id });
+                const mseInfo = this._.find(mesList, { me_id: data.id });
+                for (const uc of materialExponentColArray) {
+                    if (data[uc] !== undefined && data[uc] !== meInfo[uc]) {
+                        const updateMe = this._.find(updateMeArray, { id: meInfo.id });
+                        if (!updateMe) {
+                            const newUpdateMe = { id: meInfo.id };
+                            newUpdateMe[uc] = data[uc];
+                            updateMeArray.push(newUpdateMe);
+                        } else {
+                            updateMe[uc] = data[uc];
+                        }
+                        if (updateCalcArray.includes(uc)) {
+                            needCalc = true;
+                        }
+                    }
+                }
+                for (const uc of materialExponentShardColArray) {
+                    if (data[uc] !== undefined && data[uc] !== mseInfo[uc]) {
+                        const updateMse = this._.find(udpateMseArray, { id: mseInfo.id });
+                        if (!updateMse) {
+                            const newUpdateMse = { id: mseInfo.id };
+                            newUpdateMse[uc] = data[uc];
+                            udpateMseArray.push(newUpdateMse);
+                        } else {
+                            updateMse[uc] = data[uc];
+                        }
+                        if (updateCalcArray.includes(uc)) {
+                            needCalc = true;
+                        }
+                    }
+                }
+            }
+            console.log(updateMeArray, udpateMseArray);
+            if (updateMeArray.length > 0) {
+                await transaction.updateRows(this.tableName, updateMeArray);
+                result.exponentData = await transaction.select(this.tableName, { where: { id: this._.map(updateMeArray, 'id') } });
+            }
+            if (udpateMseArray.length > 0) {
+                await transaction.updateRows(this.ctx.service.materialExponentShard.tableName, udpateMseArray);
+                // const condition = mn_id ? { mid: this.ctx.material.id, mn_id } : { mid: this.ctx.material.id, ms_id };
+                result.exponentShardData = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { id: this._.map(udpateMseArray, 'id') } });
+            }
+            return needCalc;
+        }
+
         /**
          * 旧数据补充定值和历史数据
          * @returns {Promise<void>}
@@ -160,26 +411,57 @@ module.exports = app => {
                     in_time: new Date(),
                 };
                 const result = await transaction.insert(this.tableName, insert_data);
-                // 获取最新期数据
-                const high_material = await this.ctx.service.material.getDataByCondition({
+                // 获取所有期数据
+                const materials = await this.ctx.service.material.getAllDataByCondition({
                     tid: this.ctx.tender.id,
-                    order: this.ctx.material.highOrder,
                 });
-                const qi_order = high_material.status === auditConst.status.uncheck || high_material.status === auditConst.status.checkNo ? high_material.order - 1 : high_material;
+                // const qi_order = high_material.status === auditConst.status.uncheck || high_material.status === auditConst.status.checkNo ? high_material.order - 1 : high_material;
+                // const qi_order = high_material.status === auditConst.status.uncheck || high_material.status === auditConst.status.checkNo ? high_material.order - 1 : high_material;
                 const insert_history_data = [];
-                for (let i = 1; i <= qi_order; i++) {
-                    const one_insert = {
-                        tid: this.ctx.tender.id,
-                        mid: first_material.id,
-                        order: i,
-                        me_id: result.insertId,
-                        type: materialConst.ex_type[0].value,
-                        weight_num: 0,
-                        is_summary: materialConst.is_summary.yes,
-                    };
-                    insert_history_data.push(one_insert);
+                const insert_stage_data = [];
+                for (const m of materials) {
+                    if (m.is_stage_self && m.is_new_exponent) {
+                        const materialStages = await this.ctx.service.materialStage.getAllDataByCondition({
+                            tid: this.ctx.tender.id,
+                            mid: m.id,
+                        });
+                        for (const ms of materialStages) {
+                            const one_stage_insert = {
+                                tid: this.ctx.tender.id,
+                                mid: m.id,
+                                ms_id: ms.id,
+                                me_id: result.insertId,
+                                type: materialConst.ex_type[0].value,
+                            };
+                            insert_stage_data.push(one_stage_insert);
+                        }
+                    } else {
+                        const one_insert = {
+                            tid: this.ctx.tender.id,
+                            mid: first_material.id,
+                            order: m.order,
+                            me_id: result.insertId,
+                            type: materialConst.ex_type[0].value,
+                            weight_num: 0,
+                            is_summary: materialConst.is_summary.yes,
+                        };
+                        insert_history_data.push(one_insert);
+                    }
                 }
+                // for (let i = 1; i <= qi_order; i++) {
+                //     const one_insert = {
+                //         tid: this.ctx.tender.id,
+                //         mid: first_material.id,
+                //         order: i,
+                //         me_id: result.insertId,
+                //         type: materialConst.ex_type[0].value,
+                //         weight_num: 0,
+                //         is_summary: materialConst.is_summary.yes,
+                //     };
+                //     insert_history_data.push(one_insert);
+                // }
                 if (insert_history_data.length > 0) await transaction.insert(this.ctx.service.materialExponentHistory.tableName, insert_history_data);
+                if (insert_stage_data.length > 0) await transaction.insert(this.ctx.service.materialExponentShard.tableName, insert_stage_data);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -188,12 +470,19 @@ module.exports = app => {
         }
 
         // 更改计算总指数金额并返回值和公式
-        async calcMaterialExTp(transaction, ex_calc = (this.ctx.material.ex_calc ? JSON.parse(this.ctx.material.ex_calc) : null), mid = null, decimal = (this.ctx.material.decimal ? this.ctx.material.decimal : materialConst.decimal)) {
+        async calcMaterialExTp(transaction, ex_calc = (this.ctx.material.ex_calc ? JSON.parse(this.ctx.material.ex_calc) : null), mid = null, decimal = (this.ctx.material.exponent_decimal ? this.ctx.material.exponent_decimal : materialConst.exponent_decimal), rate = (this.ctx.material.rate ? this.ctx.material.exponent_rate : 10)) {
             let basic_calc = 0;
+            // let basic_expr = 0;
             if (ex_calc) {
                 for (const calc of ex_calc) {
                     if (calc.select) {
-                        basic_calc = this.ctx.helper.add(basic_calc, calc.value);
+                        if (calc.code === 'zdy' && calc.result) {
+                            basic_calc = this.ctx.helper.add(basic_calc, calc.result);
+                            // basic_expr = calc.value;
+                        } else {
+                            basic_calc = this.ctx.helper.add(basic_calc, calc.value);
+                            // basic_expr = this.ctx.helper.add(basic_expr, calc.value);
+                        }
                     }
                 }
             }
@@ -206,7 +495,9 @@ module.exports = app => {
                     constant = ex.weight_num ? ex.weight_num : 0;
                     expr += constant.toString();
                 } else if (ex.type === materialConst.ex_type[1].value) {
-                    const change = ex.calc_num ? ex.calc_num : 0;
+                    const calc_num = ex.basic_price > 0 ? this.ctx.helper.mul(ex.weight_num, this.ctx.helper.div(ex.m_price, ex.basic_price)) : 0;
+                    const change = calc_num ? this.ctx.helper.round(calc_num, decimal.qty) : 0;
+                    // const change = ex.calc_num ? ex.calc_num : 0;
                     expr = expr + (change !== 0 ? '+' + ex.weight_num.toString() + '*(' + ex.m_price.toString() + '/' + ex.basic_price.toString() + ')' : '');
                     sumByChange = this.ctx.helper.add(sumByChange, change);
                 }
@@ -214,10 +505,13 @@ module.exports = app => {
             expr += '-1]';
             expr = constant !== 0 ? expr : null;
             const ex_tp = constant !== 0 ? this.ctx.helper.round(this.ctx.helper.mul(basic_calc, this.ctx.helper.sub(this.ctx.helper.add(constant, sumByChange), 1)), decimal.tp) : null;
+            const ex_tax_tp = ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(ex_tp, 1 + rate / 100), decimal.tp) : null;
             await transaction.update(this.ctx.service.material.tableName, {
                 id: mid ? mid : this.ctx.material.id,
                 ex_tp,
+                ex_tax_tp,
                 ex_expr: expr,
+                ex_calc: JSON.stringify(ex_calc),
             });
             console.log(ex_tp, expr);
             if (!mid) {
@@ -235,7 +529,7 @@ module.exports = app => {
                     });
                 }
             }
-            return [ex_tp, expr];
+            return [ex_tp, ex_tax_tp, expr];
         }
 
         /**
@@ -245,7 +539,7 @@ module.exports = app => {
          * @param mid
          * @returns {Promise<number>}
          */
-        async updateNewMaterial(transaction, mid, ctx, stage_id, ex_calc, decimal) {
+        async updateNewMaterial(transaction, mid, ctx, stage_id, ex_calc, decimal, rate) {
             const stage_list = await ctx.service.stage.getStageMsgByStageId(stage_id);
             const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
             const old_ex_calc = ex_calc ? JSON.parse(ex_calc) : null;
@@ -256,9 +550,95 @@ module.exports = app => {
                 bq.value = calc.value;
                 bq.select = oldcalc ? oldcalc.select : false;
             }
-            await this.calcMaterialExTp(transaction, new_ex_calc, mid, decimal);
+            await this.calcMaterialExTp(transaction, new_ex_calc, mid, decimal, rate);
             return new_ex_calc;
         }
+
+        async calcTpByMaterialExponentNode(transaction, mid) {
+            // 汇总到material上
+            const materialNodes = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid } });
+            let ex_tax_tp = 0;
+            let total_ex_tp = 0;
+            for (const ms of materialNodes) {
+                ex_tax_tp = this.ctx.helper.add(ex_tax_tp, ms.ex_tax_tp);
+                total_ex_tp = this.ctx.helper.add(total_ex_tp, ms.ex_tp || 0);
+            }
+            // if (updateTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, updateTaxTps);
+            await transaction.update(this.ctx.service.material.tableName, {
+                id: mid,
+                ex_tp: total_ex_tp,
+                ex_tax_tp,
+            });
+            return [total_ex_tp, ex_tax_tp];
+        }
+
+        async calcTpByMaterialStageExponent(transaction, mid) {
+            // 汇总到material上
+            const materialStages = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid } });
+            let ex_tax_tp = 0;
+            let total_ex_tp = 0;
+            for (const ms of materialStages) {
+                ex_tax_tp = this.ctx.helper.add(ex_tax_tp, ms.ex_tax_tp);
+                total_ex_tp = this.ctx.helper.add(total_ex_tp, ms.ex_tp || 0);
+            }
+            // if (updateTaxTps.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, updateTaxTps);
+            await transaction.update(this.ctx.service.material.tableName, {
+                id: mid,
+                ex_tp: total_ex_tp,
+                ex_tax_tp,
+            });
+            return [total_ex_tp, ex_tax_tp];
+        }
+
+        async resetDecimal(transaction, decimal) {
+            const exponentList = await transaction.select(this.tableName, { where: { tid: this.ctx.tender.id } });
+            const exponentShardList = await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id } });
+            const updateArray = [];
+            const updateStageArray = [];
+            const upColArray = ['basic_price', 'm_price'];
+            // const qtyColArray = ['calc_num'];
+            for (const ex of exponentList) {
+                for (const col of upColArray) {
+                    if (ex[col] && ex[col] !== this.ctx.helper.round(ex[col], decimal.up)) {
+                        const upex = this._.find(updateArray, { id: ex.id });
+                        if (upex) {
+                            upex[col] = this.ctx.helper.round(ex[col], decimal.up);
+                        } else {
+                            const insertEx = {
+                                id: ex.id,
+                            };
+                            insertEx[col] = this.ctx.helper.round(ex[col], decimal.up);
+                            updateArray.push(insertEx);
+                        }
+                    }
+                }
+                // for (const col of qtyColArray) {
+                //     if (ex[col] && ex[col] !== this.ctx.helper.round(ex[col], decimal.qty)) {
+                //         const upex = this._.find(updateArray, { id: ex.id });
+                //         if (upex) {
+                //             upex[col] = this.ctx.helper.round(ex[col], decimal.qty);
+                //         } else {
+                //             const insertEx = {
+                //                 id: ex.id,
+                //             };
+                //             insertEx[col] = this.ctx.helper.round(ex[col], decimal.qty);
+                //             updateArray.push(insertEx);
+                //         }
+                //     }
+                // }
+            }
+            for (const ex of exponentShardList) {
+                if (ex.m_price && ex.m_price !== this.ctx.helper.round(ex.m_price, decimal.up)) {
+                    const insertEx = {
+                        id: ex.id,
+                    };
+                    insertEx.m_price = this.ctx.helper.round(ex.m_price, decimal.up);
+                    updateStageArray.push(insertEx);
+                }
+            }
+            if (updateArray.length > 0) await transaction.updateRows(this.tableName, updateArray);
+            if (updateStageArray.length > 0) await transaction.updateRows(this.ctx.service.materialExponentShard.tableName, updateStageArray);
+        }
     }
 
     return MaterialExponent;

+ 197 - 0
app/service/material_exponent_node.js

@@ -0,0 +1,197 @@
+'use strict';
+
+/**
+ * 调差表(多期独立单价使用) 数据模型
+ *
+ * @author Mai
+ * @date 2018/8/13
+ * @version
+ */
+const materialConst = require('../const/material');
+
+module.exports = app => {
+    class MaterialExponentNode extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_exponent_node';
+        }
+
+        async updateNode(data) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            if (!data.baseNodes || !data.addNodes || !data.delNodes) {
+                throw '参数错误';
+            }
+            // 判断t_type是否为费用,且存在quantity,m_spread值
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 清空nodes并把总金额插回到exponent_stage或者material表里
+                if (data.exponent_node.length === 0 && this.ctx.material.pre_exponent_node) throw '上期存在已调用分项,无法清空';
+                const exponentList = await transaction.select(this.ctx.service.materialExponent.tableName, { where: { tid: this.ctx.tender.id } });
+                if (data.exponent_node.length > 0) {
+                    // 之前不存在已调用分项且本次有新增分项
+                    const oldMsShardList = !this.ctx.material.exponent_node && this.ctx.material.is_stage_self ? await transaction.select(this.ctx.service.materialExponentShard.tableName, { where: { mid: this.ctx.material.id } }) : [];
+                    if (!this.ctx.material.exponent_node) {
+                        await transaction.delete(this.ctx.service.materialExponentShard.tableName, { mid: this.ctx.material.id });
+                        const insertBaseDatas = [];
+                        for (const base of data.baseNodes) {
+                            base.tid = this.ctx.tender.id;
+                            base.mid = this.ctx.material.id;
+                            base.ex_calc = JSON.stringify(base.ex_calc);
+                            insertBaseDatas.push(base);
+                        }
+                        if (insertBaseDatas.length > 0) await transaction.insert(this.tableName, insertBaseDatas);
+                    } else {
+                        const updateBaseNodes = [];
+                        const oldBaseDatas = await transaction.select(this.tableName, { where: { mid: this.ctx.material.id, node: '-1' } });
+                        for (const base of data.baseNodes) {
+                            const old = this._.find(oldBaseDatas, { id: base.id });
+                            if (old) {
+                                old.ex_calc = JSON.stringify(base.ex_calc);
+                                old.contract_tp = base.contract_tp;
+                                old.qc_tp = base.qc_tp;
+                                old.gather_tp = base.gather_tp;
+                                updateBaseNodes.push(old);
+                            }
+                        }
+                        if (updateBaseNodes.length > 0) await transaction.updateRows(this.tableName, updateBaseNodes);
+                    }
+                    const oldDatas = await transaction.select(this.tableName, { where: { mid: this.ctx.material.id } });
+                    const insertDatas = [];
+                    for (const add of data.addNodes) {
+                        add.tid = this.ctx.tender.id;
+                        add.mid = this.ctx.material.id;
+                        add.ex_calc = JSON.stringify(add.ex_calc);
+                        insertDatas.push(add);
+                    }
+                    if (insertDatas.length > 0) {
+                        await transaction.insert(this.tableName, insertDatas);
+                        const newNodeDatas = await transaction.select(this.tableName, { where: { mid: this.ctx.material.id } });
+                        const insertExponentShardDatas = [];
+                        if (this.ctx.material.is_stage_self) {
+                            const msList = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                            for (const node of newNodeDatas) {
+                                const ms = this._.find(msList, { id: node.ms_id });
+                                for (const ex of exponentList) {
+                                    const exponentShardOlds = this._.filter(oldMsShardList, { ms_id: ms.id, me_id: ex.id });
+                                    insertExponentShardDatas.push({
+                                        tid: this.ctx.tender.id,
+                                        mid: this.ctx.material.id,
+                                        ms_id: ms.id,
+                                        mn_id: node.id,
+                                        me_id: ex.id,
+                                        type: ex.type,
+                                        m_price: !this.ctx.material.exponent_node ? exponentShardOlds.m_price : 0,
+                                        remark: ex.remark,
+                                    });
+                                }
+                            }
+                        } else {
+                            for (const node of newNodeDatas) {
+                                for (const ex of exponentList) {
+                                    insertExponentShardDatas.push({
+                                        tid: this.ctx.tender.id,
+                                        mid: this.ctx.material.id,
+                                        ms_id: null,
+                                        mn_id: node.id,
+                                        me_id: ex.id,
+                                        type: ex.type,
+                                        m_price: !this.ctx.material.exponent_node ? ex.m_price : 0,
+                                        remark: ex.remark,
+                                    });
+                                }
+                            }
+                        }
+                        if (insertExponentShardDatas.length > 0) await transaction.insert(this.ctx.service.materialExponentShard.tableName, insertExponentShardDatas);
+                    }
+                    const delDatas = [];
+                    for (const del of data.delNodes) {
+                        const olds = this._.filter(oldDatas, { node: del });
+                        for (const old of olds) {
+                            delDatas.push(old.id);
+                        }
+                    }
+                    if (delDatas.length > 0) {
+                        await transaction.delete(this.tableName, { id: delDatas });
+                        await transaction.delete(this.ctx.service.materialExponentShard.tableName, { me_id: delDatas });
+                    }
+                    // 重算所有并更新到material表、material_stage表、material_exponent_node表
+                    await this.ctx.service.materialExponentShard.calcMaterialAllExTpByNode(transaction, this.ctx.material.id);
+                    await this.ctx.service.materialExponent.calcTpByMaterialExponentNode(transaction, this.ctx.material.id);
+                    await transaction.update(this.ctx.service.material.tableName, { id: this.ctx.material.id, exponent_node: data.exponent_node.join(',') });
+                } else {
+                    // 清空所有节点并重复赋值给期或者总材料节点
+                    await transaction.delete(this.tableName, { mid: this.ctx.material.id });
+                    await transaction.delete(this.ctx.service.materialExponentShard.tableName, { mid: this.ctx.material.id });
+                    await transaction.update(this.ctx.service.material.tableName, { id: this.ctx.material.id, exponent_node: '' });
+                    if (this.ctx.material.is_stage_self) {
+                        const msList = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                        for (const ms of msList) {
+                            const insertExponentShardDatas = [];
+                            for (const ex of exponentList) {
+                                insertExponentShardDatas.push({
+                                    tid: this.ctx.tender.id,
+                                    mid: this.ctx.material.id,
+                                    ms_id: ms.id,
+                                    mn_id: null,
+                                    me_id: ex.id,
+                                    type: ex.type,
+                                    m_price: 0,
+                                    remark: ex.remark,
+                                });
+                            }
+                            if (insertExponentShardDatas.length > 0) await transaction.insert(this.ctx.service.materialExponentShard.tableName, insertExponentShardDatas);
+                            const baseNode = this._.find(data.baseNodes, { ms_id: ms.id });
+                            if (!baseNode) throw '标段(不含新增分项)数据不存在';
+                            await this.ctx.service.materialExponentShard.calcMaterialExTp(transaction, baseNode.ex_calc, this.ctx.material.id, ms.id);
+                        }
+                        await this.ctx.service.materialExponent.calcTpByMaterialStageExponent(transaction, this.ctx.material.id);
+                    } else {
+                        const baseNode = data.baseNodes[0];
+                        if (!baseNode) throw '标段(不含新增分项)数据不存在';
+                        await this.ctx.service.materialExponent.calcMaterialExTp(transaction, baseNode.ex_calc);
+                    }
+                }
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async calcStageExTpByNode(transaction, mid, ms_id) {
+            const mnList = await transaction.select(this.tableName, { where: { ms_id } });
+            let stage_ex_tp = 0;
+            let stage_ex_tax_tp = 0;
+            for (const mn of mnList) {
+                stage_ex_tp = this.ctx.helper.add(stage_ex_tp, mn.ex_tp ? mn.ex_tp : 0);
+                stage_ex_tax_tp = this.ctx.helper.add(stage_ex_tax_tp, mn.ex_tax_tp ? mn.ex_tax_tp : 0);
+            }
+            await transaction.update(this.ctx.service.materialStage.tableName, {
+                id: ms_id,
+                ex_tp: stage_ex_tp,
+                ex_tax_tp: stage_ex_tax_tp,
+            });
+        }
+
+        async getPreNodeData(mid, tid) {
+            const materials = await this.ctx.service.material.getAllDataByCondition({
+                where: { tid },
+                order: ['order'],
+            });
+            const preMaterial = materials.length > 1 ? materials[materials.length - 2] : null;
+            // if (!preMaterial) throw '上期不存在';
+            const preNodes = preMaterial ? await this.getAllDataByCondition({ where: { mid: preMaterial.id } }) : [];
+            return preNodes;
+        }
+    }
+    return MaterialExponentNode;
+};

+ 332 - 0
app/service/material_exponent_shard.js

@@ -0,0 +1,332 @@
+'use strict';
+
+/**
+ * 期计量 数据模型
+ *
+ * @author Mai
+ * @date 2018/8/13
+ * @version
+ */
+
+const auditConst = require('../const/audit').material;
+const materialConst = require('../const/material');
+const MaterialCalculator = require('../lib/material_calc');
+
+const needUpdateArray = ['m_price', 'remark'];
+
+module.exports = app => {
+    class MaterialExponentShard extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_exponent_shard';
+        }
+
+        /**
+         * 添加指数清单
+         * @return {void}
+         */
+        async add(transaction, material, id, remark = null) {
+            const materialStages = material.is_stage_self ? await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: material.id } }) : [];
+            const materialNodes = material.exponent_node ? await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid: material.id } }) : [];
+            const insertDatas = [];
+            const copyInsertData = {
+                tid: this.ctx.tender.id,
+                mid: material.id,
+                me_id: id,
+            };
+            if (remark) copyInsertData.remark = remark;
+            if (materialStages.length > 0) {
+                for (const ms of materialStages) {
+                    if (materialNodes.length > 0) {
+                        const stageNodes = this._.filter(materialNodes, { ms_id: ms.id });
+                        for (const mn of stageNodes) {
+                            const insertData = this._.cloneDeep(copyInsertData);
+                            insertData.ms_id = ms.id;
+                            insertData.mn_id = mn.id;
+                            insertDatas.push(insertData);
+                        }
+                    } else {
+                        const insertData = this._.cloneDeep(copyInsertData);
+                        insertData.ms_id = ms.id;
+                        insertDatas.push(insertData);
+                    }
+                }
+            } else if (materialStages.length === 0 && materialNodes.length > 0) {
+                for (const mn of materialNodes) {
+                    const insertData = this._.cloneDeep(copyInsertData);
+                    insertData.mn_id = mn.id;
+                    insertDatas.push(insertData);
+                }
+            }
+            if (insertDatas.length > 0) await transaction.insert(this.tableName, insertDatas);
+        }
+
+        // 单个修改工料时同步修改
+        async update(transaction, data, ms_id = null, mn_id = null, needCalc = false) {
+            const mesInfo = await transaction.get(this.tableName, { tid: this.ctx.tender.id, ms_id, mn_id, me_id: data.id });
+            const updateData = {
+                id: mesInfo.id,
+            };
+            for (const nu of needUpdateArray) {
+                if (data[nu] !== undefined && mesInfo[nu] !== data[nu]) updateData[nu] = data[nu];
+            }
+            await transaction.update(this.tableName, updateData);
+            if (updateData.m_price !== undefined || needCalc) {
+                const mnInfo = mn_id ? await transaction.get(this.ctx.service.materialExponentNode.tableName, { mid: this.ctx.material.id, id: mn_id }) : null;
+                const msInfo = ms_id && !mn_id ? await transaction.get(this.ctx.service.materialStage.tableName, { mid: this.ctx.material.id, id: ms_id }) : null;
+                const ex_calc = mnInfo ? JSON.parse(mnInfo.ex_calc) : msInfo ? JSON.parse(msInfo.ex_calc) : materialConst.ex_calc;
+                await this.calcMaterialExTp(transaction, ex_calc, this.ctx.material.id, ms_id, mn_id);
+            }
+            return true;
+        }
+
+        async calcMaterialAllExTpByStage(transaction, mid, extra_ms_id = null, decimal = this.ctx.material.exponent_decimal ? this.ctx.material.exponent_decimal : materialConst.exponent_decimal, rate = this.ctx.material.exponent_rate) {
+            const msList = await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid } });
+            const exponentList = await transaction.select(this.ctx.service.materialExponent.tableName, { where: { tid: this.ctx.tender.id, is_summary: materialConst.is_summary.yes } });
+            const allStageExponentList = await transaction.select(this.tableName, { where: { mid } });
+            const updateStages = [];
+            for (const ms of msList) {
+                if (extra_ms_id && ms.id === extra_ms_id) continue;
+                const [ex_tp, ex_tax_tp, expr] = await this._calcOneExTp(transaction, JSON.parse(ms.ex_calc), ms.id, null, exponentList, allStageExponentList, decimal, rate);
+                updateStages.push({
+                    id: ms.id,
+                    ex_tp,
+                    ex_tax_tp,
+                    ex_expr: expr,
+                });
+            }
+            if (updateStages.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, updateStages);
+        }
+
+        async calcMaterialAllExTpByNode(transaction, mid, extra_mn_id = null, needCalcStage = true, decimal = this.ctx.material.exponent_decimal ? this.ctx.material.exponent_decimal : materialConst.exponent_decimal, rate = this.ctx.material.exponent_rate) {
+            const mnList = await transaction.select(this.ctx.service.materialExponentNode.tableName, { where: { mid } });
+            const exponentList = await transaction.select(this.ctx.service.materialExponent.tableName, { where: { tid: this.ctx.tender.id, is_summary: materialConst.is_summary.yes } });
+            const allExponentShardList = await transaction.select(this.tableName, { where: { mid } });
+            const updateNodes = [];
+            for (const mn of mnList) {
+                if (extra_mn_id && mn.id === extra_mn_id) continue;
+                [mn.ex_tp, mn.ex_tax_tp, mn.expr] = await this._calcOneExTp(transaction, JSON.parse(mn.ex_calc), null, mn.id, exponentList, allExponentShardList, decimal, rate);
+                updateNodes.push({
+                    id: mn.id,
+                    ex_tp: mn.ex_tp,
+                    ex_tax_tp: mn.ex_tax_tp,
+                    ex_expr: mn.expr,
+                });
+            }
+            if (updateNodes.length > 0) await transaction.updateRows(this.ctx.service.materialExponentNode.tableName, updateNodes);
+            if (needCalcStage) {
+                const updateStages = [];
+                const msList = await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid } });
+                for (const ms of msList) {
+                    // const extra_ms = extra_mn_id ? this._.find(mnList, { id: extra_mn_id }) : null;
+                    // if (extra_ms && extra_ms.ms_id === ms.id) continue;
+                    let stage_ex_tp = 0;
+                    let stage_ex_tax_tp = 0;
+                    const stageMnList = this._.filter(mnList, { ms_id: ms.id });
+                    for (const mn of stageMnList) {
+                        stage_ex_tp = this.ctx.helper.add(stage_ex_tp, mn.ex_tp ? mn.ex_tp : 0);
+                        stage_ex_tax_tp = this.ctx.helper.add(stage_ex_tax_tp, mn.ex_tax_tp ? mn.ex_tax_tp : 0);
+                    }
+                    updateStages.push({
+                        id: ms.id,
+                        ex_tp: stage_ex_tp,
+                        ex_tax_tp: stage_ex_tax_tp,
+                    });
+                }
+                if (updateStages.length > 0) await transaction.updateRows(this.ctx.service.materialStage.tableName, updateStages);
+            }
+        }
+
+        async _calcOneExTp(transaction, ex_calc, msid = null, mnid = null, exponentList, allStageExponentList, decimal, rate) {
+            let basic_calc = 0;
+            // let basic_expr = 0;
+            if (ex_calc) {
+                for (const calc of ex_calc) {
+                    if (calc.select) {
+                        if (calc.code === 'zdy' && calc.result) {
+                            basic_calc = this.ctx.helper.add(basic_calc, calc.result);
+                            // basic_expr = calc.value;
+                        } else {
+                            basic_calc = this.ctx.helper.add(basic_calc, calc.value);
+                            // basic_expr = this.ctx.helper.add(basic_expr, calc.value);
+                        }
+                    }
+                }
+            }
+            let expr = basic_calc + '*[';
+            const condition = {};
+            if (msid !== null) condition.ms_id = msid;
+            if (mnid !== null) condition.mn_id = mnid;
+            const stageExponentList = this._.filter(allStageExponentList, condition);
+            let sumByChange = 0;
+            let constant = 0;
+            for (const ex of exponentList) {
+                if (ex.type === materialConst.ex_type[0].value) {
+                    constant = ex.weight_num ? ex.weight_num : 0;
+                    expr += constant.toString();
+                } else if (ex.type === materialConst.ex_type[1].value) {
+                    const stageEx = this._.find(stageExponentList, { me_id: ex.id });
+                    const calc_num = ex.basic_price > 0 ? this.ctx.helper.mul(ex.weight_num, this.ctx.helper.div(stageEx.m_price, ex.basic_price)) : 0;
+                    const change = calc_num ? this.ctx.helper.round(calc_num, decimal.qty) : 0;
+                    expr = expr + (change !== 0 ? '+' + ex.weight_num.toString() + '*(' + stageEx.m_price.toString() + '/' + ex.basic_price.toString() + ')' : '');
+                    sumByChange = this.ctx.helper.add(sumByChange, change);
+                }
+            }
+            expr += '-1]';
+            expr = constant !== 0 ? expr : null;
+            const ex_tp = constant !== 0 ? this.ctx.helper.round(this.ctx.helper.mul(basic_calc, this.ctx.helper.sub(this.ctx.helper.add(constant, sumByChange), 1)), decimal.tp) : null;
+            const ex_tax_tp = ex_tp !== null ? this.ctx.helper.round(this.ctx.helper.mul(ex_tp, 1 + rate / 100), decimal.tp) : null;
+            return [ex_tp, ex_tax_tp, expr];
+        }
+
+        // 更改计算总指数金额并返回值和公式
+        async calcMaterialExTp(transaction, ex_calc, mid, msid = null, mnid = null, decimal = this.ctx.material.exponent_decimal, rate = this.ctx.material.exponent_rate, needCalcStage = true) {
+            if (!msid && !mnid) throw '参数有误';
+            const exponentList = await transaction.select(this.ctx.service.materialExponent.tableName, { where: { tid: this.ctx.tender.id, is_summary: materialConst.is_summary.yes } });
+            const condition = {};
+            if (msid !== null) condition.ms_id = msid;
+            if (mnid !== null) condition.mn_id = mnid;
+            const exponentShardList = await transaction.select(this.tableName, { where: condition });
+            const [ex_tp, ex_tax_tp, expr] = await this._calcOneExTp(transaction, ex_calc, msid, mnid, exponentList, exponentShardList, decimal, rate);
+            if (!mnid) {
+                await transaction.update(this.ctx.service.materialStage.tableName, {
+                    id: msid,
+                    ex_tp,
+                    ex_tax_tp,
+                    ex_expr: expr,
+                    ex_calc: JSON.stringify(ex_calc),
+                });
+            } else {
+                await transaction.update(this.ctx.service.materialExponentNode.tableName, {
+                    id: mnid,
+                    ex_tp,
+                    ex_tax_tp,
+                    ex_expr: expr,
+                    ex_calc: JSON.stringify(ex_calc),
+                });
+                if (needCalcStage && msid) {
+                    await this.ctx.service.materialExponentNode.calcStageExTpByNode(transaction, mid, msid);
+                }
+            }
+            return [ex_tp, ex_tax_tp, expr];
+        }
+
+        /**
+         * 更新新一期的现行价格指数,并返回指数总金额和公式
+         * @param transaction
+         * @param tid
+         * @param mid
+         * @returns {Promise<number>}
+         */
+        async updateNewMaterial(transaction, mid, msid, ctx, stage_id, ex_calc, decimal, rate) {
+            const stage_list = await ctx.service.stage.getStageMsgByStageId(stage_id);
+            const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
+            const old_ex_calc = ex_calc ? JSON.parse(ex_calc) : null;
+            const new_ex_calc = materialConst.ex_calc;
+            for (const bq of new_ex_calc) {
+                const calc = this._.find(calcBase, { code: bq.code }) || this._.find(calcBase, { code: 'bqwc' });
+                const oldcalc = old_ex_calc ? this._.find(old_ex_calc, { code: bq.code }) : null;
+                bq.value = calc.value;
+                bq.select = oldcalc ? oldcalc.select : false;
+            }
+            await this.calcMaterialExTp(transaction, new_ex_calc, mid, msid, decimal, rate);
+            return new_ex_calc;
+        }
+
+        async makeExponentList(tid, data) {
+            if (!data.mid || !data.stage_id) {
+                throw '参数错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const material = await this.ctx.service.material.getDataById(data.mid);
+                if (!material) {
+                    throw '调差不存在';
+                }
+                if (!material.is_stage_self || material.is_new_exponent) {
+                    throw '独立期指数调差工料已生成或非独立期调差';
+                }
+                const materialExponentList = await this.ctx.service.materialExponent.getAllDataByCondition({ where: { tid } });
+                const materialStageInfo = await this.ctx.service.materialStage.getDataByCondition({ mid: data.mid, sid: data.stage_id });
+                await transaction.delete(this.tableName, { ms_id: materialStageInfo.id });
+                await transaction.update(this.ctx.service.materialStage.tableName, {
+                    id: materialStageInfo.id,
+                    ex_tp: null,
+                    ex_tax_tp: null,
+                    ex_expr: null,
+                    ex_calc: null,
+                });
+                const newExponentList = [];
+                for (const me of materialExponentList) {
+                    const newExponent = {
+                        tid,
+                        mid: material.id,
+                        ms_id: materialStageInfo.id,
+                        me_id: me.id,
+                        type: me.type,
+                        m_price: me.m_price,
+                        remark: me.remark,
+                    };
+                    newExponentList.push(newExponent);
+                }
+                if (newExponentList.length > 0) await transaction.insert(this.tableName, newExponentList);
+                // 计算金额
+                const ex_calc = await this.updateNewMaterial(transaction, material.id, materialStageInfo.id, this.ctx, materialStageInfo.sid, material.ex_calc, material.exponent_decimal ? JSON.parse(material.exponent_decimal) : materialConst.exponent_decimal, material.exponent_rate);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async insertMsExponent(transaction, tid, mid, ms, preMaterial = null, decimal, rate) {
+            // 复制工料表并生成
+            const meList = await transaction.select(this.ctx.service.materialExponent.tableName, { where: { tid } });
+            if (meList.length > 0) {
+                const insertStageExponentData = [];
+                for (const e of meList) {
+                    const oneStageExponentData = {
+                        tid,
+                        mid,
+                        me_id: e.id,
+                        ms_id: ms.id,
+                        type: e.type,
+                        m_price: e.m_price,
+                    };
+                    insertStageExponentData.push(oneStageExponentData);
+                }
+                await transaction.insert(this.tableName, insertStageExponentData);
+                const stage_list = await this.ctx.service.stage.getStageMsgByStageId(ms.sid);
+                const calcBase = await this.ctx.service.stage.getMaterialCalcBase(stage_list, this.ctx.tender.info);
+                let old_ex_calc = null;
+                if (preMaterial && preMaterial.ex_calc) {
+                    old_ex_calc = JSON.parse(preMaterial.ex_calc);
+                } else if (preMaterial && preMaterial.is_stage_self) {
+                    // 判断历史期是否存在相同独立期
+                    const allPreMs = await this.ctx.service.materialStage.getAllDataByCondition({ where: { tid: this.ctx.tender.id, sid: ms.sid }, orders: [['id', 'desc']] });
+                    const preMs = this._.find(allPreMs, m => m.mid !== mid);
+                    if (preMs && preMs.ex_calc) {
+                        old_ex_calc = JSON.parse(preMs.ex_calc);
+                    }
+                }
+                const new_ex_calc = materialConst.ex_calc;
+                for (const bq of new_ex_calc) {
+                    const calc = this._.find(calcBase, { code: bq.code }) || this._.find(calcBase, { code: 'bqwc' });
+                    const oldcalc = old_ex_calc ? this._.find(old_ex_calc, { code: bq.code }) : null;
+                    bq.value = calc.value;
+                    bq.select = oldcalc ? oldcalc.select : false;
+                }
+                await this.calcMaterialExTp(transaction, new_ex_calc, mid, ms.id, null, decimal, rate);
+            }
+        }
+    }
+
+    return MaterialExponentShard;
+};

+ 8 - 1
app/service/material_list.js

@@ -917,6 +917,7 @@ module.exports = app => {
                 const preMaterial = materials[materials.length - 2];
                 if (material.is_stage_self) {
                     await this.ctx.service.materialStageBills.insertMsBills(transaction, this.ctx.tender.id, material.id, this._.find(materialStages, { sid: parseInt(data.stage_id) }), JSON.parse(material.decimal), preMaterial.is_stage_self, material.qty_source, material.rate);
+                    if (!material.pre_exponent_node) await this.ctx.service.materialExponentShard.insertMsExponent(transaction, this.ctx.tender.id, material.id, this._.find(materialStages, { sid: parseInt(data.stage_id) }), preMaterial, JSON.parse(material.exponent_decimal), material.exponent_rate);
                     await transaction.update(this.ctx.service.material.tableName, {
                         id: material.id, calc_stage: newCalcStage.join(','),
                     });
@@ -959,7 +960,7 @@ module.exports = app => {
                 await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id);
                 // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
                 // 找出当前人并更新tp_data
-                const tp_data = await this.ctx.service.materialAudit.getTpData(transaction, material.id, JSON.parse(material.decimal));
+                const tp_data = await this.ctx.service.materialAudit.getTpData(transaction, material.id, JSON.parse(material.decimal), JSON.parse(material.exponent_decimal));
                 const updateMaterialData = {
                     id: material.id,
                     calc_tp: 1,
@@ -997,15 +998,21 @@ module.exports = app => {
                     let m_tp = 0;
                     let m_tax_tp = 0;
                     let rate_tp = 0;
+                    let ex_tp = 0;
+                    let ex_tax_tp = 0;
                     for (const s of materialStages) {
                         m_tp = this.ctx.helper.add(m_tp, s.m_tp);
                         m_tax_tp = this.ctx.helper.add(m_tax_tp, s.m_tax_tp);
                         const sRateTp = this.ctx.helper.round(this.ctx.helper.mul(s.m_tp, (1 + this.ctx.helper.div(material.rate, 100))), materialDecimal.tp);
                         rate_tp = this.ctx.helper.add(rate_tp, sRateTp);
+                        ex_tp = this.ctx.helper.add(ex_tp, s.ex_tp);
+                        ex_tax_tp = this.ctx.helper.add(ex_tax_tp, s.ex_tax_tp);
                     }
                     updateMaterialData.m_tp = m_tp;
                     updateMaterialData.m_tax_tp = m_tax_tp;
                     updateMaterialData.rate_tp = rate_tp;
+                    updateMaterialData.ex_tp = ex_tp;
+                    updateMaterialData.ex_tax_tp = ex_tax_tp;
                 }
                 await transaction.update(this.ctx.service.material.tableName, updateMaterialData);
                 // 删除material_list表冗余数据,减少表数据量

+ 1 - 0
app/service/role_rpt_rel.js

@@ -132,6 +132,7 @@ module.exports = app => {
                     await this.transaction.rollback();
                     this.transaction = null;
                 }
+                throw ex;
             }
             return rst;
         }

Разница между файлами не показана из-за своего большого размера
+ 653 - 208
app/view/material/audit_modal.ejs


+ 164 - 85
app/view/material/exponent.ejs

@@ -16,6 +16,11 @@
                 <div class="d-inline-block ml-2" >
                     <a href="#cc-digits" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-placement="bottom" title="小数位数" data-target="#cc-digits" ><i class="fa fa-cog" aria-hidden="true"></i></a>
                 </div>
+                <% if (!ctx.material.readOnly) { %>
+                <div class="d-inline-block ml-2" >
+                    <a href="#choose2" class="btn btn-sm btn-primary" data-toggle="modal" data-placement="bottom" data-target="#choose2" >新增分项价格指数</a>
+                </div>
+                <% } %>
                 <!--<div class="d-inline-block ml-3" style="display: none!important;">-->
                     <!--本期价差:<b id="ex_expr"><%- material.ex_expr %></b>-->
                 <!--</div>-->
@@ -33,90 +38,152 @@
         <div class="row w-100 sub-content">
             <div id="left-view" class="c-body" style="width: 100%">
                 <!--上部分-->
-                <div class="sjs-height-1" id="material-exponent-spread">
-                </div>
+                <% if (material.showStageExponent) { %>
+                    <div class="c-body sjs-height-1" style="width: 100%">
+                        <ul class="nav nav-tabs" id="myTab" role="tablist">
+                            <% for (const [index,ms] of materialStageData.entries()) { %>
+                                <li class="nav-item">
+                                    <a class="nav-link change-material-stage <% if (index === 0) { %>active<% } %>" data-msid="<%- ms.id %>" data-toggle="tab" href="javascript:void(0);" role="tab" aria-selected="<% if (index === 0) { %>true<% } else { %>false<% } %>">第<%- ms.order %>期</a>
+                                </li>
+                            <% } %>
+                        </ul>
+                        <% if (material.exponent_node) { %>
+                            <% const nodeList = ctx.helper._.filter(materialExponentNodeData, { ms_id: materialStageData[0].id }); %>
+                        <div class="nav-tabs-wrapper">
+                            <ul class="nav nav-pills flex-nowrap my-1" id="myTab2" role="tablist">
+                                <% for (const [j, node] of nodeList.entries()) { %>
+                                <li class="nav-item">
+                                    <a class="nav-link change-material-node <% if (j === 0) { %>active<% } %>" data-node="<%- node.node %>" data-mnid="<%- node.id %>" href="javascript:void(0);" data-toggle="tab"><%- node.name %><% if (node.node === '-1') { %>(不含新增分项)<% } %></a>
+                                </li>
+                                <% } %>
+                            </ul>
+                        </div>
+                        <% } %>
+                        <div class="sjs-height-material" id="material-exponent-spread"></div>
+                    </div>
+                <% } else if (material.exponent_node) { %>
+                    <div class="c-body sjs-height-1" style="width: 100%">
+                        <div class="nav-tabs-wrapper">
+                            <ul class="nav nav-pills flex-nowrap my-1" id="myTab2" role="tablist">
+                                <% for (const [j,node] of materialExponentNodeData.entries()) { %>
+                                    <li class="nav-item">
+                                        <a class="nav-link change-material-node <% if (j === 0) { %>active<% } %>" data-node="<%- node.node %>" data-mnid="<%- node.id %>" href="javascript:void(0);" data-toggle="tab"><%- node.name %><% if (node.node === '-1') { %>(不含新增分项)<% } %></a>
+                                    </li>
+                                <% } %>
+                            </ul>
+                        </div>
+                        <div class="sjs-height-material" id="material-exponent-spread"></div>
+                    </div>
+                <% } else { %>
+                    <div class="sjs-height-1" id="material-exponent-spread">
+                    </div>
+                <% } %>
                 <!--下部分-->
                 <div class="bcontent-wrap" id="main-bottom">
                     <div id="main-resize" class="resize-y" r-Type="height" div1=".sjs-height-1" div2=".bcontent-wrap" title="调整大小"><!--调整上下高度条--></div>
                     <div class="bc-bar mb-1">
-                        <div class="input-group input-group-sm ">
-                            <div class="input-group-prepend">
-                                <span class="input-group-text" id="basic-addon1">增值税税率</span>
-                            </div>
-                            <input id="rateInput" class="form-control form-control-sm col-1" <% if (material.readOnly || material.editForAudit) { %>disabled<% } %> type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="100" value="<%- material.exponent_rate %>">
-                            <% if (!material.readOnly || !material.editForAudit) { %>
-                                <div class="dropdown-menu">
-                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="9">9%</a>
-                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="10">10%</a>
-                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="11">11%</a>
-                                </div>
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#fysum" role="tab">费用统计</a>
+                            </li>
+                            <% if (material.showStageExponent) { %>
+                                <li class="nav-item">
+                                    <a class="nav-link " data-toggle="tab" href="#qidetail" role="tab">各期统计</a>
+                                </li>
                             <% } %>
-                            <!--<select class="form-control form-control-sm col-1" id="changeRate">-->
-                                <!--<% if (!material.readOnly && !material.editForAudit) { %>-->
-                                    <!--<option value="9" <% if(material.exponent_rate === 9) { %>selected<% } %>>9%</option>-->
-                                    <!--<option value="10" <% if(material.exponent_rate === 10) { %>selected<% } %>>10%</option>-->
-                                    <!--<option value="11" <% if(material.exponent_rate === 11) { %>selected<% } %>>11%</option>-->
-                                <!--<% } else { %>-->
-                                    <!--<option value="<%= material.exponent_rate %>" selected><%= material.exponent_rate %>%</option>-->
-                                <!--<% } %>-->
-                            <!--</select>-->
-                        </div>
+                            <li class="nav-item ml-2">
+                                <div class="ml-2">
+                                    <!--调整税率-->
+                                    <div class="d-inline-flex">
+                                        <div class="input-group input-group-sm ">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text" id="basic-addon1">增值税税率</span>
+                                            </div>
+                                            <input id="rateInput" class="form-control form-control-sm" <% if (material.readOnly || material.editForAudit) { %>disabled<% } %> type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="100" value="<%- material.exponent_rate %>">
+                                            <% if (!material.readOnly || !material.editForAudit) { %>
+                                                <div class="dropdown-menu">
+                                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="9">9%</a>
+                                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="10">10%</a>
+                                                    <a class="dropdown-item changeRate" href="javascript:void(0);" data-value="11">11%</a>
+                                                </div>
+                                            <% } %>
+                                        </div>
+                                    </div>
+                                </div>
+                            </li>
+                        </ul>
                     </div>
-                    <% if (!material.material_tax && !old_had_tax) { %>
-                        <div class="sp-wrap" style="max-width: 800px;min-width: 500px;">
-                            <div class="col-12 p-0">
-                                <table class="table table-sm table-bordered">
-                                    <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
-                                    <tr class="text-center"><th>本期金额</th><th>截止本期金额</th>
-                                        <th>本期金额
-                                            <a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></th>
-                                        <th>截止本期金额</th></tr>
-                                    <tr id="tp_set"><td>材料价差费用</td>
-                                        <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.add(material.pre_tp, ctx.helper.round(material.m_tp, material.decimal.tp)) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.add(material.ex_pre_tp, ctx.helper.round(material.ex_tp, material.decimal.tp)) : null %></td>
-                                    </tr>
-                                    <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
-                                        <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp)) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)) : null %></td>
-                                    </tr>
-                                </table>
+                    <div class="tab-content">
+                        <div class="tab-pane active" id="fysum">
+                            <div class="sp-wrap" style="max-width: 800px;min-width: 500px;overflow: auto;">
+                                <div class="col-12 p-0">
+                                    <table class="table table-sm table-bordered">
+                                        <tr><th rowspan="2"></th><th colspan="2" class="text-center">本期金额</th><th colspan="2" class="text-center">截止本期金额</th></tr>
+                                        <tr class="text-center">
+                                            <th>材料调差费用 <% if (material.exponentStatus === materialType.exponent_status.shared_noNode) { %><a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a><% } %></th>
+                                            <th>材料调差费用(含建筑税)</th>
+                                            <th>材料调差费用</th>
+                                            <th>材料调差费用(含建筑税)</th>
+                                        </tr>
+                                        <% if (material.exponent_node) { %>
+                                            <tbody id="tj_node">
+                                            </tbody>
+                                        <% } else { %>
+                                            <tr id="tj_ledger">
+                                                <td class="text-center">标段(不含新增分项)</td>
+                                                <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.exponent_decimal.tp) : null %></td>
+                                                <td class="text-center"><%= material.ex_tax_tp !== null ? ctx.helper.round(material.ex_tax_tp,  material.exponent_decimal.tp) : null %></td>
+                                                <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.add(ctx.helper.round(material.ex_tp, material.exponent_decimal.tp), material.ex_pre_tp) : null %></td>
+                                                <td class="text-center"><%= material.ex_tax_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(material.ex_tax_tp, material.exponent_decimal.tp)) : null %></td>
+                                            </tr>
+                                        <% } %>
+                                        <tr id="total_set">
+                                            <td class="text-center">合计</td>
+                                            <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.exponent_decimal.tp) : null %></td>
+                                            <td class="text-center"><%= material.ex_tax_tp !== null ? ctx.helper.round(material.ex_tax_tp,  material.exponent_decimal.tp) : null %></td>
+                                            <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.add(ctx.helper.round(material.ex_tp, material.exponent_decimal.tp), material.ex_pre_tp) : null %></td>
+                                            <td class="text-center"><%= material.ex_tax_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(material.ex_tax_tp, material.exponent_decimal.tp)) : null %></td>
+                                        </tr>
+                                    </table>
+                                </div>
                             </div>
                         </div>
-                    <% } else { %>
-                        <div class="sp-wrap" style="max-width: 800px;min-width: 500px;">
-                            <div class="col-12 p-0">
-                                <table class="table table-sm table-bordered">
-                                    <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
-                                    <tr class="text-center"><th>本期金额</th><th>截止本期金额</th>
-                                        <th>本期金额
-                                            <a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></th>
-                                        <th>截止本期金额</th></tr>
-                                    <tr id="tp_set"><td>材料价差费用</td>
-                                        <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.add(material.pre_tp, ctx.helper.round(material.m_tp, material.decimal.tp)) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.add(material.ex_pre_tp, ctx.helper.round(material.ex_tp, material.decimal.tp)) : null %></td>
-                                    </tr>
-                                    <!--<tr><td>材料价差费用(含材料税)</td>-->
-                                        <!--<td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null ? material.m_tax_tp : null) : '-' %></td>-->
-                                        <!--<td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null || material.m_tax_pre_tp !== null ? ctx.helper.add(material.m_tax_pre_tp, material.m_tax_tp) : null) : material.m_tax_pre_tp %></td>-->
-                                        <!--<td class="text-center">-</td>-->
-                                        <!--<td class="text-center">-</td>-->
-                                    <!--</tr>-->
-                                    <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
-                                        <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp) : null) : '-' %></td>
-                                        <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), material.decimal.tp)) : null) : pre_tp_hs !== null ? pre_tp_hs : '-' %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
-                                        <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)) : null %></td>
-                                    </tr>
-                                </table>
+                        <% if (material.showStageExponent) { %>
+                        <div class="tab-pane" id="qidetail">
+                            <div class="sp-wrap" style="max-width: 800px;min-width: 500px;overflow: auto;">
+                                <div class="col-12 p-0">
+                                    <table class="table table-sm table-bordered">
+                                        <tr><th class="text-center">计量期数</th><th class="text-center">本期调差金额</th><th class="text-center">本期调差金额(建筑税)</th></tr>
+                                        <tbody>
+                                        <tr id="total_stage">
+                                            <td>合计</td>
+                                            <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.exponent_decimal.tp) : null %></td>
+                                            <td class="text-center"><%= material.ex_tax_tp !== null ? ctx.helper.round(material.ex_tax_tp, material.exponent_decimal.tp) : null %></td>
+                                        </tr>
+                                        <% for (const ms of materialStageData) { %>
+                                            <tr id="tj_stage_<%- ms.order %>">
+                                                <td>第<%- ms.order %>期 <% if (!material.exponent_node) { %><a href="javascript:void(0);" class="ex_expr_<%- ms.order %>" data-toggle="tooltip" data-placement="bottom" title="第<%- ms.order %>期价差:<%- ms.ex_expr ? ms.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a><% } %></td>
+                                                <td class="text-center"><%= ms.ex_tp !== null ? ctx.helper.round(ms.ex_tp, material.exponent_decimal.tp) : null %></td>
+                                                <td class="text-center"><%= ms.ex_tax_tp !== null ? ctx.helper.round(ms.ex_tax_tp, material.exponent_decimal.tp) : null %></td>
+                                            </tr>
+                                            <% if (material.exponent_node) { %>
+                                                <% const mnList = ctx.helper._.filter(materialExponentNodeData, { ms_id: ms.id }); %>
+                                                <% for (const mn of mnList) { %>
+                                                    <tr id="tj_node_<%- mn.id %>">
+                                                        <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<%- mn.name %>(<%- mn.code %>) <a href="javascript:void(0);" class="ex_expr_node_<%- mn.id %>" data-toggle="tooltip" data-placement="bottom" title="<%- mn.name %>(<%- mn.code %>)价差:<%- mn.ex_expr ? mn.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></td>
+                                                        <td class="text-center"><%= mn.ex_tp !== null ? ctx.helper.round(mn.ex_tp, material.exponent_decimal.tp) : null %></td>
+                                                        <td class="text-center"><%= mn.ex_tax_tp !== null ? ctx.helper.round(mn.ex_tax_tp, material.exponent_decimal.tp) : null %></td>
+                                                    </tr>
+                                                <% } %>
+                                            <% } %>
+                                        <% } %>
+                                        </tbody>
+                                    </table>
+                                </div>
                             </div>
                         </div>
-                    <% } %>
+                        <% } %>
+                    </div>
                 </div>
             </div>
             <div id="right-view" class="c-body" style="display:none;width: 33%">
@@ -126,14 +193,16 @@
                         <div class="sjs-sh-1">
                             <table class="table table-bordered">
                                 <thead>
-                                <tr><th>选择</th><th>整的价差</th><th>金额</th></tr>
+                                <tr><th width="40">选择</th><th>调差基数(P)</th><th>金额</th></tr>
                                 </thead>
                                 <tbody id="calc_basic_select">
-                                <% for (const bq of ex_calc) { %>
-                                    <tr>
-                                        <td><input type="checkbox" value="<%- bq.code %>" <% if (material.readOnly) { %>disabled<% } %> class="calc_select" <% if (bq.select) { %>checked<% } %>></td>
-                                        <td><%- materialType.ex_basic_text[bq.code] %></td><td><% if (bq.code === 'zdy' && !material.readOnly) { %><input type="number" id="calc_zdy" class="form-control form-control-sm" value="<%- bq.value %>"><% } else { %><%- bq.value %><% } %></td>
-                                    </tr>
+                                <% if (!material.showStageExponent) { %>
+                                    <% for (const bq of ex_calc) { %>
+                                        <tr>
+                                            <td><input type="checkbox" value="<%- bq.code %>" <% if (material.readOnly) { %>disabled<% } %> class="calc_select" <% if (bq.select) { %>checked<% } %>></td>
+                                            <td><%- materialType.ex_basic_text[bq.code] %></td><td><% if (bq.code === 'zdy' && !material.readOnly) { %><input type="text" id="calc_zdy" class="form-control form-control-sm" value="<%- bq.value %>"><% } else { %><%- bq.value %><% } %></td>
+                                        </tr>
+                                    <% } %>
                                 <% } %>
                                 </tbody>
                             </table>
@@ -161,19 +230,29 @@
     const materialID = <%- material.id %>;
     const materialTax = <%- material.material_tax %>;
     const materialOrder = <%- material.order %>;
+    const isStageSelf = parseInt('<%- material.is_stage_self %>');
+    const showStageExponent = <%- material.showStageExponent %>;
+    const exponent_nodes = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.material.exponent_node)) %>'));
     const editForAudit = <%- material.editForAudit %>;
     const materialDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.decimal)) %>'));
+    const materialExponentDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.exponent_decimal)) %>'));
     const decimal = JSON.parse('<%- JSON.stringify(ctx.tender.info.decimal) %>');
-    let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
     let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;
-    const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
+    let ex_tax_tp = <%= material.ex_tax_tp !== null ? material.ex_tax_tp : 0 %>;
     const ex_pre_tp = <%= material.ex_pre_tp !== null ? material.ex_pre_tp : 0 %>;
-    const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
     const ex_pre_tp_hs = <%= ex_pre_tp_hs !== null ? ex_pre_tp_hs : 0 %>;
     const materialType = JSON.parse('<%- JSON.stringify(materialType) %>');
-    const ex_calc = JSON.parse('<%- JSON.stringify(ex_calc) %>');
+    let ex_calc = JSON.parse('<%- JSON.stringify(ex_calc) %>');
+    let ex_calcList = [];
+    if (showStageExponent || exponent_nodes) {
+        ex_calcList = JSON.parse(unescape('<%- escape(JSON.stringify(ex_calc)) %>'));
+        console.log(ex_calcList);
+        ex_calc = ex_calcList[0].ex_calc;
+    }
     let ex_expr = '<%- material.ex_expr %>';
     let materialRate = parseInt('<%- material.exponent_rate %>');
     let materialExponentData = JSON.parse(unescape('<%- escape(JSON.stringify(materialExponentData)) %>'));
-    const materialBillsData = JSON.parse(unescape('<%- escape(JSON.stringify(materialBillsData)) %>'));
+    let materialStageData = showStageExponent ? JSON.parse(unescape('<%- escape(JSON.stringify(materialStageData)) %>')) : [];
+    let materialExponentNodeData = exponent_nodes ? JSON.parse(unescape('<%- escape(JSON.stringify(materialExponentNodeData)) %>')) : [];
+    let materialExponentShardData = showStageExponent || exponent_nodes ? JSON.parse(unescape('<%- escape(JSON.stringify(materialExponentShardData)) %>')) : [];
 </script>

+ 556 - 0
app/view/material/exponent_modal.ejs

@@ -1 +1,557 @@
+<% if (!ctx.material.readOnly) { %>
+<!--新增分项价格指数-->
+<div class="modal fade" id="choose2" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">新增分项价格指数</h5>
+            </div>
+            <div class="modal-body">
+<!--                <div class="custom-control custom-checkbox mb-2">-->
+<!--                    <input type="checkbox" class="custom-control-input" id="im-gather-check">-->
+<!--                    <label class="custom-control-label" for="im-gather-check">汇总计量节点</label>-->
+<!--                </div>-->
+                <div class="d-inline-block mb-2">
+                    <div class="dropdown">
+                        <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            <i class="fa fa-list-ol"></i> 显示层级
+                        </button>
+                        <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                            <a class="dropdown-item" name="showLevel" tag="1" href="javascript: void(0);">第一层</a>
+                            <a class="dropdown-item" name="showLevel" tag="2" href="javascript: void(0);">第二层</a>
+                            <a class="dropdown-item" name="showLevel" tag="3" href="javascript: void(0);">第三层</a>
+                            <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
+                            <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
+                            <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
+                            <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascript: void(0);">只显示项目节</a>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-height-500" id="gather-spread">
+                </div>
+                <div class="popover bs-popover-right" role="tooltip" id="gather-confirm" style="display: none">
+                    <div class="arrow" style="top:30px"></div>
+                    <div class="popover-body">
+                        <p id="gather-confirm-hint">父项已勾选,继续将取消父项勾选。</p>
+                        <p class="mb-0 d-flex justify-content-end" id="gather-confirm-btn">
+                            <button class="btn btn-sm btn-secondary" id="gather-confirm-cancel">取消</button>&nbsp;<button class="btn btn-sm btn-success" id="gather-confirm-ok">继续</button>
+                        </p>
+                    </div>
+                </div>
+                <div class="popover bs-popover-right" role="tooltip" id="gather-tip" style="display: none">
+                    <style>
+                        #gather-tip>.arrow::after {
+                            border-right-color: #ffc107;
+                        }
+                    </style>
+                    <div class="arrow" style="top:6px"></div>
+                    <div class="popover-body bg-warning">
+                        <span id="gather-tip-hint">父项已勾选,继续将取消父项勾选。</span>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <% if (!ctx.material.readOnly && ctx.material.user_id === ctx.session.sessionUser.accountId) { %>
+                    <button type="button" class="btn btn-primary btn-sm" id="choose2-ok">确定</button>
+                <% } %>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const pre_nodes = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.material.pre_exponent_node)) %>'));
+
+    $(function () {
+        const gatherConfirmPopover = {
+            reBind: function (obj, eventName, fun) {
+                obj.unbind(eventName);
+                obj.bind(eventName, fun);
+            },
+            check: function (pos, hint, okCallback) {
+                const confirmObj = $('#gather-confirm'), hintObj = $('#gather-confirm-hint');
+                const okObj = $('#gather-confirm-ok'), cancelObj = $('#gather-confirm-cancel');
+                this.reBind(cancelObj, 'click', function () {
+                    confirmObj.hide();
+                });
+                this.reBind(okObj, 'click', function () {
+                    okCallback();
+                    confirmObj.hide();
+                });
+                hintObj.text(hint);
+                confirmObj.css("top", pos.y).css("left", pos.x).show();
+            },
+            showTip: function (pos, hint) {
+                const confirmObj = $('#gather-tip'), hintObj = $('#gather-tip-hint');
+                hintObj.text(hint);
+                confirmObj.css("top", pos.y).css("left", pos.x).show();
+                setTimeout(function () {
+                    confirmObj.hide();
+                }, 2000);
+            }
+        };
+        let gsSpread = null;
+        const gsTreeSetting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id'],
+            stageId: 'id',
+        };
+        gsTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'];
+        gsTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp'];
+        // gsTreeSetting.updateFields = [];
+        // gsTreeSetting.calcFields = [];
+        if (isStageSelf) {
+            for (const ms of materialStageData) {
+                gsTreeSetting.updateFields.push(ms.sid + '_contract_qty', ms.sid + '_contract_tp',
+                    ms.sid + '_qc_qty', ms.sid + '_qc_tp', ms.sid + '_qc_minus_qty');
+                gsTreeSetting.calcFields.push(ms.sid + '_deal_tp', ms.sid + '_total_price', ms.sid + '_contract_tp',
+                    ms.sid + '_qc_tp', ms.sid + '_gather_tp',
+                    ms.sid + '_contract_pc_tp', ms.sid + '_qc_pc_tp', ms.sid + '_pc_tp');
+            }
+        }
+        // else {
+        //     gsTreeSetting.updateFields.push(materialSids + '_contract_qty', materialSids + '_contract_tp',
+        //         materialSids + '_qc_qty', materialSids + '_qc_tp', materialSids + '_qc_minus_qty');
+        //     gsTreeSetting.calcFields.push(materialSids + '_deal_tp', materialSids + '_total_price', materialSids + '_contract_tp',
+        //         materialSids + '_qc_tp', materialSids + '_gather_tp',
+        //         materialSids + '_contract_pc_tp', materialSids + '_qc_pc_tp', materialSids + '_pc_tp');
+        // }
+
+        gsTreeSetting.calcFun = function (node) {
+            if (node.children && node.children.length === 0) {
+                node.gather_qty = ZhCalc.add(node.contract_qty, node.qc_qty);
+                if (isStageSelf) {
+                    for (const ms of materialStageData) {
+                        node[ms.sid + '_gather_qty'] = ZhCalc.add(node[ms.sid + '_contract_qty'], node[ms.sid + '_qc_qty']);
+                    }
+                }
+            }
+            node.gather_tp = ZhCalc.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+            if (isStageSelf) {
+                for (const ms of materialStageData) {
+                    node[ms.sid + '_gather_tp'] = ZhCalc.sum([node[ms.sid + '_contract_tp'], node[ms.sid + '_qc_tp'], node[ms.sid + '_pc_tp']]);
+                }
+            }
+        };
+        const gsTree = createNewPathTree('stage', gsTreeSetting);
+        // 显示树结构信息
+        $('#choose2').on('shown.bs.modal', function () {
+            if (!gsSpread) {
+                postData(window.location.pathname + '/save', {type: 'load', filter: 'ledger'}, function (result) {
+                    gsTree.loadDatas(result.ledgerData);
+                    treeCalc.calculateAll(gsTree);
+                    gsSpread = SpreadJsObj.createNewSpread($('#gather-spread')[0]);
+                    const setting = {
+                        cols: [
+                            {title: '', colSpan: '1', rowSpan: '1', field: 'check', hAlign: 1, width: 50, formatter: '@', readOnly: true, cellType: 'checkbox'},
+                            {title: '项目节编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: true, cellType: 'tree'},
+                            {title: '清单编号', colSpan: '1', rowSpan: '1', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+                            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 230, formatter: '@', readOnly: true},
+                            {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+                        ],
+                        headRows: 1,
+                        emptyRows: 0,
+                        headRowHeight: [32],
+                        defaultRowHeight: 21,
+                        headerFont: '12px 微软雅黑',
+                        font: '12px 微软雅黑',
+                    };
+                    sjsSettingObj.setFxTreeStyle(setting, sjsSettingObj.FxTreeStyle.jz);
+                    SpreadJsObj.initSheet(gsSpread.getActiveSheet(), setting);
+                    gsSpread.bind(spreadNS.Events.ButtonClicked, function (e, info) {
+                        function checkParent(node, field = 'check') {
+                            const parent = gsTree.getParent(node);
+                            if (parent) {
+                                return parent[field] ? parent[field] : checkParent(parent, field);
+                            } else {
+                                return false;
+                            }
+                        }
+
+                        function checkChildren(node, field = 'check') {
+                            for (const child of node.children) {
+                                if (child[field]) {
+                                    return true;
+                                } else if (checkChildren(child, field)) {
+                                    return true;
+                                }
+                            }
+                            return false;
+                        }
+
+                        const sheet = info.sheet, cellType = sheet.getCellType(info.row, info.col);
+                        if (cellType instanceof  spreadNS.CellTypes.CheckBox) {
+                            if (sheet.isEditing()) {
+                                sheet.endEdit(true);
+                            }
+                        }
+                        if (info.sheet.zh_setting) {
+                            const col = info.sheet.zh_setting.cols[info.col];
+                            if (col.field !== 'check') {
+                                return;
+                            }
+
+                            const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
+                            const node = sortData[info.row];
+                            if (!node.check) {
+                                if (checkParent(node, 'pre_check')) {
+                                    const rect = info.sheet.getCellRect(info.row, info.col);
+                                    gatherConfirmPopover.showTip({
+                                        x: rect.x + rect.width / 2 + 25,
+                                        y: rect.y + rect.height / 2 + 27,
+                                    }, '该项的父项上期已勾选调用,无法勾选该项。');
+                                    SpreadJsObj.reLoadRowsData(info.sheet, [gsTree.nodes.indexOf(node)]);
+                                    return;
+                                }
+                                if (checkChildren(node, 'pre_check')) {
+                                    const rect = info.sheet.getCellRect(info.row, info.col);
+                                    gatherConfirmPopover.showTip({
+                                        x: rect.x + rect.width / 2 + 25,
+                                        y: rect.y + rect.height / 2 + 27,
+                                    }, '该项的子项上期已勾选调用,无法勾选该项。');
+                                    SpreadJsObj.reLoadRowsData(info.sheet, [gsTree.nodes.indexOf(node)]);
+                                    return;
+                                }
+                                if (checkParent(node)) {
+                                    const rect = info.sheet.getCellRect(info.row, info.col);
+                                    gatherConfirmPopover.check({
+                                        x: rect.x + rect.width / 2 + 25,
+                                        y: rect.y + rect.height / 2 + 3,
+                                    }, '父项已勾选,继续将取消父项勾选。', function () {
+                                        node.check = true;
+                                        const parents = gsTree.getFullPathNodes(gsTree.getParent(node).full_path);
+                                        const rows = [gsTree.nodes.indexOf(node)];
+                                        for (const p of parents) {
+                                            if (p.check) {
+                                                p.check = false;
+                                                rows.push(gsTree.nodes.indexOf(p));
+                                            }
+                                        }
+                                        SpreadJsObj.reLoadRowsData(info.sheet, rows);
+                                    });
+                                } else if (checkChildren(node)) {
+                                    const rect = info.sheet.getCellRect(info.row, info.col);
+                                    gatherConfirmPopover.check({
+                                        x: rect.x + rect.width / 2 + 25,
+                                        y: rect.y + rect.height / 2 + 3,
+                                    }, '子项已勾选,继续将取消子项勾选。', function () {
+                                        node.check = true;
+                                        const posterity = gsTree.getPosterity(node);
+                                        const rows = [gsTree.nodes.indexOf(node)];
+                                        for (const p of posterity) {
+                                            if (p.check) {
+                                                rows.push(gsTree.nodes.indexOf(p));
+                                                p.check = false;
+                                            }
+                                        }
+                                        SpreadJsObj.reLoadRowsData(info.sheet, rows);
+                                    });
+                                } else {
+                                    node.check = true;
+                                    SpreadJsObj.reLoadRowsData(info.sheet, [gsTree.nodes.indexOf(node)]);
+                                }
+                            } else {
+                                if (node.pre_check) {
+                                    const rect = info.sheet.getCellRect(info.row, info.col);
+                                    gatherConfirmPopover.showTip({
+                                        x: rect.x + rect.width / 2 + 25,
+                                        y: rect.y + rect.height / 2 + 27,
+                                    }, '该项上期已勾选调用,无法去除勾选。');
+                                    SpreadJsObj.reLoadRowsData(info.sheet, [gsTree.nodes.indexOf(node)]);
+                                    return;
+                                }
+                                node.check = false;
+                                SpreadJsObj.reLoadRowsData(info.sheet, [gsTree.nodes.indexOf(node)]);
+                            }
+                        }
+                    });
+                    const pre_gatherNodes = pre_nodes ? _.map(pre_nodes.split(',')) : [];
+                    const gatherNodes = exponent_nodes ? _.map(exponent_nodes.split(',')) : [];
+                    for (const node of gsTree.datas) {
+                        node.check = gatherNodes.indexOf(node.id + '') !== -1;
+                        node.pre_check = pre_gatherNodes.indexOf(node.id + '') !== -1;
+                    }
+                    SpreadJsObj.loadSheetData(gsSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, gsTree);
+                    console.log(gsTree);
+                    gsTree.expandByLevel(4);
+                    SpreadJsObj.refreshTreeRowVisible(gsSpread.getActiveSheet());
+                    // SpreadJsObj.resetFieldReadOnly(self.gsSpread.getActiveSheet, 'check', !$('#im-gather-check')[0].checked);
+
+                    (function (select, sheet) {
+                        $(select).click(function () {
+                            if (!sheet.zh_tree) return;
+                            const tag = $(this).attr('tag');
+                            const tree = sheet.zh_tree;
+                            setTimeout(() => {
+                                showWaitingView();
+                                switch (tag) {
+                                    case "1":
+                                    case "2":
+                                    case "3":
+                                    case "4":
+                                    case "5":
+                                        tree.expandByLevel(parseInt(tag));
+                                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                                        break;
+                                    case "last":
+                                        tree.expandByCustom(() => { return true; });
+                                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                                        break;
+                                    case "leafXmj":
+                                        tree.expandToLeafXmj();
+                                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                                        break;
+                                }
+                                closeWaitingView();
+                            }, 100);
+                        });
+                    })('a[name=showLevel]', gsSpread.getActiveSheet());
+                });
+            } else {
+                const gatherNodes = exponent_nodes ? _.map(exponent_nodes.split(',')) : [];
+                const pre_gatherNodes = pre_nodes ? _.map(pre_nodes.split(',')) : [];
+                for (const node of gsTree.datas) {
+                    node.check = gatherNodes.indexOf(node.id + '') !== -1;
+                    node.pre_check = pre_gatherNodes.indexOf(node.id + '') !== -1;
+                }
+                SpreadJsObj.reLoadColsData(gsSpread.getActiveSheet(), [0]);
+            }
+        });
+        // 提交 高级设置
+        $('#choose2-ok').click(() => {
+            if (!gsTree) { return; }
+            const nodes = [];
+            for (const node of gsTree.nodes) {
+                if (node.check) {
+                    nodes.push(node);
+                }
+            }
+            // 和exponent_nodes对比新增和移除,新增的告诉后台,比得出最新标段(不含新增分项)这条数据的调差基数
+            const nodeData = [];
+            const tpData = {};
+            if (showStageExponent) {
+                for (const ms of materialStageData) {
+                    tpData[ms.sid + '_tp'] = {
+                        contract_tp: 0,
+                        qc_tp: 0,
+                        gather_tp: 0,
+                    };
+                }
+            } else {
+                tpData['all'] = {
+                    contract_tp: 0,
+                    qc_tp: 0,
+                    gather_tp: 0,
+                };
+            }
+            const gatherNodes = exponent_nodes ? _.map(exponent_nodes.split(',')) : [];
+            const pre_gatherNodes = pre_nodes ? _.map(pre_nodes.split(',')) : [];
+            const newNodes = _.map(nodes, 'id');
+            console.log(gatherNodes, newNodes);
+            if (_.isEqual(_.sortBy(gatherNodes), _.sortBy(newNodes))) {
+                toastr.warning('未做任何修改');
+                return;
+            }
+            // 比较nodes和gatherNodes得出新增和移除的
+            const delNodes = _.difference(gatherNodes, newNodes);
+            const addNodes = _.difference(newNodes, gatherNodes);
+            for (const node of nodes) {
+                const isAdd = addNodes.indexOf(node.id + '') !== -1;
+                if (showStageExponent) {
+                    for (const ms of materialStageData) {
+                        const key = ms.sid + '_tp';
+                        tpData[key].contract_tp = ZhCalc.add(tpData[key].contract_tp, node[ms.sid + '_contract_tp'] || 0);
+                        tpData[key].qc_tp = ZhCalc.add(tpData[key].qc_tp, node[ms.sid + '_qc_tp'] || 0);
+                        tpData[key].gather_tp = ZhCalc.add(tpData[key].gather_tp, node[ms.sid + '_gather_tp'] || 0);
+                        if (isAdd) {
+                            nodeData.push({
+                                code: node.code || node.b_code,
+                                node: node.id,
+                                name: node.name,
+                                ms_id: ms.id,
+                                contract_tp: node[ms.sid + '_contract_tp'] || 0,
+                                qc_tp: node[ms.sid + '_qc_tp'] || 0,
+                                gather_tp: node[ms.sid + '_gather_tp'] || 0,
+                                ex_calc: setExCalc(exponent_nodes ? null : _.find(ex_calcList, {ms_id: ms.id}).ex_calc, node[ms.sid + '_contract_tp'], node[ms.sid + '_qc_tp'], node[ms.sid + '_gather_tp']),
+                            });
+                        }
+                    }
+                } else {
+                    const key = 'all';
+                    tpData[key].contract_tp = ZhCalc.add(tpData[key].contract_tp, node.contract_tp || 0);
+                    tpData[key].qc_tp = ZhCalc.add(tpData[key].qc_tp, node.qc_tp || 0);
+                    tpData[key].gather_tp = ZhCalc.add(tpData[key].gather_tp, node.gather_tp || 0);
+                    if (isAdd) {
+                        nodeData.push({
+                            code: node.code || node.b_code,
+                            node: node.id,
+                            name: node.name,
+                            ms_id: null,
+                            contract_tp: node.contract_tp || 0,
+                            qc_tp: node.qc_tp || 0,
+                            gather_tp: node.gather_tp || 0,
+                            ex_calc: setExCalc(exponent_nodes ? null : ex_calc, node.contract_tp, node.qc_tp, node.gather_tp),
+                        });
+                    }
+                }
+            }
+            // 标段(不含新增分项)调差基数数据更新或新增
+            const baseNodes = _.filter(materialExponentNodeData, { name: '标段', node: '-1' });
+            // 总金额 - tpData 里金额就是不含新增分项金额,先获取总金额
+            const totalTp = {};
+            for (const node of gsTree.children) {
+                if (showStageExponent) {
+                    for (const ms of materialStageData) {
+                        const key = ms.sid + '_tp';
+                        if (!totalTp[key]) {
+                            totalTp[key] = {
+                                contract_tp: 0,
+                                qc_tp: 0,
+                                gather_tp: 0,
+                            };
+                        }
+                        totalTp[key].contract_tp = ZhCalc.add(totalTp[key].contract_tp, node[ms.sid + '_contract_tp'] || 0);
+                        totalTp[key].qc_tp = ZhCalc.add(totalTp[key].qc_tp, node[ms.sid + '_qc_tp'] || 0);
+                        totalTp[key].gather_tp = ZhCalc.add(totalTp[key].gather_tp, node[ms.sid + '_gather_tp'] || 0);
+                    }
+                } else {
+                    const key = 'all';
+                    if (!totalTp[key]) {
+                        totalTp[key] = {
+                            contract_tp: 0,
+                            qc_tp: 0,
+                            gather_tp: 0,
+                        };
+                    }
+                    totalTp[key].contract_tp = ZhCalc.add(totalTp[key].contract_tp, node.contract_tp || 0);
+                    totalTp[key].qc_tp = ZhCalc.add(totalTp[key].qc_tp, node.qc_tp || 0);
+                    totalTp[key].gather_tp = ZhCalc.add(totalTp[key].gather_tp, node.gather_tp || 0);
+                }
+            }
+            if (showStageExponent) {
+                for (const ms of materialStageData) {
+                    const node_tp = newNodes.length > 0 ? tpData[ms.sid + '_tp'] : { contract_tp: 0, qc_tp: 0, gather_tp: 0 };
+                    const total_tp = totalTp[ms.sid + '_tp'];
+                    const contract_tp = ZhCalc.sub(total_tp.contract_tp, node_tp.contract_tp);
+                    const qc_tp = ZhCalc.sub(total_tp.qc_tp, node_tp.qc_tp);
+                    const gather_tp = ZhCalc.sub(total_tp.gather_tp, node_tp.gather_tp);
+                    const baseSid = _.find(baseNodes, { ms_id: ms.id });
+                    if (baseSid) {
+                        let ex_calc = null;
+                        if (exponent_nodes) {
+                            ex_calc = _.find(ex_calcList, { ms_id: ms.id, mn_id: baseSid.id }).ex_calc;
+                        } else {
+                            ex_calc = _.find(ex_calcList, { ms_id: ms.id }).ex_calc;
+                        }
+                        baseSid.contract_tp = contract_tp;
+                        baseSid.qc_tp = qc_tp;
+                        baseSid.gather_tp = gather_tp;
+                        baseSid.ex_calc = setExCalc(ex_calc, contract_tp, qc_tp, gather_tp);
+                    } else {
+                        baseNodes.push({
+                            code: '不含新增分项',
+                            name: '标段',
+                            node: '-1',
+                            ms_id: ms.id,
+                            ex_calc: setExCalc(exponent_nodes ? null : _.find(ex_calcList, { ms_id: ms.id }).ex_calc, contract_tp, qc_tp, gather_tp),
+                            contract_tp,
+                            qc_tp,
+                            gather_tp,
+                        });
+                    }
+                }
+            } else {
+                const node_tp = newNodes.length > 0 ? tpData['all'] : { contract_tp: 0, qc_tp: 0, gather_tp: 0 };
+                const total_tp = totalTp['all'];
+                const contract_tp = ZhCalc.sub(total_tp.contract_tp, node_tp.contract_tp);
+                const qc_tp = ZhCalc.sub(total_tp.qc_tp, node_tp.qc_tp);
+                const gather_tp = ZhCalc.sub(total_tp.gather_tp, node_tp.gather_tp);
+                if (baseNodes.length > 0) {
+                    baseNodes[0].contract_tp = contract_tp;
+                    baseNodes[0].qc_tp = qc_tp;
+                    baseNodes[0].gather_tp = gather_tp;
+                    baseNodes[0].ex_calc = setExCalc(baseNodes[0].ex_calc, contract_tp, qc_tp, gather_tp);
+                } else {
+                    baseNodes.push({
+                        code: '不含新增分项',
+                        name: '标段',
+                        node: '-1',
+                        ms_id: null,
+                        ex_calc: setExCalc(ex_calc, contract_tp, qc_tp, gather_tp),
+                        contract_tp,
+                        qc_tp,
+                        gather_tp,
+                    });
+                }
+            }
+            console.log(baseNodes, nodeData);
+            postData(window.location.pathname + '/save', { type:'update_node', updateData: { baseNodes, addNodes: nodeData, delNodes, exponent_node: newNodes } }, function (result) {
+                window.location.reload();
+            });
+        });
+
+        function setExCalc(old_ex_calc, contract_tp, qc_tp, gather_tp) {
+            const insert_ex_calc = [
+                {
+                    code: 'bqht',
+                    text: '所选分项合同计量金额',
+                    value: contract_tp || 0,
+                    select: false,
+                },
+                {
+                    code: 'bqbg',
+                    text: '所选分项变更计量金额',
+                    value: qc_tp || 0,
+                    select: false,
+                },
+                {
+                    code: 'bqwc',
+                    text: '所选分项完成计量金额',
+                    value: gather_tp || 0,
+                    select: true,
+                },
+                {
+                    code: 'zdy',
+                    text: '自定义金额',
+                    value: gather_tp || 0,
+                    result: '',
+                    select: false,
+                },
+            ];
+            const gatherNodes = exponent_nodes ? _.map(exponent_nodes.split(',')) : [];
+            if (gatherNodes.length !== 0) {
+                // 判断是否更新new_ex_calc的select为true值
+                const selectItem = _.find(old_ex_calc, { select: true });
+                if (selectItem) {
+                    const set_selectItem = _.find(insert_ex_calc, { code: selectItem.code });
+                    if (set_selectItem && !set_selectItem.select) {
+                        const oldSelectItem = _.find(insert_ex_calc, { select: true });
+                        if (oldSelectItem) {
+                            oldSelectItem.select = false;
+                        }
+                        set_selectItem.select = true;
+                    }
+                }
+                // 自定义金额要赋值
+                if (old_ex_calc) {
+                    const zdyItem = _.find(old_ex_calc, { code: 'zdy' });
+                    if (zdyItem) {
+                        const insertZdyItem = _.find(insert_ex_calc, { code: 'zdy' });
+                        if (zdyItem.result) {
+                            insertZdyItem.result = zdyItem.result;
+                            insertZdyItem.value = zdyItem.value;
+                        }
+                    }
+                }
+            }
+            return insert_ex_calc;
+        }
+    });
+</script>
+<% } %>
 <% include ./audit_modal.ejs %>
+

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

@@ -84,13 +84,13 @@
                             <% if ((openMaterialTax && !allMaterialTax) || !openMaterialTax) { %><td class="text-right"><% if (!m.material_tax) { %><%- m.rate_tp %><% } %></td><% } %>
                             <% } %>
                             <% if (materialColShow[1].checked) { %>
-                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(m.ex_tp, m.decimal.tp) : null %></td>
-                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %></td>
+                            <td class="text-right"><%= m.ex_tp !== null ? m.ex_tp : null %></td>
+                            <td class="text-right"><%= m.ex_tax_tp !== null ? m.ex_tax_tp : null %></td>
                             <% } %>
                             <% if (materialColShow[2].checked) { %>
-                            <td class="text-right"><%= ctx.helper.add(ctx.helper.round(m.ex_tp, m.decimal.tp), ctx.helper.round(m.m_tp, m.decimal.tp)) %></td>
+                            <td class="text-right"><%= ctx.helper.add(m.ex_tp, ctx.helper.round(m.m_tp, m.decimal.tp)) %></td>
                             <% if (openMaterialTax) { %><td class="text-right"><% if (m.material_tax) { %><% if (m.m_tax_tp) { %><%- m.m_tax_tp %><% } else { %><%- m.m_tp %><% } %><% } %></td><% } %>
-                            <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %><% } else { %><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp), m.rate_tp) %><% } %></td>
+                            <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tax_tp !== null ? m.ex_tax_tp : null %><% } else { %><%= ctx.helper.add(m.ex_tax_tp, m.rate_tp) %><% } %></td>
                             <% } %>
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
                                 <% if (m.status === auditConst.status.checked && m.final_auditor_str) { %>

+ 4 - 23
app/view/material/info.ejs

@@ -215,48 +215,32 @@
                                     <div class="sp-wrap" style="overflow: auto;">
                                         <% if (!material.material_tax && !old_had_tax) { %>
                                             <table class="table table-sm table-bordered">
-                                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
-                                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th>
-                                                    <th>本期金额
-                                                        <a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></th>
-                                                    <th>截止本期金额</th></tr>
+                                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th></tr>
+                                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th></tr>
                                                 <tr id="tp_set"><td>材料价差费用</td>
                                                     <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, material.decimal.tp) : null %></td>
                                                     <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.add(material.pre_tp, ctx.helper.round(material.m_tp, material.decimal.tp)) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.decimal.tp) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.add(material.ex_pre_tp, ctx.helper.round(material.ex_tp, material.decimal.tp)) : null %></td>
                                                 </tr>
                                                 <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
                                                     <td class="text-center"><%= material.m_tp !== null ? material.rate_tp : null %></td>
                                                     <td class="text-center"><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.add(pre_tp_hs, material.rate_tp) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)) : null %></td>
                                                 </tr>
                                             </table>
                                         <% } else { %>
                                             <table class="table table-sm table-bordered">
-                                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
-                                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th>
-                                                    <th>本期金额
-                                                        <a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></th>
-                                                    <th>截止本期金额</th></tr>
+                                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th></tr>
+                                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th></tr>
                                                 <tr id="tp_set"><td>材料价差费用</td>
                                                     <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, material.decimal.tp) : null %></td>
                                                     <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.add(material.pre_tp, ctx.helper.round(material.m_tp, material.decimal.tp)) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, material.decimal.tp) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.add(material.ex_pre_tp, ctx.helper.round(material.ex_tp, material.decimal.tp)) : null %></td>
                                                 </tr>
                                                 <tr id="tax_rate_set"><td>材料价差费用(含材料税)</td>
                                                     <td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null ? material.m_tax_tp : null) : '-' %></td>
                                                     <td class="text-center"><%= material.material_tax ? (material.m_tax_tp !== null || material.m_tax_pre_tp !== null ? ctx.helper.add(material.m_tax_pre_tp, material.m_tax_tp) : null) : material.m_tax_pre_tp %></td>
-                                                    <td class="text-center">-</td>
-                                                    <td class="text-center">-</td>
                                                 </tr>
                                                 <tr id="rate_set"><td>材料价差费用(含建筑税)</td>
                                                     <td class="text-center"><%= !material.material_tax ? material.rate_tp : '-' %></td>
                                                     <td class="text-center"><%= !material.material_tax ? (material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.add(pre_tp_hs, material.rate_tp) : null) : pre_tp_hs !== null ? pre_tp_hs : '-' %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp) : null %></td>
-                                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.exponent_rate/100), material.decimal.tp)) : null %></td>
                                                 </tr>
                                             </table>
                                         <% } %>
@@ -346,11 +330,8 @@
     const qtySourceValueConst = JSON.parse(unescape('<%- escape(JSON.stringify(qtySourceValueConst)) %>'));
     const materialDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.decimal)) %>'));
     let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
-    let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;
     const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
-    const ex_pre_tp = <%= material.ex_pre_tp !== null ? material.ex_pre_tp : 0 %>;
     const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
-    const ex_pre_tp_hs = <%= ex_pre_tp_hs !== null ? ex_pre_tp_hs : 0 %>;
     let m_tax_tp = <%= material.m_tax_tp !== null ? material.m_tax_tp : 0 %>;
     const m_tax_pre_tp = <%= material.m_tax_pre_tp !== null ? material.m_tax_pre_tp : 0 %>;
     let calcBase = JSON.parse(unescape('<%- escape(JSON.stringify(calcBase)) %>'));

+ 1 - 0
config/web.js

@@ -807,6 +807,7 @@ const JsFiles = {
                     '/public/js/div_resizer.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/zh_calc.js',
+                    '/public/js/shares/sjs_setting.js',
                     '/public/js/path_tree.js',
                     '/public/js/material_exponent.js',
                     '/public/js/shares/cs_tools.js',

+ 56 - 0
db_script/material_ex_tax_tp.js

@@ -0,0 +1,56 @@
+// 计算调差建筑税金额
+
+const materialConst = require('../app/const/material');
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+const ZhCalc = BaseUtil.ZhCalc;
+
+const checkMaterial = async function(material, ex_tax_tp, ex_tax_pre_tp) {
+    // if (!material.is_new_exponent) {
+    await querySql('Update zh_material Set ex_tax_tp = ?, ex_tax_pre_tp = ? Where id = ?', [ex_tax_tp, ex_tax_pre_tp, material.id]);
+    // }
+    console.log(`Update Material ${material.id}`);
+};
+
+const doComplete = async function() {
+    try {
+        const tender = await querySql('Select * From zh_tender');
+        for (const t of tender) {
+            console.log(`Update Tender ${t.id}:`);
+
+            const materials = await querySql('Select * From zh_material where tid = ? order by id asc', [t.id]);
+            let total_ex_tax_tp = 0;
+            for (const m of materials) {
+                m.decimal = m.decimal ? JSON.parse(m.decimal) : materialConst.decimal;
+                const ex_tax_tp = !m.is_new_exponent ? ZhCalc.round(ZhCalc.mul(m.ex_tp, 1 + m.exponent_rate / 100), m.decimal.tp) : m.ex_tax_tp;
+                await checkMaterial(m, ex_tax_tp, total_ex_tax_tp);
+                total_ex_tax_tp = ZhCalc.add(total_ex_tax_tp, ex_tax_tp);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+const doCompleteTest = async function(tid) {
+    try {
+        const tender = await querySql('Select * From zh_tender where id = ?', [tid]);
+        for (const t of tender) {
+            console.log(`Update Tender ${t.id}:`);
+            const materials = await querySql('Select * From zh_material where tid = ?', [t.id]);
+            for (const m of materials) {
+                await checkMaterial(m);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+
+const tenderId = process.argv[3];
+if (tenderId) {
+    doCompleteTest(tenderId);
+} else {
+    doComplete();
+}

+ 33 - 0
sql/update.sql

@@ -6,6 +6,39 @@
 ------------------------------------
 -- 表结构
 ------------------------------------
+ALTER TABLE `zh_material`
+ADD COLUMN `is_new_exponent` tinyint(1) NULL DEFAULT 0 COMMENT '是否是新建的调差期,用于区分新指数法调差规则' AFTER `is_stage_self`,
+ADD COLUMN `ex_tax_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '指数本期建筑税金额' AFTER `ex_pre_tp`,
+ADD COLUMN `ex_tax_pre_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '指数截止上期建筑税金额' AFTER `ex_tax_tp`,
+ADD COLUMN `exponent_decimal` varchar(255) NULL DEFAULT NULL COMMENT '指数小数位数设置JSON' AFTER `decimal`,
+ADD COLUMN `exponent_node` text NULL COMMENT '本期分项价格指数节点' AFTER `calc_tp`,
+ADD COLUMN `pre_exponent_node` text NULL COMMENT '截止上期分项价格指数节点,用于禁止移除节点' AFTER `exponent_node`;
+
+ALTER TABLE `zh_material_stage`
+ADD COLUMN `ex_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '指数本期金额' AFTER `m_tax_tp`,
+ADD COLUMN `ex_tax_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '指数本期建筑税金额' AFTER `ex_tp`,
+ADD COLUMN `ex_expr` varchar(5000) NULL DEFAULT NULL COMMENT '指数调差公式' AFTER `ex_tax_tp`,
+ADD COLUMN `ex_calc` varchar(1000) NULL DEFAULT NULL COMMENT '调差基数值json' AFTER `ex_expr`;
+
+CREATE TABLE `zh_material_exponent_node`  (
+  `id` int NOT NULL AUTO_INCREMENT,
+  `tid` int NOT NULL COMMENT '标段id',
+  `mid` int NOT NULL COMMENT '调差id',
+  `ms_id` int NULL DEFAULT NULL COMMENT '调差多期单独计价期id(可以为空)',
+  `node` varchar(255) NOT NULL COMMENT '分项节点id',
+  `code` varchar(255) NULL DEFAULT '' COMMENT '节点编号',
+  `name` varchar(255) NULL DEFAULT '' COMMENT '节点名称',
+  `ex_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '节点指数本期金额',
+  `ex_pre_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '截止上期节点指数金额',
+  `ex_tax_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '节点指数含税金额',
+  `ex_tax_pre_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '截止上期节点指数含税金额',
+  `ex_expr` varchar(5000) NULL DEFAULT NULL COMMENT '指数调差公式(非独立期时用)',
+  `ex_calc` varchar(1000) NULL DEFAULT NULL COMMENT '调差基数值json(非独立期时用)',
+  `contract_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '计量期该节点汇总合同计量金额(方便统计)',
+  `qc_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '计量期该节点汇总变更计量金额(方便统计)',
+  `gather_tp` decimal(30, 8) NULL DEFAULT NULL COMMENT '计量期该节点汇总完成计量金额(方便统计)',
+  PRIMARY KEY (`id`)
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '调差指数分项价格指数表';
 
 ALTER TABLE `zh_sub_project_info`
 ADD COLUMN `lx_tp_unit` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '元' COMMENT '立项-金额单位' AFTER `lx_tp`,