Browse Source

调差清单导入功能

laiguoran 2 years ago
parent
commit
a19a0ff9d3

+ 4 - 1
app/controller/material_controller.js

@@ -442,8 +442,8 @@ module.exports = app => {
         async list(ctx) {
             try {
                 await this._getMaterialAuditViewData(ctx);
-                const renderData = await this._getDefaultRenderData(ctx);
                 await this._setEditListPermission(ctx);
+                const renderData = await this._getDefaultRenderData(ctx);
                 // 根据期判断需要获取的工料信息值表
                 const searchsql = { tid: ctx.tender.id };
                 let midList = [];
@@ -1569,6 +1569,9 @@ module.exports = app => {
                     case 'resetChecklist':
                         responseData.data = await ctx.service.materialChecklist.resetData(data.pushData, data.removeData, data.updateData);
                         break;
+                    case 'exportCB':
+                        responseData.data = await ctx.service.materialChecklist.addExportCB(data.addChecklist, data.addBillsList);
+                        break;
                     default: throw '参数有误';
                 }
 

File diff suppressed because it is too large
+ 9164 - 0
app/public/js/export/iconv-lite.js


File diff suppressed because it is too large
+ 673 - 0
app/public/js/export/jschardet.min.js


+ 301 - 0
app/public/js/material_checklist.js

@@ -1081,4 +1081,305 @@ $(document).ready(() => {
         ledgerSpread.refresh();
         materialSpread.refresh();
     }
+
+    // 导入功能
+    // 上传图片
+    $('#upload-list').click(function () {
+        // if (materialChecklistData.length === 0) {
+        //     toastr.error('请选择调差清单再导入。');
+        //     return
+        // }
+        $(this).siblings('input').trigger('click');
+    });
+    $('#upload-list-file').change(function () {
+        const file = this.files[0];
+        const ext = file.name.toLowerCase().split('.').splice(-1)[0];
+        const imgStr = /(xls|xlsx|json|XLS|XLSX|JSON)$/;
+        if (!imgStr.test(ext)) {
+            toastr.error('请导入正确格式的json或excel文件。');
+            return
+        }
+        const fileReader = new FileReader();
+        fileReader.onload = async function(ev) {
+            try{
+                const data = ev.target.result;
+                let tree = [];
+                resetExport();
+                $('#okedit').modal('show');
+                setProgress($('#export-progress'), 30);
+                if (/(xls|xlsx|XLS|XLSX)$/.test(ext)) {
+                    const workbook = XLSX.read(data, {type: 'binary'}); // 以二进制流方式读取得到整份excel表格对象
+                    const jsonData = transExcel(XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { defval: null }));
+                    if (!(jsonData[0] && jsonData[0].code !== undefined && jsonData[0].code !== null &&
+                        jsonData[0].b_code !== undefined && jsonData[0].name !== undefined &&
+                        jsonData[0].unit !== undefined && jsonData[0].unit_price !== undefined)) {
+                        throw 'excel必须按指定格式内容上传';
+                    }
+                    tree = _.filter(jsonData, function (item) {
+                        return item.code !== null;
+                    });
+                    for (const [i,t] of tree.entries()) {
+                        const jIndex1 = _.findIndex(jsonData, { code: t.code, name: t.name, unit: t.unit, unit_price: t.unit_price });
+                        if (i + 1 < tree.length) {
+                            const jIndex2 = _.findIndex(jsonData, { code: tree[i+1].code, name: tree[i+1].name, unit: tree[i+1].unit, unit_price: tree[i+1].unit_price });
+                            t.children = jsonData.slice(jIndex1 + 1, jIndex2);
+                        } else {
+                            t.children = jsonData.slice(jIndex1 + 1);
+                        }
+                    }
+                } else {
+                    const ascii = jschardet.detect(data.substring(0, 10000));
+                    iconv.skipDecodeWarning = true
+                    tree = JSON.parse(iconv.decode(data, ascii.encoding));// 需要转码,否则前端处理中文会出现乱码
+                }
+                stopProgress($('#export-progress'));
+                $('#bill-detail').show();
+                setProgress($('#bill-progress'), 30);
+                // 导入先生成materialCheckList,再生成materialBillsData,最后生成materialListData
+                console.log(tree, gclGatherData, materialChecklistData, materialBillsData);
+                const pushChecklist = [];
+                const pushBillsData = [];
+                const needPushTree = [];
+                // 分析materialCheckList和tree,找出相同的清单并插入对应不存在的工料
+                for (const t of tree) {
+                    const order = _.findIndex(gclGatherData, { b_code: t.code, name: t.name, unit: t.unit, unit_price: t.unit_price ? parseFloat(t.unit_price) : null });
+                    const mlOrder = _.findIndex(materialChecklistData, { b_code: t.code, name: t.name, unit: t.unit, unit_price: t.unit_price ? parseFloat(t.unit_price) : null });
+                    if (mlOrder === -1 && order !== -1 && _.findIndex(pushChecklist, { b_code: t.code, name: t.name, unit: t.unit, unit_price: t.unit_price ? parseFloat(t.unit_price) : null}) === -1) {
+                        pushChecklist.push({
+                            b_code: gclGatherData[order].b_code,
+                            name: gclGatherData[order].name,
+                            unit: gclGatherData[order].unit,
+                            unit_price: gclGatherData[order].unit_price,
+                            quantity: gclGatherData[order].quantity ? gclGatherData[order].quantity : null,
+                            total_price: gclGatherData[order].total_price ? gclGatherData[order].total_price : null,
+                            had_bills: 0,
+                        });
+                    } else if (mlOrder === -1 && order === -1) {
+                        continue;
+                    }
+                    needPushTree.push(t);
+                    for (const c of t.children) {
+                        const mbOrder = _.findIndex(materialBillsData, { code: c.b_code, name: c.name, unit: c.unit });
+                        if (c.b_code !== null && mbOrder === -1 && _.findIndex(pushBillsData, { code: c.b_code, name: c.name, unit: c.unit }) === -1) {
+                            pushBillsData.push({
+                                code: c.b_code,
+                                name: c.name,
+                                unit: c.unit
+                            })
+                        }
+                    }
+                }
+                console.log(pushChecklist, pushBillsData);
+                // stopProgress($('#bill-progress'));
+                // await pushListData(needPushTree);
+                // 先上传需要生成的清单及工料
+                if (pushChecklist.length > 0 || pushBillsData.length > 0) {
+                    postData(window.location.pathname + '/save', { type:'exportCB', addChecklist: pushChecklist, addBillsList: pushBillsData }, async function (result) {
+                        // materialListData = result;
+                        materialChecklistData = result.materialChecklistData;
+                        materialBillsData = result.materialBillsData;
+                        materialStageBillsData = result.materialStageBillsData;
+                        stopProgress($('#bill-progress'));
+                        await pushListData(needPushTree);
+                    }, function () {
+                        stop = true;
+                        clearInterval(interval);
+                        setTimeout(function () { $('#okedit').modal('hide') }, 1000);
+                    });
+                } else {
+                    stopProgress($('#bill-progress'));
+                    await pushListData(needPushTree);
+                }
+            } catch (error) {
+                console.log(error);
+                toastr.error(error);
+                stop = true;
+                clearInterval(interval);
+                setTimeout(function () { $('#okedit').modal('hide') }, 1000);
+                return
+            }
+
+            // 分析jsondata,得出每个清单间工料数量,生成新的树结构数组
+
+            // let persons = []; // 存储获取到的数据
+            // // 表格的表格范围,可用于判断表头是否数量是否正确
+            // let fromTo = '';
+            // // 遍历每张表读取
+            // for (const sheet in workbook.Sheets) {
+            //     if (workbook.Sheets.hasOwnProperty(sheet)) {
+            //         fromTo = workbook.Sheets[sheet]['!ref'];
+            //         console.log(fromTo);
+            //         persons = persons.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]));
+            //         // break; // 如果只取第一张表,就取消注释这行
+            //     }
+            // }
+            // console.log(persons);
+        };
+
+        // 以二进制方式打开文件
+        fileReader.readAsBinaryString(file);
+        $('#upload-list-file').val('');
+    });
+
+    function sleep(millisecond) {
+        return new Promise(resolve => {
+            setTimeout(() => {
+                resolve()
+            }, millisecond)
+        })
+    }
+
+    let value = 0;
+    let interval;
+    let stop = false;
+    function setProgress(_this, time = 50) {
+        interval = setInterval(function () {
+            if (value < 100) {
+                value = parseInt(value) + 1;
+                _this.css("width", value + "%").text(value + "%");
+            } else if (value === 100) {
+                value = parseInt(value) + 1;
+                value = 30;
+            }
+        }, time);
+    }
+
+    function resetExport() {
+        resetProgress($('#export-progress'));
+        $('#bill-detail').hide();
+        resetProgress($('#bill-progress'));
+        $('#list-detail').hide();
+        resetProgress($('#list-progress'));
+    }
+
+    function resetProgress(_this) {
+        _this.removeClass('bg-success');
+        _this.css("width", "0%").text("0%").attr('aria-valuenow', '0');
+    }
+
+    function stopProgress(_this) {
+        if (interval) {
+            _this.addClass('bg-success');
+            _this.css("width", "100%").text("100%");
+            value = 0;
+            stop = true;
+            clearInterval(interval);
+            interval = 0;
+        }
+    }
+
+    async function pushListData(tree = []) {
+        console.log(tree);
+        if (tree.length > 0) {
+            for (const [i,t] of tree.entries()) {
+                $('#list-detail').find('b').text(t.code);
+                resetProgress($('#list-progress'));
+                if (!interval) {
+                    $('#list-detail').show();
+                    setProgress($('#list-progress'), 30);
+                }
+                const mbList = [];
+                for (const mb of t.children) {
+                    const mbInfo = _.find(materialBillsData, { code: mb.b_code, name: mb.name, unit: mb.unit });
+                    if (mbInfo) {
+                        const num = parseFloat(mb.quantity);
+                        if (num < 0 || !/^\d+(\.\d{1,6})?$/.test(num)) {
+                            // toastr.warning('已保留6位小数');
+                            mb.quantity = ZhCalc.round(num, 6);
+                        }
+                        mbList.push({ id: mbInfo.id, quantity: mb.quantity });
+                    }
+                }
+
+                const gclIndex = _.findIndex(gclGatherData, { b_code: t.code, name: t.name, unit: t.unit, unit_price: t.unit_price ? parseFloat(t.unit_price) : null });
+                const gcl = gclGatherData[gclIndex].leafXmjs;
+                const ms_id = isStageSelf ? materialStageData[0].id : null;
+                // const index = materialChecklistData.indexOf(select);
+                const datas = [];
+                for (const xmj of gcl) {
+                    const notx = findNotJoinLeafXmj(xmj);
+                    const data = {
+                        xmj_id: xmj.id,
+                        gcl_id: xmj.gcl_id,
+                        mx_id: xmj.mx_id !== undefined ? xmj.mx_id : '',
+                        gather_qty: xmj.gather_qty,
+                        is_join: notx === undefined ? 1 : 0,
+                    };
+                    if (ms_id) data.ms_id = ms_id;
+                    datas.push(data);
+                }
+                if (isStageSelf) {
+                    // 取所有的gclGatherData才行,然后获取下的值
+                    const gclData = gclGatherData[gclIndex];
+                    for (const [index, ms] of materialStageData.entries()) {
+                        if (ms.id !== ms_id) {
+                            const gclOther = _.find(gclGatherListData[index], { b_code: gclData.b_code, name: gclData.name, unit: gclData.unit, unit_price: gclData.unit_price });
+                            if (gclOther) {
+                                const leafXmjs = gclOther.leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                                for (const xmj of leafXmjs) {
+                                    const notx = findNotJoinLeafXmj(xmj);
+                                    const data = {
+                                        xmj_id: xmj.id,
+                                        gcl_id: xmj.gcl_id,
+                                        mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                        gather_qty: xmj.gather_qty,
+                                        is_join: notx === undefined ? 1 : 0,
+                                        ms_id: ms.id,
+                                    };
+                                    datas.push(data);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // 上传到数据库
+                const select = _.find(materialChecklistData, { b_code: t.code, name: t.name, unit: t.unit, unit_price: t.unit_price ? parseFloat(t.unit_price) : null });
+                console.log(select, datas, mbList);
+                if (select) {
+                    await postData(window.location.pathname + '/save', {type: 'adds', checklist: { id: select.id, had_bills: 1 }, postData: {xmjs: datas, mbIds: mbList, export: true}}, function (result) {
+                        // materialListData = result;
+                        select.had_bills = 1;
+                        gclList = result;
+                        stopProgress($('#list-progress'));
+                        if (i+1 === tree.length) {
+                            toastr.success('导入成功');
+                            setTimeout(function () { $('#okedit').modal('hide') }, 2000);
+                            showSjsData();
+                        }
+
+                    }, function () {
+                        stop = true;
+                        clearInterval(interval);
+                        setTimeout(function () { $('#okedit').modal('hide') }, 1000);
+                    });
+                } else {
+                    stopProgress($('#list-progress'));
+                }
+            }
+        }
+    }
+
+    function transExcel(results) {
+        const mapInfo = {
+            '清单编号': 'code',
+            '工料编号': 'b_code',
+            '名称': 'name',
+            '单位': 'unit',
+            '单价': 'unit_price',
+            '单位耗量': 'quantity',
+        };
+        return results.map(zhObj => {
+            const enObj = {}
+            const zhKeys = Object.keys(zhObj);
+
+            zhKeys.forEach(zhKey => {
+                const enKey = mapInfo[zhKey];
+
+                enObj[enKey] = zhObj[zhKey];
+            });
+
+            return enObj
+        })
+    }
 });

+ 2 - 2
app/service/material.js

@@ -209,9 +209,9 @@ module.exports = app => {
                     let m_tp = null;
                     let m_tax_tp = null;
                     if (data.is_stage_self) {
-                        [m_tp, m_tax_tp] = await this.ctx.service.materialStageBills.insertBills(transaction, this.ctx.tender.id, newMaterial.id, newMaterial.stage_id, insertMaterialStage, JSON.parse(newMaterial.decimal));
+                        [m_tp, m_tax_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);
                     } else {
-                        [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal));
+                        [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal), preMaterial.is_stage_self);
                     }
                     // 修改现行价格指数,并返回调差基数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));

+ 5 - 4
app/service/material_bills.js

@@ -431,13 +431,13 @@ module.exports = app => {
          * @param mid
          * @returns {Promise<number>}
          */
-        async updateNewMaterial(transaction, tid, mid, ctx, stage_id, decimal) {
+        async updateNewMaterial(transaction, tid, mid, ctx, stage_id, decimal, pre_is_stage_self = 0) {
             const materialBillsData = await this.getAllDataByCondition({ where: { tid } });
             let m_tp = 0;
             let m_tax_tp = 0;
             const materialCalculator = new MaterialCalculator(ctx, stage_id, ctx.tender.info);
             for (const mb of materialBillsData) {
-                const [one_tp, one_tax_tp] = await this.calcQuantityByMB(transaction, mid, mb, materialCalculator, decimal);
+                const [one_tp, one_tax_tp] = await this.calcQuantityByMB(transaction, mid, mb, materialCalculator, decimal, pre_is_stage_self);
                 m_tp = this.ctx.helper.add(m_tp, one_tp);
                 m_tax_tp = this.ctx.helper.add(m_tax_tp, one_tax_tp);
             }
@@ -451,7 +451,7 @@ module.exports = app => {
          * @param mb
          * @returns {Promise<*>}
          */
-        async calcQuantityByMB(transaction, mid, mb, materialCalculator, decimal) {
+        async calcQuantityByMB(transaction, mid, mb, materialCalculator, decimal, pre_is_stage_self) {
             const [newmsg_spread, newm_spread] = await this.getSpread(mb, null, decimal.up);
             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';
@@ -479,11 +479,12 @@ module.exports = app => {
                 const m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mb.m_tax, 100))), decimal.tp);
                 return [m_tp, m_tax_tp];
             } else if (mb.t_type === materialConst.t_type[1].value) {
-                const quantity = await materialCalculator.calculateExpr(mb.expr);
+                const quantity = pre_is_stage_self ? 0 : await materialCalculator.calculateExpr(mb.expr);
                 const newTp = quantity !== 0 && quantity !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(quantity, decimal.qty), newm_spread), decimal.tp) : null;
                 const updateData = {
                     id: mb.id,
                     quantity: quantity !== 0 && quantity !== null ? this.ctx.helper.round(quantity, decimal.qty) : null,
+                    expr: pre_is_stage_self ? null : mb.expr,
                     msg_tp: null,
                     msg_times: null,
                     msg_spread: newmsg_spread,

+ 94 - 1
app/service/material_checklist.js

@@ -5,7 +5,7 @@
  * @date 2020/6/30
  * @version
  */
-
+const materialConst = require('../const/material');
 module.exports = app => {
     class MaterialChecklist extends app.BaseService {
         /**
@@ -61,6 +61,99 @@ module.exports = app => {
             }
             return await transaction.update(this.tableName, { id, had_bills });
         }
+
+        async addExportCB(addChecklist, addBillsList) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (addChecklist.length > 0) {
+                    const insertDatas = [];
+                    for (const p of addChecklist) {
+                        p.mid = this.ctx.material.id;
+                        p.tid = this.ctx.tender.id;
+                        insertDatas.push(p);
+                    }
+                    await transaction.insert(this.tableName, insertDatas);
+                }
+                if (addBillsList.length > 0) {
+                    let order = await this.ctx.service.materialBills._getMaxOrder(this.ctx.tender.id);
+                    const pushBills = [];
+                    for (const b of addBillsList) {
+                        const newBills = {
+                            tid: this.ctx.tender.id,
+                            mid: this.ctx.material.id,
+                            code: b.code,
+                            name: b.name,
+                            unit: b.unit,
+                            order: order + 1,
+                            in_time: new Date(),
+                        };
+                        pushBills.push(newBills);
+                        ++order;
+                    }
+                    // 新增工料
+                    const result = await transaction.insert(this.ctx.service.materialBills.tableName, pushBills);
+                    // 获取刚批量添加的所有list
+                    for (let j = 0; j < pushBills.length; j++) {
+                        pushBills[j].id = result.insertId + j;
+                    }
+                    if (this.ctx.material.is_stage_self) {
+                        await this.ctx.service.materialStageBills.adds(transaction, pushBills);
+                    }
+                    const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                    if (material_month.length > 0) {
+                        const insertArray = [];
+                        for (const pb of pushBills) {
+                            for (const ym of material_month) {
+                                const one_month = {
+                                    tid: this.ctx.tender.id,
+                                    mid: this.ctx.material.id,
+                                    mb_id: pb.id,
+                                    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();
+                const materialChecklistData = await this.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
+                const searchsql = { tid: this.ctx.tender.id };
+                let midList = [];
+                if (this.ctx.material.highOrder !== this.ctx.material.order) {
+                    midList = await this.ctx.service.material.getPreMidList(this.ctx.tender.id, this.ctx.material.order);
+                    searchsql.mid = midList;
+                }
+                searchsql.t_type = materialConst.t_type[0].value;
+                const materialBillsData = await this.ctx.service.materialBills.getAllDataByCondition({ where: searchsql, orders: [['order', 'asc']] });
+                // 取对应期的截取上期的调差金额和应耗数量
+                if (this.ctx.material.highOrder !== this.ctx.material.order) {
+                    for (const [mindex, mb] of materialBillsData.entries()) {
+                        const result = await this.ctx.service.materialBillsHistory.getByMbId(this.ctx.material.id, this.ctx.material.order, mb.id);
+                        this._.forEach(result, function(value, key) {
+                            if (key === 'mb_id') {
+                                materialBillsData[mindex].id = result ? result[key] : null;
+                            } else {
+                                materialBillsData[mindex][key] = result ? result[key] : null;
+                            }
+                        });
+                    }
+                }
+                const materialStageBillsData = this.ctx.material.is_stage_self ? await this.ctx.service.materialStageBills.getAllDataByCondition({ where: { tid: this.ctx.tender.id, mid: this.ctx.material.id } }) : [];
+                const self = this;
+                return { materialChecklistData: await materialChecklistData.sort(function(a, b) {
+                    return self.ctx.helper.compareCode(a.b_code, b.b_code);
+                }), materialBillsData, materialStageBillsData };
+            } catch (err) {
+                console.log(err);
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
     return MaterialChecklist;
 };

+ 124 - 18
app/service/material_list.js

@@ -294,6 +294,83 @@ module.exports = app => {
         }
 
         /**
+         * 修改material_bills的quantity值和计算本期金额
+         * @param transaction
+         * @param mb_id
+         * @return {Promise<*>}
+         */
+        async calcAllQuantityByML(transaction, mbIds) {
+            const mbList = await transaction.select(this.ctx.service.materialBills.tableName, { where: { id: mbIds } });
+            // 修改material_bills值
+            for (const mbInfo of mbList) {
+                if (!mbInfo) {
+                    throw '不存在该工料';
+                }
+                if (this.ctx.material.is_stage_self) {
+                    const updateDatas = [];
+                    const updateMsIds = [];
+                    const msbList = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, mb_id: mbInfo.id } });
+                    for (const msb of msbList) {
+                        const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `ms_id`=? AND `is_join`=1';
+                        const sqlParam = [this.ctx.material.id, mbInfo.id, msb.ms_id];
+                        const mb_quantity = await transaction.queryOne(sql, sqlParam);
+                        const newQuantity = this.ctx.helper.round(mb_quantity.quantity, this.ctx.material.decimal.qty);
+                        const newTp = this.ctx.helper.round(this.ctx.helper.mul(newQuantity, msb.m_spread), this.ctx.material.decimal.tp);
+                        const updateData = {
+                            id: msb.id,
+                            quantity: newQuantity,
+                            m_tp: newTp,
+                            m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
+                        };
+                        updateDatas.push(updateData);
+                        updateMsIds.push(msb.ms_id);
+                    }
+                    if (updateDatas.length > 0) {
+                        await transaction.updateRows(this.ctx.service.materialStageBills.tableName, updateDatas);
+                        for (const msId of updateMsIds) {
+                            await this.ctx.service.materialStage.updateMtp(transaction, msId);
+                        }
+                    }
+                    // 还要更新bills表的m_tp和m_tax_tp值
+                    const sql3 = 'SELECT SUM(`m_tp`) as total_price, SUM(IF(`m_tax_tp` is null, `m_tp`, `m_tax_tp`)) as tax_total_price FROM ' + this.ctx.service.materialStageBills.tableName + ' WHERE `tid` = ? AND `mid` = ? AND `mb_id` = ? AND `is_summary` = 1';
+                    const sqlParam3 = [this.ctx.tender.id, this.ctx.material.id, mbInfo.id];
+                    const tp3 = await transaction.queryOne(sql3, sqlParam3);
+                    const updateBillsData = {
+                        id: mbInfo.id,
+                        m_tp: tp3.total_price,
+                        m_tax_tp: tp3.tax_total_price,
+                    };
+                    await transaction.update(this.ctx.service.materialBills.tableName, updateBillsData);
+                } else {
+                    const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `is_join`=1';
+                    const sqlParam = [this.ctx.material.id, mbInfo.id];
+                    const mb_quantity = await transaction.queryOne(sql, sqlParam);
+                    console.log(mb_quantity);
+                    const newQuantity = this.ctx.helper.round(mb_quantity.quantity, this.ctx.material.decimal.qty);
+                    const newTp = this.ctx.helper.round(this.ctx.helper.mul(newQuantity, mbInfo.m_spread), this.ctx.material.decimal.tp);
+                    const updateData = {
+                        id: mbInfo.id,
+                        quantity: newQuantity,
+                        m_tp: newTp,
+                        m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
+                    };
+                    await transaction.update(this.ctx.service.materialBills.tableName, updateData);
+                }
+            }
+            // 计算本期总金额
+            const sql2 = 'SELECT SUM(`m_tp`) as total_price, SUM(IF(`m_tax_tp` is null, `m_tp`, `m_tax_tp`)) as tax_total_price FROM ' + this.ctx.service.materialBills.tableName + ' WHERE `tid` = ? AND `is_summary` = 1';
+            const sqlParam2 = [this.ctx.tender.id];
+            const tp = await transaction.queryOne(sql2, sqlParam2);
+            console.log(tp);
+            const updateData2 = {
+                id: this.ctx.material.id,
+                m_tp: tp.total_price,
+                m_tax_tp: tp.tax_total_price,
+            };
+            return await transaction.update(this.ctx.service.material.tableName, updateData2);
+        }
+
+        /**
          * 获取工料清单关联表
          * @param {int} tid 标段id
          * @param {Object} mid 期id
@@ -454,10 +531,14 @@ module.exports = app => {
             try {
                 const list = [];
                 const listGcl = [];
+                const uplist = [];
+                const uplistGcl = [];
                 // const delList = [];
                 // const mb_idList = [];
+                const selfList = await transaction.select(this.ctx.service.materialListSelf.tableName, { where: { tid: this.ctx.tender.id, mid: this.ctx.material.id } });
+                const oldGclList = await transaction.select(this.ctx.service.materialListGcl.tableName, { where: { tid: this.ctx.tender.id } });
+                const oldMaterialList = await transaction.select(this.ctx.service.materialList.tableName, { where: { tid: this.ctx.tender.id, mid: this.ctx.material.id } });
                 for (const xmj of datas.xmjs) {
-                    const selfList = await transaction.select(this.ctx.service.materialListSelf.tableName, { where: { tid: this.ctx.tender.id, mid: this.ctx.material.id } });
                     for (const mb of datas.mbIds) {
                         // // 旧数据兼容问题,要去删除相同已存在的工料
                         // const mlInfo = await this.getDataByCondition({
@@ -471,33 +552,48 @@ module.exports = app => {
                         //     delList.push(mlInfo.id);
                         //     mb_idList.push(mb);
                         // }
+                        const mbId = typeof mb === 'object' ? mb.id : mb;
+                        const quantity = typeof mb === 'object' ? mb.quantity : 0;
                         if (xmj.gather_qty && this._.findIndex(selfList, { gcl_id: xmj.gcl_id, xmj_id: xmj.xmj_id, mx_id: xmj.mx_id }) === -1) {
-                            const newLists = {
-                                tid: this.ctx.tender.id,
-                                order: this.ctx.material.order,
-                                mid: this.ctx.material.id,
-                                mb_id: mb,
-                                gcl_id: xmj.gcl_id,
-                                xmj_id: xmj.xmj_id,
-                                mx_id: xmj.mx_id,
-                                ms_id: xmj.ms_id ? xmj.ms_id : null,
-                                gather_qty: xmj.gather_qty,
-                                in_time: new Date(),
-                                is_join: xmj.is_join,
-                            };
-                            list.push(newLists);
+                            const mlInfo = this._.find(oldMaterialList, { gcl_id: xmj.gcl_id, xmj_id: xmj.xmj_id, mx_id: xmj.mx_id, mb_id: mbId, ms_id: xmj.ms_id ? xmj.ms_id : null });
+                            if (mlInfo) {
+                                uplist.push({ id: mlInfo.id, quantity, expr: '' });
+                            } else {
+                                const newLists = {
+                                    tid: this.ctx.tender.id,
+                                    order: this.ctx.material.order,
+                                    mid: this.ctx.material.id,
+                                    mb_id: mbId,
+                                    gcl_id: xmj.gcl_id,
+                                    xmj_id: xmj.xmj_id,
+                                    mx_id: xmj.mx_id,
+                                    ms_id: xmj.ms_id ? xmj.ms_id : null,
+                                    gather_qty: xmj.gather_qty,
+                                    quantity,
+                                    in_time: new Date(),
+                                    is_join: xmj.is_join,
+                                };
+                                list.push(newLists);
+                            }
                         }
-                        if (this._.findIndex(listGcl, { gcl_id: xmj.gcl_id, mb_id: mb }) === -1) {
+                        const gclIndex = this._.findIndex(oldGclList, { gcl_id: xmj.gcl_id, mb_id: mbId });
+                        if (gclIndex === -1 && this._.findIndex(listGcl, { gcl_id: xmj.gcl_id, mb_id: mbId }) === -1) {
                             const newListGcl = {
                                 tid: this.ctx.tender.id,
                                 order: this.ctx.material.order,
                                 mid: this.ctx.material.id,
-                                mb_id: mb,
+                                mb_id: mbId,
                                 gcl_id: xmj.gcl_id,
-                                quantity: 0,
+                                quantity,
                                 expr: '',
                             };
                             listGcl.push(newListGcl);
+                        } else if (gclIndex !== -1 && this._.findIndex(uplistGcl, { id: oldGclList[gclIndex].id }) === -1) {
+                            uplistGcl.push({
+                                id: oldGclList[gclIndex].id,
+                                expr: '',
+                                quantity,
+                            });
                         }
                     }
                 }
@@ -519,6 +615,13 @@ module.exports = app => {
                         throw '新增工料关联数据失败';
                     }
                 }
+                // 覆盖
+                if (uplist.length > 0) {
+                    await transaction.updateRows(this.tableName, uplist);
+                }
+                if (uplistGcl.length > 0) {
+                    await transaction.updateRows(this.ctx.service.materialListGcl.tableName, uplistGcl);
+                }
                 if (checklist) {
                     await this.ctx.service.materialChecklist.updateHadBills(transaction, checklist.id, checklist.had_bills);
                 }
@@ -529,6 +632,9 @@ module.exports = app => {
                 //         await this.calcQuantityByML(transaction, select);
                 //     }
                 // }
+                if ((list.length > 0 || uplist.length > 0) && datas.export) {
+                    await this.calcAllQuantityByML(transaction, this._.map(datas.mbIds, 'id'));
+                }
                 await transaction.commit();
                 const gclList = await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
                 return checklist ? gclList : {

+ 23 - 4
app/service/material_stage_bills.js

@@ -25,7 +25,7 @@ module.exports = app => {
             super(ctx);
             this.tableName = 'material_stage_bills';
         }
-        async insertBills(transaction, tid, mid, stage_id, materialStageData, decimal) {
+        async insertBills(transaction, tid, mid, stage_id, materialStageData, decimal, pre_is_stage_self = 0) {
             let m_tp = 0;
             let m_tax_tp = 0;
             // 复制工料表并生成
@@ -42,7 +42,7 @@ module.exports = app => {
                             ms_id: ms.id,
                             is_summary: b.is_summary,
                         };
-                        const [one_tp, one_tax_tp] = await this.calcQuantityByMB(transaction, mid, b, oneStageBillsData, materialCalculator, decimal);
+                        const [one_tp, one_tax_tp] = await this.calcQuantityByMB(transaction, mid, b, oneStageBillsData, materialCalculator, decimal, pre_is_stage_self);
                         insertStageBillsData.push(oneStageBillsData);
                         m_tp = this.ctx.helper.add(m_tp, one_tp);
                         m_tax_tp = this.ctx.helper.add(m_tax_tp, one_tax_tp);
@@ -109,6 +109,25 @@ module.exports = app => {
             }
         }
 
+        // 批量添加工料时同步生成
+        async adds(transaction, billsList, remark = null) {
+            const insertList = [];
+            for (const sid of this.ctx.material.stage_id.split(',')) {
+                const msInfo = await transaction.get(this.ctx.service.materialStage.tableName, { tid: this.ctx.tender.id, sid });
+                for (const b of billsList) {
+                    const insertData = {
+                        tid: this.ctx.tender.id,
+                        mid: this.ctx.material.id,
+                        ms_id: msInfo.id,
+                        mb_id: b.id,
+                    };
+                    if (remark) insertData.remark = remark;
+                    insertList.push(insertData);
+                }
+            }
+            if (insertList.length > 0) await transaction.insert(this.tableName, insertList);
+        }
+
         // 单个修改工料时同步修改
         async update(transaction, data, ms_id) {
             const msbInfo = await transaction.get(this.tableName, { tid: this.ctx.tender.id, ms_id, mb_id: data.id });
@@ -133,7 +152,7 @@ module.exports = app => {
          * @param mb
          * @returns {Promise<*>}
          */
-        async calcQuantityByMB(transaction, mid, mb, msb, materialCalculator, decimal) {
+        async calcQuantityByMB(transaction, mid, mb, msb, materialCalculator, decimal, pre_is_stage_self) {
             const [newmsg_spread, newm_spread] = await this.getSpread(mb, null, decimal.up);
             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 `ms_id`=? AND `mb_id`=? AND `is_join`=1';
@@ -166,7 +185,7 @@ module.exports = app => {
                 const m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mb.m_tax, 100))), decimal.tp);
                 return [m_tp, m_tax_tp];
             } else if (mb.t_type === materialConst.t_type[1].value) {
-                const quantity = await materialCalculator.calculateExpr(mb.expr);
+                const quantity = pre_is_stage_self ? 0 : await materialCalculator.calculateExpr(mb.expr);
                 const newTp = quantity !== 0 && quantity !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(quantity, decimal.qty), newm_spread), decimal.tp) : null;
                 msb.quantity = quantity !== 0 && quantity !== null ? this.ctx.helper.round(quantity, decimal.qty) : null;
                 msb.expr = mb.expr;

+ 4 - 2
app/view/material/checklist.ejs

@@ -27,6 +27,8 @@
                 </div>
             </div>
             <div class="ml-auto">
+                <button class="btn btn-primary btn-sm" id="upload-list">导入清单工料单位消耗</button>
+                <input type="file" class="form-control-file" id="upload-list-file" style="display: none;" accept=".xls,.xlsx,.json">
             </div>
         </div>
     </div>
@@ -64,9 +66,9 @@
 <script>
     const materialType = JSON.parse('<%- materialType %>');
     const isStageSelf = parseInt('<%- material.is_stage_self %>');
-    const materialBillsData = JSON.parse(unescape('<%- escape(JSON.stringify(materialBillsData)) %>'));
+    let materialBillsData = JSON.parse(unescape('<%- escape(JSON.stringify(materialBillsData)) %>'));
     const materialStageData = isStageSelf ? JSON.parse(unescape('<%- escape(JSON.stringify(materialStageData)) %>')) : [];
-    const materialStageBillsData = isStageSelf ? JSON.parse(unescape('<%- escape(JSON.stringify(materialStageBillsData)) %>')) : [];
+    let materialStageBillsData = isStageSelf ? JSON.parse(unescape('<%- escape(JSON.stringify(materialStageBillsData)) %>')) : [];
     const readOnly = <%- material.readOnly %>;
     const stage_order = <%- material.order %>;
     const materialID = <%- material.id %>;

+ 29 - 0
app/view/material/checklist_modal.ejs

@@ -81,4 +81,33 @@
         </div>
     </div>
 </div>
+<div class="modal fade" id="okedit" 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>
+                    <h5>导入并处理文件数据:</h5>
+                    <div class="progress">
+                        <div id="export-progress" class="progress-bar" role="progressbar" style="width: 100%;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">100%</div>
+                    </div>
+                </div>
+                <div id="bill-detail" style="display: none" class="mt-3">
+                    <h5>处理清单及工料生成:</h5>
+                    <div class="progress">
+                        <div id="bill-progress" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+                    </div>
+                </div>
+                <div id="list-detail" style="display: none" class="mt-3">
+                    <h5>正在导入清单 <b>203-1-a</b> 的工料含量:</h5>
+                    <div class="progress">
+                        <div id="list-progress" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
 <% include ./audit_modal.ejs %>

+ 1 - 1
app/view/material/list.ejs

@@ -105,7 +105,7 @@
     const materialStageBillsData = isStageSelf ? JSON.parse(unescape('<%- escape(JSON.stringify(materialStageBillsData)) %>')) : [];
     const readOnly = <%- material.readOnly %>;
     const openMaterialSelf = parseInt(<%- ctx.session.sessionProject.page_show.openMaterialSelf %>);
-    const editListPermission = <%- material.editListPermission %>;
+    const editListPermission = <%- material.editListPermission ? material.editListPermission : false %>;
     const stage_order = <%- material.order %>;
     const materialID = <%- material.id %>;
     const tenderID = <%- tender.id %>;

+ 4 - 0
config/web.js

@@ -699,6 +699,10 @@ const JsFiles = {
             },
             checklist: {
                 files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js',
+                    '/public/js/js-xlsx/xlsx.full.min.js',
+                    '/public/js/js-xlsx/xlsx.utils.js',
+                    '/public/js/export/jschardet.min.js',
+                    '/public/js/export/iconv-lite.js',
                     // '/public/js/calc/calcEval.min.js'
                 ],
                 mergeFiles: [