Browse Source

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

TonyKang 5 years ago
parent
commit
31df6c6bb3
58 changed files with 3145 additions and 1329 deletions
  1. 34 0
      app/const/material.js
  2. 43 2
      app/controller/deal_bills_controller.js
  3. 150 0
      app/controller/material_controller.js
  4. 6 2
      app/controller/stage_controller.js
  5. 0 59
      app/controller/tender_controller.js
  6. 24 24
      app/extend/helper.js
  7. 1 1
      app/lib/stage_im.js
  8. 12 5
      app/public/css/main.css
  9. 203 2
      app/public/js/ledger.js
  10. 116 46
      app/public/js/material.js
  11. 454 0
      app/public/js/material_exponent.js
  12. 17 0
      app/public/js/material_file.js
  13. 15 34
      app/public/js/material_list.js
  14. 1 1
      app/public/js/se_jgcl.js
  15. 1 1
      app/public/js/se_other.js
  16. 1 1
      app/public/js/spreadjs_rela/spreadjs_zh.js
  17. 47 21
      app/public/js/stage.js
  18. 11 15
      app/public/js/stage_im.js
  19. 9 9
      app/public/js/tender_list_info.js
  20. 22 16
      app/public/js/tender_list_progress.js
  21. 4 0
      app/router.js
  22. 64 28
      app/service/change.js
  23. 87 2
      app/service/deal_bills.js
  24. 6 2
      app/service/login_logging.js
  25. 58 3
      app/service/material.js
  26. 26 0
      app/service/material_audit.js
  27. 5 3
      app/service/material_bills.js
  28. 249 0
      app/service/material_exponent.js
  29. 37 0
      app/service/material_exponent_history.js
  30. 50 20
      app/service/material_list.js
  31. 1 0
      app/service/material_month.js
  32. 2 4
      app/service/project_account.js
  33. 4 4
      app/service/report_memory.js
  34. 16 0
      app/service/stage.js
  35. 3 1
      app/service/stage_bonus.js
  36. 9 9
      app/service/stage_change.js
  37. 9 4
      app/service/stage_jgcl.js
  38. 5 2
      app/service/stage_other.js
  39. 61 15
      app/service/tender_info.js
  40. 1 0
      app/view/material/audit_modal.ejs
  41. 123 0
      app/view/material/exponent.ejs
  42. 1 0
      app/view/material/exponent_modal.ejs
  43. 14 4
      app/view/material/file.ejs
  44. 18 5
      app/view/material/index.ejs
  45. 18 4
      app/view/material/info.ejs
  46. 1 1
      app/view/material/info_modal.ejs
  47. 8 1
      app/view/material/material_sub_menu.ejs
  48. 15 1
      app/view/material/material_sub_mini_menu.ejs
  49. 5 1
      app/view/measure/stage.ejs
  50. 17 0
      app/view/measure/stage_modal.ejs
  51. 23 46
      app/view/profile/safe.ejs
  52. 5 4
      app/view/profile/sms.ejs
  53. 33 11
      app/view/shares/ledger_check_modal.ejs
  54. 912 912
      app/view/stage/audit_modal.ejs
  55. 1 1
      app/view/tender/detail_modal.ejs
  56. 1 1
      config/menu.js
  57. 17 1
      config/web.js
  58. 69 0
      sql/update.sql

+ 34 - 0
app/const/material.js

@@ -23,8 +23,42 @@ const m_type = [
     { text: '半成品', value: 6 },
     { text: '其他', value: 7 },
 ];
+// 指数调差类型
+const ex_type = [
+    { text: '定值', value: 1 },
+    { text: '变值', value: 2 },
+];
+
+const ex_calc = [
+    {
+        code: 'bqht',
+        text: '本期合同计量金额',
+        value: '',
+        select: false,
+    },
+    {
+        code: 'bqbg',
+        text: '本期变更计量金额',
+        value: '',
+        select: false,
+    },
+    {
+        code: 'bqwc',
+        text: '本期完成计量金额',
+        value: '',
+        select: true,
+    },
+];
+
+const is_summary = {
+    yes: 1,
+    no: 2,
+};
 
 module.exports = {
     t_type,
     m_type,
+    ex_type,
+    is_summary,
+    ex_calc,
 };

+ 43 - 2
app/controller/deal_bills_controller.js

@@ -18,6 +18,7 @@ const loadExcelType = {
     actual: 2,
 };
 const loadType = loadExcelType.display;
+const auditConst = require('../const/audit').ledger;
 
 module.exports = app => {
     class DealBillsController extends app.BaseController {
@@ -34,7 +35,10 @@ module.exports = app => {
                 data: [],
             };
             try {
-                responseData.data = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                responseData.data = await ctx.service.dealBills.getAllDataByCondition({
+                    where: { tender_id: ctx.tender.id },
+                    orders: [['order', 'asc']],
+                });
             } catch (error) {
                 this.log(error);
                 responseData.err = 1;
@@ -81,7 +85,10 @@ module.exports = app => {
                     }
                 }
 
-                const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({
+                    where: { tender_id: ctx.tender.id },
+                    orders: [['order', 'asc']],
+                });
                 ctx.body = {err: 0, msg: '', data: dealBills};
             } catch (err) {
                 this.log(err);
@@ -137,6 +144,40 @@ module.exports = app => {
                 }
             }
         }
+
+        async _base(ctx, type, data) {
+            if (isNaN(data.id) || data.id <= 0) throw '数据错误';
+            if (type !== 'add') {
+                if (isNaN(data.count) || data.count <= 0) data.count = 1;
+            }
+            switch (type) {
+                case 'add':
+                    return await ctx.service.dealBills.addNodeBatch(ctx.tender.id, data.id, {}, data.count);
+                case 'delete':
+                    return await ctx.service.dealBills.delete(ctx.tender.id, data.id, data.count);
+            }
+        }
+
+        /**
+         * 更新清单相关 (Ajax)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async update(ctx) {
+            try {
+                if (!ctx.tender.data) throw '标段数据错误';
+                if (ctx.tender.data.user_id !== ctx.session.sessionUser.accountId ||
+                    (ctx.tender.ledger_status === auditConst.status.checking || ctx.tender.ledger_status === auditConst.status.checked))
+                    throw '您无权进行该操作';
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.dealBills.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err);
+            }
+
+        }
     }
 
     return DealBillsController;

+ 150 - 0
app/controller/material_controller.js

@@ -248,6 +248,7 @@ module.exports = app => {
             const monthsList = [];
             if (ctx.material.months) {
                 const material_month = ctx.material.months.split(',');
+                material_month.sort();
                 const materialMonthList = await ctx.service.materialMonth.getListByMid(ctx.material.id);
                 for (const mbd of materialBillsData) {
                     const one_mb = {
@@ -268,6 +269,40 @@ module.exports = app => {
         }
 
         /**
+         * 检查旧数据是否存在指数法调差数据,没有自动添加定值
+         * @param ctx
+         * @return {Promise<void>}
+         * @private
+         */
+        async _checkExponentExist(ctx) {
+            const material_exponent_constant = await ctx.service.materialExponent.getDataByCondition({
+                tid: ctx.tender.id,
+                type: materialConst.ex_type[0].value,
+            });
+            // 不存在则生成指数调差清单表定值和历史期定值(防止变更定值发生变化)
+            if (!material_exponent_constant) {
+                await ctx.service.materialExponent.addOldData();
+            }
+        }
+
+        /**
+         * 获取当前期指数列表信息
+         * @param ctx
+         * @return {Promise<void>}
+         * @private
+         */
+        async _getMaterialExponentData(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.materialExponent.getAllDataByCondition({ where: searchsql });
+        }
+
+        /**
          * 调差工料页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @return {Promise<void>}
@@ -296,6 +331,7 @@ module.exports = app => {
 
                 // 取当前期截止上期含税金额
                 renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order);
+                renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order);
 
                 renderData.months = ctx.material.months ? ctx.material.months.split(',') : [];
                 renderData.monthsList = await this._getMaterialMonthsData(ctx, renderData.materialBillsData);
@@ -346,6 +382,58 @@ module.exports = app => {
         }
 
         /**
+         * 指数调差页面 (Get)
+         * @param {Object} ctx - egg全局变量
+         * @return {Promise<void>}
+         */
+        async exponent(ctx) {
+            try {
+                await this._checkExponentExist(ctx);
+                await this._getMaterialAuditViewData(ctx);
+                const renderData = await this._getDefaultRenderData(ctx);
+                const stage_list = await ctx.service.stage.getStageMsgByStageId(ctx.material.stage_id);
+                renderData.ex_calc = materialConst.ex_calc;
+                if (!ctx.material.ex_calc) {
+                    const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
+                    for (const bq of renderData.ex_calc) {
+                        const calc = _.find(calcBase, function(item) {
+                            return bq.code === item.code;
+                        });
+                        bq.value = calc.value;
+                        if (calc.code === 'bqwc') {
+                            ctx.material.ex_expr = calc.value.toString() + '*[0-1]';
+                        }
+                    }
+                    // 并更新至调差表中
+                    await ctx.service.materialExponent.update({ ex_calc: renderData.ex_calc, ex_expr: ctx.material.ex_expr }, { id: ctx.material.id });
+                } else {
+                    renderData.ex_calc = JSON.parse(ctx.material.ex_calc);
+                }
+
+                renderData.materialExponentData = await this._getMaterialExponentData(ctx);
+                // 取对应期的截取上期的调差金额和应耗数量
+                if (ctx.material.highOrder !== ctx.material.order) {
+                    for (const [mindex, me] of renderData.materialExponentData.entries()) {
+                        const result = await ctx.service.materialExponentHistory.getByMeId(ctx.material.id, ctx.material.order, me.id);
+                        _.forEach(result, function(value, key) {
+                            renderData.materialExponentData[mindex][key] = result ? result[key] : null;
+                        });
+                    }
+                }
+
+                // 取当前期截止上期含税金额
+                renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order);
+                renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order);
+                renderData.materialType = materialConst;
+                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.exponent);
+                await this.layout('material/exponent.ejs', renderData, 'material/exponent_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/material');
+            }
+        }
+
+        /**
          * 附件页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @return {Promise<void>}
@@ -561,6 +649,68 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 指数调差 - 编辑指数清单项 (Ajax)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveExponentData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {
+                    err: 0,
+                    msg: '',
+                    data: {},
+                };
+                let ex_tp = ctx.material.ex_tp;
+                let ex_expr = ctx.material.ex_expr;
+                switch (data.type) {
+                    case 'add':
+                        responseData.data = await ctx.service.materialExponent.add();
+                        break;
+                    case 'del':
+                        [ex_tp, ex_expr] = await ctx.service.materialExponent.del(data.id);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        break;
+                    case 'update':
+                        [ex_tp, ex_expr] = await ctx.service.materialExponent.save(data.updateData);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        break;
+                    case 'rate':
+                        // 判断数量是否为数字
+                        if (isNaN(data.rate)) {
+                            throw '不能输入其它非数字类型字符';
+                        }
+                        if (ctx.material.readOnly) {
+                            throw '无权操作';
+                        }
+                        await ctx.service.material.changeRate(data.rate);
+                        break;
+                    case 'paste':
+                        [ex_tp, ex_expr] = await ctx.service.materialExponent.saveDatas(data.updateData);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        // 取所有指数清单
+                        responseData.data.info = await this._getMaterialExponentData(ctx);
+                        break;
+                    case 'ex_calc':
+                        // 判断数量是否为数字
+                        [ex_tp, ex_expr] = await ctx.service.material.changeExCalc(data.updateData);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        break;
+                    default: throw '参数有误';
+                }
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         // 审批相关
         /**
          * 添加审批人

+ 6 - 2
app/controller/stage_controller.js

@@ -439,8 +439,12 @@ module.exports = app => {
                 }
                 const bills = data.bills ? data.bills : await ctx.service.ledger.getDataById(data.pos.lid);
                 const pos = data.pos;
-                const changes = await ctx.service.change.getValidChanges(ctx.tender.id, bills);
-                const useChanges = await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, bills.id, pos ? pos.id : -1);
+                const changes = ctx.stage.readOnly
+                    ? await ctx.service.change.getAuditValidChanges(ctx.tender.id, bills, null, ctx.stage.curTimes, ctx.stage.curOrder)
+                    : await ctx.service.change.getValidChanges(ctx.tender.id, bills);
+                const useChanges = ctx.stage.readOnly
+                    ? await ctx.service.stageChange.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder, bills.id, pos ? pos.id : -1)
+                    : await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, bills.id, pos ? pos.id : -1);
                 ctx.body = { err: 0, msg: '', data: { changes, useChanges } };
             } catch (err) {
                 this.log(err);

+ 0 - 59
app/controller/tender_controller.js

@@ -116,40 +116,6 @@ module.exports = app => {
 
                 const tenderList = await this.ctx.service.tender.getList('', userPermission);
 
-                // for (const t of tenderList) {
-                //     if (t.user_id === this.ctx.session.sessionUser.accountId && (
-                //         t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
-                //         const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
-                //         t.total_price = sum.total_price;
-                //         t.deal_tp = sum.deal_tp;
-                //     }
-                //     if (t.ledger_status === auditConst.ledger.status.checked) {
-                //         t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
-
-                //         if (!t.lastStage) continue;
-                //         if (t.lastStage.status === auditConst.stage.status.uncheck &&
-                //             t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                //             t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
-                //         }
-
-                //         if (!t.lastStage) continue;
-                //         await this.ctx.service.stage.checkStageGatherData(t.lastStage);
-                //     }
-                //     if (t.lastStage) {
-                //         if (t.lastStage.status === auditConst.stage.status.uncheck) {
-                //             const status_name = await this.ctx.service.projectAccount.getAccountInfoById(t.lastStage.user_id);
-                //             t.status_users = status_name ? status_name.name : '';
-                //         } else {
-                //             const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(t.lastStage.id, t.lastStage.status, t.lastStage.times);
-                //             t.status_users = status_name ? status_name.name : '';
-                //         }
-                //     } else {
-                //         if (t.ledger_status !== auditConst.ledger.status.uncheck) {
-                //             const status_name = await this.ctx.service.ledgerAudit.getStatusName(t.id, t.ledger_times);
-                //             t.status_users = status_name ? status_name.name : '';
-                //         }
-                //     }
-                // }
                 for (const t of tenderList) {
                     if (t.user_id === this.ctx.session.sessionUser.accountId && (
                             t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
@@ -175,31 +141,6 @@ module.exports = app => {
                         await this._getLedgerAuditInfo(t);
                     }
                 }
-                // tenderList.forEach(async t => {
-                //     if (t.user_id === this.ctx.session.sessionUser.accountId && (
-                //             t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
-                //         const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
-                //         t.total_price = sum.total_price;
-                //         t.deal_tp = sum.deal_tp;
-                //     }
-                //     t.advance_tp = await this.ctx.service.advance.getSumAdvance(t.id);
-                //
-                //     if (t.ledger_status === auditConst.ledger.status.checked) {
-                //         t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
-                //         if (t.lastStage && t.lastStage.status === auditConst.stage.status.uncheck &&
-                //             t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                //             t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
-                //         }
-                //         if (t.lastStage) await this.ctx.service.stage.checkStageGatherData(t.lastStage);
-                //         t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
-                //     }
-                //
-                //     if (t.lastStage) {
-                //         await this._getStageAuditInfo(t, t.lastStage);
-                //     } else {
-                //         await this._getLedgerAuditInfo(t);
-                //     }
-                // });
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 const renderData = {

+ 24 - 24
app/extend/helper.js

@@ -987,30 +987,30 @@ module.exports = {
     },
 
     async sendAliSms(userId, type, judge, code, data = {}) {
-        const mobiles = [];
-        if (!userId || (userId instanceof Array && userId.length === 0)) return;
-        const smsUser = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: userId } });
-        for (const su of smsUser) {
-            if (!su.auth_mobile || su.auth_mobile === '') continue;
-            if (!su.sms_type || su.sms_type === '') continue;
-
-            const smsType = JSON.parse(su.sms_type);
-            if (smsType[type] && smsType[type].indexOf(judge) !== -1) {
-                mobiles.push(su.auth_mobile);
-            }
-        }
-
-        if (mobiles.length > 0) {
-            const sms = new SMS(this.ctx);
-            const tenderName = await sms.contentChange(this.ctx.tender.data.name);
-            const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
-            const param = {
-                project: projectName,
-                number: tenderName,
-            };
-            const postParam = Object.assign(param, data);
-            sms.aliSend(mobiles, postParam, code);
-        }
+        // const mobiles = [];
+        // if (!userId || (userId instanceof Array && userId.length === 0)) return;
+        // const smsUser = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: userId } });
+        // for (const su of smsUser) {
+        //     if (!su.auth_mobile || su.auth_mobile === '') continue;
+        //     if (!su.sms_type || su.sms_type === '') continue;
+        //
+        //     const smsType = JSON.parse(su.sms_type);
+        //     if (smsType[type] && smsType[type].indexOf(judge) !== -1) {
+        //         mobiles.push(su.auth_mobile);
+        //     }
+        // }
+        //
+        // if (mobiles.length > 0) {
+        //     const sms = new SMS(this.ctx);
+        //     const tenderName = await sms.contentChange(this.ctx.tender.data.name);
+        //     const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
+        //     const param = {
+        //         project: projectName,
+        //         number: tenderName,
+        //     };
+        //     const postParam = Object.assign(param, data);
+        //     sms.aliSend(mobiles, postParam, code);
+        // }
     },
 
 

+ 1 - 1
app/lib/stage_im.js

@@ -859,7 +859,7 @@ class StageIm {
                     end_tp: p.end_gather_tp, end_contract_tp: p.end_contract_tp, end_qc_tp: p.end_qc_tp,
                     bw,
                     peg: peg ? this._getPegStr(peg.name) : '',
-                    xm: node.name, jldy: node.name,
+                    xm: node.name,
                     drawing_code: this._getDrawingCode(p),
                     changes: [],
                     position: '',

+ 12 - 5
app/public/css/main.css

@@ -3,7 +3,11 @@
 body {
     font-size: 0.9rem;
     overflow: hidden;
-    background: #e4e7ea
+    background: #e4e7ea;
+
+}
+.text-warning {
+  color: #da9500 !important;
 }
 .dropdown-menu {
     font-size: 12px
@@ -1119,17 +1123,20 @@ a.maintain-icon:hover .fa{
   margin: 0 0 0 70px;
   word-break: break-word;
 }
-
 .book-list{
   padding: 0;
-  margin: 0;
-  height: 285px;
-  overflow-y: auto;
+margin: 0;
+height: 285px;
+overflow-y: auto;
 }
 .book-list dt{
   padding:5px 0 5px 5px;
   background-color: #f2f2f2;
 }
+.book-list dd{
+  padding-left:15px;
+  cursor: pointer;
+}
 .book-list dd:hover{
   background-color: #f2f2f2
 }

+ 203 - 2
app/public/js/ledger.js

@@ -2198,7 +2198,7 @@ $(document).ready(function() {
                             {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
                             {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
                             {title: '数量', field: 'quantity', hAlign: 2, width: 50},
-                            {title: '金额', field: 'total_price', hAlign: 2, width: 50},
+                            {title: '金额', field: 'total_price', hAlign: 2, width: 50, readOnly: true},
                         ],
                         emptyRows: 3,
                         headRows: 1,
@@ -2266,6 +2266,180 @@ $(document).ready(function() {
             this.spread = SpreadJsObj.createNewSpread(this.obj);
             this.sheet = this.spread.getActiveSheet();
             SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
+
+
+            this.OprObj = {
+                /**
+                 * 删除按钮响应事件
+                 * @param sheet
+                 */
+                deletePress: function (sheet) {
+                    if (!sheet.zh_setting || readOnly || sheet.zh_setting.readOnly) return;
+
+                    const sortData = sheet.zh_data;
+                    const datas = [];
+                    const sels = sheet.getSelections();
+                    if (!sels || !sels[0]) return;
+
+                    for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                        let bDel = false;
+                        const node = sortData[iRow];
+                        if (node) {
+                            const data = {id: node.id};
+                            for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].colCount; iCol++) {
+                                const colSetting = sheet.zh_setting.cols[iCol];
+                                if (colSetting.field === 'code') {
+                                    toastr.error('清单编号不能为空,如需删除签约清单请使用右键删除');
+                                    return;
+                                }
+                                const style = sheet.getStyle(iRow, iCol);
+                                if (!style.locked) {
+                                    const colSetting = sheet.zh_setting.cols[iCol];
+                                    data[colSetting.field] = null;
+                                    bDel = true;
+                                }
+                            }
+                            if (bDel) {
+                                datas.push(data);
+                            }
+                        }
+                    }
+                    if (datas.length > 0) {
+                        postData(self.url + '/update', {update: datas}, function (result) {
+                            self.loadUpdateData(result);
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        }, function () {
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        });
+                    }
+                },
+                delete: function (sheet) {
+                    if (!sheet.zh_setting || readOnly || sheet.zh_setting.readOnly) return;
+
+                    const sortData = sheet.zh_data;
+                    const datas = [];
+                    const sels = sheet.getSelections();
+                    if (!sels || !sels[0]) return;
+
+                    for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                        const node = sortData[iRow];
+                        datas.push(node.id);
+                    }
+                    if (datas.length > 0) {
+                        postData(self.url + '/update', {del: datas}, function (result) {
+                            self.loadUpdateData(result);
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        }, function () {
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        });
+                    }
+                },
+                editEnded: function (e, info) {
+                    if (!info.sheet.zh_setting || !info.sheet.zh_data || info.sheet.zh_setting.readOnly) return;
+
+                    const node = info.sheet.zh_data[info.row];
+                    const col = info.sheet.zh_setting.cols[info.col];
+                    const data = {};
+
+                    if (node) {
+                        data.update = {};
+                        data.update.id = node.id;
+
+                        const oldValue = node ? node[col.field] : null;
+                        const newValue = trimInvalidChar(info.editingText);
+                        if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                        data.update[col.field] = newValue;
+                    } else {
+                        if (col.field !== 'code') {
+                            toastr.warning('新增签约清单,请先输入清单编号');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                        data.add = {};
+                        data.add.order = info.row + 1;
+                        data.add.code = trimInvalidChar(info.editingText);
+                    }
+
+                    postData(self.url +  '/update', data, function (result) {
+                        self.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                    }, function () {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                },
+                clipboardPasting(e, info) {
+                    const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+                    info.cancel = true;
+
+                    if (!setting || !sortData || setting.readOnly) return;
+                    const pasteData = info.pasteData.html
+                        ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                        : (info.pasteData.text === ''
+                            ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                            : SpreadJsObj.analysisPasteText(info.pasteData.text));
+                    const hint = {
+                        code: {type: 'warning', msg: '签约清单编号不可为空,已过滤'},
+                        unit_price: {type: 'warning', msg: '输入的 单价 非法,已过滤'},
+                        quantity: {type: 'warning', msg: '输入的 数量 非法,已过滤'},
+                    };
+
+                    const uDatas = [], iDatas = [];
+                    for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                        const curRow = info.cellRange.row + iRow;
+                        const node = sortData[curRow];
+
+                        let bPaste = false;
+                        const data = {};
+                        for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                            const curCol = info.cellRange.col + iCol;
+                            const colSetting = setting.cols[curCol];
+                            const value = trimInvalidChar(pasteData[iRow][iCol]);
+
+                            if (colSetting.field === 'code' && (!value || value === '')) {
+                                toastMessageUniq(hint.code);
+                                break;
+                            }
+                            if (colSetting.type === 'Number') {
+                                const num = _.toNumber(value);
+                                if (num) {
+                                    data[colSetting.field] = num;
+                                    bPaste = true;
+                                } else if (colSetting.field === 'unit_price') {
+                                    toastMessageUniq(hint.unit_price);
+                                } else if (colSetting.field === 'quantity') {
+                                    toastMessageUniq(hint.quantity);
+                                }
+                            } else {
+                                data[colSetting.field] = value;
+                                bPaste = true;
+                            }
+                        }
+                        if (bPaste) {
+                            if (node) {
+                                data.id = node.id;
+                                uDatas.push(data);
+                            } else {
+                                data.order = curRow + 1;
+                                iDatas.push(data);
+                            }
+                        }
+                    }
+                    const updateData = {};
+                    if (uDatas.length > 0) updateData.update = uDatas;
+                    if (iDatas.length > 0) updateData.add = iDatas;
+                    if (uDatas.length > 0 || iDatas.length > 0) {
+                        postData(self.url + '/update', updateData, function (result) {
+                            self.loadUpdateData(result);
+                            SpreadJsObj.reLoadSheetData(info.sheet);
+                        });
+                    } else {
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                    }
+                },
+            };
             if (!readOnly) {
                 this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
                     const dealSheet = info.sheet;
@@ -2305,6 +2479,9 @@ $(document).ready(function() {
                         posOperationObj.loadCurPosData();
                     });
                 });
+                this.spread.bind(spreadNS.Events.EditEnded, this.OprObj.editEnded);
+                this.spread.bind(spreadNS.Events.ClipboardPasting, this.OprObj.clipboardPasting);
+                SpreadJsObj.addDeleteBind(this.spread, this.OprObj.deletePress);
             }
             $('#upload-deal-bills').click(function () {
                     const file = $('#deal-bills-file')[0];
@@ -2359,7 +2536,7 @@ $(document).ready(function() {
                                 return !select;
                             },
                             callback: function (key, opt) {
-
+                                self.OprObj.delete(self.sheet);
                             },
                         },
                         sprEdit: '---------',
@@ -2412,6 +2589,30 @@ $(document).ready(function() {
                 }
             }
         }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+        }
     }
     class BatchInsertBillsPosObj {
         constructor (obj) {

+ 116 - 46
app/public/js/material.js

@@ -95,23 +95,6 @@ const is_numeric = (value) => {
     }
 };
 
-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]);
@@ -128,13 +111,14 @@ $(document).ready(() => {
             {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.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: '|1', rowSpan: '|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'},
             {title: '|下跌幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 100, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
-            {title: '|调差金额', colSpan: '|1', rowSpan: '1|1', field: 'm_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
+            {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '截止上期调差金额', colSpan: '1', rowSpan: '2', field: 'pre_tp', hAlign: 2, width: 120, type: 'Number', readOnly: true},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.remark'},
+            {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -297,6 +281,9 @@ $(document).ready(() => {
             if (info.sheet.zh_setting) {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field === 'is_summary') {
+                    return;
+                }
                 // 未改变值则不提交
                 const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                 const orgValue = select[col.field];
@@ -389,6 +376,30 @@ $(document).ready(() => {
                 });
             }
         },
+        buttonClicked: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (materialCol.readOnly.isEdit(select)) {
+                    return;
+                }
+                if (col.field === 'is_summary') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                    // 更新至服务器
+                    postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                        m_tp = result.m_tp;
+                        resetTpTable();
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    }, function () {
+                        select.is_summary = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
+            }
+        },
         deletePress: function (sheet) {
             return;
         },
@@ -544,15 +555,80 @@ $(document).ready(() => {
     // msg_range.cellType(new DatePickerCellType());
     // msg_range.formatter("yyyy-MM-dd");
     sheet.resumePaint();
+    const static_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'},
+    ];
+    // 月信息价方法集合
+    const monthFunGather = {
+        _setMonthHtml: function() {
+            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);
+        },
+        _monthHeaderSet: function() {
+            const newMonths = [];
+            for (const m of months) {
+                const year = m.split('-')[0];
+                const month = parseInt(m.split('-')[1]);
+                let one = _.find(newMonths, { 'year': year });
+                let oneIndex = _.findIndex(newMonths, { 'year': year });
+                if (one) {
+                    one.month.push(month);
+                    newMonths.splice(oneIndex, 1, one);
+                } else {
+                    one = {
+                        year: year,
+                        month: [ month ],
+                    };
+                    newMonths.push(one);
+                }
+            }
+            const pushMonth = [];
+            for(const mo of newMonths) {
+                for (let i in mo.month) {
+                    i = parseInt(i);
+                    const newCols = {
+                        title: (i === 0 ? mo.year: '') + '|' + mo.month[i] + '月',
+                        colSpan: (i === 0 ? (mo.month.length === 1 ? '0' : mo.month.length) : '') + '|1',
+                        rowSpan: i === 0 ? '1|1' : '|1',
+                        field: mo.year + '-' + (mo.month[i] < 10 ? '0' + mo.month[i] : mo.month[i]),
+                        hAlign: 2, width: 60, type: '@', readOnly: 'readOnly.isEdit'};
+                    pushMonth.push(newCols);
+                }
+            }
+            return pushMonth;
+        },
+        monthSheetReset: function () {
+            const monthCols = monthFunGather._monthHeaderSet();
+            const newMonthSpreadHeaderCols = static_cols.concat(monthCols);
+            materialMonthSpreadSetting.cols = newMonthSpreadHeaderCols;
+            // 表头变化需要重新绘制,不然报错;
+            materialMonthSpread.getActiveSheet().reset();
+            SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
+            SpreadJsObj.initSheet(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
+            SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
+            monthFunGather._setMonthHtml();
+        }
+    }
 
     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'},
-        ],
+        cols: static_cols,
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
@@ -562,11 +638,9 @@ $(document).ready(() => {
         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 monthCols = monthFunGather._monthHeaderSet();
+        const monthSpreadHeaderCols = static_cols.concat(monthCols);
+        materialMonthSpreadSetting.cols = monthSpreadHeaderCols;
     }
 
     const materialMonthCol = {
@@ -725,7 +799,9 @@ $(document).ready(() => {
         $('#add').click(materialSpreadObj.add);
         $('#del').click(materialSpreadObj.del);
         materialSpread.bind(spreadNS.Events.EditEnded, materialSpreadObj.editEnded);
+        materialSpread.bind(spreadNS.Events.ButtonClicked, materialSpreadObj.buttonClicked);
         materialMonthSpread.bind(spreadNS.Events.EditEnded, materialMonthSpreadObj.editEnded);
+
         // 右键菜单
         $.contextMenu({
             selector: '#material-spread',
@@ -765,9 +841,13 @@ $(document).ready(() => {
             const rate = parseInt($(this).val());
             postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
                 const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
+                const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), 2);
                 const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), 2);
+                const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), 2);
                 $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
                 $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+                $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
             });
         });
 
@@ -941,17 +1021,10 @@ $(document).ready(() => {
                 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);
+                months.sort();
                 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();
-
+                monthFunGather.monthSheetReset();
                 // 工料表单价显示也要更新
                 materialBillsData = data.materialBillsData;
                 SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
@@ -972,18 +1045,15 @@ $(document).ready(() => {
             }
             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(materialMonthSpreadSetting.cols, function (n) {
+                    //     return n.field === dm;
+                    // });
                     _.remove(months, function (n) {
                         return n === dm;
                     });
                 }
                 monthsList = data.monthsList;
-                SpreadJsObj.reinitSheetHeader(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
-                SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
-                SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
-                setMonthHtml();
+                monthFunGather.monthSheetReset();
 
                 // 工料表单价显示也要更新
                 materialBillsData = data.materialBillsData;

+ 454 - 0
app/public/js/material_exponent.js

@@ -0,0 +1,454 @@
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};
+function getPasteHint (str, row = '') {
+    let returnObj = str;
+    if (row) {
+        returnObj.msg = '指数清单第' + (row+1) + '行' + str.msg;
+    }
+    return returnObj;
+}
+function resetExTpTable() {
+    const rate = $('#changeRate').val();
+    const bqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), 2);
+    const jzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, bqhs), 2);
+    $('#tp_set').find('td').eq(3).text(ZhCalc.round(ex_tp, 2));
+    $('#tp_set').find('td').eq(4).text(ZhCalc.round(ZhCalc.add(ex_pre_tp, ex_tp), 2));
+    $('#rate_set').find('td').eq(3).text(bqhs !== 0 ? bqhs : '');
+    $('#rate_set').find('td').eq(4).text(jzbqhs !== 0 ? jzbqhs : '');
+    $('#ex_expr').html(ex_expr);
+}
+$(document).ready(() => {
+    autoFlashHeight();
+    const materialExponentSpread = SpreadJsObj.createNewSpread($('#material-exponent-spread')[0]);
+    const materialExponentSpreadSetting = {
+        cols: [
+            {title: '类型', colSpan: '1', rowSpan: '2', field: 'type', hAlign: 1, width: 60, formatter: '@', readOnly: true,cellType: 'customizeCombo', comboItems: materialType.ex_type, cellTypeKey: 1},
+            {title: '符号', colSpan: '1', rowSpan: '2', field: 'symbol', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '符号说明', colSpan: '1', rowSpan: '2', field: 'symbol_desc', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '加权系数', colSpan: '1', rowSpan: '2', field: 'weight_num', hAlign: 2, width: 120, formatter: '@', readOnly: 'readOnly.isConstant'},
+            {title: '基本价格指数', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 120, readOnly: 'readOnly.isEdit'},
+            {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '现行价格指数', colSpan: '1', rowSpan: '2', field: 'm_price', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.remark'},
+            {title: '计算值', colSpan: '1', rowSpan: '2', field: 'calc_num', hAlign: 2, width: 80, formatter: '@', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.remark'},
+            {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: readOnly,
+    };
+    const materialExponentBase = {
+        isUsed: function (data) {
+            if (data.type === 2) {
+                return data.mid === materialID;
+            } else {
+                return false;
+            }
+        },
+        isEdit: function (data) {
+            return data.mid === materialID && data.type === 2;
+        },
+        isConstant: function (data) {
+            return (materialOrder === 1 && data.type === 1) || (data.mid === materialID && data.type === 2);
+        }
+    };
+    const materialExponentCol = {
+        getValue: {
+            calc_num : function (data) {
+                const calc_num = data.basic_price > 0 ? ZhCalc.mul(data.weight_num, ZhCalc.div(data.m_price, data.basic_price)) : 0;
+                return calc_num > 0 ? ZhCalc.round(calc_num, 3) : 0;
+            },
+        },
+        readOnly: {
+            isEdit: function (data) {
+                return !(!readOnly && materialExponentBase.isEdit(data));
+            },
+            isUsed: function (data) {
+                return !(!readOnly && materialExponentBase.isUsed(data));
+            },
+            remark: function (data) {
+                return !(!readOnly && data.type === 2);
+            },
+            isConstant: function (data) {
+                return !(!readOnly && materialExponentBase.isConstant(data));
+            }
+        },
+    };
+    SpreadJsObj.initSpreadSettingEvents(materialExponentSpreadSetting, materialExponentCol);
+    SpreadJsObj.initSheet(materialExponentSpread.getActiveSheet(), materialExponentSpreadSetting);
+    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+
+    const materialExponentSpreadObj = {
+        refreshActn: function (rowCount = 1) {
+            const setObjEnable = function (obj, enable) {
+                if (enable) {
+                    obj.removeClass('disabled');
+                } else {
+                    obj.addClass('disabled');
+                }
+            };
+            const sheet = materialExponentSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(sheet);
+            // 还需判断是否已被调差清单调用
+            setObjEnable($('#del'), !readOnly && select && materialExponentBase.isUsed(select) && rowCount === 1);
+        },
+        add: function () {
+            const sheet = materialExponentSpread.getActiveSheet();
+            postData(window.location.pathname + '/save', {type: 'add'}, function (result) {
+                if (result) {
+                    materialExponentData.push(result);
+                    sheet.addRows(materialExponentData.length - 1, 1);
+                    SpreadJsObj.reLoadRowData(sheet, materialExponentData.length - 1);
+                    sheet.setSelection(materialExponentData.length - 1, 0, 1, 1);
+                    materialExponentSpreadObj.refreshActn();
+                }
+            });
+        },
+        del: function () {
+            const sheet = materialExponentSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(sheet);
+            postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
+                ex_tp = result.ex_tp;
+                ex_expr = result.ex_expr;
+                resetExTpTable();
+                const index = materialExponentData.indexOf(select);
+                materialExponentData.splice(index, 1);
+                sheet.deleteRows(index, 1);
+                const sel = sheet.getSelections();
+                sheet.setSelection(index > 0 ? index - 1 : 0, sel.length > 0 ? sel[0].col : 0, 1, 1);
+                materialExponentSpreadObj.refreshActn();
+            });
+        },
+        selectionChanged: function (e, info) {
+            const sel = info.sheet.getSelections()[0];
+            const col = info.sheet.zh_setting.cols[sel.col];
+            materialExponentSpreadObj.refreshActn(sel.rowCount);
+            const data = SpreadJsObj.getSelectObject(info.sheet);
+            materialExponentSpreadObj.setReadOnly(true);
+        },
+        editEnded: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field === 'is_summary') {
+                    return;
+                }
+                // 未改变值则不提交
+                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 (col.field === 'basic_price' || col.field === 'm_price') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (validText !== null && (num < 0 || !/^(\d{1,10}|\d{1,7}\.\d{1,3})?$/.test(num))) {
+                        toastr.error('请输入10位以内有效数字并且小于3位小数的浮点数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                if (col.field === 'weight_num') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (validText !== null && (num < 0 || num >= 1 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                        toastr.error('请输入0~1范围内并且小于3位小数的浮点数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
+                    if (total_weight > 1) {
+                        toastr.error('加权系数总和不能大于1');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                select[col.field] = validText;
+                select.calc_num = materialExponentCol.getValue.calc_num(select);
+                // console.log(select);
+
+                // 更新至服务器
+                postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                    ex_tp = result.ex_tp;
+                    ex_expr = result.ex_expr;
+                    resetExTpTable();
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    materialExponentData.splice(info.row, 1, select);
+                }, function () {
+                    select[col.field] = orgValue;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            }
+        },
+        buttonClicked: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (materialExponentCol.readOnly.isEdit(select)) {
+                    return;
+                }
+                if (col.field === 'is_summary') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                    // 更新至服务器
+                    postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                        ex_tp = result.ex_tp;
+                        ex_expr = result.ex_expr;
+                        resetExTpTable();
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    }, function () {
+                        select.is_summary = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                        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: '请粘贴10位以内有效数字并且小于3位小数的浮点数'},
+                numberCan2: {type: 'error', msg: '请粘贴0~1范围内并且小于3位小数的浮点数'},
+                weightNumberCan: {type: 'error', msg: '粘贴的加权系数总和不能大于1'},
+            };
+            const range = info.cellRange;
+            const sortData = info.sheet.zh_data || [];
+            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+                toastMessageUniq(hint.cellError);
+                // SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                SpreadJsObj.reLoadSheetHeader(materialExponentSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialExponentSpread.getActiveSheet());
+                return;
+            }
+            if (sortData.length > 0 && range.col + range.colCount > 10) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialExponentSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialExponentSpread.getActiveSheet());
+                return;
+            }
+            const data = [];
+            // const rowData = [];
+            for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                let bPaste = true;
+                const curRow = range.row + iRow;
+                // const materialData = JSON.parse(JSON.stringify(sortData[curRow]));
+                const materialExData = { id: sortData[curRow].id };
+                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;
+                    }
+                    if (colSetting.field === 'basic_price' || colSetting.field === 'm_price') {
+                        if (isNaN(validText)) {
+                            toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                        const num = parseFloat(validText);
+                        if (validText !== null && (num < 0 || !/^(\d{1,10}|\d{1,7}\.\d{1,3})?$/.test(num))) {
+                            toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                    }
+                    if (colSetting.field === 'weight_num') {
+                        if (isNaN(validText)) {
+                            toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                        const num = parseFloat(validText);
+                        if (validText !== null && (num < 0 || num >= 1 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                            toastMessageUniq(getPasteHint(hint.numberCan2, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                        const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
+                        console.log(total_weight, _.sumBy(materialExponentData, 'weight_num'), orgValue, num);
+                        if (total_weight > 1) {
+                            toastMessageUniq(getPasteHint(hint.weightNumberCan, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                    }
+                    materialExData[colSetting.field] = validText;
+                    sortData[curRow][colSetting.field] = validText;
+                }
+                if (bPaste) {
+                    materialExData.calc_num = materialExponentCol.getValue.calc_num(sortData[curRow]);
+                    data.push(materialExData);
+                    // rowData.push(curRow);
+                } else {
+                    SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                }
+            }
+            if (data.length === 0) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            // console.log(data);
+            // 更新至服务器
+            postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
+                materialExponentData = result.info;
+                SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+                ex_tp = result.ex_tp;
+                ex_expr = result.ex_expr;
+                resetExTpTable();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            });
+        },
+        setReadOnly: function(readOnly) {
+            // SpreadJsObj.resetFieldReadOnly(materialSpread.getActiveSheet(), 'msg_spread', 'm_spread', 'm_tp', 'pre_tp', readOnly);
+        }
+    };
+    materialExponentSpreadObj.refreshActn();
+    materialExponentSpread.bind(spreadNS.Events.SelectionChanged, materialExponentSpreadObj.selectionChanged);
+    materialExponentSpread.bind(spreadNS.Events.ClipboardPasted, materialExponentSpreadObj.clipboardPasted);
+    SpreadJsObj.addDeleteBind(materialExponentSpread, materialExponentSpreadObj.deletePress);
+
+    if (!readOnly) {
+        $('#add').click(materialExponentSpreadObj.add);
+        $('#del').click(materialExponentSpreadObj.del);
+        materialExponentSpread.bind(spreadNS.Events.EditEnded, materialExponentSpreadObj.editEnded);
+        materialExponentSpread.bind(spreadNS.Events.ButtonClicked, materialExponentSpreadObj.buttonClicked);
+        // 右键菜单
+        $.contextMenu({
+            selector: '#material-exponent-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, materialExponentSpread);
+                return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+            },
+            items: {
+                'create': {
+                    name: '新增',
+                    icon: 'fa-sign-in',
+                    callback: function (key, opt) {
+                        materialExponentSpreadObj.add(materialExponentSpread.getActiveSheet());
+                    },
+                },
+                'delete': {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        materialExponentSpreadObj.del(materialExponentSpread.getActiveSheet());
+                    },
+                    disabled: function (key, opt) {
+                        const sheet = materialExponentSpread.getActiveSheet();
+                        const select = SpreadJsObj.getSelectObject(sheet);
+                        const sel = sheet.getSelections()[0];
+                        materialExponentSpreadObj.refreshActn(sel.rowCount);
+                        if (!readOnly && select && materialExponentBase.isUsed(select) && sel.rowCount === 1) {
+                            return false;
+                        } else {
+                            return true;
+                        }
+                    }
+                },
+            }
+        });
+
+        // 调差基数选中
+        $('.calc_select').on('click', function () {
+            // 如果是选中则清除其余2个的选中
+            const code = $(this).val();
+            for (const calc of ex_calc) {
+                calc.select = $(this).is(':checked') && code === calc.code ? true : false;
+                if (!calc.select) {
+                    $('.calc_select[value="'+ calc.code +'"]').prop('checked', false);
+                }
+            }
+            postData(window.location.pathname + '/save', { type:'ex_calc', updateData: ex_calc }, function (result) {
+                ex_tp = result.ex_tp;
+                ex_expr = result.ex_expr;
+                resetExTpTable();
+            });
+        });
+
+        $('#changeRate').change(function () {
+            const rate = parseInt($(this).val());
+            postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
+                const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
+                const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), 2);
+                const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), 2);
+                const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), 2);
+                $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
+                $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+                $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+            });
+        });
+    }
+
+    $.divResizer({
+        select: '#right-spr',
+        callback: function () {
+            materialExponentSpread.refresh();
+            const width = (($('#right-view').width()/$('#right-view').parent('div').width())*100).toFixed();
+            // setLocalCache('material_month_' + materialID, width);
+        }
+    });
+
+    // 展开收起月信息价并浏览器记住本期展开收起
+    $('a', '.right-nav').bind('click', function () {
+        //const main = $('#main-view'), tool = $('#tools-view');
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        if (!tab.hasClass('active')) {
+            $('a', '.side-menu').removeClass('active');
+            $('.tab-content .tab-select-show').removeClass('active');
+            tab.addClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            if (tab.attr('content') === '#base-tab') {
+                const width = (($('#right-view').width()/$('#right-view').parent('div').width())*100).toFixed();
+                // setLocalCache('material_month_' + materialID, width);
+                // materialMonthSpread.refresh();
+            }
+        } else {
+            // removeLocalCache('material_month_' + materialID);
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        // materialSpread.refresh();
+        materialExponentSpread.refresh();
+    });
+});

+ 17 - 0
app/public/js/material_file.js

@@ -234,6 +234,23 @@ $(document).ready(function () {
         }
         return total
     }
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+        }
+    });
 });
 
 /**

+ 15 - 34
app/public/js/material_list.js

@@ -138,6 +138,7 @@ $(document).ready(() => {
             {title: '本期计量数量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 120, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 120, type: 'Number'},
             {title: '|小计', colSpan: '|1', rowSpan: '|1', field: 'gather_qty', hAlign: 2, width: 120, type: 'Number'},
+            {title: '本期完成金额', colSpan: '1', rowSpan: '2', field: 'gather_tp', hAlign: 2, width: 150, type: 'Number'},
             {title: '本期价差', colSpan: '1', rowSpan: '2', field: 'total_jiacha', hAlign:3, width: 150, type: 'Number'}
         ],
         emptyRows: 0,
@@ -153,10 +154,8 @@ $(document).ready(() => {
     gclGatherModel.loadPosData(pos, curPosData);
     let gclGatherData = gclGatherModel.gatherGclData().filter(item => {
         return item.qc_qty || item.contract_qty
-    })
-
-
-    calculateJiaCha(gclGatherData)
+    });
+    calculateJiaCha(gclGatherData);
     // let gclGatherData = gclGatherModel.gatherGclData()
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
@@ -406,8 +405,9 @@ $(document).ready(() => {
                     visible: function (key, opt) {
                         const sheet = leafXmjSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
+                        const sel = sheet.getSelections()[0];
                         const notx = findNotJoinLeafXmj(select);
-                        if (!select) {
+                        if (!select || sel.rowCount !== 1) {
                             return false;
                         }
                         if (!readOnly && select && notx === undefined) {
@@ -426,8 +426,9 @@ $(document).ready(() => {
                     visible: function (key, opt) {
                         const sheet = leafXmjSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
+                        const sel = sheet.getSelections()[0];
                         const notx = findNotJoinLeafXmj(select);
-                        if (!select) {
+                        if (!select || sel.rowCount !== 1) {
                             return false;
                         }
                         if (!readOnly && select && notx === undefined) {
@@ -593,40 +594,20 @@ $(document).ready(() => {
         SpreadJsObj.addDeleteBind(materialSpread, materialSpreadObj.deletePress);
         // 应用调差工料至其他清单明细
         $('#user_all_material').click(function () {
-            const sheet = materialSpread.getActiveSheet();
-            const select = SpreadJsObj.getSelectObject(sheet);
-            if (select === undefined) {
-                toastr.warning('请选中需要应用到其他清单明细的调差工料');
-                return false;
-            }
-            const leafXmjSheet = leafXmjSpread.getActiveSheet();
-            const leafXmjSelect = SpreadJsObj.getSelectObject(leafXmjSheet);
-            const notl = findNotJoinLeafXmj(leafXmjSelect);
-            if (notl !== undefined) {
-                toastr.error('该清单不参与调差,调差工料无法应用到其它清单中');
-                return false;
-            }
             const ledgerSheet = ledgerSpread.getActiveSheet();
             const ledgerSelect = SpreadJsObj.getSelectObject(ledgerSheet);
             if (ledgerSelect.leafXmjs.length < 2) {
                 toastr.warning('没有需要应用调差工料的其它清单明细');
                 return false;
             }
+            const xmjSheet = leafXmjSpread.getActiveSheet();
+            const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
             // 判断需要应用调差工料的清单明细
             const needAddList = [];
-            // const gather_qty = [];
             for (const xmj of ledgerSelect.leafXmjs) {
-                if (xmj.mx_id !== undefined) {
-                    const notx = findNotJoinLeafXmj(xmj);
-                    if (notx === undefined) {
-                        const ml = materialListData.find(function (item) {
-                            return xmj.mx_id === item.mx_id && select.mb_id === item.mb_id;
-                        });
-                        if (ml === undefined) {
-                            needAddList.push(xmj);
-                            // gather_qty.push(xmj.gather_qty);
-                        }
-                    }
+                const notx = findNotJoinLeafXmj(xmj);
+                if (notx === undefined && xmjSelect !== xmj) {
+                    needAddList.push(xmj);
                 }
             }
             if (needAddList.length === 0) {
@@ -634,10 +615,10 @@ $(document).ready(() => {
                 return false;
             }
             // 更新至服务器
-            postData(window.location.pathname + '/save', { type:'useOther', postData: { addXmj: needAddList, select: select } }, function (result) {
+            postData(window.location.pathname + '/save', { type:'useOther', postData: { addXmj: needAddList, materialBills: materialList } }, function (result) {
                 materialListData = result;
-                toastr.success('成功添加了' + needAddList.length + '条调差工料到其他清单明细中');
-                calculateJiaCha(gclGatherData)
+                toastr.success('已成功应用');
+                calculateJiaCha(gclGatherData);
                 const index = gclGatherData.indexOf(ledgerSelect);
                 loadLeafXmjData(index);
                 const xmjSheet = leafXmjSpread.getActiveSheet();

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

@@ -224,7 +224,7 @@ $(document).ready(() => {
 
                 for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
                     const node = sortData[iRow];
-                    if (node.pre_used) {
+                    if (node.pre_used || !checkZero(node.end_arrive_qty) || !checkZero(node.end_deduct_qty)) {
                         toastMessageUniq(hint.isOld);
                         continue;
                     } else {

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

@@ -230,7 +230,7 @@ $(document).ready(() => {
 
                 for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
                     const node = sortData[iRow];
-                    if (node.pre_used) {
+                    if (node.pre_used || !checkZero(node.end_tp)) {
                         toastMessageUniq(hint.isOld);
                         continue;
                     } else {

+ 1 - 1
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -637,7 +637,7 @@ const SpreadJsObj = {
             sheet.setRowCount(totalRow, spreadNS.SheetArea.viewport);
             // 控制空白行
             const emptyRows = sheet.getRange(sortData.length, -1, sheet.zh_setting.emptyRows, -1);
-            emptyRows.locked(sheet.zh_dataType === 'tree');
+            emptyRows.locked(sheet.zh_dataType === 'tree' || sheet.zh_setting.readOnly);
             if (sortData) {
                 // 单元格写入数据
                 sortData.forEach(function (data, i) {

+ 47 - 21
app/public/js/stage.js

@@ -519,16 +519,24 @@ $(document).ready(() => {
                 if (def && def.color) return def.color;
             }
 
-            const posRange = stagePos.ledgerPos[itemsPre + data.id] || [];
-            if (posRange.length > 0) {
-                for (const p of posRange) {
-                    if (p.end_contract_qty > p.quantity) return '#f8d7da';
+            if (checkTzMeasureType()) {
+                const posRange = stagePos.ledgerPos[itemsPre + data.id] || [];
+                if (posRange.length > 0) {
+                    for (const p of posRange) {
+                        if (p.end_contract_qty > p.quantity) return '#f8d7da';
+                    }
+                }
+                if (data.is_tp) {
+                    return data.end_contract_tp > data.total_price ? '#f8d7da' : defaultColor;
+                } else {
+                    return data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
                 }
-            }
-            if (data.is_tp) {
-                return data.end_contract_tp > data.total_price ? '#f8d7da' : defaultColor;
             } else {
-                return data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
+                if (data.is_tp) {
+                    return data.end_contract_tp > data.deal_tp ? '#f8d7da' : defaultColor;
+                } else {
+                    return data.end_contract_qty > data.deal_qty ? '#f8d7da' : defaultColor;
+                }
             }
         } else {
             return defaultColor;
@@ -2713,7 +2721,7 @@ $(document).ready(() => {
                     $('#calc-img').attr('src', '');
                     $('#view-calc-img').attr('src', '');
                     $('#view-calc-remark').val('');
-                    $('#text-edit').val('')
+                    $('#text-edit').val('');
                     $('#edit-img').modal('hide');
                     // postData(window.location.pathname + '/detail/merge-img', {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid}, function (result) {
                     //     stageIm.loadUpdateDetailData(result);
@@ -2731,7 +2739,7 @@ $(document).ready(() => {
                     $('#calc-img').attr('src', '');
                     $('#view-calc-img').attr('src', '');
                     $('#view-calc-remark').val('');
-                    $('#text-edit').val('')
+                    $('#text-edit').val('');
                     $('#edit-img').modal('hide');
                 }
             });
@@ -2997,16 +3005,24 @@ $(document).ready(() => {
                         }, {
                             key: 'over', title: '超计', valid: true,
                             check: function (node) {
-                                const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
-                                if (posRange.length > 0) {
-                                    for (const p of posRange) {
-                                        if (p.end_contract_qty > p.quantity) return true;
+                                if (checkTzMeasureType()) {
+                                    const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
+                                    if (posRange.length > 0) {
+                                        for (const p of posRange) {
+                                            if (p.end_contract_qty > p.quantity) return true;
+                                        }
+                                        return false;
+                                    } else if (node.end_gather_qty) {
+                                        return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
+                                    } else if (node.end_gather_tp) {
+                                        return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
+                                    }
+                                } else {
+                                    if (node.end_gather_qty) {
+                                        return !node.deal_qty || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.deal_qty, node.end_qc_qty));
+                                    } else if (node.end_gather_tp) {
+                                        return !node.deal_tp || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.deal_tp, node.end_qc_tp));
                                     }
-                                    return false;
-                                } else if (node.end_gather_qty) {
-                                    return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
-                                } else if (node.end_gather_tp) {
-                                    return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
                                 }
                             }
                         }, {
@@ -3359,7 +3375,7 @@ $(document).ready(() => {
         });
     })('a[name=showLevel]', slSpread.getActiveSheet());
 
-    LedgerChecker({
+    const stageCheckerSetting = {
         ledgerTree: stageTree,
         ledgerPos: stagePos,
         checkList: checkList,
@@ -3383,7 +3399,17 @@ $(document).ready(() => {
                 }
             },
         }
-    });
+    };
+    if (!checkTzMeasureType()) {
+        stageCheckerSetting.checkOption.calc.fields.push('sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity');
+        stageCheckerSetting.checkOption.tp.fields.push(
+            {qty: 'sgfh_qty', tp: 'sgfh_tp'},
+            {qty: 'sjcl_qty', tp: 'sjcl_tp'},
+            {qty: 'qtcl_qty', tp: 'qtcl_tp'},
+            {qty: 'quantity', tp: 'total_price'},
+        );
+    }
+    LedgerChecker(stageCheckerSetting);
 
     const dataChecker = DataChecker({
         checkUrl: window.location.pathname + '/check',

+ 11 - 15
app/public/js/stage_im.js

@@ -253,6 +253,15 @@ const stageIm = (function () {
         }
     }
 
+    function loadCustomDetail(im, detail) {
+        im.custom_define = detail.custom_define ? detail.custom_define.split(',') : imFields;
+        _.assignInWith(im, detail, function (oV, sV, key) {
+            return (im.custom_define.indexOf(key) > -1 && sV !== undefined && sV !== null) ? sV : oV;
+        });
+        im.calc_img_org = detail.calc_img_org;
+        im.calc_img_remark = detail.calc_img_remark;
+    }
+
     function checkCustomDetail(im) {
         const cd = _.find(details, function (d) {
             return im.lid === d.lid &&
@@ -263,12 +272,7 @@ const stageIm = (function () {
                 (!im.pid || im.pid === d.pid) &&
                 (!im.pos_name || im.pos_name === d.pos_name);
         });
-        if (cd) {
-            im.custom_define = cd.custom_define ? cd.custom_define.split(',') : imFields;
-            _.assignInWith(im, cd, function (oV, sV, key) {
-                return (im.custom_define.indexOf(key) > -1 && sV !== undefined && sV !== null) ? sV : oV;
-            });
-        }
+        if (cd) loadCustomDetail(im, cd);
     }
 
     function getCalcMemo(im) {
@@ -881,15 +885,7 @@ const stageIm = (function () {
                         (!im.pid || im.pid === d.pid);
                 });
             }
-            if (imData) {
-                _.assignInWith(imData, d, function (oV, sV, key) {
-                    return imFields.indexOf(key) > -1 && !_.isUndefined(sV) && !_.isNull(sV) ? sV : oV;
-
-                });
-                imData.calc_img = d.calc_img;
-                imData.calc_img_org = d.calc_img_org;
-                imData.calc_img_remark = d.calc_img_remark;
-            }
+            if (imData) loadCustomDetail(imData, d);
         }
     }
 

+ 9 - 9
app/public/js/tender_list_info.js

@@ -333,39 +333,39 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     html.push('</td>');
     // 0号台账合同
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.total_price);
+    html.push(node.total_price || '');
     html.push('</td>');
     // 本期完成
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.gather_tp);
+    html.push(node.gather_tp || '');
     html.push('</td>');
     // 截止本期合同
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.end_contract_tp);
+    html.push(node.end_contract_tp || '');
     html.push('</td>');
     // 截止本期变更
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.end_qc_tp);
+    html.push(node.end_qc_tp || '');
     html.push('</td>');
     // 截止本期完成
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.end_gather_tp);
+    html.push(node.end_gather_tp || '');
     html.push('</td>');
     // 截止上期完成
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.pre_gather_tp);
+    html.push(node.pre_gather_tp || '');
     html.push('</td>');
     // 预付款
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.advance_tp);
+    html.push(node.advance_tp || '');
     html.push('</td>');
     // 本期应付
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.yf_tp);
+    html.push(node.yf_tp || '');
     html.push('</td>');
     // 截止本期应付
     html.push('<td style="width: 6%" class="text-right">');
-    html.push(node.end_yf_tp);
+    html.push(node.end_yf_tp || '');
     html.push('</td>');
     html.push('</tr>');
     if (node.children) {

+ 22 - 16
app/public/js/tender_list_progress.js

@@ -308,7 +308,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td width="30%" class="in-' + node.level + '">');
+    html.push('<td width="25%" class="in-' + node.level + '">');
     if (node.cid) {
         html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
     } else {
@@ -319,21 +319,26 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
         html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a>');
     }
     html.push('</td>');
-    // 计量期数
-    html.push('<td width="120" class="text-center">');
-    if (!node.cid) {
-        html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
+    // 计量进度
+    html.push('<td style="width: 10%">');
+    if (!node.cid && node.cur_flow) {
+        html.push(node.cur_flow.title + ' (' + '<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>' + ')');
     }
     html.push('</td>');
-
-    // 审批状态
+    // 当前流程
     html.push('<td style="width: 10%">');
-    html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);
-    html.push(node.status_users ? '(' + node.status_users + ')' : '');
+    if (!node.cid && node.cur_flow) {
+        html.push(node.cur_flow.name + ' ' + '<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+    }
     html.push('</td>');
-
-    // 累计合同计量
-    html.push('<td width="15%" class="text-right">');
+    // 上一流程审批时间
+    html.push('<td style="width: 10%">');
+    if (!node.cid && node.pre_flow) {
+        html.push(node.pre_flow.name + ' ' + moment(node.pre_flow.time).format('YYYY-MM-DD'));
+    }
+    html.push('</td>');
+    // 总价
+    html.push('<td width="10%" class="text-right">');
     const sum = node.lastStage ? ZhCalc.add(node.total_price, node.lastStage.end_qc_tp) : node.total_price;
     html.push(node.sum_tp ? node.sum_tp : '');
     html.push('</td>');
@@ -359,11 +364,12 @@ function getTenderTreeHtml () {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
         html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
-        html.push('<th style="width: 30%" class="text-center">', '标段名称', '</th>');
-        html.push('<th style="width: 10%" class="text-center">', '计量期数', '</th>');
-        html.push('<th style="width: 10%" class="text-center">','审批状态','</th>');
+        html.push('<th style="width: 25%" class="text-center">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '计量进度', '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '当前流程', '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '上一流程审批时间', '</th>');
         html.push('<th style="width: 10%" class="text-center">', '总价 <i class="fa fa-question-circle text-primary"  data-placement="bottom" data-toggle="tooltip" data-original-title="0号台账+截止本期数量变更"></i>', '</th>');
-        html.push('<th style="width: 40%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
+        html.push('<th style="width: 35%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {

+ 4 - 0
app/router.js

@@ -184,6 +184,7 @@ module.exports = app => {
     app.post('/tender/:id/deal/get-data', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.getData');
     app.post('/tender/:id/deal/upload-excel', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.loadExcel');
     app.get('/tender/:id/deal/download/:file', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.download');
+    app.post('/tender/:id/deal/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.update');
 
     // 计量台账
     // 期计量
@@ -331,6 +332,9 @@ module.exports = app => {
     app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveBillsData');
     // 月信息价
     app.post('/tender/:id/measure/material/:order/month/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveMonth');
+    // 指数调差
+    app.get('/tender/:id/measure/material/:order/exponent', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.exponent');
+    app.post('/tender/:id/measure/material/:order/exponent/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveExponentData');
     // 调差清单
     app.get('/tender/:id/measure/material/:order/list', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.list');
     app.post('/tender/:id/measure/material/:order/list/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveListsData');

+ 64 - 28
app/service/change.js

@@ -958,51 +958,87 @@ module.exports = app => {
         async getValidChanges(tid, bills, pos) {
             const timesLen = 100;
             const filter =
-                'cb.`code` = ' +
-                this.db.escape(bills.b_code) +
-                ' And cb.`name` = ' +
-                this.db.escape(bills.name) +
-                ' And cb.`unit` = ' +
-                this.db.escape(bills.unit) +
-                ' And cb.`unit_price` = ' +
-                this.db.escape(bills.unit_price) +
+                'cb.`code` = ' + this.db.escape(bills.b_code) +
+                ' And cb.`name` = ' + this.db.escape(bills.name) +
+                ' And cb.`unit` = ' + this.db.escape(bills.unit) +
+                ' And cb.`unit_price` = ' + this.db.escape(bills.unit_price) +
                 (pos ? ' And cb.`bwmx` = ' + this.db.escape(pos.name) : '');
             const sql =
                 'SELECT c.cid, c.code, c.name, c.w_code, c.p_code, c.peg, c.org_name, c.org_code, c.new_name, c.new_code,' +
                 '    c.content, c.basis, c.memo, c.type, c.class, c.quality, c.company, c.charge, ' +
                 '    cb.id As cbid, cb.code As b_code, cb.name As b_name, cb.unit As b_unit, cb.samount As b_amount, cb.detail As b_detail, cb.bwmx As b_bwmx, ' +
                 '    scb.used_amount' +
-                '  FROM ' +
-                this.tableName +
-                ' As c ' +
-                '  Left Join ' +
-                this.ctx.service.changeAuditList.tableName +
-                ' As cb On c.cid = cb.cid ' +
+                '  FROM ' + this.tableName + ' As c ' +
+                '  Left Join ' + this.ctx.service.changeAuditList.tableName + ' As cb On c.cid = cb.cid ' +
                 '  Left Join (' +
                 '    SELECT SUM(sc.qty) As used_amount, sc.cbid' +
-                '      FROM ' +
-                this.ctx.service.stageChange.tableName +
-                ' As sc' +
-                '      INNER JOIN (SELECT MAX(`stimes` * ' +
-                timesLen +
-                ' + `sorder`) As `flow`, cbid, sid ' +
-                '        FROM ' +
-                this.ctx.service.stageChange.tableName +
+                '      FROM ' + this.ctx.service.stageChange.tableName + ' As sc' +
+                '      INNER JOIN (SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `flow`, cbid, sid ' +
+                '        FROM ' + this.ctx.service.stageChange.tableName +
                 '        WHERE tid = ?' +
                 '        GROUP BY cbid, sid' +
                 '      ) As MF' +
-                '      ON (sc.stimes * ' +
-                timesLen +
-                ' + sc.sorder) = MF.flow And sc.cbid = MF.cbid And sc.sid = MF.sid' +
+                '      ON (sc.stimes * ' + timesLen + ' + sc.sorder) = MF.flow And sc.cbid = MF.cbid And sc.sid = MF.sid' +
                 '    GROUP BY sc.cbid' +
                 '  ) As scb ON cb.id = scb.cbid' +
-                '  WHERE c.tid = ? And c.status = ? And c.valid And ' +
-                filter +
+                '  WHERE c.tid = ? And c.status = ? And c.valid And ' + filter +
                 '  ORDER BY c.in_time';
             const sqlParam = [tid, tid, audit.flow.status.checked];
             const changes = await this.db.query(sql, sqlParam);
             for (const c of changes) {
-                const aSql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' + '  FROM ?? As ca ' + '  Left Join ?? As pa ' + '  On ca.uid = pa.id ' + '  Where ca.cid = ?';
+                const aSql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' +
+                    '  FROM ?? As ca ' +
+                    '  Left Join ?? As pa ' +
+                    '  On ca.uid = pa.id ' +
+                    '  Where ca.cid = ?';
+                const aSqlParam = [this.ctx.service.changeAtt.tableName, this.ctx.service.projectAccount.tableName, c.cid];
+                c.attachments = await this.db.query(aSql, aSqlParam);
+            }
+            return changes;
+        }
+
+        /**
+         * 查询审批人可用的变更令
+         * @param bills - 查询的清单
+         * @param pos - 查询的部位
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async getAuditValidChanges(tid, bills, pos, times, order) {
+            const timesLen = 100;
+            const filter =
+                'cb.`code` = ' + this.db.escape(bills.b_code) +
+                ' And cb.`name` = ' + this.db.escape(bills.name) +
+                ' And cb.`unit` = ' + this.db.escape(bills.unit) +
+                ' And cb.`unit_price` = ' + this.db.escape(bills.unit_price) +
+                (pos ? ' And cb.`bwmx` = ' + this.db.escape(pos.name) : '');
+            const sql =
+                'SELECT c.cid, c.code, c.name, c.w_code, c.p_code, c.peg, c.org_name, c.org_code, c.new_name, c.new_code,' +
+                '    c.content, c.basis, c.memo, c.type, c.class, c.quality, c.company, c.charge, ' +
+                '    cb.id As cbid, cb.code As b_code, cb.name As b_name, cb.unit As b_unit, cb.samount As b_amount, cb.detail As b_detail, cb.bwmx As b_bwmx, ' +
+                '    scb.used_amount' +
+                '  FROM ' + this.tableName + ' As c ' +
+                '  Left Join ' + this.ctx.service.changeAuditList.tableName + ' As cb On c.cid = cb.cid ' +
+                '  Left Join (' +
+                '    SELECT SUM(sc.qty) As used_amount, sc.cbid' +
+                '      FROM ' + this.ctx.service.stageChange.tableName + ' As sc' +
+                '      INNER JOIN (SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `flow`, cbid, sid ' +
+                '        FROM ' + this.ctx.service.stageChange.tableName +
+                '        WHERE tid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?)) ' +
+                '        GROUP BY cbid, sid' +
+                '      ) As MF' +
+                '      ON (sc.stimes * ' + timesLen + ' + sc.sorder) = MF.flow And sc.cbid = MF.cbid And sc.sid = MF.sid' +
+                '    GROUP BY sc.cbid' +
+                '  ) As scb ON cb.id = scb.cbid' +
+                '  WHERE c.tid = ? And c.status = ? And c.valid And ' + filter +
+                '  ORDER BY c.in_time';
+            const sqlParam = [tid, times, times, order, tid, audit.flow.status.checked];
+            const changes = await this.db.query(sql, sqlParam);
+            for (const c of changes) {
+                const aSql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' +
+                    '  FROM ?? As ca ' +
+                    '  Left Join ?? As pa ' +
+                    '  On ca.uid = pa.id ' +
+                    '  Where ca.cid = ?';
                 const aSqlParam = [this.ctx.service.changeAtt.tableName, this.ctx.service.projectAccount.tableName, c.cid];
                 c.attachments = await this.db.query(aSql, aSqlParam);
             }

+ 87 - 2
app/service/deal_bills.js

@@ -161,7 +161,8 @@ module.exports = app => {
                     //if (this.ctx.helper.validBillsCode(code)) {
                     if (code) {
                         const data = {
-                            deal_id: bills.length + 1,
+                            id: this.uuid.v4(),
+                            order: bills.length + 1,
                             tender_id: tenderId,
                             code: code,
                             name: this.ctx.helper.replaceReturn(row[iName]),
@@ -206,12 +207,96 @@ module.exports = app => {
         * @param {Number} tenderId - 所属标段Id
         */
         async getDataByTenderId(tenderId) {
-            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills WHERE tender_id = ?';
+            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills WHERE tender_id = ? ORDER BY `order` ASC';
             const sqlParam = [tenderId];
             return await this.db.query(sql, sqlParam);
             // let rst = await this.getDataByCondition({tender_id: tenderId});
             // return rst;
         }
+
+        async _addDatas(data) {
+            const info =  this.ctx.tender.info;
+
+            const datas = data instanceof Array ? data : [data];
+            const insertData = [];
+            for (const d of datas) {
+                if (!d.code || !d.order) throw '新增签约清单,提交的数据错误';
+                const nd = { id: this.uuid.v4(), tender_id: this.ctx.tender.id };
+                nd.code = d.code;
+                nd.order = d.order;
+                if (d.name) nd.name = d.name;
+                if (d.unit) nd.unit = d.unit;
+                if (d.unit_price) nd.unit_price = this.ctx.helper.round(d.unit_price, info.decimal.up);
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
+                if (d.quantity) {
+                    nd.quantity = this.ctx.helper.round(d.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
+                }
+                insertData.push(nd);
+            }
+            const result = await this.db.insert(this.tableName, insertData);
+            return insertData;
+        }
+
+        async _delDatas (data) {
+            await this.db.delete(this.tableName, {id: data});
+            return data;
+        }
+
+        async _updateDatas (data) {
+            const info =  this.ctx.tender.info;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({where: {id: this.ctx.helper._.map(datas, 'id')}});
+
+            const uDatas = [];
+            for (const d of datas) {
+                const od = this.ctx.helper._.find(orgDatas, {id: d.id});
+                if (!od) continue;
+
+                const nd = {id: od.id};
+
+                if (d.code !== undefined) nd.code = d.code;
+                if (d.order !== undefined) nd.order = d.order;
+                if (d.name !== undefined) nd.name = d.name;
+                if (d.unit !== undefined) nd.unit = d.unit;
+                nd.unit_price = d.unit_price !== undefined ? this.ctx.helper.round(d.unit_price, info.decimal.up) : od.unit_price;
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
+                if (d.quantity !== undefined) {
+                    nd.quantity = this.ctx.helper.round(d.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
+                } else {
+                    nd.quantity = this.ctx.helper.round(od.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
+                }
+                uDatas.push(nd);
+            }
+            if (uDatas.length > 0) {
+                await this.db.updateRows(this.tableName, uDatas);
+                return uDatas;
+            } else {
+                return [];
+            }
+        }
+
+        async updateDatas(data) {
+            const result = {add: [], del: [], update: []};
+            try {
+                if (data.add) {
+                    result.add = await this._addDatas(data.add);
+                }
+                if (data.update) {
+                    result.update = await this._updateDatas(data.update);
+                }
+                if (data.del) {
+                    result.del = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                if (err) result.err = err;
+                return result;
+            }
+        }
     }
 
     return DealBills;

+ 6 - 2
app/service/login_logging.js

@@ -34,8 +34,10 @@ module.exports = app => {
         /**
          * 创建登录日志
          * @return {Boolean} 日志是否创建成功
+         * @param {Number} type - 登录类型
+         * @param {Number} status - 是否显示记录
          */
-        async addLoginLog() {
+        async addLoginLog(type, status) {
             const { ctx } = this;
             const ip = ctx.request.ip ? ctx.request.ip : '';
             const ipInfo = await this.getIpInfoFromApi(ip);
@@ -50,6 +52,8 @@ module.exports = app => {
                 address: ipInfo,
                 uid: ctx.session.sessionUser.accountId,
                 pid: ctx.session.sessionProject.id,
+                type,
+                show: status,
             };
             return await this.createLog(payload);
         }
@@ -122,7 +126,7 @@ module.exports = app => {
          */
         async getLoginLogs(pid, uid) {
             return this.db.select(this.tableName, {
-                where: { pid, uid },
+                where: { pid, uid, show: 0 },
                 orders: [['create_time', 'desc']],
                 columns: ['browser', 'create_time', 'ip', 'os', 'address'],
                 limit: 10, offset: 0,

+ 58 - 3
app/service/material.js

@@ -127,6 +127,7 @@ module.exports = app => {
                 if (preMaterial) {
                     newMaterial.rate = preMaterial.rate;
                     newMaterial.pre_tp = this.ctx.helper.add(preMaterial.m_tp, preMaterial.pre_tp);
+                    newMaterial.ex_pre_tp = this.ctx.helper.add(preMaterial.ex_tp, preMaterial.ex_pre_tp);
                 }
                 // 新增期记录
                 const result = await transaction.insert(this.tableName, newMaterial);
@@ -148,10 +149,13 @@ module.exports = app => {
                     await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
                     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
                     const m_tp = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id);
+                    // 修改现行价格指数,并返回调差基数json
+                    const ex_calc = await this.ctx.service.materialExponent.updateNewMaterial(transaction, newMaterial.id, this.ctx, newMaterial.stage_id, preMaterial.ex_calc);
                     // 计算得出本期总金额
                     const updateMaterialData = {
                         id: newMaterial.id,
                         m_tp,
+                        ex_calc: JSON.stringify(ex_calc),
                     };
                     await transaction.update(this.tableName, updateMaterialData);
                 }
@@ -182,15 +186,31 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBillsHistory.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialFile.tableName, { mid: id });
-                // 如果存在上一期,把上一期的quantity,pre_tp添加到bill中
+                await transaction.delete(this.ctx.service.materialExponent.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialExponentHistory.tableName, { mid: id });
+                // 如果存在上一期,把上一期的quantity,expr,msg_tp,msg_times,msg_spread,m_up_risk,m_down_risk,m_spread,m_tp,pre_tp,is_summary添加到bill中
                 const materialInfo = await this.getDataById(id);
                 if (materialInfo.order > 1) {
                     const sql = 'UPDATE ' + this.ctx.service.materialBills.tableName + ' as mb, ' +
                         this.ctx.service.materialBillsHistory.tableName + ' as mbh ' +
-                        'SET mb.`quantity` = mbh.`quantity`, mb.`pre_tp` = mbh.`pre_tp` ' +
+                        'SET mb.`quantity` = mbh.`quantity`, mb.`expr` = mbh.`expr`, ' +
+                        'mb.`msg_tp` = mbh.`msg_tp`, mb.`msg_times` = mbh.`msg_times`, ' +
+                        'mb.`msg_spread` = mbh.`msg_spread`, mb.`m_up_risk` = mbh.`m_up_risk`, ' +
+                        'mb.`m_down_risk` = mbh.`m_down_risk`, mb.`m_spread` = mbh.`m_spread`, ' +
+                        'mb.`m_tp` = mbh.`m_tp`, mb.`pre_tp` = mbh.`pre_tp`, ' +
+                        'mb.`is_summary` = mbh.`is_summary` ' +
                         'WHERE mbh.`tid` = ? AND mbh.`order` = ? AND mbh.`mb_id` = mb.`id`';
                     const sqlParam = [this.ctx.tender.id, materialInfo.order - 1];
                     await transaction.query(sql, sqlParam);
+
+                    const sql2 = 'UPDATE ' + this.ctx.service.materialExponent.tableName + ' as me, ' +
+                        this.ctx.service.materialExponentHistory.tableName + ' as meh ' +
+                        'SET me.`weight_num` = meh.`weight_num`, me.`basic_price` = meh.`basic_price`, ' +
+                        'me.`basic_times` = meh.`basic_times`, me.`m_price` = meh.`m_price`, ' +
+                        'me.`calc_num` = meh.`calc_num`, me.`is_summary` = meh.`is_summary` ' +
+                        'WHERE meh.`tid` = ? AND meh.`order` = ? AND meh.`me_id` = me.`id`';
+                    const sqlParam2 = [this.ctx.tender.id, materialInfo.order - 1];
+                    await transaction.query(sql2, sqlParam2);
                 }
                 await transaction.delete(this.tableName, { id });
                 await transaction.commit();
@@ -235,7 +255,29 @@ module.exports = app => {
         }
 
         /**
-         * 修改增税税率
+         * 修改调差基数
+         * @param {int} rate 税率
+         * @return {Promise<*>}
+         */
+        async changeExCalc(ex_calc) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const updateData = {
+                    id: this.ctx.material.id,
+                    ex_calc: JSON.stringify(ex_calc),
+                };
+                await transaction.update(this.tableName, updateData);
+                const [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction, ex_calc);
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 取当前期截止上期含税金额
          * @param {int} tid 标段id
          * @param {int} order 调差期数
          * @return {Promise<*>}
@@ -246,6 +288,19 @@ module.exports = app => {
             const result = await this.db.queryOne(sql, sqlParam);
             return result.pre_tp_hs;
         }
+
+        /**
+         * 取当前期截止上期含税指数金额
+         * @param {int} tid 标段id
+         * @param {int} order 调差期数
+         * @return {Promise<*>}
+         */
+        async getExPreTpHs(tid, order) {
+            const sql = 'SELECT SUM(ROUND(`ex_tp`*(1+ `rate`/100),2)) AS `ex_pre_tp_hs` FROM ?? WHERE `tid` = ? AND `order` < ?';
+            const sqlParam = [this.tableName, tid, order];
+            const result = await this.db.queryOne(sql, sqlParam);
+            return result.ex_pre_tp_hs;
+        }
     }
 
     return Material;

+ 26 - 0
app/service/material_audit.js

@@ -219,11 +219,32 @@ module.exports = app => {
                         m_spread: mb.m_spread,
                         m_tp: mb.m_tp,
                         pre_tp: mb.pre_tp,
+                        is_summary: mb.is_summary,
                     };
                     mbhList.push(newMbh);
                 }
                 await transaction.insert(this.ctx.service.materialBillsHistory.tableName, mbhList);
 
+                const materialExponentData = await this.ctx.service.materialExponent.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
+                const mehList = [];
+                for (const me of materialExponentData) {
+                    const newMeh = {
+                        tid: this.ctx.tender.id,
+                        mid: this.ctx.material.id,
+                        order: this.ctx.material.order,
+                        me_id: me.id,
+                        type: me.type,
+                        weight_num: me.weight_num,
+                        basic_price: me.basic_price,
+                        basic_times: me.basic_times,
+                        m_price: me.m_price,
+                        calc_num: me.calc_num,
+                        is_summary: me.is_summary,
+                    };
+                    mehList.push(newMeh);
+                }
+                await transaction.insert(this.ctx.service.materialExponentHistory.tableName, mehList);
+
                 // 微信模板通知
                 const materialInfo = await this.ctx.service.material.getDataById(materialId);
                 const wechatData = {
@@ -430,6 +451,11 @@ module.exports = app => {
                     tid: this.ctx.tender.id,
                     order: this.ctx.material.order,
                 });
+                // 删除material_exponent_history信息
+                await transaction.delete(this.ctx.service.materialExponentHistory.tableName, {
+                    tid: this.ctx.tender.id,
+                    order: this.ctx.material.order,
+                });
 
                 // 微信模板通知
                 const begin_audit = await this.getDataByCondition({

+ 5 - 3
app/service/material_bills.js

@@ -193,10 +193,11 @@ module.exports = app => {
                     msg_times: null,
                     msg_spread: newmsg_spread,
                     m_spread: newm_spread,
+                    m_tp: this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(mb_quantity.quantity, 3), newm_spread), 2),
                     pre_tp: mb.m_tp !== null ? this.ctx.helper.round(this.ctx.helper.add(mb.pre_tp, mb.m_tp), 2) : mb.pre_tp,
                 };
                 await transaction.update(this.tableName, updateData);
-                return await this.ctx.helper.round(this.ctx.helper.mul(mb_quantity.quantity, newm_spread), 2);
+                return mb.is_summary === 1 ? await this.ctx.helper.round(this.ctx.helper.mul(mb_quantity.quantity, newm_spread), 2) : 0;
             } else if (mb.t_type === materialConst.t_type[1].value) {
                 const quantity = await materialCalculator.calculateExpr(mb.expr);
                 const updateData = {
@@ -206,10 +207,11 @@ module.exports = app => {
                     msg_times: null,
                     msg_spread: newmsg_spread,
                     m_spread: newm_spread,
+                    m_tp: quantity !== 0 && quantity !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(quantity, 3), newm_spread), 2) : null,
                     pre_tp: mb.m_tp !== null ? this.ctx.helper.round(this.ctx.helper.add(mb.pre_tp, mb.m_tp), 2) : mb.pre_tp,
                 };
                 await transaction.update(this.tableName, updateData);
-                return await this.ctx.helper.round(this.ctx.helper.mul(quantity, newm_spread), 2);
+                return mb.is_summary === 1 ? await this.ctx.helper.round(this.ctx.helper.mul(quantity, newm_spread), 2) : 0;
             }
         }
 
@@ -259,7 +261,7 @@ module.exports = app => {
         // 更改计算总金额并返回值
         async calcMaterialMTp(transaction) {
             // 金额发生变化,则重新计算本期金额
-            const sql = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.tableName + ' WHERE `tid` = ?';
+            const sql = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.tableName + ' WHERE `tid` = ? AND `is_summary` = 1';
             const sqlParam = [this.ctx.tender.id];
             const tp = await transaction.queryOne(sql, sqlParam);
             console.log(tp);

+ 249 - 0
app/service/material_exponent.js

@@ -0,0 +1,249 @@
+'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 MaterialExponent extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_exponent';
+        }
+
+        /**
+         * 添加指数清单
+         * @return {void}
+         */
+        async add() {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const newExponent = {
+                    tid: this.ctx.tender.id,
+                    mid: this.ctx.material.id,
+                    in_time: new Date(),
+                };
+                // 新增工料
+                const result = await transaction.insert(this.tableName, newExponent);
+                if (result.affectedRows !== 1) {
+                    throw '新增指数数据失败';
+                }
+                await transaction.commit();
+                return await this.getDataById(result.insertId);
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                throw error;
+            }
+        }
+
+        /**
+         * 删除指数清单
+         * @param {int} id 工料id
+         * @return {void}
+         */
+        async del(id) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            // 判断t_type是否为费用,且存在quantity,m_spread值
+            const transaction = await this.db.beginTransaction();
+            try {
+                const meInfo = await this.getDataById(id);
+                await transaction.delete(this.tableName, { id });
+                let ex_tp = this.ctx.material.ex_tp;
+                let ex_expr = this.ctx.material.ex_expr;
+                if (meInfo.is_summary === materialConst.is_summary.yes) {
+                    // 金额发生变化,则重新计算本期金额
+                    [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction);
+                }
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 修改指数清单信息
+         * @param {Object} data 工料内容
+         * @return {void}
+         */
+        async save(data) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            delete data.in_time;
+            // delete data.m_tp;
+            // 判断是否可修改
+            // 判断t_type是否为费用
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, data);
+                const [ex_tp, ex_expr] = await this.calcMaterialExTp(transaction);
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 复制粘贴指数清单
+         * @param {Object} data 工料内容
+         * @return {void}
+         */
+        async saveDatas(datas) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            // 判断是否可修改
+            // 判断t_type是否为费用
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.updateRows(this.tableName, datas);
+                const [ex_tp, ex_expr] = await this.calcMaterialExTp(transaction);
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 旧数据补充定值和历史数据
+         * @returns {Promise<void>}
+         */
+        async addOldData() {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 先获取第一期的mid
+                const first_material = await this.ctx.service.material.getDataByCondition({
+                    tid: this.ctx.tender.id,
+                    order: 1,
+                });
+                const insert_data = {
+                    tid: this.ctx.tender.id,
+                    mid: first_material.id,
+                    type: materialConst.ex_type[0].value,
+                    symbol: 'X',
+                    symbol_desc: '非可调因子',
+                    code: 'A',
+                    weight_num: 0,
+                    is_summary: materialConst.is_summary.yes,
+                    in_time: new Date(),
+                };
+                const result = await transaction.insert(this.tableName, insert_data);
+                // 获取最新期数据
+                const high_material = await this.ctx.service.material.getDataByCondition({
+                    tid: this.ctx.tender.id,
+                    order: this.ctx.material.highOrder,
+                });
+                const qi_order = high_material.status === auditConst.status.uncheck || high_material.status === auditConst.status.checkNo ? high_material.order - 1 : high_material;
+                const insert_history_data = [];
+                for (let i = 1; i <= qi_order; i++) {
+                    const one_insert = {
+                        tid: this.ctx.tender.id,
+                        mid: first_material.id,
+                        order: i,
+                        me_id: result.insertId,
+                        type: materialConst.ex_type[0].value,
+                        weight_num: 0,
+                        is_summary: materialConst.is_summary.yes,
+                    };
+                    insert_history_data.push(one_insert);
+                }
+                if (insert_history_data.length > 0) await transaction.insert(this.ctx.service.materialExponentHistory.tableName, insert_history_data);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        // 更改计算总指数金额并返回值和公式
+        async calcMaterialExTp(transaction, ex_calc = (this.ctx.material.ex_calc ? JSON.parse(this.ctx.material.ex_calc) : null), mid = null) {
+            let basic_calc = 0;
+            if (ex_calc) {
+                for (const calc of ex_calc) {
+                    if (calc.select) {
+                        basic_calc = this.ctx.helper.add(basic_calc, calc.value);
+                    }
+                }
+            }
+            let expr = basic_calc + '*[';
+            const exponentList = await transaction.select(this.tableName, { where: { tid: this.ctx.tender.id, is_summary: materialConst.is_summary.yes } });
+            let sumByChange = 0;
+            let constant = 0;
+            for (const ex of exponentList) {
+                if (ex.type === materialConst.ex_type[0].value) {
+                    constant = ex.weight_num ? ex.weight_num : 0;
+                    expr += constant.toString();
+                } else if (ex.type === materialConst.ex_type[1].value) {
+                    const change = ex.calc_num ? ex.calc_num : 0;
+                    expr = expr + (change !== 0 ? '+' + ex.weight_num.toString() + '*(' + ex.m_price.toString() + '/' + ex.basic_price.toString() + ')' : '');
+                    sumByChange = this.ctx.helper.add(sumByChange, change);
+                }
+            }
+            expr += '-1]';
+            const ex_tp = this.ctx.helper.round(this.ctx.helper.mul(basic_calc, this.ctx.helper.sub(this.ctx.helper.add(constant, sumByChange), 1)), 2);
+            await transaction.update(this.ctx.service.material.tableName, {
+                id: mid ? mid : this.ctx.material.id,
+                ex_tp,
+                ex_expr: expr,
+            });
+            console.log(ex_tp, expr);
+            return [ex_tp, expr];
+        }
+
+        /**
+         * 更新新一期的现行价格指数,并返回指数总金额和公式
+         * @param transaction
+         * @param tid
+         * @param mid
+         * @returns {Promise<number>}
+         */
+        async updateNewMaterial(transaction, mid, ctx, stage_id, ex_calc) {
+            const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_id, ctx.tender.info);
+            const old_ex_calc = ex_calc ? JSON.parse(ex_calc) : null;
+            const new_ex_calc = materialConst.ex_calc;
+            for (const bq of new_ex_calc) {
+                const calc = this._.find(calcBase, function(item) {
+                    return bq.code === item.code;
+                });
+                const oldcalc = old_ex_calc ? this._.find(JSON.parse(old_ex_calc), function(item) {
+                    return bq.code === item.code;
+                }) : null;
+                bq.value = calc.value;
+                bq.select = oldcalc ? oldcalc.select : false;
+            }
+            await this.calcMaterialExTp(transaction, new_ex_calc, mid);
+            return new_ex_calc;
+        }
+    }
+
+    return MaterialExponent;
+};

+ 37 - 0
app/service/material_exponent_history.js

@@ -0,0 +1,37 @@
+'use strict';
+
+/**
+ * 指数历史期数据表 数据模型
+ *
+ * @author Mai
+ * @date 2018/8/13
+ * @version
+ */
+
+
+module.exports = app => {
+    class MaterialExponentHistory extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_exponent_history';
+        }
+
+        /**
+         * 获取历史本期应耗数量和上期调差金额
+         * @param mid
+         * @param order
+         * @param mb_id
+         * @returns {Promise<*[]>}
+         */
+        async getByMeId(mid, order, me_id) {
+            return await this.getDataByCondition({ mid, order, me_id });
+        }
+    }
+    return MaterialExponentHistory;
+};

+ 50 - 20
app/service/material_list.js

@@ -139,29 +139,59 @@ module.exports = app => {
             }
             const transaction = await this.db.beginTransaction();
             try {
+                // 先删除addxmj里所有的清单工料再添加新工料,并重新计算每个工料的单价数量
+                // 还要找出删除的工料,更新单价数量
                 const list = [];
-                const select = data.select;
+                const delList = [];
+                const materialBills = data.materialBills;
+                const mb_idList = [];
                 for (const xmj of data.addXmj) {
-                    const newLists = {
-                        tid: this.ctx.tender.id,
-                        order: this.ctx.material.order,
-                        mid: this.ctx.material.id,
-                        mb_id: select.mb_id,
-                        gcl_id: xmj.gcl_id,
-                        xmj_id: xmj.id,
-                        mx_id: xmj.mx_id,
-                        gather_qty: xmj.gather_qty,
-                        quantity: select.quantity,
-                        in_time: new Date(),
-                    };
-                    list.push(newLists);
+                    const mlList = await this.getAllDataByCondition({
+                        where: {
+                            mid: this.ctx.material.id,
+                            gcl_id: xmj.gcl_id,
+                            xmj_id: xmj.id,
+                            mx_id: xmj.mx_id ? xmj.mx_id : [null, ''],
+                        },
+                    });
+                    const mbIdList = this._.map(mlList, 'mb_id');
+                    mb_idList.push(...mbIdList);
+                    delList.push(...this._.map(mlList, 'id'));
+                    for (const mb of materialBills) {
+                        const newLists = {
+                            tid: this.ctx.tender.id,
+                            order: this.ctx.material.order,
+                            mid: this.ctx.material.id,
+                            mb_id: mb.mb_id,
+                            gcl_id: xmj.gcl_id,
+                            xmj_id: xmj.id,
+                            mx_id: xmj.mx_id ? xmj.mx_id : '',
+                            gather_qty: xmj.gather_qty ? xmj.gather_qty : null,
+                            quantity: mb.quantity,
+                            in_time: new Date(),
+                        };
+                        list.push(newLists);
+                        mb_idList.push(mb.mb_id);
+                    }
                 }
-                // 新增工料
-                const result = await transaction.insert(this.tableName, list);
-                if (result.affectedRows === 0) {
-                    throw '新增工料数据失败';
+                // 删除工料清单关联
+                if (delList.length > 0) await transaction.delete(this.tableName, { id: delList });
+                // 新增工料清单关联
+                if (list.length > 0) {
+                    const result = await transaction.insert(this.tableName, list);
+                    if (result.affectedRows === 0) {
+                        throw '新增工料数据失败';
+                    }
                 }
-                await this.calcQuantityByML(transaction, select.mb_id);
+                // 重算工料和总金额
+                const calcMBIdList = this._.uniq(mb_idList);
+                if (calcMBIdList.length > 0) {
+                    for (const select of calcMBIdList) {
+                        await this.calcQuantityByML(transaction, select);
+                    }
+                }
+                // throw 'fail';
+                // await this.calcQuantityByML(transaction, select.mb_id);
                 await transaction.commit();
                 return await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
             } catch (err) {
@@ -193,7 +223,7 @@ module.exports = app => {
             };
             await transaction.update(this.ctx.service.materialBills.tableName, updateData);
             // 计算本期总金额
-            const sql2 = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.ctx.service.materialBills.tableName + ' WHERE `tid` = ?';
+            const sql2 = 'SELECT SUM(`m_tp`) as 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);

+ 1 - 0
app/service/material_month.js

@@ -45,6 +45,7 @@ module.exports = app => {
             try {
                 const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
                 material_month.push(data.yearmonth);
+                material_month.sort();
                 if (mbList.length !== 0) {
                     const insertArray = [];
                     const updateArray = [];

+ 2 - 4
app/service/project_account.js

@@ -243,10 +243,8 @@ module.exports = app => {
                     }
                     this.ctx.session.sessionProject = projectInfo;
                     this.ctx.session.sessionProjectList = projectList;
-                    if (loginStatus === loginWay.normalPsw) {
-                        // 正常登录-记录登录日志
-                        await this.ctx.service.loginLogging.addLoginLog();
-                    }
+                    // 记录登录日志
+                    await this.ctx.service.loginLogging.addLoginLog(loginType, loginStatus);
 
                 }
             } catch (error) {

+ 4 - 4
app/service/report_memory.js

@@ -361,12 +361,12 @@ module.exports = app => {
                     const curStage = await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id,
                         this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid'}
                     ]);
                 } else {
                     const curStage = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id);
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid'}
                     ]);
                 }
             }
@@ -441,12 +441,12 @@ module.exports = app => {
                     const curPosStage = await this.ctx.service.stagePos.getAuditorStageData2(this.ctx.tender.id,
                         this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr'], prefix: '', relaId: 'pid'}
+                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'}
                     ]);
                 } else {
                     const curPosStage = await this.ctx.service.stagePos.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr'], prefix: '', relaId: 'pid'}
+                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'}
                     ]);
                 }
             }

+ 16 - 0
app/service/stage.js

@@ -298,6 +298,16 @@ module.exports = app => {
             return stages;
         }
 
+        async _getSumTp(condition, ...field) {
+            const fieldSql = [];
+            for (const f of field) {
+                fieldSql.push('SUM(`' + f + '`) as ' + '`' + f + '`');
+            }
+
+            const sql = 'SELECT ' + fieldSql.join(', ') + ' FROM ' + this.tableName + ' ' + this.ctx.helper.whereSql(condition);
+            return await this.db.queryOne(sql);
+        }
+
         /**
          *
          * @param tenderId - 标段id
@@ -337,6 +347,12 @@ module.exports = app => {
                 newStage.pre_contract_tp = this.ctx.helper.add(preStage.pre_contract_tp, preStage.contract_tp);
                 newStage.pre_qc_tp = this.ctx.helper.add(preStage.pre_qc_tp, preStage.qc_tp);
                 newStage.pre_yf_tp = this.ctx.helper.add(preStage.pre_yf_tp, preStage.yf_tp);
+                if (preStage.order === 1 || preStage.pre_sf_tp) {
+                    newStage.pre_sf_tp = this.ctx.helper.add(preStage.pre_sf_tp, preStage.sf_tp);
+                } else {
+                    const sumTp = await this._getSumTp({tid: preStage.tid}, 'sf_tp');
+                    newStage.pre_sf_tp = sumTp.sf_tp || 0;
+                }
             }
             const transaction = await this.db.beginTransaction();
             try {

+ 3 - 1
app/service/stage_bonus.js

@@ -121,7 +121,9 @@ module.exports = app => {
                 : this.ctx.tender.info.decimal.tp;
 
             const datas = data instanceof Array ? data : [data];
-            const orgDatas = await this.getAllDataByCondition({sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')});
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
 
             const uDatas = [];
             for (const d of datas) {

+ 9 - 9
app/service/stage_change.js

@@ -10,7 +10,7 @@
 
 const defaultPid = -1; // 非pid
 const audit = require('../const/audit');
-const timesLen = 100;
+const timesLen = audit.stage.timesLen;
 
 module.exports = app => {
 
@@ -40,11 +40,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And lid = ? And pid = ?' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid, lid, pid ? pid : -1];
@@ -66,11 +66,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?)) And lid = ? And pid = ?' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid, times, times, order, lid, pid ? pid : -1];
@@ -82,11 +82,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ?' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.`sid` = m.`sid` And c.lid = m.lid And c.pid = m.pid' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid];
@@ -98,11 +98,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?))' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.`sid` = m.`sid` And c.lid = m.lid And c.pid = m.pid' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid, times, times, order];

+ 9 - 4
app/service/stage_jgcl.js

@@ -83,13 +83,14 @@ module.exports = app => {
                     uuid: this.uuid.v4(),
                     add_sid: this.ctx.stage.id,
                     add_uid: this.ctx.session.sessionUser.accountId,
+                    tid: this.ctx.tender.id,
                     sid: this.ctx.stage.id,
                 };
                 nd.name = d.name;
                 nd.order = d.order;
                 if (d.unit) nd.unit = d.unit;
                 if (d.unit_price) nd.unit_price = d.unit_price;
-                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, d.unit_price);
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, d.unit);
                 if (d.arrive_qty) {
                     nd.arrive_qty = this.ctx.helper.round(d.arrive_qty, precision.value);
                     nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, tpDecimal);
@@ -126,7 +127,9 @@ module.exports = app => {
                 : this.ctx.tender.info.decimal.tp;
 
             const datas = data instanceof Array ? data : [data];
-            const orgDatas = await this.getAllDataByCondition({sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')});
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
             const info = this.ctx.tender.info;
 
             const uDatas = [];
@@ -142,7 +145,7 @@ module.exports = app => {
                 } else {
                     nd.unit_price = od.unit_price;
                 }
-                const precision = this.ctx.helper.findPrecision(info.precision, d.unit_price);
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
                 if (d.arrive_qty !== undefined) {
                     nd.arrive_qty = this.ctx.helper.round(d.arrive_qty, precision.value);
                     nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, tpDecimal);
@@ -223,12 +226,14 @@ module.exports = app => {
                 throw '标段数据有误';
             }
             const preDatas = await this.getAllDataByCondition({
-                columns: ['uuid', 'name', 'unit', 'unit_price', 'source', 'bills_code', 'check_code', 'memo', 'add_uid', 'add_sid', 'order'],
+                columns: ['uuid', 'name', 'unit', 'unit_price', 'source', 'bills_code', 'check_code', 'memo', 'add_uid', 'add_sid', 'order', 'arrive_qty', 'deduct_qty'],
                 where: { sid: preStage.id },
             });
             if (preDatas.length > 0) {
                 for (const pd of preDatas) {
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.arrive_qty) || !this.ctx.helper.checkZero(pd.deduct_qty);
+                    delete pd.arrive_qty;
+                    delete pd.deduct_qty;
                     pd.sid = stage.id;
                 }
                 const result = await transaction.insert(this.tableName, preDatas);

+ 5 - 2
app/service/stage_other.js

@@ -108,7 +108,9 @@ module.exports = app => {
                 : this.ctx.tender.info.decimal.tp;
 
             const datas = data instanceof Array ? data : [data];
-            const orgDatas = await this.getAllDataByCondition({sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')});
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
 
             const uDatas = [];
             for (const d of datas) {
@@ -192,12 +194,13 @@ module.exports = app => {
                 throw '标段数据有误';
             }
             const preDatas = await this.getAllDataByCondition({
-                columns: ['uuid', 'tid', 'add_uid', 'add_sid', 'add_time', 'name', 'o_type', 'total_price', 'order', 'memo', 'real_time'],
+                columns: ['uuid', 'tid', 'add_uid', 'add_sid', 'add_time', 'name', 'o_type', 'tp', 'total_price', 'order', 'memo', 'real_time'],
                 where: { sid: preStage.id }
             });
             if (preDatas.length > 0) {
                 for (const pd of preDatas) {
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.tp);
+                    delete pd.tp;
                     pd.sid = stage.id;
                     pd.sorder = stage.order;
                 }

+ 61 - 15
app/service/tender_info.js

@@ -86,13 +86,11 @@ module.exports = app => {
          */
         async getTenderInfoEx(tenderId) {
             const sql = 'select t2.name, t1.* from zh_tender_info t1 inner join zh_tender t2 on t2.id = t1.tid where t1.tid = ?';
-            // console.log('getTenderInfoEx: ' + tenderId);
             const sqlParam = [tenderId];
             const list = await this.db.query(sql, sqlParam);
             const info = list[0];
             const len = info.deal_info.length;
             info.deal_info = info.deal_info.slice(0, len - 1) + ',"name":"' + info.name + '"' + info.deal_info.slice(len - 1);
-            // console.log(info);
             for (const pi of parseInfo) {
                 info[pi] = !info[pi] || info[pi] === '' ? defaultInfo[pi] : JSON.parse(info[pi]);
                 this.ctx.helper._.defaults(info[pi], defaultInfo[pi]);
@@ -200,12 +198,9 @@ module.exports = app => {
             }
         }
 
-        async saveDecimal(tenderId, newDecimal, oldDecimal) {
-            const changeBills = [],
-                changeAdvanceBills = [],
-                calcUp = newDecimal.up < oldDecimal.up,
-                calcTp = newDecimal.tp !== oldDecimal.tp,
-                caclPayTp = (newDecimal.pay ? newDecimal.payTp : newDecimal.tp) < (oldDecimal.pay ? oldDecimal.payTp : oldDecimal.tp);
+        async _reCalcLedger(tenderId, newDecimal, oldDecimal) {
+            const changeBills = [];
+            const calcUp = newDecimal.up < oldDecimal.up, calcTp = newDecimal.tp !== oldDecimal.tp;
             if (calcUp || calcTp) {
                 const bills = await this.ctx.service.ledger.getAllDataByCondition({
                     columns: ['id', 'unit_price', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'deal_qty', 'quantity'],
@@ -222,6 +217,54 @@ module.exports = app => {
                     changeBills.push(cb);
                 }
             }
+            return changeBills;
+        }
+        async _reCalcStageExtra(tenderId, newDecimal, oldDecimal) {
+            let changeSj = [], changeSb = [], changeSo = [];
+            const stage = await this.ctx.service.stage.getLastestStage(tenderId, true);
+            if (!stage) return [changeSj, changeSb, changeSo];
+
+            const upDecimal = newDecimal.up, tpDecimal = newDecimal.extra ? newDecimal.extraTp : newDecimal.tp;
+            const calcUp = upDecimal < oldDecimal.up,
+                calcTp = tpDecimal < (oldDecimal.extra ? oldDecimal.extraTp : oldDecimal.tp);
+            console.log(calcUp, calcTp);
+
+            if (calcUp || calcTp) {
+                const stageJgcl = await this.ctx.service.stageJgcl.getAllDataByCondition({
+                    columns: ['id', 'unit_price', 'arrive_qty', 'deduct_qty'],
+                    where: { sid: stage.id }
+                });
+                for (const sj of stageJgcl) {
+                    const cj = { id: sj.id };
+                    cj.unit_price = calcUp ? this.ctx.helper.round(sj.unit_price, upDecimal) : sj.unit_price;
+                    cj.arrive_tp = this.ctx.helper.mul(sj.arrive_qty, sj.unit_price, tpDecimal);
+                    cj.deduct_tp = this.ctx.helper.mul(sj.deduct_qty, sj.unit_price, tpDecimal);
+                    changeSj.push(cj);
+                }
+            }
+            if (calcTp) {
+                changeSb = await this.ctx.service.stageBonus.getAllDataByCondition({
+                    columns: ['id', 'tp'],
+                    where: { sid: stage.id }
+                });
+                for (const cb of changeSb) {
+                    cb.tp = this.ctx.helper.round(cb.tp, tpDecimal);
+                }
+                changeSo = await this.ctx.service.stageOther.getAllDataByCondition({
+                    columns: ['id', 'total_price', 'tp'],
+                    where: { sid: stage.id }
+                });
+                for (const co of changeSo) {
+                    if (stage.order === 1) co.total_price = this.ctx.helper.round(co.total_price, tpDecimal);
+                    co.tp = this.ctx.helper.round(co.tp, tpDecimal);
+                }
+            }
+            return [changeSj, changeSb, changeSo];
+        }
+
+        async saveDecimal(tenderId, newDecimal, oldDecimal) {
+            const changeAdvanceBills = [];
+            const caclPayTp = (newDecimal.pay ? newDecimal.payTp : newDecimal.tp) < (oldDecimal.pay ? oldDecimal.payTp : oldDecimal.tp);
             if (caclPayTp) {
                 // 获取预付款需要修改的相关记录
                 const ad_bills = await this.ctx.service.advance.getAllDataByCondition({
@@ -238,14 +281,21 @@ module.exports = app => {
                     changeAdvanceBills.push(cb);
                 }
             }
-            if (changeBills.length > 0) {
+            const changeBills = await this._reCalcLedger(tenderId, newDecimal, oldDecimal);
+            const [changeSj, changeSb, changeSo] = await this._reCalcStageExtra(tenderId, newDecimal, oldDecimal);
+            if (changeBills.length > 0 ||
+                changeAdvanceBills.length > 0 ||
+                changeSj.length > 0 || changeSb.length > 0 || changeSo.length > 0) {
                 const transaction = await this.db.beginTransaction();
                 try {
                     await transaction.update(this.tableName,
                         { decimal: JSON.stringify(newDecimal) }, { where: { tid: tenderId } });
-                    await transaction.updateRows(this.ctx.service.ledger.tableName, changeBills);
+                    if (changeBills.length > 0) await transaction.updateRows(this.ctx.service.ledger.tableName, changeBills);
+                    if (changeSj.length > 0) await transaction.updateRows(this.ctx.service.stageJgcl.tableName, changeSj);
+                    if (changeSb.length > 0) await transaction.updateRows(this.ctx.service.stageBonus.tableName, changeSb);
+                    if (changeSo.length > 0) await transaction.updateRows(this.ctx.service.stageOther.tableName, changeSo);
 
-                    // await transaction.updateRows(this.ctx.service.advance.tableName, changeAdvanceBills);
+                    if (changeAdvanceBills.length) await transaction.updateRows(this.ctx.service.advance.tableName, changeAdvanceBills);
                     await transaction.commit();
                 } catch (error) {
                     await transaction.rollback();
@@ -255,10 +305,6 @@ module.exports = app => {
                 await this.db.update(this.tableName,
                     { decimal: JSON.stringify(newDecimal) }, { where: { tid: tenderId } });
             }
-            // 更新预付款记录
-            if (changeAdvanceBills.length) {
-                await this.db.updateRows(this.ctx.service.advance.tableName, changeAdvanceBills);
-            }
         }
     }
 

+ 1 - 0
app/view/material/audit_modal.ejs

@@ -679,6 +679,7 @@
     <script>
         const accountGroup = JSON.parse('<%- JSON.stringify(accountGroup) %>');
         const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
+        const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');
     </script>
 <% } %>
 <script>

+ 123 - 0
app/view/material/exponent.ejs

@@ -0,0 +1,123 @@
+<% include ./material_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <% 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 ml-3">
+                    本期调差计量期:第<span class="mx-2"><%= material.s_order.split(',').join(',') %></span>期
+                </div>
+                <div class="d-inline-block ml-3">
+                    本期价差:<b id="ex_expr"><%- material.ex_expr %></b>
+                </div>
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap pr-46">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <div id="left-view" class="c-body" style="width: 100%">
+                <!--上部分-->
+                <div class="sjs-height-1" id="material-exponent-spread">
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap">
+                    <div class="bc-bar mb-1">
+                        <div class="input-group input-group-sm ">
+                            <div class="input-group-prepend">
+                                <span class="input-group-text" id="basic-addon1">增值税税率</span>
+                            </div>
+                            <select class="form-control form-control-sm col-1" id="changeRate">
+                                <% if (!material.readOnly) { %>
+                                    <option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>
+                                    <option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>
+                                    <option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>
+                                <% } else { %>
+                                    <option value="<%= material.rate %>" selected><%= material.rate %>%</option>
+                                <% } %>
+                            </select>
+                        </div>
+                    </div>
+                    <div class="sp-wrap">
+                        <div class="col-7 p-0">
+                            <table class="table table-sm table-bordered">
+                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
+                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th><th>本期金额</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>
+                                    <td><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, 2) : null %></td>
+                                    <td><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.ex_pre_tp, material.ex_tp), 2) : null %></td>
+                                </tr>
+                                <tr id="rate_set"><td>材料价差费用(含税)</td>
+                                    <td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                    <td><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                </tr>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="right-view" class="c-body" style="display:none;width: 33%">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content" style="width: 100%">
+                    <div id="tiaochaje" class="tab-pane active">
+                        <div class="sjs-sh-1">
+                            <table class="table table-bordered">
+                                <thead>
+                                <tr><th>需调整的价差</th><th>金额</th><th>选择</th></tr>
+                                </thead>
+                                <tbody id="calc_basic_select">
+                                <% for (const bq of ex_calc) { %>
+                                    <tr>
+                                        <td><%- bq.text %></td><td><%- bq.value %></td>
+                                        <td><input type="checkbox" value="<%- bq.code %>" <% if (material.readOnly) { %>disabled<% } %> class="calc_select" <% if (bq.select) { %>checked<% } %>></td>
+                                    </tr>
+                                <% } %>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link" content="#base-tab" href="javascript: void(0);">调差基数</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<div style="display: none">
+    <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
+    <img src="/public/images/icon-ok.png" id="icon-ok" />
+</div>
+<script>
+    const readOnly = <%- material.readOnly %>;
+    const materialID = <%- material.id %>;
+    const materialOrder = <%- material.order %>;
+    let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
+    let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;
+    const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
+    const ex_pre_tp = <%= material.ex_pre_tp !== null ? material.ex_pre_tp : 0 %>;
+    const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
+    const ex_pre_tp_hs = <%= ex_pre_tp_hs !== null ? ex_pre_tp_hs : 0 %>;
+    const materialType = JSON.parse('<%- JSON.stringify(materialType) %>');
+    const ex_calc = JSON.parse('<%- JSON.stringify(ex_calc) %>');
+    let ex_expr = '<%- material.ex_expr %>';
+    let materialExponentData = JSON.parse('<%- JSON.stringify(materialExponentData) %>');
+</script>

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

@@ -0,0 +1 @@
+<% include ./audit_modal.ejs %>

+ 14 - 4
app/view/material/file.ejs

@@ -1,15 +1,26 @@
 <% include ./material_sub_menu.ejs %>
 <div class="panel-content">
   <div class="panel-title">
-    <div class="title-main d-flex justify-content-between">
-      <div class="d-flex justify-content-start align-items-center">
+    <div class="title-main d-flex">
+      <% include ./material_sub_mini_menu.ejs %>
+      <div>
+        <div class="d-inline-block">
           <a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>
+        </div>
+        <div class="d-inline-block">
           <span class="d-flex align-items-center" style="margin-left: 5px;">
             <input type="checkbox" id="file-checkbox">
             <span class="text-primary" style="margin-left: 5px;">所有期</span>
-
           </span>
         </div>
+      </div>
+      <!--<div class="d-flex justify-content-start align-items-center">-->
+          <!--<a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>-->
+          <!--<span class="d-flex align-items-center" style="margin-left: 5px;">-->
+            <!--<input type="checkbox" id="file-checkbox">-->
+            <!--<span class="text-primary" style="margin-left: 5px;">所有期</span>-->
+          <!--</span>-->
+        <!--</div>-->
     </div>
   </div>
   <div class="content-wrap">
@@ -92,7 +103,6 @@
   const curAuditor = JSON.parse('<%- JSON.stringify(ctx.material.curAuditor) %>');
   const material = JSON.parse('<%- JSON.stringify(ctx.material) %>');
   const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
-  const cur_uid = '<%- ctx.session.sessionUser.accountId %>';
   const tid = '<%- ctx.tender.id %>';
   const mid = '<%- ctx.material.id %>';
   const fileList = JSON.parse('<%- JSON.stringify(fileList) %>');

+ 18 - 5
app/view/material/index.ejs

@@ -20,13 +20,21 @@
                 <table class="table table-bordered">
                     <thead>
                     <tr>
-                        <th>期数</th>
-                        <th class="text-center">添加时间</th>
-                        <th class="text-center">计量期</th>
+                        <th rowspan="2">期数</th>
+                        <th class="text-center" rowspan="2">添加时间</th>
+                        <th class="text-center" rowspan="2">计量期</th>
+                        <th class="text-center" colspan="2">信息价调差</th>
+                        <th class="text-center" colspan="2">指数法调差</th>
+                        <th class="text-center" rowspan="2">合计(含税)</th>
+                        <th class="text-center" rowspan="2">合计</th>
+                        <th class="text-center" rowspan="2">审批进度</th>
+                        <th class="text-center" rowspan="2" width="140">操作</th>
+                    </tr>
+                    <tr>
+                        <th class="text-center">价差费用(含税)</th>
+                        <th class="text-center">价差费用</th>
                         <th class="text-center">价差费用(含税)</th>
                         <th class="text-center">价差费用</th>
-                        <th class="text-center">审批进度</th>
-                        <th class="text-center">操作</th>
                     </tr>
                     </thead>
                     <tbody>
@@ -35,10 +43,15 @@
                             <td>
                                 <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>">第 <%- m.order %> 期</a>
                             </td>
+                            <% console.log(ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), 2), ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), 2))) %>
                             <td class="text-center"><%= moment(m.in_time).format('YYYY-MM-DD') %></td>
                             <td class="text-center">第 <%= m.s_order %> 期</td>
                             <td class="text-right"><%= m.m_tp !== null ? ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), 2) : null %></td>
                             <td class="text-right"><%= m.m_tp !== null ? ctx.helper.round(m.m_tp, 2) : null %></td>
+                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), 2) : null %></td>
+                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(m.ex_tp, 2) : null %></td>
+                            <td class="text-right"><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), 2), ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), 2)) %></td>
+                            <td class="text-right"><%= ctx.helper.add(ctx.helper.round(m.ex_tp, 2), ctx.helper.round(m.m_tp, 2)) %></td>
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
                                 <% if (m.curAuditor) { %>
                                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditor.name %><%if (m.curAuditor.role !== '' && m.curAuditor.role !== null) { %>-<%- m.curAuditor.role %><% } %></a>

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

@@ -50,11 +50,22 @@
                         </div>
                     </div>
                     <div class="sp-wrap">
-                        <div class="col-4 p-0">
+                        <div class="col-7 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>
-                                <tr id="rate_set"><td>材料价差费用(含税)</td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td><td><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td></tr>
+                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
+                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th><th>本期金额</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>
+                                    <td><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, 2) : null %></td>
+                                    <td><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.ex_pre_tp, material.ex_tp), 2) : null %></td>
+                                </tr>
+                                <tr id="rate_set"><td>材料价差费用(含税)</td>
+                                    <td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                    <td><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                </tr>
                             </table>
                         </div>
                     </div>
@@ -102,8 +113,11 @@
     const readOnly = <%- material.readOnly %>;
     const materialID = <%- material.id %>;
     let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
+    let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;
     const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
+    const ex_pre_tp = <%= material.ex_pre_tp !== null ? material.ex_pre_tp : 0 %>;
     const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
+    const ex_pre_tp_hs = <%= ex_pre_tp_hs !== null ? ex_pre_tp_hs : 0 %>;
     const calcBase = JSON.parse('<%- JSON.stringify(calcBase) %>');
     const months = JSON.parse('<%- JSON.stringify(months) %>');
     let monthsList = JSON.parse('<%- JSON.stringify(monthsList) %>');

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

@@ -42,7 +42,7 @@
             <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">
+                    <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" data-auto-close="true">
                 </div>
                 <!--首次创建-->
                 <div class="alert alert-primary">创建月信息价后,「本期信息价」 「单价」列将无法自行填写,而是从「月信息价」中获取,如果有多个「月信息价」,将取平均值。</div>

+ 8 - 1
app/view/material/material_sub_menu.ejs

@@ -11,11 +11,18 @@
         <div class="nav-box">
                 <ul class="nav-list list-unstyled">
                     <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order) { %>active<% } %>">
-                        <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">调差工料</span></a>
+                        <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">信息价调差</span></a>
                     </li>
                 </ul>
             </div>
         <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/exponent') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>/exponent"><span class="ml-3">指数法调差</span></a>
+                </li>
+            </ul>
+        </div>
+        <div class="nav-box">
                 <ul class="nav-list list-unstyled">
                     <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/list') { %>active<% } %>">
                         <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- material.order %>/list"><span class="ml-3">调差清单</span></a>

+ 15 - 1
app/view/material/material_sub_mini_menu.ejs

@@ -12,7 +12,14 @@
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order) { %>active<% } %>">
-                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">调差工料</span></a>
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">信息价调差</span></a>
+                </li>
+            </ul>
+        </div>
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/exponent') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>/exponent"><span class="ml-3">指数法调差</span></a>
                 </li>
             </ul>
         </div>
@@ -23,6 +30,13 @@
                 </li>
             </ul>
         </div>
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/file') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- material.order %>/file"><span class="ml-3">附件</span></a>
+                </li>
+            </ul>
+        </div>
         <% include ./audit_btn.ejs %>
         <div class="side-fold"><a href="javascript: void(0);" data-toggle="tooltip" data-placement="top" data-original-title="展开侧栏" id="to-menu"><i class="fa fa-thumb-tack"></i></a></div>
     </div>

+ 5 - 1
app/view/measure/stage.ejs

@@ -9,7 +9,11 @@
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && ctx.tender.data.ledger_status === auditConst.status.checked &&
                         (stages.length === 0 || stages[0].status === auditConst.status.checked)) { %>
-                <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% if ((ctx.helper.checkZero(ctx.tender.info.deal_param.contractPrice) || ctx.helper.checkZero(ctx.tender.info.deal_param.startAdvance)) && stages.length === 0) { %>
+                    <a href="#add-qi" data-toggle="modal" data-target="#tips" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% } else { %>
+                    <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% } %>
                 <% } %>
             </div>
         </div>

+ 17 - 0
app/view/measure/stage_modal.ejs

@@ -1,5 +1,22 @@
 <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && ctx.tender.data.ledger_status === auditConst.status.checked &&
         (stages.length === 0 || stages[stages.length- 1].status === auditConst.status.checked)) { %>
+<!--弹出填写合同参数-->
+<div class="modal fade" id="tips" 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">
+                <h6>请先去“标段概况-合同参数”填写数据,再开始第一期计量</h6>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <a type="button" class="btn btn-sm btn-primary" href="/tender/<%- ctx.tender.id %>" >进入标段概况</a>
+            </div>
+        </div>
+    </div>
+</div>
 <!--弹出添加期-->
 <div class="modal fade" id="add-qi" data-backdrop="static">
     <div class="modal-dialog" role="document">

+ 23 - 46
app/view/profile/safe.ejs

@@ -23,54 +23,31 @@
                             <% } %>
                         </form>
                         <!-- 访问日志 -->
-                        <% if(ctx.session.sessionUser.loginStatus === loginWay.normalPsw) { %>
-                            <div class="col-12 mt-5">
-                                <h4>访问日志</h4>
-                                <table class="table table-hover">
-                                    <thead>
+                        <div class="col-12 mt-5">
+                            <h4>访问日志</h4>
+                            <table class="table table-hover">
+                                <thead>
+                                    <tr>
+                                        <th></th>
+                                        <th>系统</th>
+                                        <th>浏览器</th>
+                                        <th>登录时间</th>
+                                        <th>登录地址</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    <% loginLogging.forEach((item, idx) => { %>
                                         <tr>
-                                            <th></th>
-                                            <th>系统</th>
-                                            <th>浏览器</th>
-                                            <th>登录时间</th>
-                                            <th>登录地址</th>
+                                            <td><%- idx + 1 %></td>
+                                            <td><%- item.os %></td>
+                                            <td><%- item.browser %></td>
+                                            <td><%- ctx.helper.formatFullDate(item.create_time) %></td>
+                                            <td><%- item.address %></td>
                                         </tr>
-                                    </thead>
-                                    <tbody>
-                                        <% loginLogging.forEach((item, idx) => { %>
-                                            <tr>
-                                                <td><%- idx + 1 %></td>
-                                                <td><%- item.os %></td>
-                                                <td><%- item.browser %></td>
-                                                <td><%- ctx.helper.formatFullDate(item.create_time) %></td>
-                                                <td><%- item.address %></td>
-                                            </tr>
-                                        <% }) %>
-                                        <!-- <tr>
-                                            <td>1</td>
-                                            <td>Windows NT 10.0 64-bit</td>
-                                            <td>Chrome 55.0.2883.87 m (64-bit)</td>
-                                            <td>2017-01-12 09:09</td>
-                                            <td>广东省珠海市 电信(116.19.86.133)</td>
-                                        </tr>
-                                        <tr>
-                                            <td>2</td>
-                                            <td>Windows NT 10.0 64-bit</td>
-                                            <td>Chrome 55.0.2883.87 m (64-bit)</td>
-                                            <td>2017-01-12 09:09</td>
-                                            <td>广东省珠海市 电信(116.19.86.133)</td>
-                                        </tr>
-                                        <tr>
-                                            <td>3</td>
-                                            <td>Windows NT 10.0 64-bit</td>
-                                            <td>Chrome 55.0.2883.87 m (64-bit)</td>
-                                            <td>2017-01-12 09:09</td>
-                                            <td>广东省珠海市 电信(116.19.86.133)</td>
-                                        </tr> -->
-                                    </tbody>
-                                </table>
-                            </div>
-                        <% } %>
+                                    <% }) %>
+                                </tbody>
+                            </table>
+                        </div>
                     </div>
                 </div>
             </div>

+ 5 - 4
app/view/profile/sms.ejs

@@ -2,7 +2,7 @@
 <div class="panel-content" id="app">
     <div class="panel-title">
         <div class="title-main">
-            <h2>短信通知</h2>
+            <h2>认证手机</h2>
         </div>
     </div>
     <div class="content-wrap">
@@ -13,7 +13,7 @@
                     <% if (accountData.auth_mobile !== '') { %>
                     <!--已绑定手机-->
                     <div class="form-group">
-                        <label>已认证手机(用于 找回密码、接收通知)</label>
+                        <label>已认证手机</label>
                         <div class="input-group mb-3">
                             <input class="form-control form-control-sm" readonly="" value="<%= accountData.auth_mobile %>">
                             <div class="input-group-append">
@@ -23,9 +23,10 @@
                     </div>
                     <% } %>
                     <!--绑定手机-->
+                    <% if (accountData.auth_mobile === '') { %><div class="alert alert-warning">认证手机用户找回密码操作,请优先设置。</div><% } %>
                     <form id="mobile-form" <% if (accountData.auth_mobile !== '') { %>style="display: none" <% } %>>
                         <div class="form-group">
-                            <label>认证手机(用于 找回密码、接收通知)</label>
+                            <label>认证手机</label>
                             <div class="input-group mb-3">
                                 <input class="form-control form-control-sm" placeholder="输入11位手机号码" value="" name="auth_mobile" maxlength="11"/>
                                 <div class="input-group-append">
@@ -41,7 +42,7 @@
                         </div>
                         <button type="button" class="btn btn-secondary btn-sm disabled" id="bind-btn">确认绑定</button>
                     </form>
-                    <% if (accountData.auth_mobile !== '') { %>
+                    <% if (accountData.auth_mobile !== '' && false) { %>
                     <!--短信通知开关(已有认证手机后显示)-->
                     <div class="mt-5">
                         <h4>通知类型</h4>

+ 33 - 11
app/view/shares/ledger_check_modal.ejs

@@ -1,4 +1,3 @@
-
 <!--数据检查-->
 <div class="modal fade" id="ledger-check-modal" data-backdrop="static">
     <div class="modal-dialog" role="document">
@@ -7,6 +6,21 @@
                 <h5 class="modal-title">数据检查</h5>
             </div>
             <div class="modal-body">
+                <div class="text-center my-3">
+                    <button class="btn btn-primary px-5" id="ledger-check-begin">开始检查</button>
+                    <h6 class="text-center mt-3">数据检查可以为您排查有问题项</h6>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="ledger-check-result" 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">
                 <p>数据检查,将检查罗列台帐中以下内容:</p>
                 <div class="card mb-2 p-2 border-success" id="check-sibling">
                     <div class="d-flex justify-content-between">
@@ -38,9 +52,8 @@
                         <span class="text-success" title="完成" name="check-status"><i class="fa fa-check"></i></span>
                     </div>
                 </div>
-                 <a href="javascript: void(0);" class="btn btn-sm btn-block btn-primary" id="ledger-check-begin">开始检查</a>
                  <a href="#" class="btn btn-sm btn-block btn-primary disabled" id="ledger-check-waiting">检查中,请等待...</a>
-                <p class="text-center text-success" id="ledger-check-hint">检查完成,现在您可以查看结果。</p>
+                 <p class="text-center text-success" id="ledger-check-hint">检查完成,现在您可以查看结果。</p>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
@@ -71,8 +84,9 @@
         }
 
         const initWaitingModal = function () {
-            $('.card', '#ledger-check-modal').removeClass('border-success');
-            $('[name=check-status]', '#ledger-check-modal').removeClass('text-success').addClass('text-muted').html('待检查');
+            $('.card', '#ledger-check-result').removeClass('border-success').removeClass('border-warning');
+            $('[name=check-status]', '#ledger-check-result').removeClass('text-success').removeClass('text-warning')
+                .addClass('text-muted').html('待检查');
             showCheckPart($('#check-sibling'), checkOption.sibling.enable);
             showCheckPart($('#check-empty-code'), checkOption.empty_code.enable);
             showCheckPart($('#check-calc'), checkOption.calc.enable);
@@ -88,8 +102,13 @@
             const checkStatus = $('[name=check-status]', selector);
             checkStatus.html('<i class="fa fa-spinner fa-spin"></i>');
             const result = checkFun(ledger, option);
-            checkStatus.removeClass('text-muted').addClass('text-success').html('<i class="fa fa-check"></i>');
-            $(selector).addClass('border-success');
+            if (result && result.length > 0) {
+                checkStatus.removeClass('text-muted').addClass('text-warning').html('<i class="fa fa-exclamation-triangle"></i>');
+                $(selector).addClass('border-warning');
+            } else {
+                checkStatus.removeClass('text-muted').addClass('text-success').html('<i class="fa fa-check"></i>');
+                $(selector).addClass('border-success');
+            }
             return result;
         }
 
@@ -189,7 +208,6 @@
 
 
         const checkData = function () {
-            $('#ledger-check-begin').hide();
             $('#ledger-check-waiting').show();
             const checkData = {
                 check_time: new Date(),
@@ -229,10 +247,14 @@
             }
         }
 
-        $('#ledger-check-begin').bind('click', checkData);
-        $('#ledger-check-modal').bind('show.bs.modal', initWaitingModal);
-        $('#ledger-check-show').bind('click', function () {
+        $('#ledger-check-begin').bind('click', () => {
             $('#ledger-check-modal').modal('hide');
+            initWaitingModal();
+            checkData();
+            $('#ledger-check-result').modal('show');
+        });
+        $('#ledger-check-show').bind('click', function () {
+            $('#ledger-check-result').modal('hide');
             setting.checkList.show();
         });
     }

File diff suppressed because it is too large
+ 912 - 912
app/view/stage/audit_modal.ejs


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

@@ -613,7 +613,7 @@
 </div>
 <script>
     let property = JSON.parse('<%- JSON.stringify(tenderInfo) %>');
-    let ledgerChecked = <%- tender.ldeger_status === audit.ledger.status.checked %>;
+    let ledgerChecked = <%- tender.ledger_status === audit.ledger.status.checked %>;
     let firstStageChecked = <%- lastStage !== undefined && lastStage !== null && (lastStage.order > 1 || (lastStage.order === 1 && lastStage.status === audit.stage.status.checked)) %>;
 
     // 根据Min Max限制Input输入

+ 1 - 1
config/menu.js

@@ -282,7 +282,7 @@ const profileMenu = {
         url: '/profile/info',
     },
     sms: {
-        name: '短信通知',
+        name: '认证手机',
         display: false,
         url: '/profile/sms',
     },

+ 17 - 1
config/web.js

@@ -76,7 +76,7 @@ const JsFiles = {
                 mergeFile: 'tender_list_info',
             },
             progress: {
-                files: ['/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js', '/public/js/decimal.min.js'],
+                files: ['/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js', '/public/js/decimal.min.js', '/public/js/moment/moment.min.js'],
                 mergeFiles: [
                     '/public/js/zh_calc.js',
                     '/public/js/PinYinOrder.bundle.js',
@@ -485,6 +485,22 @@ const JsFiles = {
                 ],
                 mergeFile: 'material',
             },
+            exponent: {
+                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js', '/public/js/toastr.min.js'],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/material_exponent.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/material_audit.js',
+                    '/public/js/datepicker/datepicker.min.js',
+                    '/public/js/datepicker/datepicker.zh.js',
+                ],
+                mergeFile: 'material_exponent',
+            },
             list: {
                 files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js', '/public/js/toastr.min.js'],
                 mergeFiles: [

+ 69 - 0
sql/update.sql

@@ -0,0 +1,69 @@
+ALTER TABLE `zh_project` CHANGE `page_show` `page_show` VARCHAR(5000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT '{\"bwtz\":\"1\"}' COMMENT '前台页面或功能展示与隐藏';
+
+ALTER TABLE `zh_change_audit_list` ADD `xmj_code` VARCHAR(500) NULL DEFAULT NULL COMMENT '项目节编号' AFTER `detail`, ADD `xmj_jldy` VARCHAR(500) NULL DEFAULT NULL COMMENT '细目' AFTER `xmj_code`;
+
+CREATE TABLE `zh_login_logging` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `browser` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '浏览器信息',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间',
+  `ip` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '登录ip',
+  `os` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '操作系统',
+  `address` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '登录地址',
+  `uid` int(11) NOT NULL COMMENT '用户id',
+  `pid` int(11) NOT NULL COMMENT '项目id',
+  `type` int(1) NOT NULL COMMENT '登录类型:1(sso登录) | 2(正常或副密码登录) | 3(接口登录或微信登录)',
+  `show` int(1) NOT NULL COMMENT '是否显示: 0-显示,1-不显示',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=382 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='登录日志';
+
+ALTER TABLE `zh_material_bills` ADD `is_summary` TINYINT(1) NOT NULL DEFAULT '1' COMMENT '是否汇总' AFTER `remark`;
+
+ALTER TABLE `zh_material_bills_history` ADD `is_summary` TINYINT(1) NOT NULL DEFAULT '1' COMMENT '是否汇总' AFTER `pre_tp`;
+
+ALTER TABLE `zh_deal_bills`
+MODIFY COLUMN `id`  varchar(50) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL COMMENT '主键(uuid)' FIRST ,
+CHANGE COLUMN `deal_id` `order`  int(11) NOT NULL AFTER `tender_id`;
+
+-- 2020-9-21
+ALTER TABLE `zh_stage_jgcl`
+ADD COLUMN `tid`  int(11) NULL COMMENT '标段id' AFTER `add_sid`;
+
+Update zh_stage_jgcl LEFT JOIN zh_stage ON zh_stage_jgcl.sid = zh_stage.id Set zh_stage_jgcl.tid = zh_stage.tid
+
+-- 指数调差
+ALTER TABLE `zh_material` ADD `ex_tp` DECIMAL(30,8) NULL DEFAULT NULL COMMENT '指数本期金额' AFTER `pre_tp`, ADD `ex_pre_tp` DECIMAL(30,8) NULL DEFAULT NULL COMMENT '指数截止上期金额' AFTER `ex_tp`, ADD `ex_expr` VARCHAR(3000) NULL DEFAULT NULL COMMENT '指数调差结果公式' AFTER `ex_pre_tp`, ADD `ex_base_selested` VARCHAR(255) NULL DEFAULT NULL COMMENT '已选调差基数值' AFTER `ex_expr`;
+
+CREATE TABLE `zh_material_exponent` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `mid` int(11) NOT NULL COMMENT '调差期id',
+  `type` tinyint(1) NOT NULL DEFAULT '2' COMMENT '类型(1定值或2变值)',
+  `symbol` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '符号',
+  `symbol_desc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '符号说明',
+  `code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '编号',
+  `weight_num` decimal(30,8) DEFAULT NULL COMMENT '加权系数',
+  `basic_price` decimal(30,8) DEFAULT NULL COMMENT '基本价格',
+  `basic_times` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '基准时间',
+  `m_price` decimal(30,8) DEFAULT NULL COMMENT '现行价格指数',
+  `calc_num` decimal(30,8) DEFAULT NULL COMMENT '计算值',
+  `remark` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
+  `is_summary` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否汇总,默认为0否',
+  `in_time` datetime NOT NULL COMMENT '添加时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='指数调差表';
+
+CREATE TABLE `zh_material_exponent_history` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '所属标段id',
+  `mid` int(11) NOT NULL COMMENT '调差期id',
+  `order` tinyint(4) NOT NULL COMMENT '调差期期数',
+  `me_id` int(11) NOT NULL COMMENT '指数id',
+  `type` tinyint(1) NOT NULL DEFAULT '2' COMMENT '类型(1定值或2变值)',
+  `weight_num` decimal(30,8) DEFAULT NULL COMMENT '本期加权系数',
+  `basic_price` decimal(30,8) DEFAULT NULL COMMENT '本期基本价格指数',
+  `basic_times` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '本期基准时间',
+  `m_price` decimal(30,8) DEFAULT NULL COMMENT '本期现行价格指数',
+  `calc_num` decimal(30,8) DEFAULT NULL COMMENT '计算值',
+  `is_summary` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否汇总',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='指数调差清单历史表';