浏览代码

调差月信息价功能

laiguoran 5 年之前
父节点
当前提交
ebdfc67bb1

+ 106 - 8
app/controller/material_controller.js

@@ -219,6 +219,52 @@ module.exports = app => {
         }
 
         /**
+         * 获取当前期工料列表信息
+         * @param ctx
+         * @return {Promise<void>}
+         * @private
+         */
+        async _getMaterialBillsData(ctx) {
+            // 根据期判断需要获取的工料信息值
+            const searchsql = { tid: ctx.tender.id };
+            if (ctx.material.highOrder !== ctx.material.order) {
+                const midList = await ctx.service.material.getPreMidList(ctx.tender.id, ctx.material.order);
+                searchsql.mid = midList;
+            }
+            // 取所有工料表
+            return await ctx.service.materialBills.getAllDataByCondition({ where: searchsql });
+        }
+
+        /**
+         * 获取当前月信息价列表信息
+         * @param ctx
+         * @return {Promise<void>}
+         * @private
+         */
+        async _getMaterialMonthsData(ctx, materialBillsData) {
+            // 取月信息价表
+            const monthsList = [];
+            if (ctx.material.months) {
+                const material_month = ctx.material.months.split(',');
+                const materialMonthList = await ctx.service.materialMonth.getListByMid(ctx.material.id);
+                for (const mbd of materialBillsData) {
+                    const one_mb = {
+                        code: mbd.code,
+                        name: mbd.name,
+                        unit: mbd.unit,
+                        mb_id: mbd.id,
+                    };
+                    for (const m of material_month) {
+                        const one_mm = _.find(materialMonthList, { mb_id: mbd.id, yearmonth: m });
+                        one_mb[m] = one_mm.msg_tp;
+                    }
+                    monthsList.push(one_mb);
+                }
+            }
+            return monthsList;
+        }
+
+        /**
          * 调差工料页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @return {Promise<void>}
@@ -227,14 +273,7 @@ module.exports = app => {
             try {
                 await this._getMaterialAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
-                // 根据期判断需要获取的工料信息值
-                const searchsql = { tid: ctx.tender.id };
-                if (ctx.material.highOrder !== ctx.material.order) {
-                    const midList = await ctx.service.material.getPreMidList(ctx.tender.id, ctx.material.order);
-                    searchsql.mid = midList;
-                }
-                // 取所有工料表
-                renderData.materialBillsData = await ctx.service.materialBills.getAllDataByCondition({ where: searchsql });
+                renderData.materialBillsData = await this._getMaterialBillsData(ctx);
                 // 取对应期的截取上期的调差金额和应耗数量
                 if (ctx.material.highOrder !== ctx.material.order) {
                     for (const [mindex, mb] of renderData.materialBillsData.entries()) {
@@ -270,6 +309,9 @@ module.exports = app => {
                 // 取当前期截止上期含税金额
                 renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order);
 
+                renderData.months = ctx.material.months ? ctx.material.months.split(',') : [];
+                renderData.monthsList = await this._getMaterialMonthsData(ctx, renderData.materialBillsData);
+
                 renderData.materialType = JSON.stringify(materialConst);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.info);
                 await this.layout('material/info.ejs', renderData, 'material/info_modal.ejs');
@@ -475,6 +517,62 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 调差工料 - 编辑月信息价 (Ajax)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveMonth(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {
+                    err: 0,
+                    msg: '',
+                    data: {},
+                };
+                const monthList = await ctx.service.materialMonth.getListByMid(ctx.material.id);
+                const mbList = await this._getMaterialBillsData(ctx);
+                switch (data.type) {
+                    case 'add':
+                        const tp = await ctx.service.materialMonth.add(data.updateData, monthList, mbList);
+                        const materialBillsData = await this._getMaterialBillsData(ctx);
+                        const monthsList = await this._getMaterialMonthsData(ctx, materialBillsData);
+                        responseData.data = {
+                            m_tp: tp,
+                            monthsList,
+                        };
+                        break;
+                    case 'del':
+                        const tp2 = await ctx.service.materialMonth.del(data.updateData.del_yearmonth, monthList, mbList);
+                        const materialBillsData2 = await this._getMaterialBillsData(ctx);
+                        const monthsList2 = await this._getMaterialMonthsData(ctx, materialBillsData2);
+                        responseData.data = {
+                            m_tp: tp2,
+                            monthsList: monthsList2,
+                        };
+                        break;
+                    case 'update':
+                        const tp3 = await ctx.service.materialMonth.save(data.updateData);
+                        responseData.data = {
+                            m_tp: tp3,
+                        };
+                        break;
+                    case 'paste':
+                        const tp4 = await ctx.service.materialMonth.saveDatas(data.updateData, mbList);
+                        responseData.data = {
+                            m_tp: tp4,
+                        };
+                        break;
+                    default: throw '参数有误';
+                }
+                responseData.data.materialBillsData = await this._getMaterialBillsData(ctx);
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         // 审批相关
         /**
          * 添加审批人

+ 301 - 9
app/public/js/material.js

@@ -94,6 +94,24 @@ const is_numeric = (value) => {
         return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
     }
 };
+
+function setMonthHtml() {
+    let html = '';
+    let qihtml = '';
+    for (const m of months) {
+        html += '<div class="custom-control custom-checkbox mb-2">\n' +
+            '                            <input type="checkbox" name="del_month" value="' + m + '" class="custom-control-input" id="month_' + m + '">\n' +
+            '                            <label class="custom-control-label" for="month_' + m + '">' + m + '月</label>\n' +
+            '                        </div>';
+        qihtml += parseInt(m.split('-')[1]) + '月,';
+    }
+    if (months.length > 0) {
+        qihtml = '<span class="mx-2 text-muted">/</span>本期月信息价:' + qihtml;
+        qihtml = qihtml.substring(0, qihtml.length-1);
+    }
+    $('#show_month').html(html);
+    $('#qi-month').html(qihtml);
+}
 $(document).ready(() => {
     autoFlashHeight();
     const materialSpread = SpreadJsObj.createNewSpread($('#material-spread')[0]);
@@ -108,7 +126,7 @@ $(document).ready(() => {
             {title: '本期应耗数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '基准价', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '本期信息价|单价', colSpan: '3|1', rowSpan: '1|1', field: 'msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.remark'},
+            {title: '本期信息价|单价', colSpan: '3|1', rowSpan: '1|1', field: 'msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.msg_tp'},
             {title: '|时间', colSpan: '|1', rowSpan: '|1', field: 'msg_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.remark'},
             {title: '|价差', colSpan: '1', rowSpan: '1|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.msg_spread'},
             {title: '本期材料调差|上涨幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 100, type: 'Number', readOnly: 'readOnly.isEdit'},
@@ -202,6 +220,9 @@ $(document).ready(() => {
             remark: function () {
                 return readOnly;
             },
+            msg_tp: function () {
+                return !(!readOnly && months.length === 0);
+            }
         },
     };
     SpreadJsObj.initSpreadSettingEvents(materialSpreadSetting, materialCol);
@@ -209,7 +230,7 @@ $(document).ready(() => {
     SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
 
     const materialSpreadObj = {
-        refreshActn: function () {
+        refreshActn: function (rowCount = 1) {
             const setObjEnable = function (obj, enable) {
                 if (enable) {
                     obj.removeClass('disabled');
@@ -220,7 +241,7 @@ $(document).ready(() => {
             const sheet = materialSpread.getActiveSheet();
             const select = SpreadJsObj.getSelectObject(sheet);
             // 还需判断是否已被调差清单调用
-            setObjEnable($('#del'), !readOnly && select && materialBase.isUsed(select));
+            setObjEnable($('#del'), !readOnly && select && materialBase.isUsed(select) && rowCount === 1);
         },
         add: function () {
             const sheet = materialSpread.getActiveSheet();
@@ -231,6 +252,16 @@ $(document).ready(() => {
                     SpreadJsObj.reLoadRowData(sheet, materialBillsData.length - 1);
                     sheet.setSelection(materialBillsData.length - 1, 0, 1, 1);
                     materialSpreadObj.refreshActn();
+                    // 月信息价需要同时添加空白的list
+                    if (months.length > 0) {
+                        const one_month ={ mb_id: result.id, code: '', name: null, unit: null, };
+                        for (const m of months) {
+                            one_month[m] = null;
+                        }
+                        monthsList.push(one_month);
+                        materialMonthSpread.getActiveSheet().addRows(monthsList.length - 1, 1);
+                        SpreadJsObj.reLoadRowData(materialMonthSpread.getActiveSheet(), monthsList.length - 1);
+                    }
                 }
             });
         },
@@ -243,16 +274,22 @@ $(document).ready(() => {
                 const index = materialBillsData.indexOf(select);
                 materialBillsData.splice(index, 1);
                 sheet.deleteRows(index, 1);
-                SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
+                // SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
                 const sel = sheet.getSelections();
                 sheet.setSelection(index > 0 ? index - 1 : 0, sel.length > 0 ? sel[0].col : 0, 1, 1);
                 materialSpreadObj.refreshActn();
+                // 月信息价需要同时删除
+                if (months.length > 0) {
+                    monthsList.splice(index, 1);
+                    materialMonthSpread.getActiveSheet().deleteRows(index, 1);
+                    // SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                }
             });
         },
         selectionChanged: function (e, info) {
-            materialSpreadObj.refreshActn();
             const sel = info.sheet.getSelections()[0];
             const col = info.sheet.zh_setting.cols[sel.col];
+            materialSpreadObj.refreshActn(sel.rowCount);
             const data = SpreadJsObj.getSelectObject(info.sheet);
             materialSpreadObj.setReadOnly(true);
         },
@@ -341,6 +378,11 @@ $(document).ready(() => {
                     m_tp = result.m_tp;
                     resetTpTable();
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    // 判断如果是更改了编号,名称,单位,月信息价需要跟着改变值
+                    if (months.length > 0 && (col.field === 'code' || col.field === 'name' || col.field === 'unit')) {
+                        monthsList[info.row][col.field] = validText;
+                        SpreadJsObj.reLoadRowData(materialMonthSpread.getActiveSheet(), info.row);
+                    }
                 }, function () {
                     select[col.field] = orgValue;
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -465,11 +507,19 @@ $(document).ready(() => {
             // 更新至服务器
             postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
                 materialBillsData = result.info;
-                SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
                 // for (const row in rowData) {
                 //     materialBillsData.splice(index, 1, result.info[row]);
                 //     SpreadJsObj.reLoadRowData(info.sheet, row);
                 // }
+                if (months.length > 0) {
+                    for (const [i,m] of monthsList.entries()) {
+                        m.code = materialBillsData[i].code;
+                        m.name = materialBillsData[i].name;
+                        m.unit = materialBillsData[i].unit;
+                    }
+                    SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                }
                 m_tp = result.m_tp;
                 resetTpTable();
             }, function () {
@@ -495,10 +545,182 @@ $(document).ready(() => {
     // msg_range.formatter("yyyy-MM-dd");
     sheet.resumePaint();
 
+    const materialMonthSpread = SpreadJsObj.createNewSpread($('#material-month-spread')[0]);
+    const materialMonthSpreadSetting = {
+        cols: [
+            {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '平均单价', colSpan: '1', rowSpan: '2', field: 'average_msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue:'getValue.average_msg_tp'},
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: readOnly,
+    };
+    if (months.length > 0) {
+        for (const m of months) {
+            const month = parseInt(m.split('-')[1]);
+            const newCols = {title: month + '月|单价', colSpan: '1|1', rowSpan: '1|1', field: m, hAlign: 2, width: 60, type: '@', readOnly: 'readOnly.isEdit'};
+            materialMonthSpreadSetting.cols.push(newCols);
+        }
+    }
+
+    const materialMonthCol = {
+        getValue: {
+            average_msg_tp: function (data) {
+                let msg_tp = 0;
+                for (const m of months) {
+                    msg_tp += data[m];
+                }
+                const average_tp = ZhCalc.round(ZhCalc.div(msg_tp, months.length), 3);
+                return average_tp;
+            },
+        },
+        readOnly: {
+            isEdit: function (data) {
+                return readOnly;
+            },
+        },
+    };
+
+    SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
+    SpreadJsObj.initSheet(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
+    SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
+
+    const materialMonthSpreadObj = {
+        editEnded: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                // 未改变值则不提交
+                const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                const orgValue = select[col.field];
+                if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                // 判断部分值是否输入的是数字判断和数据计算
+                if (isNaN(validText)) {
+                    toastr.error('不能输入其它非数字类型字符');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                const num = parseFloat(validText);
+                if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                    toastr.error('请输入大于0并且小于3位小数的浮点数');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                select[col.field] = validText;
+
+                // 更新至服务器
+                postData(window.location.pathname + '/month/save', { type:'update', updateData: { mb_id: select.mb_id, yearmonth: col.field, value: validText } }, function (result) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    materialBillsData = result.materialBillsData;
+                    SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                    m_tp = result.m_tp;
+                    resetTpTable();
+                }, function () {
+                    select[col.field] = orgValue;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            }
+        },
+        deletePress: function (sheet) {
+            return;
+        },
+        clipboardPasted(e, info) {
+            const hint = {
+                cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
+                numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
+            };
+            const range = info.cellRange;
+            const sortData = info.sheet.zh_data || [];
+            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                return;
+            }
+            if (sortData.length > 0 && range.col + range.colCount > 4 + months.length) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                return;
+            }
+            const data = [];
+            for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                let bPaste = true;
+                const curRow = range.row + iRow;
+                const materialMonthData = sortData[curRow];
+                const hintRow = range.rowCount > 1 ? curRow : '';
+                let sameCol = 0;
+                for (let iCol = 0; iCol < range.colCount; iCol++) {
+                    const curCol = range.col + iCol;
+                    const colSetting = info.sheet.zh_setting.cols[curCol];
+                    if (!colSetting) continue;
+
+                    let validText = info.sheet.getText(curRow, curCol);
+                    validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                    const orgValue = sortData[curRow][colSetting.field];
+                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                        sameCol++;
+                        if (range.colCount === sameCol)  {
+                            bPaste = false;
+                        }
+                        continue;
+                    }
+                    const num = parseFloat(validText);
+                    if (isNaN(validText)) {
+                        toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                        bPaste = false;
+                        continue;
+                    }
+                    if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                        toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
+                        bPaste = false;
+                        continue;
+                    }
+                    materialMonthData[colSetting.field] = validText;
+                    sortData[curRow][colSetting.field] = validText;
+                }
+                if (bPaste) {
+                    data.push(materialMonthData);
+                } else {
+                    SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                }
+            }
+            if (data.length === 0) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            // 更新至服务器
+            postData(window.location.pathname + '/month/save', { type:'paste', updateData: data }, function (result) {
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                materialBillsData = result.materialBillsData;
+                SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                m_tp = result.m_tp;
+                resetTpTable();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            });
+        },
+    };
+
+    materialMonthSpread.bind(spreadNS.Events.ClipboardPasted, materialMonthSpreadObj.clipboardPasted);
+    SpreadJsObj.addDeleteBind(materialMonthSpread, materialMonthSpreadObj.deletePress);
+
     if (!readOnly) {
         $('#add').click(materialSpreadObj.add);
         $('#del').click(materialSpreadObj.del);
         materialSpread.bind(spreadNS.Events.EditEnded, materialSpreadObj.editEnded);
+        materialMonthSpread.bind(spreadNS.Events.EditEnded, materialMonthSpreadObj.editEnded);
         // 右键菜单
         $.contextMenu({
             selector: '#material-spread',
@@ -523,8 +745,9 @@ $(document).ready(() => {
                     disabled: function (key, opt) {
                         const sheet = materialSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
-                        materialSpreadObj.refreshActn();
-                        if (!readOnly && select && materialBase.isUsed(select)) {
+                        const sel = sheet.getSelections()[0];
+                        materialSpreadObj.refreshActn(sel.rowCount);
+                        if (!readOnly && select && materialBase.isUsed(select) && sel.rowCount === 1) {
                             return false;
                         } else {
                             return true;
@@ -697,7 +920,76 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadRowData(sheet, index);
                 $('#bcyy').modal('hide');
             });
-        })
+        });
+
+        // 创建月信息价
+        $('#make-month').click(function() {
+            const yearmonth = $('#months').val();
+            const result = yearmonth.match(/^(\d{1,4})(-|\/)(\d{1,2})$/);
+            if (result === null) {
+                toastr.error('请选择正确的信息价月份');
+                return false;
+            }
+            //判断是否已存在当前月份
+            if (months.indexOf(yearmonth) !== -1) {
+                toastr.error('调差期已创建过本月的信息价');
+                return false;
+            }
+            postData(window.location.pathname + '/month/save', { type: 'add', updateData: { yearmonth: yearmonth } }, function (data) {
+                const month = parseInt(yearmonth.split('-')[1]);
+                const newCols = {title: month + '月|单价', colSpan: '1|1', rowSpan: '1|1', field: yearmonth, hAlign: 2, width: 60, type: '@', readOnly: 'readOnly.isEdit'};
+                materialMonthSpreadSetting.cols.push(newCols);
+                months.push(yearmonth);
+                monthsList = data.monthsList;
+                SpreadJsObj.reinitSheetHeader(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
+                SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
+                // SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
+                setMonthHtml();
+
+                // 工料表单价显示也要更新
+                materialBillsData = data.materialBillsData;
+                SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                m_tp = data.m_tp;
+                resetTpTable();
+                $('#add-month').modal('hide');
+            });
+        });
+        // 删除月信息价
+        $('#del-month').click(function () {
+            const del_month_array = [];
+            $("input[name='del_month']:checked").each(function () {
+                del_month_array.push($(this).val());
+            });
+            if (del_month_array.length === 0) {
+                toastr.error('请选择要移除的月信息价');
+                return false;
+            }
+            postData(window.location.pathname + '/month/save', { type: 'del', updateData: { del_yearmonth: del_month_array } }, function (data) {
+                for (const dm of del_month_array) {
+                    _.remove(materialMonthSpreadSetting.cols, function (n) {
+                        return n.field === dm;
+                    });
+                    _.remove(months, function (n) {
+                        return n === dm;
+                    });
+                }
+                monthsList = data.monthsList;
+                console.log(monthsList);
+                SpreadJsObj.reinitSheetHeader(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
+                SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
+                SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
+                setMonthHtml();
+
+                // 工料表单价显示也要更新
+                materialBillsData = data.materialBillsData;
+                console.log(materialBillsData);
+                SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                m_tp = data.m_tp;
+                resetTpTable();
+                $('#remove-month').modal('hide');
+            });
+        });
     } else {
         // SpreadJsObj.forbiddenSpreadContextMenu('#material-spread', materialSpread);
     }

+ 2 - 0
app/router.js

@@ -327,6 +327,8 @@ module.exports = app => {
     // 调差工料
     app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, materialCheck, 'materialController.info');
     app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveBillsData');
+    // 月信息价
+    app.post('/tender/:id/measure/material/:order/month/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveMonth');
     // 调差清单
     app.get('/tender/:id/measure/material/:order/list', sessionAuth, tenderCheck, materialCheck, 'materialController.list');
     app.post('/tender/:id/measure/material/:order/list/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveListsData');

+ 38 - 14
app/service/material_bills.js

@@ -33,18 +33,38 @@ module.exports = app => {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
-            const newBills = {
-                tid: this.ctx.tender.id,
-                mid: this.ctx.material.id,
-                in_time: new Date(),
-            };
-
-            // 新增工料
-            const result = await this.db.insert(this.tableName, newBills);
-            if (result.affectedRows !== 1) {
-                throw '新增工料数据失败';
+            const transaction = await this.db.beginTransaction();
+            try {
+                const newBills = {
+                    tid: this.ctx.tender.id,
+                    mid: this.ctx.material.id,
+                    in_time: new Date(),
+                };
+                // 新增工料
+                const result = await transaction.insert(this.tableName, newBills);
+                if (result.affectedRows !== 1) {
+                    throw '新增工料数据失败';
+                }
+                const insertArray = [];
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                for (const ym of material_month) {
+                    const one_month = {
+                        tid: this.ctx.tender.id,
+                        mid: this.ctx.material.id,
+                        mb_id: result.insertId,
+                        msg_tp: null,
+                        yearmonth: ym,
+                    };
+                    insertArray.push(one_month);
+                }
+                if (insertArray.length !== 0) await transaction.insert(this.ctx.service.materialMonth.tableName, insertArray);
+                await transaction.commit();
+                return await this.getDataById(result.insertId);
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                throw error;
             }
-            return await this.getDataById(result.insertId);
         }
 
         /**
@@ -66,6 +86,10 @@ module.exports = app => {
                     // 金额发生变化,则重新计算本期金额
                     m_tp = await this.calcMaterialMTp(transaction);
                 }
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                if (material_month.length > 0) {
+                    await transaction.delete(this.ctx.service.materialMonth.tableName, { mb_id: id });
+                }
                 await transaction.commit();
                 return m_tp;
             } catch (err) {
@@ -155,7 +179,7 @@ module.exports = app => {
          * @returns {Promise<*>}
          */
         async calcQuantityByMB(transaction, mid, mb, materialCalculator) {
-            const [newmsg_spread, newm_spread] = await this.getSpread(mb);
+            const [newmsg_spread, newm_spread] = await this.getSpread(mb, null);
             if (mb.t_type === materialConst.t_type[0].value) {
                 const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.ctx.service.materialList.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `is_join`=1';
                 const sqlParam = [mid, mb.id];
@@ -194,8 +218,8 @@ module.exports = app => {
          * @param data
          * @returns {Promise<void>}
          */
-        async getSpread(data) {
-            data.msg_tp = null;
+        async getSpread(data, msg_tp) {
+            data.msg_tp = msg_tp;
             const msg_spread = this.ctx.helper.round(this.ctx.helper.sub(data.msg_tp, data.basic_price), 3);
             const cor = msg_spread >= 0 ? this.ctx.helper.mul(data.basic_price, this.ctx.helper.div(data.m_up_risk, 100)) : this.ctx.helper.mul(data.basic_price, this.ctx.helper.div(data.m_down_risk, 100));
             const m_spread = Math.abs(msg_spread) > Math.abs(cor) ? (msg_spread > 0 ? this.ctx.helper.round(this.ctx.helper.sub(msg_spread, cor), 3) : this.ctx.helper.round(this.ctx.helper.add(msg_spread, cor), 3)) : 0;

+ 224 - 0
app/service/material_month.js

@@ -0,0 +1,224 @@
+'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');
+
+module.exports = app => {
+    class MaterialMonth extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_month';
+        }
+
+        /**
+         * 返回月信息价列表
+         * @return {void}
+         */
+        async getListByMid(mid) {
+            return await this.getAllDataByCondition({ where: { mid } });
+        }
+
+        /**
+         * 添加月信息价 并更新 工料平均单价、本期单价,调差金额等
+         * @return {void}
+         */
+        async add(data, monthList, mbList) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                material_month.push(data.yearmonth);
+                if (mbList.length !== 0) {
+                    const insertArray = [];
+                    const updateArray = [];
+                    for (const mb of mbList) {
+                        const one_month = {
+                            tid: this.ctx.tender.id,
+                            mid: this.ctx.material.id,
+                            mb_id: mb.id,
+                            msg_tp: monthList.length !== 0 ? null : mb.msg_tp,
+                            yearmonth: data.yearmonth,
+                        };
+                        insertArray.push(one_month);
+                        if (monthList.length !== 0) {
+                            const mb_msg_tp_sum = this._.sumBy(this._.filter(monthList, { mb_id: mb.id }), 'msg_tp');
+                            const new_msg_tp = this.ctx.helper.round(this.ctx.helper.div(this.ctx.helper.add(mb_msg_tp_sum, one_month.msg_tp), material_month.length), 3);
+                            const [newmsg_spread, newm_spread] = await this.ctx.service.materialBills.getSpread(mb, new_msg_tp);
+                            updateArray.push({
+                                id: mb.id,
+                                msg_tp: new_msg_tp,
+                                msg_spread: newmsg_spread,
+                                m_spread: newm_spread,
+                                m_tp: this.ctx.helper.round(this.ctx.helper.mul(mb.quantity, newm_spread), 2),
+                            });
+                        }
+                    }
+                    if (insertArray.length !== 0) await transaction.insert(this.tableName, insertArray);
+                    if (updateArray.length !== 0) await transaction.updateRows(this.ctx.service.materialBills.tableName, updateArray);
+                }
+                await transaction.update(this.ctx.service.material.tableName, { id: this.ctx.material.id, months: material_month.join(',') });
+                const m_tp = await this.ctx.service.materialBills.calcMaterialMTp(transaction);
+                await transaction.commit();
+                this.ctx.material.months = material_month.join(',');
+                return m_tp;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 删除月信息价 并更新 工料平均单价、本期单价,调差金额等
+         * @return {void}
+         */
+        async del(data, monthList, mbList) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                this._.remove(material_month, function(n) {
+                    return data.indexOf(n) !== -1;
+                });
+                await transaction.delete(this.tableName, { mid: this.ctx.material.id, yearmonth: data });
+                if (mbList.length !== 0) {
+                    const updateArray = [];
+                    for (const mb of mbList) {
+                        if (monthList.length !== 0) {
+                            this._.remove(monthList, function(m) {
+                                return data.indexOf(m.yearmonth) !== -1;
+                            });
+                            const mb_msg_tp_sum = this._.sumBy(this._.filter(monthList, { mb_id: mb.id }), 'msg_tp');
+                            const new_msg_tp = material_month.length !== 0 ? this.ctx.helper.round(this.ctx.helper.div(mb_msg_tp_sum, material_month.length), 3) : null;
+                            const [newmsg_spread, newm_spread] = await this.ctx.service.materialBills.getSpread(mb, new_msg_tp);
+                            updateArray.push({
+                                id: mb.id,
+                                msg_tp: new_msg_tp,
+                                msg_spread: newmsg_spread,
+                                m_spread: newm_spread,
+                                m_tp: this.ctx.helper.round(this.ctx.helper.mul(mb.quantity, newm_spread), 2),
+                            });
+                        }
+                    }
+                    if (updateArray.length !== 0) await transaction.updateRows(this.ctx.service.materialBills.tableName, updateArray);
+                }
+                await transaction.update(this.ctx.service.material.tableName, { id: this.ctx.material.id, months: material_month.join(',') });
+                const m_tp = await this.ctx.service.materialBills.calcMaterialMTp(transaction);
+                await transaction.commit();
+                this.ctx.material.months = material_month.join(',');
+                return m_tp;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 修改月信息价值 并更新 本期单价,调差金额等
+         * @return {void}
+         */
+        async save(data) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                await transaction.update(this.tableName, { msg_tp: data.value }, { where: { mb_id: data.mb_id, yearmonth: data.yearmonth } });
+                const monthList = await transaction.select(this.tableName, { where: { mb_id: data.mb_id } });
+                const mbInfo = await transaction.get(this.ctx.service.materialBills.tableName, { id: data.mb_id });
+                if (monthList.length !== 0) {
+                    const mb_msg_tp_sum = this._.sumBy(monthList, 'msg_tp');
+                    const new_msg_tp = this.ctx.helper.round(this.ctx.helper.div(mb_msg_tp_sum, material_month.length), 3);
+                    const [newmsg_spread, newm_spread] = await this.ctx.service.materialBills.getSpread(mbInfo, new_msg_tp);
+                    await transaction.update(this.ctx.service.materialBills.tableName, {
+                        id: mbInfo.id,
+                        msg_tp: new_msg_tp,
+                        msg_spread: newmsg_spread,
+                        m_spread: newm_spread,
+                        m_tp: this.ctx.helper.round(this.ctx.helper.mul(mbInfo.quantity, newm_spread), 2),
+                    });
+                }
+                const m_tp = await this.ctx.service.materialBills.calcMaterialMTp(transaction);
+                await transaction.commit();
+                return m_tp;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 修改多个月信息价值 并更新 本期单价,调差金额等
+         * @return {void}
+         */
+        async saveDatas(datas, mbList) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                const updateArray = [];
+                for (const data of datas) {
+                    for (const m of material_month) {
+                        const one_update = {
+                            row: {
+                                msg_tp: data[m],
+                            },
+                            where: {
+                                mb_id: data.mb_id,
+                                yearmonth: m,
+                            },
+                        };
+                        updateArray.push(one_update);
+                    }
+                }
+                await transaction.updateRows(this.tableName, updateArray);
+                const monthList = await transaction.select(this.tableName, { where: { mid: this.ctx.material.id } });
+                if (mbList.length !== 0) {
+                    const mbUpdateArray = [];
+                    for (const mb of mbList) {
+                        const mb_msg_tp_sum = this._.sumBy(this._.filter(monthList, { mb_id: mb.id }), 'msg_tp');
+                        const new_msg_tp = material_month.length !== 0 ? this.ctx.helper.round(this.ctx.helper.div(mb_msg_tp_sum, material_month.length), 3) : null;
+                        const [newmsg_spread, newm_spread] = await this.ctx.service.materialBills.getSpread(mb, new_msg_tp);
+                        mbUpdateArray.push({
+                            id: mb.id,
+                            msg_tp: new_msg_tp,
+                            msg_spread: newmsg_spread,
+                            m_spread: newm_spread,
+                            m_tp: this.ctx.helper.round(this.ctx.helper.mul(mb.quantity, newm_spread), 2),
+                        });
+                    }
+                    if (mbUpdateArray.length !== 0) await transaction.updateRows(this.ctx.service.materialBills.tableName, mbUpdateArray);
+                }
+                const m_tp = await this.ctx.service.materialBills.calcMaterialMTp(transaction);
+                await transaction.commit();
+                return m_tp;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+    }
+
+    return MaterialMonth;
+};

+ 40 - 16
app/view/material/info.ejs

@@ -5,13 +5,18 @@
             <% include ./material_sub_mini_menu.ejs %>
             <div>
                 <% if ((material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === material.user_id) { %>
-                <div class="d-inline-block">
-                    <a href="javascript: void(0);" id="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增材料"><i class="fa fa-plus" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" id="del" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除材料"><i class="fa fa-remove" aria-hidden="true"></i></a>
-                </div>
+                    <div class="d-inline-block">
+                        <a href="javascript: void(0);" id="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增材料"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                        <a href="javascript: void(0);" id="del" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除材料"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    </div>
                 <% } %>
                 <div class="d-inline-block">
                     本期调差计量期:第<span class="mx-2"><%= material.s_order.split(',').join(',') %></span>期
+                    <span id="qi-month">
+                        <% if (months.length > 0) { %>
+                            <span class="mx-2 text-muted">/</span>本期月信息价:<% let monthstr = ''; for (const m of months) { monthstr += parseInt(m.split('-')[1]) + '月,'} %><%- monthstr.substring(0,monthstr.length-1) %>
+                        <% } %>
+                    </span>
                 </div>
             </div>
             <div class="ml-auto">
@@ -22,7 +27,7 @@
         <div class="c-header p-0">
         </div>
         <div class="row w-100 sub-content">
-            <div id="main-view" class="c-body" style="width: 100%">
+            <div id="main-view" class="c-body col-8" style="width: 100%">
                 <!--上部分-->
                 <div class="sjs-height-1" id="material-spread">
                 </div>
@@ -35,17 +40,17 @@
                             </div>
                             <select class="form-control form-control-sm col-1" id="changeRate">
                                 <% if (!material.readOnly) { %>
-                                <option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>
-                                <option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>
-                                <option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>
+                                    <option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>
+                                    <option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>
+                                    <option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>
                                 <% } else { %>
-                                <option value="<%= material.rate %>" selected><%= material.rate %>%</option>
+                                    <option value="<%= material.rate %>" selected><%= material.rate %>%</option>
                                 <% } %>
                             </select>
                         </div>
                     </div>
                     <div class="sp-wrap">
-                        <div class="col-4 p-0">
+                        <div class="col-6 p-0">
                             <table class="table table-sm table-bordered">
                                 <tr><th></th><th>本期金额</th><th>截止本期金额</th></tr>
                                 <tr id="tp_set"><td>材料价差费用</td><td><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td><td><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td></tr>
@@ -55,6 +60,22 @@
                     </div>
                 </div>
             </div>
+            <div class="c-body col-4">
+                <div class="tab-content" style="width: 100%">
+                    <div id="qianyue" class="tab-pane active">
+                        <div class="sjs-bar-1">
+                            <% if ((material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === material.user_id) {%>
+                            <div class="pb-1">
+                                <a href="#add-month" data-toggle="modal" data-target="#add-month" class="btn btn-sm btn-primary">创建月信息价</a>
+                                <a href="#remove-month" data-toggle="modal" data-target="#remove-month" class="btn btn-sm btn-outline-danger">移除月信息价</a>
+                            </div>
+                            <% } %>
+                        </div>
+                        <div id="material-month-spread">
+                        </div>
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
 </div>
@@ -62,13 +83,13 @@
     <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
     <img src="/public/images/icon-ok.png" id="icon-ok" />
 </div>
-<link rel="stylesheet" href="/public/css/jquery-ui/datepicker.css">
-<script src="/public/js/jquery-ui/datepicker.js"></script>
-<script src="/public/js/jquery-ui/datepicker-zh-CN.js"></script>
+<!--<link rel="stylesheet" href="/public/css/jquery-ui/datepicker.css">-->
+<!--<script src="/public/js/jquery-ui/datepicker.js"></script>-->
+<!--<script src="/public/js/jquery-ui/datepicker-zh-CN.js"></script>-->
 <% if ((material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === material.user_id) {%>
-<script>
-    const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
-</script>
+    <script>
+        const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
+    </script>
 <% } %>
 <script>
     const materialType = JSON.parse('<%- materialType %>');
@@ -80,4 +101,7 @@
     const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
     const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
     const calcBase = JSON.parse('<%- JSON.stringify(calcBase) %>');
+    const months = JSON.parse('<%- JSON.stringify(months) %>');
+    let monthsList = JSON.parse('<%- JSON.stringify(monthsList) %>');
+    console.log(monthsList);
 </script>

+ 47 - 0
app/view/material/info_modal.ejs

@@ -32,4 +32,51 @@
         </div>
     </div>
 </div>
+<!--创建月信息价-->
+<div class="modal fade" id="add-month" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">创建月信息价</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>信息价月份</label>
+                    <input class="datepicker-here form-control" id="months" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text">
+                </div>
+                <!--首次创建-->
+                <div class="alert alert-primary">创建月信息价后,「本期信息价」 「单价」列将无法自行填写,而是从「月信息价」中获取,如果有多个「月信息价」,将取平均值。</div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-primary" id="make-month">确认添加</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--移除月信息价-->
+<div class="modal fade" id="remove-month" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">移除月信息价</h5>
+            </div>
+            <div class="modal-body">
+                <div id="show_month">
+                    <% for (const m of months) { %>
+                        <div class="custom-control custom-checkbox mb-2">
+                            <input type="checkbox" name="del_month" value="<%- m %>" class="custom-control-input" id="month_<%- m %>">
+                            <label class="custom-control-label" for="month_<%- m %>"><%- m %>月</label>
+                        </div>
+                    <% } %>
+                </div>
+                <div class="alert alert-warning">移除后,对应月份信息价将删除,本期调差数据也将变动,请确认移除。</div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-danger" id="del-month">确认移除</button>
+            </div>
+        </div>
+    </div>
+</div>
 <% include ./audit_modal.ejs %>

+ 2 - 0
config/web.js

@@ -476,6 +476,8 @@ const JsFiles = {
                     '/public/js/path_tree.js',
                     '/public/js/material.js',
                     '/public/js/material_audit.js',
+                    '/public/js/datepicker/datepicker.min.js',
+                    '/public/js/datepicker/datepicker.zh.js',
                 ],
                 mergeFile: 'material',
             },

+ 15 - 0
sql/update.sql

@@ -95,5 +95,20 @@ CREATE TABLE `zh_advance_file` (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=124 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='预付款附件';
 
+-- ----------------------------
+-- Table structure for zh_material_month
+-- ----------------------------
+CREATE TABLE `zh_material_month` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `mid` int(11) NOT NULL COMMENT '调差期id',
+  `mb_id` int(11) NOT NULL COMMENT '工料id',
+  `msg_tp` decimal(30,8) DEFAULT NULL COMMENT '月信息单价',
+  `yearmonth` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '对应年月份',
+  PRIMARY KEY (`id`),
+  KEY `mid` (`mid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='调差月信息价工料单价表';
+
+ALTER TABLE `zh_material` ADD `months` VARCHAR(1000) NULL DEFAULT NULL COMMENT '月信息价-年月份列表' AFTER `s_order`;