Przeglądaj źródła

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

TonyKang 5 lat temu
rodzic
commit
d742bd4742
53 zmienionych plików z 1672 dodań i 474 usunięć
  1. 15 15
      app/const/spread.js
  2. 3 6
      app/controller/ledger_controller.js
  3. 30 5
      app/controller/material_controller.js
  4. 1 1
      app/controller/profile_controller.js
  5. 11 2
      app/controller/revise_controller.js
  6. 29 3
      app/controller/stage_controller.js
  7. 1 2
      app/controller/tender_controller.js
  8. 40 29
      app/lib/pay_calc.js
  9. 21 0
      app/public/js/global.js
  10. 178 16
      app/public/js/ledger.js
  11. 84 8
      app/public/js/material.js
  12. 1 0
      app/public/js/material_audit.js
  13. 155 49
      app/public/js/material_list.js
  14. 1 0
      app/public/js/measure_stage.js
  15. 233 21
      app/public/js/revise.js
  16. 2 2
      app/public/js/revise_history.js
  17. 250 23
      app/public/js/stage.js
  18. 1 0
      app/public/js/stage_audit.js
  19. 15 19
      app/public/js/stage_detail.js
  20. 14 0
      app/public/js/stage_im.js
  21. 2 2
      app/public/js/stage_pay.js
  22. 2 0
      app/public/js/tender_list.js
  23. 5 5
      app/router.js
  24. 4 4
      app/service/ledger.js
  25. 34 1
      app/service/material.js
  26. 5 3
      app/service/material_list.js
  27. 65 0
      app/service/material_list_notjoin.js
  28. 202 78
      app/service/pos.js
  29. 143 127
      app/service/revise_pos.js
  30. 7 2
      app/service/stage.js
  31. 2 1
      app/service/stage_audit.js
  32. 15 9
      app/service/stage_pos.js
  33. 3 0
      app/view/layout/layout.ejs
  34. 11 1
      app/view/ledger/explode.ejs
  35. 1 1
      app/view/material/audit_modal.ejs
  36. 1 1
      app/view/material/index.ejs
  37. 11 2
      app/view/material/info.ejs
  38. 3 2
      app/view/material/list.ejs
  39. 2 0
      app/view/material/list_modal.ejs
  40. 1 1
      app/view/material/modal.ejs
  41. 1 1
      app/view/measure/gather.ejs
  42. 0 3
      app/view/revise/history.ejs
  43. 13 1
      app/view/revise/info.ejs
  44. 7 1
      app/view/revise/info_modal.ejs
  45. 2 2
      app/view/stage/detail.ejs
  46. 3 3
      app/view/stage/gather.ejs
  47. 12 1
      app/view/stage/index.ejs
  48. 12 6
      app/view/tender/detail.ejs
  49. 3 3
      app/view/tender/sub_menu.ejs
  50. 1 1
      app/view/tender/tender_sub_menu.ejs
  51. 5 5
      config/config.default.js
  52. 6 6
      config/menu.js
  53. 3 0
      config/web.js

+ 15 - 15
app/const/spread.js

@@ -20,17 +20,17 @@ const withCl = {
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
-            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.unit_price'},
-            {title: '设计数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.dgnQty'},
-            {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.dgnQty'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '设计数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.quantity'},
+            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '施工图复核|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.quantity'},
+            {title: '施工图复核|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sgfh_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '设计错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sjcl_qty', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.quantity'},
+            {title: '设计错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sjcl_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sjcl_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '其他错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qtcl_qty', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.quantity'},
+            {title: '其他错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qtcl_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qtcl_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '小计|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
@@ -64,13 +64,13 @@ const withoutCl = {
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
-            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.unit_price'},
-            {title: '设计数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.dgnQty'},
-            {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.dgnQty'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '设计数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.quantity'},
+            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '施工图复核|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.quantity'},
+            {title: '施工图复核|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sgfh_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}
@@ -124,7 +124,7 @@ const stageTz = {
             {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@', cellType: 'autoTip'},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
             //{title: '累计完成率(%)', colSpan: '1', rowSpan: '2', field: 'percent', hAlign: 0, width: 100, readOnly: true, type: 'Number'},
-            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -183,7 +183,7 @@ const stageCl = {
             {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@', cellType: 'autoTip'},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
             //{title: '累计完成率(%)', colSpan: '1', rowSpan: '2', field: 'percent', hAlign: 0, width: 100, readOnly: true, type: 'Number'},
-            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -245,7 +245,7 @@ const stageNoCl = {
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'final_dgn_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@', cellType: 'autoTip'},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
-            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
         ],
         emptyRows: 0,
         headRows: 2,

+ 3 - 6
app/controller/ledger_controller.js

@@ -112,9 +112,6 @@ module.exports = app => {
                 const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(tender.id, tender.data.ledger_times);
                 const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
                 const auditors = await ctx.service.ledgerAudit.getAuditors(tender.id, times);
-                const content = auditors.length > 0 ? await ctx.service.ledgerAuditContent.getAllDataByCondition({
-                    where: { tender_id: tender.id, times, audit_id: auditors[0].audit_id },
-                }) : null;
                 const user = await ctx.service.projectAccount.getAccountInfoById(ctx.tender.data.user_id);
                 const auditHistory = [];
                 if (ctx.tender.data.ledger_times > 1) {
@@ -131,7 +128,6 @@ module.exports = app => {
                     curAuditor,
                     user,
                     auditHistory,
-                    content,
                     ledgerSpreadSetting: JSON.stringify(ledgerSpread),
                     posSpreadSetting: JSON.stringify(posSpread),
                     tenderMenu,
@@ -155,7 +151,7 @@ module.exports = app => {
                 await this.layout('ledger/explode.ejs', renderData, 'ledger/explode_modal.ejs');
             } catch (err) {
                 this.log(err);
-                await this.layout('/dashboard');
+                await this.redirect('/dashboard');
             }
         }
 
@@ -478,7 +474,8 @@ module.exports = app => {
                 const compressData = ctx.request.body.data;
                 const data = JSON.parse(LzString.decompressFromUTF16(compressData));
                 const responseData = { err: 0, msg: '', data: {}, };
-                await ctx.service.ledger.importExcel(data);
+                const templateId = await this.ctx.service.valuation.getValuationTemplate(this.ctx.tender.data.valuation);
+                await ctx.service.ledger.importExcel(templateId, data);
                 responseData.data.bills = await ctx.service.ledger.getData(ctx.tender.id);
                 responseData.data.pos = await ctx.service.pos.getPosData({tid: this.ctx.tender.id});
                 ctx.body = responseData;

+ 30 - 5
app/controller/material_controller.js

@@ -218,10 +218,23 @@ module.exports = app => {
             try {
                 await this._getMaterialAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
+                // 根据期判断需要获取的工料信息值
+                const searchsql = { tid: ctx.tender.id };
+                if (ctx.material.highOrder !== ctx.material.order) {
+                    const midList = await ctx.service.material.getPreMidList(ctx.tender.id, ctx.material.order);
+                    searchsql.mid = midList;
+                }
                 // 取所有工料表
-                renderData.materialBillsData = await ctx.service.materialBills.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                renderData.materialBillsData = await ctx.service.materialBills.getAllDataByCondition({ where: searchsql });
+                // 取对应期的截取上期的调差金额和应耗数量
+                for (const mindex in renderData.materialBillsData) {
+                    const pre_tp = renderData.materialBillsData[mindex].pre_tp !== null ? renderData.materialBillsData[mindex].pre_tp.split(',')[ctx.material.order - 1] : null;
+                    const quantity = renderData.materialBillsData[mindex].quantity !== null ? renderData.materialBillsData[mindex].quantity.split(',')[ctx.material.order - 1] : null;
+                    renderData.materialBillsData[mindex].pre_tp = pre_tp;
+                    renderData.materialBillsData[mindex].quantity = quantity;
+                }
                 // 取所有已被调用的工料清单表
-                renderData.materialListData = await ctx.service.materialList.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                renderData.materialListData = await ctx.service.materialList.getAllDataByCondition({ where: searchsql });
                 renderData.materialType = JSON.stringify(materialConst);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.info);
                 await this.layout('material/info.ejs', renderData, 'material/info_modal.ejs');
@@ -240,10 +253,16 @@ module.exports = app => {
             try {
                 await this._getMaterialAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
-                // 取所有工料表
-                renderData.materialBillsData = await ctx.service.materialBills.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                // 根据期判断需要获取的工料信息值表
+                const searchsql = { tid: ctx.tender.id };
+                let midList = [];
+                if (ctx.material.highOrder !== ctx.material.order) {
+                    midList = await ctx.service.material.getPreMidList(ctx.tender.id, ctx.material.order);
+                    searchsql.mid = midList;
+                }
+                renderData.materialBillsData = await ctx.service.materialBills.getAllDataByCondition({ where: searchsql });
                 // 取所有已被调用的工料清单表
-                renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id);
+                renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, midList);
                 renderData.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
                 renderData.materialType = JSON.stringify(materialConst);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.list);
@@ -293,6 +312,12 @@ module.exports = app => {
                     case 'useOther':
                         responseData.data = await ctx.service.materialList.addOther(data.postData);
                         break;
+                    case 'join':
+                        await ctx.service.materialListNotjoin.del(data.select.id);
+                        break;
+                    case 'notjoin':
+                        responseData.data = await ctx.service.materialListNotjoin.add(data.select);
+                        break;
                     default: throw '参数有误';
                 }
 

+ 1 - 1
app/controller/profile_controller.js

@@ -287,7 +287,7 @@ module.exports = app => {
                 // 获取当前用户数据
                 const sessionUser = ctx.session.sessionUser;
 
-                const text = ctx.request.header.host + '/sign?user_id=' + sessionUser.accountId + '&app_token=' + sessionUser.sessionToken;
+                const text = 'http://' + ctx.request.header.host + '/sign?user_id=' + sessionUser.accountId + '&app_token=' + sessionUser.sessionToken;
 
                 // 大小默认5,二维码周围间距默认1
                 const img = qr.image(text || '', { type: 'png', size: size || 5, margin: margin || 1 });

+ 11 - 2
app/controller/revise_controller.js

@@ -42,9 +42,11 @@ module.exports = app => {
          */
         async _getAddReviseValid(ctx) {
             const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
+            const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true);
             return (ctx.tender.data.user_id === ctx.session.sessionUser.accountId) &&
                 (ctx.tender.data.ledger_status === audit.revise.status.checked) &&
-                (!revise || !revise.valid || revise.status === audit.revise.status.checked);
+                (!revise || !revise.valid || revise.status === audit.revise.status.checked) &&
+                (!lastStage || lastStage.status !== audit.stage.status.checking);
         }
 
         /**
@@ -468,7 +470,11 @@ module.exports = app => {
                 case 'add':
                     return await this.ctx.service.revisePos.addPos(revise.tid, revise.id, data.postData);
                 case 'update':
-                    return await this.ctx.service.revisePos.updatePos(revise.tid, data.postData);
+                    if (data.postData instanceof Array) {
+                        return await this.ctx.service.revisePos.updatePosArr(revise.tid, data.postData);
+                    } else {
+                        return await this.ctx.service.revisePos.updatePos(revise.tid, data.postData);
+                    }
                 case 'delete':
                     return await this.ctx.service.revisePos.deletePos(revise.tid, data.postData);
                 case 'paste':
@@ -603,6 +609,9 @@ module.exports = app => {
                 if (!revise || revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked) {
                     throw '台账修订数据有误';
                 }
+                if (revise.content === '') {
+                    throw '未填写修订内容,请先填写并保存修订内容';
+                }
                 if (revise.uid !== ctx.session.sessionUser.accountId) throw '上报失败';
 
                 await ctx.service.reviseAudit.start(revise, revise.times);

+ 29 - 3
app/controller/stage_controller.js

@@ -143,7 +143,6 @@ module.exports = app => {
          */
         async index(ctx) {
             try {
-                console.log(ctx.stage);
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 [renderData.ledgerSpread, renderData.posSpread] = this._getSpreadSetting();
@@ -199,7 +198,7 @@ module.exports = app => {
                 const responseData = {err: 0, msg: '', data: {}};
 
                 let curStageData, preStageData;
-                responseData.data = await ctx.service.pos.getPosData(condition);
+                responseData.data = await ctx.service.pos.getPosDataWithAddStageOrder(condition);
                 // 根据当前人,或指定对象查询数据
                 //console.time('cur');
                 const curWhere = JSON.parse(ctx.request.body.data);
@@ -248,6 +247,11 @@ module.exports = app => {
                 if (data.pos) {
                     responseData.data = await ctx.service.stagePos.updateStageData(data.pos);
                 } else if (data.bills) {
+                    if (data.bills.main) {
+                        const updateDatas = data.bills.main instanceof Array ? data.bills.main : [data.bills.main];
+                        const result = await ctx.app.mysql.updateRows(ctx.service.ledger.tableName, updateDatas);
+                        responseData.data.bills = await ctx.service.ledger.getDataByIds(this.ctx.helper._.map(updateDatas, 'id'));
+                    }
                     if (data.bills.dgn) {
                         responseData.data.dgn = await ctx.service.stageBillsDgn.saveDgnData(data.bills.dgn);
                     }
@@ -256,6 +260,7 @@ module.exports = app => {
                     }
                 }
                 await ctx.service.stage.updateCheckDetailFlag(ctx.stage.id, true);
+                await ctx.service.stage.updateCheckCalcFlag(ctx.stage.id, true);
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
@@ -303,6 +308,7 @@ module.exports = app => {
                     result = await ctx.service.stageChange.billsChange(data.target.bills, data.change);
                 }
                 await ctx.service.stage.updateCheckDetailFlag(ctx.stage.id, true);
+                await ctx.service.stage.updateCheckCalcFlag(ctx.stage.id, true);
                 ctx.body = {err: 0, msg: '', data: result};
             } catch(err) {
                 this.log(err);
@@ -335,7 +341,6 @@ module.exports = app => {
          */
         async detail(ctx) {
             try {
-                console.log(ctx.stage);
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.detail);
@@ -575,6 +580,11 @@ module.exports = app => {
                     // 计算 本期金额
                     const payCalculator = new PayCalculator(ctx, ctx.stage, ctx.tender.info);
                     await payCalculator.calculateAll(renderData.dealPay);
+                    await this.ctx.service.stage.update({
+                        check_calc: false,
+                        contract_tp: payCalculator.cur.contract_tp, qc_tp: payCalculator.cur.qc_tp,
+                        yf_tp: payCalculator.yf.tp,
+                    }, {id: this.ctx.stage.id});
                 }
                 await this.layout('stage/pay.ejs', renderData, 'stage/pay_modal.ejs');
             } catch (err) {
@@ -610,6 +620,12 @@ module.exports = app => {
                         await ctx.service.pay.del(data.id);
                         responseData.data = await ctx.service.stagePay.getStagePays(ctx.stage);
                         await payCalculator.calculate(responseData.data);
+                        await this.ctx.service.stage.update({
+                            check_calc: false,
+                            contract_tp: payCalculator.cur.contract_tp, qc_tp: payCalculator.cur.qc_tp,
+                            yf_tp: payCalculator.yf.tp,
+                        }, {id: this.ctx.stage.id});
+
                         break;
                     case 'changeOrder':
                         responseData.data = await ctx.service.pay.changeOrder(data.id1, data.id2);
@@ -620,6 +636,11 @@ module.exports = app => {
                         if (bReCalc) {
                             responseData.data = await ctx.service.stagePay.getStagePays(ctx.stage);
                             await payCalculator.calculateAll(responseData.data);
+                            await this.ctx.service.stage.update({
+                                check_calc: false,
+                                contract_tp: payCalculator.cur.contract_tp, qc_tp: payCalculator.cur.qc_tp,
+                                yf_tp: payCalculator.yf.tp,
+                            }, {id: this.ctx.stage.id});
                         } else {
                             if (data.updateData instanceof Array) {
                                 responseData.data = await ctx.service.stagePay.getStagePay(ctx.stage, this.app._.map(responseData.data, 'id'));
@@ -632,6 +653,11 @@ module.exports = app => {
                         await ctx.service.stagePay.save(data.updateData);
                         responseData.data = await ctx.service.stagePay.getStagePays(ctx.stage);
                         await payCalculator.calculateAll(responseData.data);
+                        await this.ctx.service.stage.update({
+                            check_calc: false,
+                            contract_tp: payCalculator.cur.contract_tp, qc_tp: payCalculator.cur.qc_tp,
+                            yf_tp: payCalculator.yf.tp,
+                        }, {id: this.ctx.stage.id});
                         break;
                 }
 

+ 1 - 2
app/controller/tender_controller.js

@@ -38,6 +38,7 @@ module.exports = app => {
                 const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
 
                 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)) {
@@ -67,7 +68,6 @@ module.exports = app => {
                 };
                 await this.layout(view, renderData, modal);
             } catch (err) {
-                console.log(err);
                 this.log(err);
                 this.ctx.redirect('/dashboard');
             }
@@ -152,7 +152,6 @@ module.exports = app => {
                 if (userPermission === null || userPermission.tender === undefined || userPermission.tender.indexOf('1') === -1) {
                     throw '当前用户没有创建标段的权限';
                 }
-                console.log(ctx.request.body.data);
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.name || data.name === '' || !data.valuation) {
                     throw '标段信息不完整';

+ 40 - 29
app/lib/pay_calc.js

@@ -20,6 +20,12 @@ class PayCalculate {
         this.percentReg = /[0-9]+%/g;
         this.tenderInfo = tenderInfo;
         this.decimal = tenderInfo.decimal.pay ? tenderInfo.decimal.payTp : tenderInfo.decimal.tp;
+        /* 以下变量在调用calculate方法后获得
+        this.add;
+        this.pre;
+        this.cur;
+        this.yf;
+         */
     }
 
     /**
@@ -42,11 +48,11 @@ class PayCalculate {
         }
     }
 
-    calculateTpExpr(pay, addRela) {
+    _calculateTpExpr(pay) {
         let formula = pay.expr;
         for (const b of this.bases) {
             if ((b.code === 'bqwc') && (!pay.pre_used && pay.sprice)) {
-                formula = formula.replace(b.reg, this.ctx.helper.sub(addRela.gather_tp, pay.sprice));
+                formula = formula.replace(b.reg, this.ctx.helper.sub(this.add.gather_tp, pay.sprice));
             } else {
                 formula = formula.replace(b.reg, b.value);
             }
@@ -66,7 +72,7 @@ class PayCalculate {
         }
     }
 
-    calculateExpr(expr) {
+    _calculateExpr(expr) {
         let formula = expr;
         for (const b of this.bases) {
             formula = formula.replace(b.reg, b.value);
@@ -99,12 +105,12 @@ class PayCalculate {
             if (p.csorder === this.stage.order || (p.csorder === 0 || this.stage.order === 1)) {
                 if (p.csaorder === order) {
                     if (!p.sprice && p.sexpr && p.sexpr !== '') {
-                        p.sprice = this.ctx.helper.round(this.calculateExpr(p.sexpr), this.decimal);
+                        p.sprice = this.ctx.helper.round(this._calculateExpr(p.sexpr), this.decimal);
                     } else if (p.sprice && !p.sexpr) {
                         p.sprice = this.ctx.helper.round(p.sprice, this.decimal);
                     }
                     if (!p.rprice && p.rexpr && p.rexpr !== '') {
-                        p.rprice = this.ctx.helper.round(this.calculateExpr(p.rexpr), this.decimal);
+                        p.rprice = this.ctx.helper.round(this._calculateExpr(p.rexpr), this.decimal);
                     } else if (p.rprice && !p.rexpr) {
                         p.rprice = this.ctx.helper.round(p.rprice, this.decimal);
                     }
@@ -116,29 +122,28 @@ class PayCalculate {
     /**
      * 累计 计量等数据
      */
-    async getAddCalcRela() {
+    async _getAddCalcRela() {
         // todo 获取截止上期数据
-        const pre = this.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getSumTotalPrice(this.stage.tid, this.stage.order - 1) : null;
-        const cur = await this.ctx.service.stageBills.getSumTotalPrice(this.stage);
-        const add = {};
-        if (pre) {
-            add.contract_tp = this.ctx.helper.add(pre.contract_tp, cur.contract_tp);
-            add.qc_tp = this.ctx.helper.add(pre.qc_tp, cur.qc_tp);
+        this.pre = this.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getSumTotalPrice(this.stage.tid, this.stage.order - 1) : null;
+        this.cur = await this.ctx.service.stageBills.getSumTotalPrice(this.stage);
+        this.add = {};
+        if (this.pre) {
+            this.add.contract_tp = this.ctx.helper.add(this.pre.contract_tp, this.cur.contract_tp);
+            this.add.qc_tp = this.ctx.helper.add(this.pre.qc_tp, this.cur.qc_tp);
         } else {
-            add.contract_tp = cur.contract_tp;
-            add.qc_tp = cur.qc_tp;
+            this.add.contract_tp = this.cur.contract_tp;
+            this.add.qc_tp = this.cur.qc_tp;
         }
-        add.gather_tp = this.ctx.helper.add(add.contract_tp, add.qc_tp);
-        return add;
+        this.add.gather_tp = this.ctx.helper.add(this.add.contract_tp, this.add.qc_tp);
     }
 
     /**
      * 检查是否到达 计提期限
      * @param pay
      */
-    checkDeadline(pay, addRela) {
+    _checkDeadline(pay) {
         if (pay.dl_type === deadlineType.tp.value) {
-            const deadlineTp = addRela[pay.dl_tp_type + '_tp'];
+            const deadlineTp = this.add[pay.dl_tp_type + '_tp'];
             if (deadlineTp > pay.dl_tp) {
                 return true;
             }
@@ -155,19 +160,19 @@ class PayCalculate {
      */
     async calculate(pays) {
         await this.getCalcBase();
-        const yfPay = pays.find(function (p) {
+        this.yf = pays.find(function (p) {
             return p.ptype === payType.yf;
         });
-        const addRela = await this.getAddCalcRela();
-        if (!yfPay) return false;
-        yfPay.tp = 0;
+        if (!this.yf) return false;
+        await this._getAddCalcRela();
+        this.yf.tp = 0;
         for (const p of pays) {
             if (p.ptype === payType.normal || p.ptype === payType.wc) {
-                if (!p.pause && (!p.sprice || addRela.gather_tp >= p.sprice)) {
+                if (!p.pause && (!p.sprice || this.add.gather_tp >= p.sprice)) {
                     if (p.expr && p.expr !== '') {
-                        const value = this.ctx.helper.round(this.calculateTpExpr(p, addRela), this.decimal);
+                        const value = this.ctx.helper.round(this._calculateTpExpr(p), this.decimal);
                         if (p.rprice) {
-                            if (this.checkDeadline(p, addRela)) {
+                            if (this._checkDeadline(p)) {
                                 p.tp = this.ctx.helper.sub(p.rprice, p.pre_tp);
                             } else {
                                 p.tp = Math.min(this.ctx.helper.sub(p.rprice, p.pre_tp), value);
@@ -177,7 +182,7 @@ class PayCalculate {
                         }
                     } else if (p.tp && !this.ctx.helper.checkZero(p.tp)) {
                         if (p.rprice) {
-                            if (this.checkDeadline(p, addRela)) {
+                            if (this._checkDeadline(p)) {
                                 p.tp = this.ctx.helper.sub(p.rprice, p.pre_tp);
                             } else {
                                 p.tp = Math.min(this.ctx.helper.sub(p.rprice, p.pre_tp), this.ctx.helper.round(p.tp, this.decimal));
@@ -185,6 +190,12 @@ class PayCalculate {
                         } else {
                             p.tp = this.ctx.helper.round(p.tp, this.decimal);
                         }
+                    } else {
+                        if (p.rprice) {
+                            if (this._checkDeadline(p)) {
+                                p.tp = this.ctx.helper.sub(p.rprice, p.pre_tp);
+                            }
+                        }
                     }
                 } else {
                     p.tp = 0;
@@ -192,15 +203,15 @@ class PayCalculate {
                 // 累加 至 应付
                 if (p.is_yf) {
                     if (p.minus) {
-                        yfPay.tp = this.ctx.helper.sub(yfPay.tp, p.tp);
+                        this.yf.tp = this.ctx.helper.sub(this.yf.tp, p.tp);
                     } else {
-                        yfPay.tp = this.ctx.helper.add(yfPay.tp, p.tp);
+                        this.yf.tp = this.ctx.helper.add(this.yf.tp, p.tp);
                     }
                 }
             }
             p.end_tp = this.ctx.helper.round(this.ctx.helper.add(p.tp, p.pre_tp), this.decimal);
         }
-        yfPay.end_tp = this.ctx.helper.add(yfPay.tp, yfPay.pre_tp);
+        this.yf.end_tp = this.ctx.helper.add(this.yf.tp, this.yf.pre_tp);
     }
 
     async calculateAll(pays) {

+ 21 - 0
app/public/js/global.js

@@ -405,3 +405,24 @@ function copyToClipboard( text ) {
     // 删除创建元素
     document.body.removeChild(aux);
 }
+
+function toastMessageUniq (obj) {
+    if (!obj.msg || !obj.type) return;
+    if (!obj.once) {
+        switch (obj.type) {
+            case 'error':
+                toastr.error(obj.msg);
+                break;
+            case 'warning':
+                toastr.warning(obj.msg);
+                break;
+            case 'info':
+                toastr.info(obj.msg);
+                break;
+            case 'success':
+                toastr.success(obj.msg);
+                break;
+        }
+        obj.once = true;
+    }
+}

+ 178 - 16
app/public/js/ledger.js

@@ -22,6 +22,9 @@ const invalidFields = {
     posCode: ['b_code'],
     posCalc: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp'],
 };
+function transExpr(expr) {
+    return expr.replace('=', '').replace('%', '/100');
+}
 
 $(document).ready(function() {
     autoFlashHeight();
@@ -72,6 +75,21 @@ $(document).ready(function() {
 
     // 定义事件
     const treeOperationObj = {
+        loadExprToInput(sheet) {
+            const sel = sheet.getSelections()[0];
+            const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
+            if (col.type === 'Number') {
+                const data = SpreadJsObj.getSelectObject(sheet);
+                if (data) {
+                    $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                        .attr('readOnly', readOnly || cell.locked());
+                } else {
+                    $('#bills-expr').val('').attr('readOnly', true);
+                }
+            } else {
+                $('#bills-expr').val('').attr('readOnly', true);
+            }
+        },
         getSelectNode: function (sheet) {
             if (!sheet || !sheet.zh_tree) {
                 return null;
@@ -385,7 +403,18 @@ $(document).ready(function() {
                 }
                 // 获取更新数据
                 if (info.editingText) {
-                    data[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText.replace('\n', '');
+                    const num = _.toNumber(info.editingText);
+                    if (num) {
+                        data[col.field] = num;
+                    } else {
+                        try {
+                            data[col.field] = math.evaluate(transExpr(info.editingText));
+                        } catch(err) {
+                            toastr.error('输入的表达式非法');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                    }
                 } else {
                     data[col.field] = null;
                 }
@@ -444,6 +473,16 @@ $(document).ready(function() {
                                 if (num) {
                                     data[colSetting.field] = num;
                                     bPaste = true;
+                                } else {
+                                    try {
+                                        data[colSetting.field] = math.evaluate(transExpr(value));
+                                        bPaste = true;
+                                    } catch(err) {
+                                        if (!bHint) {
+                                            toastr.warning('输入的表达式非法');
+                                            bHint = true;
+                                        }
+                                    }
                                 }
                             } else {
                                 data[colSetting.field] = value;
@@ -520,7 +559,11 @@ $(document).ready(function() {
                             data[colSetting.field] = num;
                             bPaste = true;
                         } else {
-                            bNumHint = true;
+                            try {
+                                data[colSetting.field] = math.evaluate(transExpr(value));
+                            } catch(err) {
+                                bNumHint = true;
+                            }
                         }
                     } else {
                         data[colSetting.field] = value;
@@ -538,7 +581,7 @@ $(document).ready(function() {
             if (bGclHint) hint.push('工程量清单,不可粘贴设计数量');
             if (bQtyHint) hint.push('含有部位明细的清单,不可粘贴数量');
             if (bCodeHint) hint.push('含有部位明细的清单,清单编号不可粘贴空值');
-            if (bNumHint) hint.push('单价、数量等应输入数字');
+            if (bNumHint) hint.push('单价、数量等应输入数字或可计算的表达式');
             // if (hint.length > 0) hint.unshift('复制粘贴的数据中,存在非法数据,已过滤:');
             // if (hint.length > 0) toastr.warning(hint.join('</br>'));
             if (hint.length > 0) toastr.warning('复制粘贴的数据中,存在非法数据,已过滤');
@@ -651,6 +694,7 @@ $(document).ready(function() {
                 posSearch.search($('#pos-keyword').val());
             }
             SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
+            treeOperationObj.loadExprToInput(info.sheet);
         },
         topRowChanged(e, info) {
             SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
@@ -782,6 +826,38 @@ $(document).ready(function() {
             });
         });
 
+        $('#bills-expr').bind('change mouseleave', function () {
+            const expr = $(this);
+            const sheet = ledgerSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(sheet);
+            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = expr.val();
+            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
+
+            const data = {
+                id: select.id,
+                tender_id: select.tender_id,
+                ledger_id: select.ledger_id
+            };
+            const num = _.toNumber(newValue);
+            if (num) {
+                data[field] = num;
+            } else {
+                try {
+                    data[field] = math.evaluate(transExpr(newValue));
+                } catch (err) {
+                    toastr.error('输入的表达式非法');
+                    return;
+                }
+            }
+
+            // 更新至服务器
+            postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
+                const refreshNode = ledgerTree.loadPostData(result);
+                expr.val(select[field]);
+                treeOperationObj.refreshTree(sheet, refreshNode);
+            });
+        });
+
         let batchInsertObj;
         // 右键菜单
         $.contextMenu({
@@ -937,7 +1013,7 @@ $(document).ready(function() {
                     name: '导入分项清单Excel',
                     icon: 'file-excel-o',
                     disabled: function (key, opt) {
-                        return tender.ledger_status !== 1;
+                        return readOnly;
                     },
                     callback: function (key, opt) {
                         $('#upload-ledger').modal('show');
@@ -1037,7 +1113,23 @@ $(document).ready(function() {
                 } else {
                     data.updateType = 'update';
                     data.updateData = {id: posData.id};
-                    data.updateData[col.field] = col.type === 'Number' ? parseFloat(newText) : newText;
+
+                    if (col.type === 'Number') {
+                        const num = _.toNumber(newText);
+                        if (num) {
+                            data.updateData[col.field] = num;
+                        } else {
+                            try {
+                                data.updateData[col.field] = math.evaluate(transExpr(newText));
+                            } catch(err) {
+                                toastr.error('输入的表达式非法');
+                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                                return;
+                            }
+                        }
+                    } else {
+                        data.updateData[col.field] = newText;
+                    }
                 }
                 postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                     const updateRst = pos.updateDatas(result.pos);
@@ -1164,32 +1256,100 @@ $(document).ready(function() {
                 }
                 const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
                 for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                    let bPaste = false;
                     const curRow = info.cellRange.row + iRow;
                     const posData = curRow >= sortData.length ? {lid: node.id, porder: lastOrder + curRow - sortData.length} : {id: sortData[curRow].id, lid: node.id};
                     for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                         const curCol = info.cellRange.col + iCol;
                         const colSetting = info.sheet.zh_setting.cols[curCol];
                         posData[colSetting.field] = info.sheet.getText(curRow, curCol);
+
                         if (colSetting.type === 'Number') {
-                            posData[colSetting.field] = _.toNumber(posData[colSetting.field]);
+                            const num = _.toNumber(posData[colSetting.field]);
+                            if (num) {
+                                posData[colSetting.field] = num;
+                                bPaste = true;
+                            } else {
+                                try {
+                                    posData[colSetting.field] = math.evaluate(transExpr(posData[colSetting.field]));
+                                    bPaste = true;
+                                } catch(err) {
+                                    toastr.warning('粘贴了表达式非法,已过滤');
+                                }
+                            }
+                        } else {
+                            bPaste = true;
                         }
                     }
-                    data.push(posData);
+                    if (bPaste) {
+                        data.push(posData);
+                    }
                 }
-                postData('/tender/' + getTenderId() + '/pos/paste', data, function (result) {
-                    pos.updateDatas(result.pos);
-                    posOperationObj.loadCurPosData();
-                    const loadResult = ledgerTree.loadPostData(result.ledger);
-                    treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
-                    posOperationObj.loadCurPosData();
-                    treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
-                }, function () {
+                if (data.length > 0) {
+                    postData('/tender/' + getTenderId() + '/pos/paste', data, function (result) {
+                        pos.updateDatas(result.pos);
+                        posOperationObj.loadCurPosData();
+                        const loadResult = ledgerTree.loadPostData(result.ledger);
+                        treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
+                        posOperationObj.loadCurPosData();
+                        treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+                    }, function () {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                    });
+                } else {
                     SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                });
+                }
+            }
+        },
+        selectionChanged: function (e, info) {
+            const col = info.sheet.zh_setting.cols[info.newSelections[0].col];
+            const cell = info.sheet.getCell(info.newSelections[0].row, info.newSelections[0].col);
+            if (col.type === 'Number') {
+                const data = SpreadJsObj.getSelectObject(info.sheet);
+                if (data) {
+                    $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                        .attr('row', info.newSelections[0].row).attr('readOnly', readOnly || cell.locked());
+                } else {
+                    $('#pos-expr').val('').attr('readOnly', true);
+                }
+            } else {
+                $('#pos-expr').val('').attr('readOnly', true);
             }
         },
     };
+    posSpread.bind(spreadNS.Events.SelectionChanged, posOperationObj.selectionChanged)
     if (!posSpreadSetting.readOnly) {
+        $('#pos-expr').bind('change mouseleave', function () {
+            const expr = $(this);
+            const posSheet = posSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(posSheet);
+            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = expr.val(), row = expr.attr('row');
+            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
+
+            const data = {id: select.id};
+            const num = _.toNumber(newValue);
+            if (num) {
+                data[field] = num;
+            } else {
+                try {
+                    data[field] = math.evaluate(transExpr(newValue));
+                } catch (err) {
+                    toastr.error('输入的表达式非法');
+                    return;
+                }
+            }
+
+            // 更新至服务器
+            postData('/tender/' + getTenderId() + '/pos/update', {updateType: 'update', updateData: data}, function (result) {
+                const updateRst = pos.updateDatas(result.pos);
+                expr.val(select[field]);
+                SpreadJsObj.reLoadRowData(posSheet, _.toNumber(row));
+                const loadResult = ledgerTree.loadPostData(result.ledger);
+                treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
+                treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+            });
+        });
+
         SpreadJsObj.addDeleteBind(posSpread, posOperationObj.deletePress);
         posSpread.bind(spreadNS.Events.EditStarting, posOperationObj.editStarting);
         posSpread.bind(spreadNS.Events.EditEnded, posOperationObj.editEnded);
@@ -1235,6 +1395,7 @@ $(document).ready(function() {
         SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
 
         treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+        treeOperationObj.loadExprToInput(ledgerSpread.getActiveSheet());
     }, null, true);
 
     let stdXmj, stdGcl, dealBills, searchLedger;
@@ -1946,5 +2107,6 @@ function checkAuditorFrom () {
         toastr.error('请先选择审批人,再上报数据');
         return false;
     }
+    $('#hide-all').show();
 }
 

+ 84 - 8
app/public/js/material.js

@@ -28,17 +28,18 @@ $(document).ready(() => {
             {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '规格', colSpan: '1', rowSpan: '2', field: 'spec', hAlign: 0, width: 230, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '规格', colSpan: '1', rowSpan: '2', field: 'spec', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '工料分类', colSpan: '1', rowSpan: '2', field: 'm_type', hAlign: 0, width: 60, readOnly: 'readOnly.isEdit', cellType: 'customizeCombo', comboItems: materialType.m_type, cellTypeKey: 2},
-            {title: '本期应耗数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 100, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '本期应耗数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '基准价', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '本期信息价|单价', colSpan: '3|1', rowSpan: '1|1', field: 'msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '|时间', colSpan: '|1', rowSpan: '|1', field: 'msg_times', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '|价差', colSpan: '1', rowSpan: '1|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '本期材料调差|风险幅度(%)', colSpan: '3|1', rowSpan: '1|1', field: 'm_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},
-            {title: '|调整金额', colSpan: '|1', rowSpan: '1|1', field: 'm_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true},
+            {title: '|价差', colSpan: '1', rowSpan: '1|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.msg_spread'},
+            {title: '本期材料调差|上涨幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 100, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {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: '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'},
         ],
@@ -56,13 +57,29 @@ $(document).ready(() => {
             return materialListData.find(function (item) {
                 return item.mb_id === data.id;
             });
+        },
+        isStage: function (data) {
+            return data.mid === materialID;
         }
     }
 
     const materialCol = {
+        getValue: {
+            msg_spread: function (data) {
+                return ZhCalc.round(ZhCalc.sub(data.msg_tp, data.basic_price), 2);
+            },
+            m_spread : function (data) {
+                const msg_spread = materialCol.getValue.msg_spread(data);
+                const cor = msg_spread >= 0 ? ZhCalc.mul(data.basic_price, ZhCalc.div(data.m_up_risk, 100)) : ZhCalc.mul(data.basic_price, ZhCalc.div(data.m_down_risk, 100));
+                return Math.abs(msg_spread) > Math.abs(cor) ? (msg_spread > 0 ? ZhCalc.round(ZhCalc.sub(msg_spread, cor), 2) : ZhCalc.round(ZhCalc.add(msg_spread, cor), 2)) : 0;
+            },
+            m_tp: function (data) {
+                return ZhCalc.round(ZhCalc.mul(materialCol.getValue.m_spread(data), data.quantity), 2);
+            }
+        },
         readOnly: {
             isEdit: function (data) {
-                return !(readOnly || materialBase.isEdit(data) === undefined);
+                return !(!readOnly && materialBase.isEdit(data) === undefined && materialBase.isStage(data));
             },
             remark: function () {
                 return readOnly;
@@ -131,7 +148,66 @@ $(document).ready(() => {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
                 }
+                // 判断部分值是否输入的是数字判断和数据计算
+                if (col.field === 'basic_price') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num)) {
+                        toastr.error('请输入大于0并且小于3位小数的浮点数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                if (col.field === 'msg_tp') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num)) {
+                        toastr.error('请输入大于0并且小于3位小数的浮点数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                if (col.field === 'm_up_risk') {
+                    // 只能输入正整数
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (num < 0 || num > 100 || !/^\d+$/.test(num)) {
+                        toastr.error('只能输入0-100的正整数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                if (col.field === 'm_down_risk') {
+                    // 只能输入正整数
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (num < 0 || num > 100 || !/^\d+$/.test(num)) {
+                        toastr.error('只能输入0-100的正整数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
                 select[col.field] = validText;
+                select.msg_spread = materialCol.getValue.msg_spread(select);
+                select.m_spread = materialCol.getValue.m_spread(select);
+                select.m_tp = materialCol.getValue.m_tp(select);
+                console.log(select);
                 // 更新至服务器
                 postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -186,7 +262,7 @@ $(document).ready(() => {
             }
         });
     } else {
-        SpreadJsObj.forbiddenSpreadContextMenu('#material-spread', materialSpread);
+        // SpreadJsObj.forbiddenSpreadContextMenu('#material-spread', materialSpread);
     }
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',

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

@@ -144,4 +144,5 @@ function checkAuditorFrom () {
         toast('请先选择审批人,再上报数据', 'error', 'exclamation-circle');
         return false;
     }
+    $('#hide-all').show();
 }

+ 155 - 49
app/public/js/material_list.js

@@ -8,6 +8,25 @@
  * @version
  */
 
+function findNotJoinLeafXmj(x, type = '') {
+    if (type === 'index') {
+        return notJoinList.findIndex(function (item) {
+            return item.gcl_id === x.gcl_id && item.xmj_id === x.id && (x.mx_id === undefined || (x.mx_id !== undefined && x.mx_id === item.mx_id));
+        });
+    }
+    return notJoinList.find(function (item) {
+        return item.gcl_id === x.gcl_id && item.xmj_id === x.id && (x.mx_id === undefined || (x.mx_id !== undefined && x.mx_id === item.mx_id));
+    });
+}
+
+function getMpSpreadByMBData(id) {
+    const info = materialBillsData.find(function (item) {
+        return item.id === parseInt(id);
+    });
+    console.log(info);
+    return info.m_spread;
+}
+
 $(document).ready(() => {
     autoFlashHeight();
     // 清单table
@@ -33,7 +52,7 @@ $(document).ready(() => {
     // 解析清单汇总数据
     gclGatherModel.loadLedgerData(ledger, curLedgerData);
     gclGatherModel.loadPosData(pos, curPosData);
-    const gclGatherData = gclGatherModel.gatherGclData();
+    let gclGatherData = gclGatherModel.gatherGclData();
     console.log(gclGatherData);
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
@@ -41,13 +60,8 @@ $(document).ready(() => {
         if (gcl) {
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gcl.leafXmjs);
             // 对清单调差工料table的单位数量进行改变
-            // console.log(materialSpreadSetting.cols[materialSpreadSetting.cols.length - 1]);
-            // materialSpreadSetting.cols[materialSpreadSetting.cols.length - 1].title = '|' + gcl.unit + '数量<a href="" data-toggle="tooltip" data-placement="bottom" title="单位数量:每一单位清单下所需工料消耗量。"><i class="fa fa-question-circle-o"></i></a>';
             materialSpreadSetting.cols[materialSpreadSetting.cols.length - 1].title = '|' + gcl.unit + '数量';
-            // const sheet = materialSpread.getActiveSheet();
-            // sheet.setCellType(2,3, '1', spreadNS.SheetArea.colHeader);
             SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
-            // materialSpread.resumePaint();
         } else {
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
         }
@@ -68,7 +82,7 @@ $(document).ready(() => {
             {title: '本期计量数量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 80, type: 'Number'},
             {title: '|小计', colSpan: '|1', rowSpan: '|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
-            {title: '本期价差', colSpan: '1', rowSpan: '2', field: 'jijia', hAlign: 2, width: 80, type: 'Number'},
+            {title: '本期价差', colSpan: '1', rowSpan: '2', field: 'jiacha', hAlign: 2, width: 80, type: 'Number', getValue: 'getValue.jiacha'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -78,6 +92,23 @@ $(document).ready(() => {
         readOnly: true,
         font: '10pt 微软雅黑',
     };
+    const leafXmjCol = {
+        getValue: {
+            jiacha: function (data) {
+                let sum = 0;
+                const sheet = leafXmjSpread.getActiveSheet();
+                const select = SpreadJsObj.getSelectObject(sheet);
+                const notx = findNotJoinLeafXmj(select);
+                if (notx === undefined) {
+                    for(const ml of materialList) {
+                        sum = ZhCalc.add(sum, ZhCalc.mul(ZhCalc.mul(data.gather_qty, ml.quantity), getMpSpreadByMBData(ml.mb_id)));
+                    }
+                }
+                return sum;
+            }
+        }
+    };
+    SpreadJsObj.initSpreadSettingEvents(leafXmjSpreadSetting, leafXmjCol);
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
     // 加载清单数据
     SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
@@ -89,7 +120,7 @@ $(document).ready(() => {
             {title: '清单调差工料|编号', colSpan: '4|1', rowSpan: '1|1', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
             {title: '|名称', colSpan: '|1', rowSpan: '|1', field: 'name', hAlign: 0, width: 100, formatter: '@', readOnly: true},
             {title: '|单位', colSpan: '|1', rowSpan: '|1', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
-            {title: '|数量', colSpan: '1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: readOnly.quantity},
+            {title: '|数量', colSpan: '1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -99,22 +130,25 @@ $(document).ready(() => {
         font: '10pt 微软雅黑',
     };
 
+    const materialBase = {
+        isEdit: function (data) {
+            // 是否本期添加的工料
+            return data.mid === materialID;
+        }
+    };
+
     const materialCol = {
         readOnly: {
-            quantity: function () {
+            isEdit: function (data) {
                 const sheet = leafXmjSpread.getActiveSheet();
                 const select = SpreadJsObj.getSelectObject(sheet);
-                const notx = notJoinList.find(function (item) {
-                    return item.gcl_id === select.gcl_id && item.xmj_id === select.id && (select.mx_id === undefined || (select.mx_id !== undefined && select.mx_id === item.mx_id));
-                });
-                console.log(notx);
-                return readOnly || notx === undefined;
+                const notx = findNotJoinLeafXmj(select);
+                return !(!readOnly && notx === undefined && materialBase.isEdit(data));
             },
         },
     };
     SpreadJsObj.initSpreadSettingEvents(materialSpreadSetting, materialCol);
-    // SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
-    // 获取第一个调差工料数据
+
     // 获取项目节数据
     let materialList = [];
     function loadMaterialData(iGclRow, iLXmjRow) {
@@ -127,11 +161,11 @@ $(document).ready(() => {
                     materialList.push(m);
                 }
             }
-            console.log(materialList);
             SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialList);
         } else {
             SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
         }
+        SpreadJsObj.reLoadSheetData(leafXmjSpread.getActiveSheet());
     }
 
     loadLeafXmjData(0);
@@ -145,9 +179,7 @@ $(document).ready(() => {
             const xmj = gclGatherData[index].leafXmjs;
             const leafXmjSheet = leafXmjSpread.getActiveSheet();
             for (const [iRow,x] of xmj.entries()) {
-                const notx = notJoinList.find(function (item) {
-                    return item.gcl_id === x.gcl_id && item.xmj_id === x.id && (x.mx_id === undefined || (x.mx_id !== undefined && x.mx_id === item.mx_id));
-                });
+                const notx = findNotJoinLeafXmj(x);
                 const color = notx === undefined ? '' : '#d6d8db';
                 leafXmjSheet.getRange(iRow, -1, 1, -1).backColor(color);
             }
@@ -209,6 +241,36 @@ $(document).ready(() => {
         $('#materialBills').find('input:disabled').prop('checked', true);
     });
     if (!readOnly) {
+        const leafXmjSpreadObj = {
+            getSelect : function () {
+                const sheet = ledgerSpread.getActiveSheet();
+                const select = SpreadJsObj.getSelectObject(sheet);
+                const index = gclGatherData.indexOf(select);
+                const leafXmjSheet = leafXmjSpread.getActiveSheet();
+                const leafXmjSelect = SpreadJsObj.getSelectObject(leafXmjSheet);
+                const iRow = gclGatherData[index].leafXmjs.indexOf(leafXmjSelect);
+                return [index, iRow, leafXmjSheet, leafXmjSelect];
+            },
+            checkJoinMaterial: function (type) {
+                const [iGclRow, iRow, sheet, select] = leafXmjSpreadObj.getSelect();
+                const color = type === 'join' ? '' : '#d6d8db';
+                const data = {
+                    type: type,
+                    select: type === 'join' ? findNotJoinLeafXmj(select) : select,
+                }
+                // 添加到
+                postData(window.location.pathname + '/save', data, function (result) {
+                    if (type === 'join') {
+                        const index = findNotJoinLeafXmj(select, 'index');
+                        notJoinList.splice(index, 1);
+                    } else {
+                        notJoinList.push(result);
+                    }
+                    sheet.getRange(iRow, -1, 1, -1).backColor(color);
+                    loadMaterialData(iGclRow, iRow);
+                });
+            },
+        }
         // leafXmj右键功能
         $.contextMenu({
             selector: '#leaf-xmj-spread',
@@ -221,42 +283,44 @@ $(document).ready(() => {
                     name: '不参与调差',
                     icon: 'fa-remove',
                     callback: function (key, opt) {
-                        const sheet = ledgerSpread.getActiveSheet();
+                        leafXmjSpreadObj.checkJoinMaterial('notjoin');
+                    },
+                    visible: function (key, opt) {
+                        const sheet = leafXmjSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
-                        const index = gclGatherData.indexOf(select);
-                        const leafXmjSheet = leafXmjSpread.getActiveSheet();
-                        const leafXmjSelect = SpreadJsObj.getSelectObject(leafXmjSheet);
-                        const iRow = gclGatherData[index].leafXmjs.indexOf(leafXmjSelect);
-                        leafXmjSheet.getRange(iRow, -1, 1, -1).backColor('#d6d8db');
+                        const notx = findNotJoinLeafXmj(select);
+                        if (!readOnly && select && notx === undefined) {
+                            return true;
+                        } else {
+                            return false;
+                        }
+                    }
+                },
+                'start': {
+                    name: '参与调差',
+                    icon: 'fa-sign-in',
+                    callback: function (key, opt) {
+                        leafXmjSpreadObj.checkJoinMaterial('join');
                     },
-                    disabled: function (key, opt) {
+                    visible: function (key, opt) {
                         const sheet = leafXmjSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
-                        const notx = notJoinList.find(function (item) {
-                            return item.gcl_id === select.gcl_id && item.xmj_id === select.id && (select.mx_id === undefined || (select.mx_id !== undefined && select.mx_id === item.mx_id));
-                        });
+                        const notx = findNotJoinLeafXmj(select);
                         if (!readOnly && select && notx === undefined) {
                             return false;
                         } else {
                             return true;
                         }
-                    }
-                },
+                    },
+
+                }
             }
         });
-        const materialBase = {
-            isEdit: function (data) {
-                return materialListData.find(function (item) {
-                    return item.mbid === data.id;
-                });
-            }
-        };
         // material-spread右键功能
         const materialSpreadObj = {
             del: function () {
                 const sheet = materialSpread.getActiveSheet();
                 const select = SpreadJsObj.getSelectObject(sheet);
-                console.log(select);
                 postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
                     const index = materialList.indexOf(select);
                     materialList.splice(index, 1);
@@ -275,7 +339,7 @@ $(document).ready(() => {
                     // 未改变值则不提交
                     const validText = info.editingText ? (typeof(info.editingText) === 'String' ? info.editingText.replace('\n', '') : info.editingText) : null;
                     const orgValue = select[col.field];
-                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === '' || validText === null))) {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
@@ -287,13 +351,12 @@ $(document).ready(() => {
                         materialListData.splice(materialListIndex, 1, select);
                         materialList.indexOf(index, 1, select);
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        const [iGclRow, iRow, sheet, lselect] = leafXmjSpreadObj.getSelect();
+                        SpreadJsObj.reLoadRowData(sheet, iRow);
                     }, function () {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     });
                 }
-            },
-            setReadOnly: function(readOnly) {
-                SpreadJsObj.resetFieldReadOnly(materialSpread.getActiveSheet(), 'quantity', readOnly);
             }
         };
         materialSpread.bind(spreadNS.Events.EditEnded, materialSpreadObj.editEnded);
@@ -305,6 +368,13 @@ $(document).ready(() => {
                 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) {
@@ -315,11 +385,16 @@ $(document).ready(() => {
             const needAddList = [];
             for (const xmj of ledgerSelect.leafXmjs) {
                 if (xmj.mx_id !== undefined) {
-                    const ml = materialListData.find(function (item) {
-                        return xmj.mx_id === item.mx_id && select.mb_id === item.mb_id;
+                    const notx = notJoinList.find(function (item) {
+                        return item.gcl_id === xmj.gcl_id && item.xmj_id === xmj.id && (xmj.mx_id === undefined || (xmj.mx_id !== undefined && xmj.mx_id === item.mx_id));
                     });
-                    if (ml === undefined) {
-                        needAddList.push(xmj.mx_id);
+                    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.mx_id);
+                        }
                     }
                 }
             }
@@ -370,7 +445,7 @@ $(document).ready(() => {
                     disabled: function (key, opt) {
                         const sheet = materialSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
-                        if (!readOnly && select && materialBase.isEdit(select) === undefined) {
+                        if (!readOnly && select && materialBase.isEdit(select)) {
                             return false;
                         } else {
                             return true;
@@ -408,6 +483,37 @@ $(document).ready(() => {
         }
     });
 
+    // 显示有调差工料清单
+    $('#show_material_gcl').click(function () {
+        if ($(this).is(':checked')) {
+            const hadMaterialGclGatherData = [];
+            const hadGclIdList = [];
+            for (const ml of materialListData) {
+                if (hadGclIdList.indexOf(ml.gcl_id) === -1) {
+                    hadGclIdList.push(ml.gcl_id);
+                }
+            }
+            for (const gcl of gclGatherData) {
+                const gcl_id = gcl.leafXmjs[0].gcl_id;
+                if (hadGclIdList.indexOf(gcl_id) !== -1) {
+                    hadMaterialGclGatherData.push(gcl);
+                }
+            }
+            gclGatherData = hadMaterialGclGatherData;
+        } else {
+            gclGatherModel.loadLedgerData(ledger, curLedgerData);
+            gclGatherModel.loadPosData(pos, curPosData);
+            gclGatherData = gclGatherModel.gatherGclData();
+        }
+        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
+        loadLeafXmjData(0);
+        loadMaterialData(0, 0);
+        SpreadJsObj.resetTopAndSelect(ledgerSpread.getActiveSheet());
+        SpreadJsObj.resetTopAndSelect(leafXmjSpread.getActiveSheet());
+        SpreadJsObj.resetTopAndSelect(materialSpread.getActiveSheet());
+
+    });
+
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',

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

@@ -82,5 +82,6 @@ $('a[data-target="#sp-list" ]').on('click', function () {
 
 function checkValidForm() {
     $('#add-stage-btn').attr('disabled', true);
+    $('#hide-all').show();
     return true;
 }

+ 233 - 21
app/public/js/revise.js

@@ -9,6 +9,9 @@
  */
 
 const ckBillsSpread = window.location.pathname + '-billsSelect';
+function transExpr(expr) {
+    return expr.replace('=', '').replace('%', '/100');
+}
 
 $(document).ready(() => {
     autoFlashHeight();
@@ -66,6 +69,21 @@ $(document).ready(() => {
     pos.loadDatas(posData);
 
     const billsTreeSpreadObj = {
+        loadExprToInput(sheet) {
+            const sel = sheet.getSelections()[0];
+            const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
+            if (col.type === 'Number') {
+                const data = SpreadJsObj.getSelectObject(sheet);
+                if (data) {
+                    $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                        .attr('readOnly', readOnly || cell.locked());
+                } else {
+                    $('#bills-expr').val('').attr('readOnly', true);
+                }
+            } else {
+                $('#bills-expr').val('').attr('readOnly', true);
+            }
+        },
         getDefaultSelectInfo: function (sheet) {
             const tree = sheet.zh_tree;
             if (!tree) return;
@@ -104,10 +122,11 @@ $(document).ready(() => {
             const tree = sheet.zh_tree;
             if (!tree) return;
             const first = sheet.zh_tree.nodes[row];
-            let last = first, sameParent = true;
+            let last = first, sameParent = true, nodeUsed = first.used;
             if (sel.rowCount > 1) {
                 for (let r = 1; r < sel.rowCount; r++) {
                     const rNode = tree.nodes[sel.row + r];
+                    nodeUsed = nodeUsed || rNode.used;
                     if (rNode.level > first.level) continue;
                     if ((rNode.level < first.level) || (rNode.level === first.level && rNode.pid !== first.pid)) {
                         sameParent = false;
@@ -119,15 +138,17 @@ $(document).ready(() => {
             const preNode = tree.getPreSiblingNode(first);
             const valid = !sheet.zh_setting.readOnly;
 
-            setObjEnable($('#insert'), valid && first && first.level > 1);
-            setObjEnable($('#delete'), valid && first && sameParent && first.level > 1);
-            setObjEnable($('#up-move'), valid && first && sameParent && first.level > 1 && preNode);
-            setObjEnable($('#down-move'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
+            setObjEnable($('a[type=insert]'), valid && first && first.level > 1);
+            setObjEnable($('a[type=delete]'), valid && first && sameParent && first.level > 1 && !nodeUsed);
+            setObjEnable($('a[type=up-move]'), valid && first && sameParent && first.level > 1 && preNode);
+            setObjEnable($('a[type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
             if (isTz) {
                 const posRange = last ? pos.getLedgerPos(last.id) : [];
-                setObjEnable($('#up-level'), valid && first && sameParent && tree.getParent(first) && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
+                setObjEnable($('a[type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed
+                    && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
                 const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
-                setObjEnable($('#down-level'), valid && first && sameParent && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0));
+                setObjEnable($('a[type=down-level]'), valid && first && sameParent && !nodeUsed
+                    && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0));
             } else {
                 setObjEnable($('#up-level'), valid && first && sameParent && first.level > 2 && tree.getParent(first));
                 setObjEnable($('#down-level'), valid && first && sameParent && first.level > 1 && preNode);
@@ -196,6 +217,7 @@ $(document).ready(() => {
                 SpreadJsObj.saveTopAndSelect(billsSheet, ckBillsSpread);
                 posSearch.search($('#pos-keyword').val());
             }
+            billsTreeSpreadObj.loadExprToInput(info.sheet);
         },
         /**
          * 新增节点
@@ -318,8 +340,34 @@ $(document).ready(() => {
                 }
                 // 获取更新数据
                 if (info.editingText) {
-                    data[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText.replace('\n', '');
+                    const text = info.editingText.replace('\n', '');
+                    if (node.used && (col.field === 'code' || col.field ==='b_code') && orgValue !== '' && text === '') {
+                        toastr.error('清单已计量,请删除编号');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    if (col.type === 'Number') {
+                        const num = _.toNumber(text);
+                        if (num) {
+                            data[col.field] = num;
+                        } else {
+                            try {
+                                data[col.field] = math.evaluate(transExpr(text));
+                            } catch(err) {
+                                toastr.error('输入的表达式非法');
+                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                                return;
+                            }
+                        }
+                    } else {
+                        data[col.field] = text;
+                    }
                 } else {
+                    if (node.used && (col.field === 'code' || col.field ==='b_code') && orgValue !== '') {
+                        toastr.error('清单已计量,请删除编号');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
                     data[col.field] = null;
                 }
                 // 更新至服务器
@@ -343,15 +391,22 @@ $(document).ready(() => {
             }
         },
         clipboardPasted: function (e, info) {
+            const hint = {
+                usedUp: {type: 'error', msg: '清单已计量,不可修改单价'},
+                usedCode: {type: 'error', msg: '清单已计量,编号不可修改为空值'},
+                invalidExpr: {type: 'error', msg: '粘贴的表达式非法'},
+                posCode: {type: 'error', msg: '清单含有部位明细,请先删除部位明细,再修改清单编号为空'},
+                posQty: {type: 'error', msg: '清单含有部位明细,数量金额根据部位明细汇总计算所得,不可修改'},
+            };
+
             const tree = info.sheet.zh_tree;
             if (!tree) { return; }
 
             const sortData = info.sheet.zh_tree.nodes;
             const datas = [], filterNodes = [];
-            let bHint = false, bPaste;
 
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
-                bPaste = false;
+                let bPaste = false;
                 const curRow = info.cellRange.row + iRow;
                 const node = sortData[curRow];
                 if (node) {
@@ -363,23 +418,40 @@ $(document).ready(() => {
                         const lPos = pos.getLedgerPos(node.id);
                         if (lPos && lPos.length > 0) {
                             if (value === '' && colSetting.field === 'b_code') {
-                                if (!bHint) {
-                                    toastr.warning('清单含有部位明细,请先删除部位明细,再删除清单编号');
-                                    bHint = true;
-                                }
+                                toastMessageUniq(hint.posCode);
                                 continue;
                             }
                             if (colSetting.field === 'sgfh_qty' || colSetting.field === 'sgfh_tp' ||
                                 colSetting.field === 'sjcl_qty' || colSetting.field === 'sjcl_tp' ||
                                 colSetting.field === 'qtcl_qty' || colSetting.field === 'qtcl_tp') {
-                                if (!bHint) {
-                                    toastr.warning('清单含有部位明细,数量金额根据部位明细汇总计算所得,不可编辑');
-                                    bHint = true;
+                                toastMessageUniq(hint.posQty);
+                                continue;
+                            }
+                        }
+                        if (node.used && col.field === 'unit_price') {
+                            toastMessageUniq (hint.usedUp);
+                            continue;
+                        }
+                        if (colSetting.type === 'Number') {
+                            const num = _.toNumber(value);
+                            if (num) {
+                                data[colSetting.field] = num;
+                            } else {
+                                try {
+                                    data[colSetting.field] = math.evaluate(transExpr(value));
+                                } catch (err) {
+                                    toastMessageUniq(hint.invalidExpr);
+                                    continue;
                                 }
+                            }
+                        } else {
+                            if (node.used && (col.field === 'code' || col.field ==='b_code')
+                                && data[colSetting.field] !== '' && value === '') {
+                                toastMessageUniq(hint.usedCode);
                                 continue;
                             }
+                            data[colSetting.field] = value;
                         }
-                        data[colSetting.field] = value;
                         bPaste = true;
                     }
                     if (bPaste) {
@@ -449,8 +521,32 @@ $(document).ready(() => {
         topRowChanged: function (e, info) {
             SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
         },
+        editStarting(e, info) {
+            if (!info.sheet.zh_setting || !info.sheet.zh_tree) return;
+            const col = info.sheet.zh_setting.cols[info.col];
+            const node = info.sheet.zh_tree.nodes[info.row];
+            switch (col.field) {
+                case 'unit_price':
+                    info.cancel = (node.children && node.children.length > 0) || node.used;
+                case 'sgfh_qty':
+                case 'sgfh_tp':
+                case 'sjcl_qty':
+                case 'sjcl_tp':
+                case 'qtcl_qty':
+                case 'qtcl_tp':
+                case 'deal_qty':
+                case 'deal_tp':
+                    info.cancel = (node.children && node.children.length > 0);
+                    break;
+                case 'dgn_qty1':
+                case 'dgn_qty2':
+                    info.cancel = !_.isEmpty(node.b_code);
+                    break;
+            }
+        }
     };
     billsTreeSpreadObj.refreshOperationValid(billsSheet);
+    billsTreeSpreadObj.loadExprToInput(billsSheet);
     billsSpread.bind(spreadNS.Events.SelectionChanged, billsTreeSpreadObj.selectionChanged);
     billsSpread.bind(spreadNS.Events.topRowChanged, billsTreeSpreadObj.topRowChanged);
     if (!readOnly) {
@@ -458,6 +554,39 @@ $(document).ready(() => {
         $('a[name="base-opr"]').click(function () {
             billsTreeSpreadObj.baseOpr(billsSheet, this.getAttribute('type'));
         });
+
+        $('#bills-expr').bind('change mouseleave', function () {
+            const expr = $(this);
+            const select = SpreadJsObj.getSelectObject(billsSheet);
+            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = expr.val();
+            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
+
+            const data = {
+                id: select.id,
+                tender_id: select.tender_id,
+                ledger_id: select.ledger_id
+            };
+            const num = _.toNumber(newValue);
+            if (num) {
+                data[field] = num;
+            } else {
+                try {
+                    data[field] = math.evaluate(transExpr(newValue));
+                } catch (err) {
+                    toastr.error('输入的表达式非法');
+                    return;
+                }
+            }
+
+            // 更新至服务器
+            postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
+                const refreshNode = billsTree.loadPostData(result);
+                expr.val(select[field]);
+                billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
+            });
+        });
+
+        billsSpread.bind(spreadNS.Events.EditStarting, billsTreeSpreadObj.editStarting);
         billsSpread.bind(spreadNS.Events.EditEnded, billsTreeSpreadObj.editEnded);
         billsSpread.bind(spreadNS.Events.ClipboardPasting, billsTreeSpreadObj.clipboardPasting);
         billsSpread.bind(spreadNS.Events.ClipboardPasted, billsTreeSpreadObj.clipboardPasted);
@@ -581,7 +710,22 @@ $(document).ready(() => {
             } else {
                 data.posPostType = 'update';
                 data.postData = {id: posData.id};
-                data.postData[col.field] = col.type === 'Number' ? parseFloat(newText) : newText;
+                if (col.type === 'Number') {
+                    const num = _.toNumber(newText);
+                    if (num) {
+                        data.postData[col.field] = num;
+                    } else {
+                        try {
+                            data.postData[col.field] = math.evaluate(transExpr(newText));
+                        } catch(err) {
+                            toastr.error('输入的表达式非法');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                    }
+                } else {
+                    data.postData[col.field] = newText;
+                }
             }
             postData(window.location.pathname + '/update', data, function (result) {
                 const updateRst = pos.updateDatas(result.pos);
@@ -723,8 +867,10 @@ $(document).ready(() => {
                     return;
                 }
             }
+            let bHint = false;
             const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                let bPaste = true;
                 const curRow = info.cellRange.row + iRow;
                 const posData = curRow >= sortData.length ? {lid: node.id, porder: lastOrder + curRow - sortData.length} : {id: sortData[curRow].id, lid: node.id};
                 for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
@@ -732,10 +878,29 @@ $(document).ready(() => {
                     const colSetting = info.sheet.zh_setting.cols[curCol];
                     posData[colSetting.field] = info.sheet.getText(curRow, curCol);
                     if (colSetting.type === 'Number') {
-                        posData[colSetting.field] = _.toNumber(posData[colSetting.field]);
+                        const num = _.toNumber(posData[colSetting.field]);
+                        if (num) {
+                            posData[colSetting.field] = num;
+                        } else {
+                            try {
+                                posData[colSetting.field] = math.evaluate(transExpr(posData[colSetting.field]));
+                            } catch (err) {
+                                if (!bHint) {
+                                    toastr.warning('粘贴了非法表达式,已过滤');
+                                    bHint = true;
+                                }
+                                bPaste = false;
+                            }
+                        }
                     }
                 }
-                data.push(posData);
+                if (bPaste) {
+                    data.push(posData);
+                }
+            }
+            if (data.length === 0) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
             }
             postData(window.location.pathname + '/update', {postType: 'pos', posPostType: 'paste', postData: data}, function (result) {
                 pos.updateDatas(result.pos);
@@ -748,10 +913,57 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
             });
         },
+        selectionChanged: function (e, info) {
+            const col = info.sheet.zh_setting.cols[info.newSelections[0].col];
+            const cell = info.sheet.getCell(info.newSelections[0].col, info.newSelections[0].col);
+            if (col.type === 'Number') {
+                const data = SpreadJsObj.getSelectObject(info.sheet);
+                if (data) {
+                    $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                        .attr('row', info.newSelections[0].row).attr('readOnly', readOnly || cell.locked());
+                } else {
+                    $('#pos-expr').val('').attr('readOnly', true);
+                }
+            } else {
+                $('#pos-expr').val('').attr('readOnly', true);
+            }
+        },
     };
     posSpreadObj.loadCurPosData();
     SpreadJsObj.resetTopAndSelect(posSheet);
+    posSpread.bind(spreadNS.Events.SelectionChanged, posSpreadObj.selectionChanged);
     if (!readOnly) {
+        $('#pos-expr').bind('change mouseleave', function () {
+            const expr = $(this);
+            const select = SpreadJsObj.getSelectObject(posSheet);
+            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = expr.val();
+            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
+
+            const data = {id: select.id};
+            const num = _.toNumber(newValue);
+            if (num) {
+                data[field] = num;
+            } else {
+                try {
+                    data[field] = math.evaluate(transExpr(newValue));
+                } catch (err) {
+                    toastr.error('输入的表达式非法');
+                    return;
+                }
+            }
+
+            // 更新至服务器
+            postData(window.location.pathname + '/update', {posPostType: 'update', posData: data}, function (result) {
+                const updateRst = pos.updateDatas(result.pos);
+                expr.val(select[field]);
+                // 刷新当前行, 不适用于新增(在非下一空白行新增)
+                SpreadJsObj.reLoadRowData(info.sheet, _.toNumber(row));
+                const loadResult = billsTree.loadPostData(result.ledger);
+                billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
+                billsTreeSpreadObj.refreshOperationValid(billsSheet);
+            });
+        });
+
         posSpread.bind(spreadNS.Events.EditStarting, posSpreadObj.editStarting);
         posSpread.bind(spreadNS.Events.EditEnded, posSpreadObj.editEnded);
         posSpread.bind(spreadNS.Events.ClipboardPasting, posSpreadObj.clipboardPasting);

+ 2 - 2
app/public/js/revise_history.js

@@ -278,9 +278,9 @@ $(document).ready(() => {
 
     $('#reviseHistory').change(function () {
         postData(window.location.pathname + '/info', { rid: this.value }, function (result) {
-            $('#user-name').value(result.user_name);
+            $('#user-name').val(result.user_name);
             $('#content')[0].textContent = result.content;
-            $('#end-time').value(result.end_time.toLocaleString());
+            $('#end-time').val(result.end_time.toLocaleString());
         });
     });
 });

+ 250 - 23
app/public/js/stage.js

@@ -12,6 +12,10 @@ function checkTzMeasureType () {
     return tender.measure_type === measureType.tz.value;
 }
 
+function transExpr(expr) {
+    return expr.replace('=', '').replace('%', '/100');
+}
+
 /**
  * 从cookie中读取缓存的列显示设置,没有则取默认
  * @returns {*[]}
@@ -444,6 +448,9 @@ $(document).ready(() => {
         changesObj.loadChanges({bills: data});
     };
     ledgerSpreadSetting.dgnUpFields = ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'];
+    ledgerSpreadSetting.getColor = function (sheet, data, col, defaultColor) {
+        return data && data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
+    };
     SpreadJsObj.initSheet(slSpread.getActiveSheet(), ledgerSpreadSetting);
 
     stageTree.loadDatas(ledgerData);
@@ -472,9 +479,32 @@ $(document).ready(() => {
         const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
         changesObj.loadChanges({bills: node, pos: data});
     };
+    posSpreadSetting.getColor = function (sheet, data, col, defaultColor) {
+        return data && data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
+    };
     SpreadJsObj.initSheet(spSpread.getActiveSheet(), posSpreadSetting);
 
     const stageTreeSpreadObj = {
+        loadExprToInput(sheet) {
+            const sel = sheet.getSelections()[0];
+            const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
+            if (col.type === 'Number') {
+                const data = SpreadJsObj.getSelectObject(sheet);
+                if (!data) {
+                    $('#bills-expr').val('').attr('readOnly', true);
+                    return;
+                }
+                const nodePos = stagePos.getLedgerPos(data.id);
+                if (nodePos && nodePos.length > 0) {
+                    $('#bills-expr').val('').attr('readOnly', true);
+                } else {
+                    $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                        .attr('readOnly', readOnly || cell.locked());
+                }
+            } else {
+                $('#bills-expr').val('').attr('readOnly', true);
+            }
+        },
         refreshTreeNodes: function (sheet, nodes) {
             const tree = sheet.zh_tree;
             if (!tree) { return }
@@ -491,17 +521,32 @@ $(document).ready(() => {
                 const node = sortData[info.row], updateData = {};
 
                 const orgValue = node[col.field];
-                const newValue =  col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
-                if (orgValue == newValue || ((!orgValue || orgValue === '') && (newValue === ''))) {
+                let newValue = info.editingText;
+                if (orgValue == newValue || ((!orgValue || orgValue === '') && (!newValue || newValue === ''))) {
                     return;
                 }
+                if (col.type === 'Number' && newValue && newValue !== '') {
+                    const num = _.toNumber(newValue);
+                    if (num) {
+                        newValue = num;
+                    } else {
+                        try {
+                            newValue = math.evaluate(transExpr(newValue));
+                        } catch(err) {
+                            toastr.error('输入的表达式非法');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                    }
+                }
+
                 if (col.field.indexOf('_dgn_') > 0) {
                     if (node.b_code && node.b_code !== '') {
                         toastr.error('仅项目节可输入设计数量');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
-                } else if (col.field !== 'postil') {
+                } else if (col.field !== 'postil' && col.field !== 'memo') {
                     if (node.children && node.children.length > 0) {
                         toastr.error('清单父项不可计量');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -516,7 +561,12 @@ $(document).ready(() => {
                     }
                 }
 
-                if (col.field.indexOf('_dgn_') > 0) {
+                if (col.field === 'memo') {
+                    updateData.main = {
+                        id: node.id
+                    };
+                    updateData.main[col.field] = newValue;
+                } else if (col.field.indexOf('_dgn_') > 0) {
                     updateData.dgn = {
                         id: node.id
                     };
@@ -542,6 +592,7 @@ $(document).ready(() => {
                 posSearch.search();
             }
             SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
+            stageTreeSpreadObj.loadExprToInput(info.sheet);
         },
         deletePress(sheet) {
             if (sheet.zh_setting && sheet.zh_dataType === 'tree') {
@@ -558,12 +609,12 @@ $(document).ready(() => {
                 if (validCols.length === 0) { return; }
 
                 const sortData = sheet.zh_tree.nodes;
-                const datas = [], dgnDatas = [];
+                const datas = [], dgnDatas = [], mainDatas = [];
                 for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
                     const node = sortData[iRow];
                     if (node) {
-                        const data = { lid: node.id }, dgnData = { id: node.id };
-                        let filter = true, filterDgn = true;
+                        const data = { lid: node.id }, dgnData = { id: node.id }, mainData = { id: node.id };
+                        let filter = true, filterDgn = true, filterMain = true;
                         for (const iCol of validCols) {
                             const colSetting = sheet.zh_setting.cols[iCol];
                             if (sheet.zh_setting.dgnUpFields.indexOf(colSetting.field) !== -1) {
@@ -579,6 +630,9 @@ $(document).ready(() => {
                                     dgnData[colSetting.field] = 0;
                                     filterDgn = false;
                                 }
+                            } else if (colSetting.field === 'memo') {
+                                mainData[colSetting.field] = null;
+                                filterMain = false;
                             } else {
                                 data[colSetting.field] = null;
                                 filter = false;
@@ -586,12 +640,14 @@ $(document).ready(() => {
                         }
                         if (!filter) datas.push(data);
                         if (!filterDgn) dgnDatas.push(dgnData);
+                        if (!filterMain) mainDatas.push(mainData);
                     }
                 }
-                if (datas.length > 0 || dgnDatas.length > 0) {
+                if (datas.length > 0 || dgnDatas.length > 0 || mainDatas.length > 0) {
                     const bills = {};
                     if (datas.length > 0) bills.stage = datas;
                     if (dgnDatas.length > 0) bills.dgn = dgnDatas;
+                    if (mainDatas.length > 0) bills.main = mainDatas;
                     postData(window.location.href + '/update', {bills: bills}, function (result) {
                         const nodes = stageTree.loadPostStageData(result);
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
@@ -607,7 +663,8 @@ $(document).ready(() => {
                 const stageField = ['contract_qty', 'contract_tp', 'qc_qty', 'postil'];
                 for (let iCol = range.col; iCol < range.col + range.colCount; iCol++) {
                     const col = info.sheet.zh_setting.cols[iCol];
-                    if ((stageField.indexOf(col.field) === -1) && setting.dgnUpFields.indexOf(col.field) === -1) {
+                    if ((stageField.indexOf(col.field) === -1) && setting.dgnUpFields.indexOf(col.field) === -1
+                        && col.field !== 'memo') {
                         toastr.error('不可修改此数据');
                         info.cancel = true;
                         return;
@@ -618,14 +675,15 @@ $(document).ready(() => {
         clipboardPasted(e, info) {
             if (info.sheet.zh_setting && info.sheet.zh_tree) {
                 const sheet = info.sheet, setting = info.sheet.zh_setting;
-                const filterNodes = [], datas = [], dgnDatas = [];
+                const filterNodes = [], datas = [], dgnDatas = [], mainDatas = [];
+                let bHint = false;
 
                 for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                     const curRow = iRow + info.cellRange.row;
                     const node = sheet.zh_tree.getItemsByIndex(curRow);
 
-                    const data = {lid: node.id}, dgnData = {id: node.id};
-                    let filter = true, filterDgn = true;
+                    const data = {lid: node.id}, dgnData = {id: node.id}, mainData = {id: node.id};
+                    let filter = true, filterDgn = true, filterMain = true;
                     for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                         const curCol = info.cellRange.col + iCol;
                         const col = info.sheet.zh_setting.cols[curCol];
@@ -638,25 +696,61 @@ $(document).ready(() => {
                             if (nodePos && nodePos.length > 0) continue;
                         }
 
+                        const text = sheet.getText(curRow, curCol);
                         if (setting.dgnUpFields.indexOf(col.field) !== -1) {
-                            dgnData[col.field] = _.toNumber(sheet.getText(curRow, curCol));
-                            filterDgn = false;
+                            const num = _.toNumber(text);
+                            if (num) {
+                                dgnData[col.field] = num;
+                                filterDgn = false;
+                            } else {
+                                try {
+                                    dgnData[col.field] = math.evaluate(transExpr(text));
+                                    filterDgn = false;
+                                } catch(err) {
+                                    if (!bHint) {
+                                        toastr.warning('粘贴了非法表达式,已过滤');
+                                        bHint = true;
+                                    }
+                                }
+                            }
+                        } else if (col.field === 'memo') {
+                            mainData.memo = text;
+                            filterMain = false;
                         } else {
-                            data[col.field] = col.type === 'Number' ? _.toNumber(sheet.getText(curRow, curCol)) : sheet.getText(curRow, curCol);
-                            filter = false;
+                            if (col.type === 'Number') {
+                                const num = _.toNumber(text);
+                                if (num) {
+                                    data[col.field] = num;
+                                    filter = false;
+                                } else {
+                                    try {
+                                        data[col.field] = math.evaluate(transExpr(text));
+                                        filter = false;
+                                    } catch(err) {
+                                        if (!bHint) {
+                                            toastr.warning('粘贴了非法表达式,已过滤');
+                                            bHint = true;
+                                        }
+                                    }
+                                }
+                            } else {
+                                data[col.field] = text;
+                            }
                         }
                     }
-                    if (filter && filterDgn) {
+                    if (filter && filterDgn && filterMain) {
                         filterNodes.push(node);
                     } else {
                         if (!filter) datas.push(data);
                         if (!filterDgn) dgnDatas.push(dgnData);
+                        if (!filterMain) mainDatas.push(mainData);
                     }
                 }
-                if (datas.length > 0 || dgnDatas.length > 0) {
+                if (datas.length > 0 || dgnDatas.length > 0 || mainDatas.length > 0) {
                     const updateData = {};
                     if (datas.length > 0) updateData.stage = datas;
                     if (dgnDatas.length > 0) updateData.dgn = dgnDatas;
+                    if (mainDatas.length > 0) updateData.main = mainDatas;
                     postData(window.location.href + '/update', {bills: updateData}, function (data) {
                         const nodes = stageTree.loadPostStageData(data);
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes.concat(filterNodes));
@@ -831,8 +925,65 @@ $(document).ready(() => {
             },
         }
     });
+    if (!readOnly) {
+        $('#bills-expr').bind('change mouseleave', function () {
+            const expr = $(this);
+            const sheet = slSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(sheet);
+            const field = expr.attr('field'), orgValue = expr.attr('org'), updateData = {};
+            let newValue = expr.val();
+
+            const num = _.toNumber(newValue);
+            if (num) {
+                newValue = num;
+            } else {
+                try {
+                    newValue = math.evaluate(transExpr(newValue));
+                } catch(err) {
+                    toastr.error('输入的表达式非法');
+                    return;
+                }
+            }
+            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
+
+            if (field.indexOf('_dgn_') > 0) {
+                updateData.dgn = {
+                    id: select.id
+                };
+                updateData.dgn[field] = newValue;
+            } else {
+                updateData.stage = {
+                    lid: select.id
+                };
+                updateData.stage[field] = newValue;
+            }
+
+            // 更新至服务器
+            postData(window.location.pathname + '/update', {bills: updateData}, function (result) {
+                const nodes = stageTree.loadPostStageData(result);
+                expr.val(select[field]);
+                stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+            });
+        });
+    }
+    stageTreeSpreadObj.loadExprToInput(slSpread.getActiveSheet());
 
     const stagePosSpreadObj = {
+        loadExprToInput(sheet) {
+            const sel = sheet.getSelections()[0];
+            const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
+            if (col.type === 'Number') {
+                const data = SpreadJsObj.getSelectObject(sheet);
+                if (data) {
+                    $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                        .attr('row', sel.row).attr('readOnly', readOnly || cell.locked());
+                } else {
+                    $('#pos-expr').val('').attr('readOnly', true);
+                }
+            } else {
+                $('#pos-expr').val('').attr('readOnly', true);
+            }
+        },
         /**
          * 加载部位明细 根据当前台账选择节点
          */
@@ -907,7 +1058,22 @@ $(document).ready(() => {
                 } else {
                     data.updateType = 'update';
                     data.updateData = {pid: posData.id, lid: posData.lid};
-                    data.updateData[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
+                    if (col.type === 'Number') {
+                        const num = _.toNumber(info.editingText);
+                        if (num) {
+                            data.updateData[col.field] = num;
+                        } else {
+                            try {
+                                data.updateData[col.field] = math.evaluate(transExpr(info.editingText));
+                            } catch(err) {
+                                toastr.error('输入的表达式非法');
+                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                                return;
+                            }
+                        }
+                    } else {
+                        data.updateData[col.field] = info.editingText;
+                    }
                 }
                 // 提交数据到服务器
                 postData(window.location.pathname + '/update', {pos: data}, function (result) {
@@ -976,8 +1142,20 @@ $(document).ready(() => {
                             const curCol = info.cellRange.col + iCol;
                             const colSetting = info.sheet.zh_setting.cols[curCol];
                             newData[colSetting.field] = info.sheet.getText(curRow, curCol);
-                            if (colSetting.type === 'Number') {
-                                newData[colSetting.field] = _.toNumber(newData[colSetting.field]);
+                            if (col.type === 'Number') {
+                                const num = _.toNumber(newData[colSetting.field]);
+                                if (num) {
+                                    newData[colSetting.field] = num;
+                                } else {
+                                    try {
+                                        newData[colSetting.field] = math.evaluate(transExpr(newData[colSetting.field]));
+                                    } catch(err) {
+                                        toastr.error('输入的表达式非法');
+                                        self.loadCurPosData();
+                                        return;
+
+                                    }
+                                }
                             }
                         }
                         data.updateData.push(newData);
@@ -993,8 +1171,19 @@ $(document).ready(() => {
                                 const curCol = info.cellRange.col + iCol;
                                 const colSetting = info.sheet.zh_setting.cols[curCol];
                                 newData[colSetting.field] = info.sheet.getText(curRow, curCol);
-                                if (colSetting.type === 'Number') {
-                                    newData[colSetting.field] = _.toNumber(newData[colSetting.field]);
+                                if (col.type === 'Number') {
+                                    const num = _.toNumber(newData[colSetting.field]);
+                                    if (num) {
+                                        newData[colSetting.field] = num;
+                                    } else {
+                                        try {
+                                            newData[colSetting.field] = math.evaluate(transExpr(newData[colSetting.field]));
+                                        } catch(err) {
+                                            toastr.error('输入的表达式非法');
+                                            self.loadCurPosData();
+                                            return;
+                                        }
+                                    }
                                 }
                             }
                             data.updateData.push(newData);
@@ -1084,6 +1273,9 @@ $(document).ready(() => {
                 }
             }
         },
+        selectionChanged: function (e, info) {
+            stagePosSpreadObj.loadExprToInput(info.sheet);
+        }
     };
     // 加载上下窗口resizer
     $.divResizer({
@@ -1110,7 +1302,42 @@ $(document).ready(() => {
     spSpread.bind(spreadNS.Events.ClipboardPasting, stagePosSpreadObj.clipboardPasting);
     spSpread.bind(spreadNS.Events.ClipboardPasted, stagePosSpreadObj.clipboardPasted);
     spSpread.bind(spreadNS.Events.EditStarting, stagePosSpreadObj.editStarting);
+    spSpread.bind(spreadNS.Events.SelectionChanged, stagePosSpreadObj.selectionChanged);
     SpreadJsObj.addDeleteBind(spSpread, stagePosSpreadObj.deletePress);
+    if (!readOnly) {
+        $('#pos-expr').bind('change mouseleave', function () {
+            const expr = $(this);
+            const posSheet = spSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(posSheet);
+            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = expr.val(), row = expr.attr('row');
+            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
+
+            const data = {pid: select.id, lid: select.lid};
+            const num = _.toNumber(newValue);
+            if (num) {
+                data[field] = num;
+            } else {
+                try {
+                    data[field] = math.evaluate(transExpr(newValue));
+                } catch (err) {
+                    toastr.error('输入的表达式非法');
+                    return;
+                }
+            }
+
+            // 提交数据到服务器
+            postData(window.location.pathname + '/update', {pos: {updateType: 'update', updateData: data}}, function (result) {
+                if (result.pos) {
+                    stagePos.updateDatas(result.pos.pos);
+                    stagePos.loadCurStageData(result.pos.curStageData);
+                    expr.val(select[field]);
+                    SpreadJsObj.reLoadRowData(posSheet, _.toNumber(row));
+                }
+                const refreshData = stageTree.loadPostStageData(result.ledger);
+                stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), refreshData);
+            });
+        });
+    }
     if (!checkTzMeasureType()) {
         $.contextMenu({
             selector: '#stage-pos',

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

@@ -148,4 +148,5 @@ function checkAuditorFrom () {
         toast.error('请先选择审批人,再上报数据');
         return false;
     }
+    $('#hide-all').show();
 }

+ 15 - 19
app/public/js/stage_detail.js

@@ -33,7 +33,7 @@ $(document).ready(() => {
             {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
             {title: '中间计量表号', colSpan: '1', rowSpan: '1', field: 'im_code', hAlign: 0, width: 150, formatter: '@', readOnly: true},
             {title: '交工证书/凭证号', colSpan: '1', rowSpan: '1', field: 'doc_code', hAlign: 0, width: 180, formatter: '@'},
-            {title: '分分项工程', colSpan: '1', rowSpan: '1', field: 'fbfx', hAlign: 0, width: 150, formatter: '@', readOnly: true},
+            {title: '分分项工程', colSpan: '1', rowSpan: '1', field: 'fbfx', hAlign: 0, width: 150, formatter: '@', readOnly: true},
             {title: '本期计量数量/金额', colSpan: '1', rowSpan: '1', field: 'jl', hAlign: 2, width: 220, formatter: '@', readOnly: true},
         ],
         headRows: 1,
@@ -111,16 +111,22 @@ $(document).ready(() => {
         // 中间计量数据
         reLoadDetailData: function () {
             const data = SpreadJsObj.getSelectObject(detailSpread.getActiveSheet());
-            $('#edit-detail').show();
+            if (data) {
+                $('#edit-detail').show();
+                $('#modify-img').show();
+            } else {
+                $('#edit-detail').hide();
+                $('#modify-img').hide();
+            }
             $('#save-detail').hide();
             $('#cancel-detail').hide();
-            $('#bgl-code').val(data && data.bgl_code ? data.bgl_code : '');
-            $('#bw-name').val(data && data.bw ? data.bw : '');
-            $('#start-peg').val(data && data.start_peg ? data.start_peg : '');
-            $('#end-peg').val(data && data.end_peg ? data.end_peg : '');
-            $('#unit-name').val(data && data.jldy ? data.jldy : '');
-            $('#drawing-code').val(data && data.drawing_code ? data.drawing_code : '');
-            $('#calc-memo').val(data && data.calc_memo ? data.calc_memo : '');
+            $('#bgl-code').val(data && data.bgl_code ? data.bgl_code : '').attr('readonly', '');
+            $('#bw-name').val(data && data.bw ? data.bw : '').attr('readonly', '');
+            $('#start-peg').val(data && data.start_peg ? data.start_peg : '').attr('readonly', '');
+            $('#end-peg').val(data && data.end_peg ? data.end_peg : '').attr('readonly', '');
+            $('#unit-name').val(data && data.jldy ? data.jldy : '').attr('readonly', '');
+            $('#drawing-code').val(data && data.drawing_code ? data.drawing_code : '').attr('readonly', '');
+            $('#calc-memo').val(data && data.calc_memo ? data.calc_memo : '').attr('readonly', '');
             if (data && data.calc_img) {
                 $('#calc-img').html('<img src="/' + data.calc_img + '" class="d-100" width="100%">');
             } else {
@@ -128,16 +134,6 @@ $(document).ready(() => {
             }
         },
         selectionChanged: function (e, info) {
-            $('#edit-detail').show();
-            $('#cancel-detail').hide();
-            $('#save-detail').hide();
-            $('#bgl-code').attr('readonly', '');
-            $('#bw-name').attr('readonly', '');
-            $('#start-peg').attr('readonly', '');
-            $('#end-peg').attr('readonly', '');
-            $('#unit-name').attr('readonly', '');
-            $('#drawing-code').attr('readonly', '');
-            $('#calc-memo').attr('readonly', '');
             detailOperationObj.reLoadDetailData();
             detailOperationObj.loadLeafXmjsData();
         },

+ 14 - 0
app/public/js/stage_im.js

@@ -217,6 +217,17 @@ const stageIm = (function () {
         generatePosData(node, lx);
     }
 
+    function getCalcMemo(im) {
+        if (im.calc_memo !== undefined && im.calc_memo !== null && im.calc_memo !== '') return;
+        const memo = ['本期计量:' + im.jl + ' ' + im.unit];
+        for (const lx of im.leafXmjs) {
+            for (const p of lx.pos) {
+                memo.push(p.name + ':' + p.qty + ' ' + im.unit);
+            }
+        }
+        return memo.join('\n');
+    }
+
     /**
      * 生成 0号台账 中间计量数据
      * @param {Object} node - 生成中间计量表的节点
@@ -320,6 +331,9 @@ const stageIm = (function () {
         }
         // 生成数据
         recursiveBuildImData(gsTree.children);
+        for (const im of ImData) {
+            im.calc_memo = getCalcMemo(im);
+        }
         return ImData;
     }
 

+ 2 - 2
app/public/js/stage_pay.js

@@ -69,12 +69,12 @@ $(document).ready(() => {
                 const percent = formula.match(this.percentReg);
                 if (percent) {
                     for (const p of percent) {
-                        const v = math.eval(p.replace('%', '/100'));
+                        const v = math.evaluate(p.replace('%', '/100'));
                         formula = formula.replace(p, v);
                     }
                 }
                 try {
-                    const value = math.eval(formula);
+                    const value = math.evaluate(formula);
                     return value;
                 } catch(err) {
                     return 0;

+ 2 - 0
app/public/js/tender_list.js

@@ -417,6 +417,7 @@ $(document).ready(() => {
             }
             data.category.push(cate);
         }
+        $('#hide-all').show();
         postData('/list/add', data, function (result) {
             tenders.push(result);
             initTenderTree();
@@ -424,6 +425,7 @@ $(document).ready(() => {
             bindTenderUrl();
             $('#add-bd').modal('hide');
             $('[name=name]', '#add-bd').val('');
+            $('#hide-all').hide();
         });
     });
 });

+ 5 - 5
app/router.js

@@ -144,6 +144,11 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/update', sessionAuth, tenderCheck, stageCheck, 'stageController.updateStageData');
     app.post('/tender/:id/measure/stage/:order/valid-change', sessionAuth, tenderCheck, stageCheck, 'stageController.searchValidChange');
     app.post('/tender/:id/measure/stage/:order/use-change',sessionAuth, tenderCheck, stageCheck, 'stageController.useChange');
+    // 计量附件
+    app.post('/tender/:id/measure/stage/:order/upload/file', sessionAuth, tenderCheck, stageCheck, 'stageController.uploadFile');
+    app.get('/tender/:id/measure/stage/:order/download/file/:fid', sessionAuth, 'stageController.downloadFile');
+    app.post('/tender/:id/measure/stage/:order/delete/file', sessionAuth, tenderCheck, stageCheck, 'stageController.deleteFile');
+    app.post('/tender/:id/measure/stage/:order/save/file', sessionAuth, tenderCheck, stageCheck, 'stageController.saveFile');
     // 中间计量
     app.get('/tender/:id/measure/stage/:order/detail', sessionAuth, tenderCheck, stageCheck, 'stageController.detail');
     app.post('/tender/:id/measure/stage/:order/detail/build', sessionAuth, tenderCheck, stageCheck, 'stageController.buildDetailData');
@@ -187,11 +192,6 @@ module.exports = app => {
     app.post('/tender/report_api/createSignatureRole', sessionAuth, 'signatureController.createSignatureRole');
     app.post('/tender/report_api/updateRoleRelationship', sessionAuth, 'signatureController.updateRoleRel');
     app.post('/tender/report_api/createRoleRelationship', sessionAuth, 'signatureController.createRoleRel');
-    // 计量附件
-    app.post('/tender/:id/measure/stage/:order/upload/file', sessionAuth, 'stageController.uploadFile');
-    app.get('/tender/:id/measure/stage/:order/download/file/:fid', sessionAuth, 'stageController.downloadFile');
-    app.post('/tender/:id/measure/stage/:order/delete/file', sessionAuth, 'stageController.deleteFile');
-    app.post('/tender/:id/measure/stage/:order/save/file', sessionAuth, 'stageController.saveFile');
     // 变更管理
     app.get('/tender/:id/change', sessionAuth, tenderCheck, 'changeController.index');
     app.get('/tender/:id/change/status/:status', sessionAuth, tenderCheck, 'changeController.status');

+ 4 - 4
app/service/ledger.js

@@ -751,7 +751,7 @@ module.exports = app => {
          * @returns {Promise<void>}
          * @private
          */
-        async _calcNode(node, transaction) {
+        async calcNode(node, transaction) {
             const info = this.ctx.tender.info;
             const precision = this.ctx.helper.findPrecision(info.precision, node.unit);
 
@@ -781,7 +781,7 @@ module.exports = app => {
             if (!node) {
                 throw '数据错误';
             }
-            await this._calcNode(node, transaction);
+            await this.calcNode(node, transaction);
         }
 
         async _importCacheTreeNodes(transaction, nodes) {
@@ -819,11 +819,11 @@ module.exports = app => {
          * @param excelData
          * @returns {Promise<void>}
          */
-        async importExcel(excelData) {
+        async importExcel(templateId, excelData) {
             //console.time('analysis');
             const AnalysisExcel = require('../lib/analysis_excel');
             const analysisExcel = new AnalysisExcel(this.ctx);
-            const tempData = await this.ctx.service.tenderNodeTemplate.getData(true);
+            const tempData = await this.ctx.service.tenderNodeTemplate.getData(templateId, true);
             const cacheTree = analysisExcel.analysisData(excelData, tempData);
             const cacheKey = keyPre + this.ctx.tender.id;
             const orgMaxId = parseInt(await this.cache.get(cacheKey));

+ 34 - 1
app/service/material.js

@@ -125,6 +125,11 @@ module.exports = app => {
             };
             const transaction = await this.db.beginTransaction();
             try {
+                if (preMaterial) {
+                    newMaterial.rate = preMaterial.rate;
+                    newMaterial.pre_tp = preMaterial.m_tp + preMaterial.pre_tp;
+                    // 计算新一期本期金额
+                }
                 // 新增期记录
                 const result = await transaction.insert(this.tableName, newMaterial);
                 if (result.affectedRows === 1) {
@@ -132,12 +137,17 @@ module.exports = app => {
                 } else {
                     throw '新增期数据失败';
                 }
-                // 存在上一期时,复制上一期审批流程
+                // 存在上一期时,复制上一期审批流程、不参与调差的清单
                 if (preMaterial) {
                     const auditResult = await this.ctx.service.materialAudit.copyPreMaterialAuditors(transaction, preMaterial, newMaterial);
                     if (!auditResult) {
                         throw '复制上一期审批流程失败';
                     }
+                    const preNotJoinList = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: this.ctx.tender.id, mid: preMaterial.id } });
+                    const materialResult = await this.ctx.service.materialListNotjoin.copyNewStageNotJoinList(transaction, preNotJoinList, newMaterial.id);
+                    if (materialResult.affectedRows === 0) {
+                        throw '新增不参与调差清单数据失败';
+                    }
                 }
 
                 await transaction.commit();
@@ -159,6 +169,9 @@ module.exports = app => {
             try {
                 await transaction.delete(this.tableName, { id });
                 await transaction.delete(this.ctx.service.materialAudit.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialBills.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialList.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { mid: id });
                 await transaction.commit();
                 return true;
             } catch (err) {
@@ -166,6 +179,26 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        /**
+         * 获取包含当前期之前的调差期id
+         *
+         * @param {Number} id - 期Id
+         * @returns {Promise<void>}
+         */
+        async getPreMidList(tid, order) {
+            const midList = await this.getAllDataByCondition({
+                where: { tid },
+                columns: ['id'],
+                limit: order,
+                offset: 0,
+            });
+            const list = [];
+            for (const ml of midList) {
+                list.push(ml.id);
+            }
+            return list;
+        }
     }
 
     return Material;

+ 5 - 3
app/service/material_list.js

@@ -114,14 +114,16 @@ module.exports = app => {
         /**
          * 获取工料清单关联表
          * @param {int} tid 标段id
+         * @param {Object} midList 期id列表
          * @return {void}
          */
-        async getMaterialData(tid) {
-            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`quantity`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`tid`' +
+        async getMaterialData(tid, midList = []) {
+            const midsql = midList.length !== 0 ? ' AND ml.mid in (' + this.ctx.helper.getInArrStrSqlFilter(midList) + ')' : '';
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`quantity`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`tid`, ml.`mid`' +
                 ' FROM ' + this.tableName + ' as ml' +
                 ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb' +
                 ' ON ml.`mb_id` = mb.`id`' +
-                ' WHERE ml.`tid` = ?';
+                ' WHERE ml.`tid` = ?' + midsql;
             const sqlParam = [tid];
             return await this.db.query(sql, sqlParam);
         }

+ 65 - 0
app/service/material_list_notjoin.js

@@ -21,6 +21,71 @@ module.exports = app => {
             super(ctx);
             this.tableName = 'material_list_notjoin';
         }
+
+        /**
+         * 添加不参与调差的清单
+         * @return {void}
+         */
+        async add(data) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const newListNotJoin = {
+                tid: this.ctx.tender.id,
+                mid: this.ctx.material.id,
+                gcl_id: data.gcl_id,
+                xmj_id: data.id,
+                mx_id: data.mx_id !== undefined ? data.mx_id : '',
+                in_time: new Date(),
+            };
+
+            // 新增不参与调差清单
+            const result = await this.db.insert(this.tableName, newListNotJoin);
+            if (result.affectedRows === 0) {
+                throw '新增不参与调差清单数据失败';
+            }
+            return await this.getDataById(result.insertId);
+        }
+
+        /**
+         * 删除不参与调差的清单
+         * @param {int} id 工料id
+         * @return {void}
+         */
+        async del(id) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            // 判断是否可删
+            return await this.deleteById(id);
+        }
+
+        /**
+         * 复制上一期不参与调差的清单到下一期中
+         * @param {Object} transaction - 新增一期的事务
+         * @param {Object} list 上期清单
+         * @param {int} mid 工料id
+         * @return {void}
+         */
+        async copyNewStageNotJoinList(transaction, list, mid) {
+            if (!this.ctx.tender) {
+                throw '数据错误';
+            }
+            const notJoinlist = [];
+            for (const mb of list) {
+                const newLists = {
+                    tid: mb.tid,
+                    mid,
+                    gcl_id: mb.gcl_id,
+                    xmj_id: mb.xmj_id,
+                    mx_id: mb.mx_id,
+                    in_time: new Date(),
+                };
+                notJoinlist.push(newLists);
+            }
+            // 复制上一期不参与调差的清单
+            return await transaction.insert(this.tableName, notJoinlist);
+        }
     }
     return MaterialListNotJoin;
 };

+ 202 - 78
app/service/pos.js

@@ -23,17 +23,20 @@ module.exports = app => {
         }
 
         async getPosData(condition) {
+            return await this.db.select(this.tableName, {
+                where: condition,
+                columns: ['id', 'tid', 'lid', 'name', 'quantity', 'drawing_code', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'in_time', 'porder', 'add_stage'],
+                order: [['porder', 'ASC']],
+            });
+        }
+
+        async getPosDataWithAddStageOrder(condition) {
             const sql = 'SELECT p.id, p.tid, p.lid, p.name, p.quantity, p.drawing_code, p.sgfh_qty, p.sjcl_qty, p.qtcl_qty, p.porder, p.add_stage, p.add_times, p.add_user, s.order As add_stage_order ' +
                 '  FROM ' + this.tableName + ' p ' +
                 '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s' +
-                '  ON add_stage = s.id'
+                '  ON p.add_stage = s.id'
                 + this.ctx.helper.whereSql(condition, 'p');
             return await this.db.query(sql);
-            // return await this.db.select(this.tableName, {
-            //     where: condition,
-            //     columns: ['id', 'tid', 'lid', 'name', 'quantity', 'drawing_code', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'in_time', 'porder', 'add_stage'],
-            //     order: [['porder', 'ASC']],
-            // });
         }
 
         async getPosDataByIds(ids) {
@@ -56,7 +59,6 @@ module.exports = app => {
         }
 
         async getPosDataByUnits(tenderId, units) {
-            console.log(units);
             const sql = 'SELECT p.id, p.lid, p.sgfh_qty, p.sjcl_qty, p.qtcl_qty' +
                 '  FROM ' + this.tableName + ' p' +
                 '  LEFT JOIN ' + this.ctx.service.ledger.tableName + ' b' +
@@ -79,7 +81,185 @@ module.exports = app => {
                 const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
                 data.quantity = this.round(data.quantity, precision.value);
             }
-            const addRst = await transaction.insert(this.tableName, data);
+            if (transaction) {
+                const addRst = await transaction.insert(this.tableName, data);
+            } else {
+                const addRst = await this.db.insert(this.tableName, data);
+            }
+        }
+
+        async _completeInsertPosData(tid, data) {
+            data.id = this.uuid.v4();
+            data.tid = tid;
+            // todo 新增期
+            data.add_stage = 0;
+            data.add_times = 0;
+            data.in_time = new Date();
+            data.add_user = this.ctx.session.sessionUser.accountId;
+        }
+
+        async _addPosData(tid, data) {
+            if (data.updateData instanceof Array) {
+                for (const d of data.updateData) {
+                    this._completeInsertPosData(tid, d);
+                }
+            } else {
+                this._completeInsertPosData(tid, data);
+            }
+            await this.db.insert(this.tableName, data);
+            return { pos: data }
+        }
+
+        async _updatePosData(tid, data) {
+            if (data.sgfh_qty !== undefined || data.sjcl_qty !== undefined || data.qtcl_qty !== undefined) {
+                const op = await this.getDataById(data.id);
+                const bills = await this.ctx.service.ledger.getDataById(op.lid);
+                const billsPos = await this.getAllDataByCondition({where: {lid: op.lid} });
+
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+                if (data.sgfh_qty !== undefined) {
+                    data.sgfh_qty = this.round(data.sgfh_qty, precision.value);
+                } else if (op) {
+                    data.sgfh_qty = op.sgfh_qty;
+                }
+                if (data.sjcl_qty !== undefined) {
+                    data.sjcl_qty = this.round(data.sjcl_qty, precision.value);
+                } else if (op) {
+                    data.sjcl_qty = op.sjcl_qty;
+                }
+                if (data.qtcl_qty !== undefined) {
+                    data.qtcl_qty = this.round(data.qtcl_qty, precision.value);
+                } else if (op) {
+                    data.qtcl_qty = op.qtcl_qty;
+                }
+                data.quantity = this.ctx.helper.sum([data.sgfh_qty, data.qtcl_qty, data.sjcl_qty]);
+
+                const updateBills = {id: bills.id};
+                for (const bp of billsPos) {
+                    const calcData = bp.id === data.id ? data : bp;
+                    updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, calcData.sgfh_qty);
+                    updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, calcData.sjcl_qty);
+                    updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, calcData.qtcl_qty);
+                    updateBills.quantity = this.ctx.helper.add(updateBills.quantity, calcData.quantity);
+                }
+                const info = this.ctx.tender.info;
+                updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+                updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+
+                const transaction = await this.db.beginTransaction();
+                try {
+                    transaction.update(this.tableName, data);
+                    transaction.update(this.ctx.service.ledger.tableName, updateBills);
+                    await transaction.commit();
+                    updateBills.ledger_id = bills.ledger_id;
+                    return {
+                        ledger: { update: [updateBills] },
+                        pos: data,
+                    }
+                } catch(err) {
+                    await transaction.rollback();
+                    throw err;
+                }
+            } else {
+                await this.db.update(this.tableName, data);
+                return {pos: data};
+            }
+        }
+
+        async _updatePosDatas(tid, data) {
+            if (data.length === 0) return;
+            const op = await this.getDataById(data[0].id);
+            const bills = await this.ctx.service.ledger.getDataById(op.lid);
+            const billsPos = await this.getAllDataByCondition({where: {lid: op.lid} });
+            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+            for (const d of data) {
+                if (d.sgfh_qty !== undefined || d.sjcl_qty !== undefined || d.qtcl_qty !== undefined) {
+                    if (d.sgfh_qty !== undefined) {
+                        d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
+                    } else if (op) {
+                        d.sgfh_qty = op.sgfh_qty;
+                    }
+                    if (d.sjcl_qty !== undefined) {
+                        d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
+                    } else if (op) {
+                        d.sjcl_qty = op.sjcl_qty;
+                    }
+                    if (d.qtcl_qty !== undefined) {
+                        d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
+                    } else if (op) {
+                        d.qtcl_qty = op.qtcl_qty;
+                    }
+                    d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
+                }
+            }
+            const updateBills = {id: bills.id};
+            for (const bp of billsPos) {
+                const newPos = data.find(function (x) { return x.id === bp.id });
+                const calcData = newPos ? newPos : bp;
+                updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, calcData.sgfh_qty);
+                updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, calcData.sjcl_qty);
+                updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, calcData.qtcl_qty);
+                updateBills.quantity = this.ctx.helper.add(updateBills.quantity, calcData.quantity);
+            }
+            const info = this.ctx.tender.info;
+            updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+            updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const d of data) {
+                    transaction.update(this.tableName, d);
+                }
+                transaction.update(this.ctx.service.ledger.tableName, updateBills);
+                await transaction.commit();
+                updateBills.ledger_id = bills.ledger_id;
+                return {
+                    ledger: { update: [updateBills] },
+                    pos: data,
+                }
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async _deletePosData(tid, data) {
+            if (!data || data.length === 0) {
+                throw '提交数据错误';
+            }
+
+            const pos = await this.getPosData({tid: tid, id: data});
+            const bills = await this.ctx.service.ledger.getDataById(pos[0].lid);
+            const billsPos = await this.getAllDataByCondition({ where: {lid: bills.id} });
+            const updateBills = {id: bills.id};
+            for (const bp of billsPos) {
+                if (data.indexOf(bp.id) >= 0) continue;
+                updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, bp.sgfh_qty);
+                updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, bp.sjcl_qty);
+                updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, bp.qtcl_qty);
+                updateBills.quantity = this.ctx.helper.add(updateBills.quantity, bp.quantity);
+            }
+            const info = this.ctx.tender.info;
+            updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+            updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, {tid: tid, id: data});
+                await transaction.update(this.ctx.service.ledger.tableName, updateBills);
+                await transaction.commit();
+                updateBills.ledger_id = bills.ledger_id;
+                return { ledger: { update: [updateBills] }, pos: data };
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
         }
 
         /**
@@ -89,74 +269,18 @@ module.exports = app => {
          * @returns {Promise<{ledger: {}, pos: null}>}
          */
         async savePosData(data, tid) {
-            const result = { ledger: {}, pos: null };
-            const transaction = await this.db.beginTransaction();
-            try {
-                if (data.updateType === 'add') {
-                    const tender = await this.ctx.service.tender.getTender(tid);
+            switch (data.updateType) {
+                case 'add':
+                    return await this._addPosData(tid, data.updateData);
+                case 'update':
                     if (data.updateData instanceof Array) {
-                        for (const d of data.updateData) {
-                            this._insertPosData(transaction, d, tid);
-                        }
+                        return await this._updatePosDatas(tid, data.updateData);
                     } else {
-                        this._insertPosData(transaction, data.updateData, tid);
-                    }
-                } else if (data.updateType === 'update') {
-                    const datas = data.updateData instanceof Array ? data.updateData : [data.updateData];
-                    result.ledger.update = [];
-                    const orgPos = await this.getPosData({tid: tid, id: this._.map(datas, 'id')});
-                    for (const d of datas) {
-                        const op = this._.find(orgPos, function (p) { return p.id = d.id; });
-                        if (d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined) {
-                            const bills = await this.ctx.service.ledger.getDataById(op.lid);
-                            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
-                            if (d.sgfh_qty !== undefined) {
-                                d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
-                            } else if (op) {
-                                d.sgfh_qty = op.sgfh_qty;
-                            }
-                            if (d.sjcl_qty !== undefined) {
-                                d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
-                            } else if (op) {
-                                d.sjcl_qty = op.sjcl_qty;
-                            }
-                            if (d.qtcl_qty !== undefined) {
-                                d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
-                            } else if (op) {
-                                d.qtcl_qty = op.qtcl_qty;
-                            }
-                            d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
-                        }
-                        await transaction.update(this.tableName, d, {tid: tid, id: d.id});
-                        if (d.quantity !== undefined && op && (result.ledger.update.indexOf(op.lid) === -1)) {
-                            result.ledger.update.push(op.lid);
-                        }
-                    }
-                    for (const lid of result.ledger.update) {
-                        await this.ctx.service.ledger.calc(tid, lid, transaction);
-                    }
-                } else if (data.updateType === 'delete') {
-                    if (!data.updateData || data.updateData.length === 0) {
-                        throw '提交数据错误';
+                        return await this._updatePosData(tid, data.updateData);
                     }
-                    const pos = await this.getPosData({tid: tid, id: data.updateData});
-                    const ledgerIds = this._.map(pos, 'lid');
-                    await transaction.delete(this.tableName, {tid: tid, id: data.updateData});
-                    for (const lid of ledgerIds) {
-                        await this.ctx.service.ledger.calc(tid, lid, transaction);
-                    }
-                    result.ledger.update = ledgerIds;
-                } else {
-                    throw '提交数据错误';
-                }
-                await transaction.commit();
-            } catch (err) {
-                await transaction.rollback();
-                throw err;
+                case 'delete':
+                    return await this._deletePosData(tid, data.updateData);
             }
-            result.pos = data.updateData;
-            result.ledger.update = await this.ctx.service.ledger.getDataByIds(result.ledger.update);
-            return result;
         }
 
         /**
@@ -171,7 +295,7 @@ module.exports = app => {
             }
 
             const transaction = await this.db.beginTransaction();
-            const result = { ledger: {}, pos: null }, updateLid = [];
+            const result = { ledger: {}, pos: null }, updateBills = [];
             const orgPos = await this.getPosData({tid: tid, id: this._.map(data, 'id')});
             let bills = null, precision = null;
             try {
@@ -181,7 +305,7 @@ module.exports = app => {
                         if (!bills || bills.id !== d.lid) {
                             bills = await this.ctx.service.ledger.getDataById(d.lid);
                             precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
-                            updateLid.push(d.lid);
+                            updateBills.push(bills);
                         }
                         if (d.sgfh_qty !== undefined) {
                             d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
@@ -206,8 +330,8 @@ module.exports = app => {
                         this._insertPosData(transaction, d, tid);
                     }
                 }
-                for (const lid of updateLid) {
-                    await this.ctx.service.ledger.calc(tid, lid, transaction);
+                for (const ub of updateBills) {
+                    await this.ctx.service.ledger.calcNode(ub, transaction);
                 }
                 await transaction.commit();
             } catch (err) {
@@ -215,8 +339,8 @@ module.exports = app => {
                 throw err;
             }
             result.pos = data;
-            if (updateLid.length > 0) {
-                result.ledger.update = await this.ctx.service.ledger.getDataByIds(updateLid);
+            if (updateBills.length > 0) {
+                result.ledger.update = await this.ctx.service.ledger.getDataByIds(this._.map(updateBills, 'id'));
             }
             return result;
         }

+ 143 - 127
app/service/revise_pos.js

@@ -96,163 +96,179 @@ module.exports = app => {
             const addRst = await transaction.insert(this.tableName, data);
         }
 
+        async _completeInsertPosData(tid, rid, data) {
+            data.id = this.uuid.v4();
+            data.tid = tid;
+            // todo 新增期
+            data.add_stage = 0;
+            data.add_times = 0;
+            data.in_time = new Date();
+            data.add_user = this.ctx.session.sessionUser.accountId;
+            data.crid = rid;
+        }
+
         async addPos(tid, rid, data) {
-            const transaction = await this.db.beginTransaction();
-            try {
-                if (data instanceof Array) {
-                    for (const d of data) {
-                        this._insertPosData(transaction, d, tid, rid);
-                    }
-                } else {
-                    this._insertPosData(transaction, data, tid, rid);
+            if (data instanceof Array) {
+                for (const d of data) {
+                    this._completeInsertPosData(tid, rid, d);
                 }
-                await transaction.commit();
-            } catch (err) {
-                await transaction.rollback();
-                throw err;
+            } else {
+                this._completeInsertPosData(tid, rid, data);
             }
+            this.db.insert(this.tableName, data);
             return {pos: data};
         }
 
         async updatePos(tid, data) {
-            const billsIds = [];
-            const datas = data instanceof Array ? data : [data];
-            const orgPos = await this.getPosData({tid: tid, id: this._.map(datas, 'id')});
-            const transaction = await this.db.beginTransaction();
-            try {
-                for (const d of datas) {
-                    const op = this._.find(orgPos, function (p) { return p.id = d.id; });
-                    if (d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined) {
-                        const bills = await this.ctx.service.reviseBills.getDataById(op.lid);
-                        const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
-                        if (d.sgfh_qty !== undefined) {
-                            d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
-                        } else if (op) {
-                            d.sgfh_qty = op.sgfh_qty;
-                        }
-                        if (d.sjcl_qty !== undefined) {
-                            d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
-                        } else if (op) {
-                            d.sjcl_qty = op.sjcl_qty;
-                        }
-                        if (d.qtcl_qty !== undefined) {
-                            d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
-                        } else if (op) {
-                            d.qtcl_qty = op.qtcl_qty;
-                        }
-                        d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
+            if (data.sgfh_qty !== undefined || data.sjcl_qty !== undefined || data.qtcl_qty !== undefined) {
+                const op = await this.getDataById(data.id);
+                const bills = await this.ctx.service.ledger.getDataById(op.lid);
+                const billsPos = await this.getAllDataByCondition({where: {lid: op.lid} });
+
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+                if (data.sgfh_qty !== undefined) {
+                    data.sgfh_qty = this.round(data.sgfh_qty, precision.value);
+                } else if (op) {
+                    data.sgfh_qty = op.sgfh_qty;
+                }
+                if (data.sjcl_qty !== undefined) {
+                    data.sjcl_qty = this.round(data.sjcl_qty, precision.value);
+                } else if (op) {
+                    data.sjcl_qty = op.sjcl_qty;
+                }
+                if (data.qtcl_qty !== undefined) {
+                    data.qtcl_qty = this.round(data.qtcl_qty, precision.value);
+                } else if (op) {
+                    data.qtcl_qty = op.qtcl_qty;
+                }
+                data.quantity = this.ctx.helper.sum([data.sgfh_qty, data.qtcl_qty, data.sjcl_qty]);
+
+                const updateBills = {id: bills.id};
+                for (const bp of billsPos) {
+                    const calcData = bp.id === data.id ? data : bp;
+                    updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, calcData.sgfh_qty);
+                    updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, calcData.sjcl_qty);
+                    updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, calcData.qtcl_qty);
+                    updateBills.quantity = this.ctx.helper.add(updateBills.quantity, calcData.quantity);
+                }
+                const info = this.ctx.tender.info;
+                updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+                updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+
+                const transaction = await this.db.beginTransaction();
+                try {
+                    transaction.update(this.tableName, data);
+                    transaction.update(this.ctx.service.ledger.tableName, updateBills);
+                    await transaction.commit();
+                    updateBills.ledger_id = bills.ledger_id;
+                    return {
+                        ledger: { update: [updateBills] },
+                        pos: data,
                     }
-                    await transaction.update(this.tableName, d, {tid: tid, id: d.id});
-                    if (d.quantity !== undefined && op && (billsIds.indexOf(op.lid) === -1)) {
-                        billsIds.push(op.lid);
+                } catch(err) {
+                    await transaction.rollback();
+                    throw err;
+                }
+            } else {
+                await this.db.update(this.tableName, data, {tid: tid, id: data.id});
+                return {pos: data};
+            }
+        }
+
+        async updatePosArr(tid, data) {
+            if (data.length === 0) return;
+            const op = await this.getDataById(data[0].id);
+            const bills = await this.ctx.service.ledger.getDataById(op.lid);
+            const billsPos = await this.getAllDataByCondition({where: {lid: op.lid} });
+            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+            for (const d of data) {
+                if (d.sgfh_qty !== undefined || d.sjcl_qty !== undefined || d.qtcl_qty !== undefined) {
+                    if (d.sgfh_qty !== undefined) {
+                        d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
+                    } else if (op) {
+                        d.sgfh_qty = op.sgfh_qty;
+                    }
+                    if (d.sjcl_qty !== undefined) {
+                        d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
+                    } else if (op) {
+                        d.sjcl_qty = op.sjcl_qty;
                     }
+                    if (d.qtcl_qty !== undefined) {
+                        d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
+                    } else if (op) {
+                        d.qtcl_qty = op.qtcl_qty;
+                    }
+                    d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
                 }
-                for (const lid of billsIds) {
-                    await this.ctx.service.reviseBills.calc(tid, lid, transaction);
+            }
+            const updateBills = {id: bills.id};
+            for (const bp of billsPos) {
+                const newPos = data.find(function (x) { return x.id === bp.id });
+                const calcData = newPos ? newPos : bp;
+                updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, calcData.sgfh_qty);
+                updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, calcData.sjcl_qty);
+                updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, calcData.qtcl_qty);
+                updateBills.quantity = this.ctx.helper.add(updateBills.quantity, calcData.quantity);
+            }
+            const info = this.ctx.tender.info;
+            updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+            updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const d of data) {
+                    transaction.update(this.tableName, d);
                 }
+                transaction.update(this.ctx.service.ledger.tableName, updateBills);
                 await transaction.commit();
-            } catch (err) {
+                updateBills.ledger_id = bills.ledger_id;
+                return {
+                    ledger: { update: [updateBills] },
+                    pos: data,
+                }
+            } catch(err) {
                 await transaction.rollback();
                 throw err;
             }
-            const bills = await this.ctx.service.reviseBills.getDataById(billsIds);
-            return {pos: data, ledger: {update: bills}};
         }
 
         async deletePos(tid, data) {
             if (!data || data.length === 0) {
                 throw '提交数据错误';
             }
+
             const pos = await this.getPosData({tid: tid, id: data});
-            const ledgerIds = this._.map(pos, 'lid');
-            const transaction = await this.db.beginTransaction();
-            try {
-                await transaction.delete(this.tableName, {tid: tid, id: data});
-                for (const lid of ledgerIds) {
-                    await this.ctx.service.reviseBills.calc(tid, lid, transaction);
-                }
-                await transaction.commit();
-            } catch (err) {
-                await transaction.rollback();
-                throw err;
+            const bills = await this.ctx.service.ledger.getDataById(pos[0].lid);
+            const billsPos = await this.getAllDataByCondition({ where: {lid: bills.id} });
+            const updateBills = {id: bills.id};
+            for (const bp of billsPos) {
+                if (data.indexOf(bp.id) >= 0) continue;
+                updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, bp.sgfh_qty);
+                updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, bp.sjcl_qty);
+                updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, bp.qtcl_qty);
+                updateBills.quantity = this.ctx.helper.add(updateBills.quantity, bp.quantity);
             }
-            const bills = await this.ctx.service.reviseBills.getDataById(ledgerIds);
-            return {ledger: {update: bills}, pos: data};
-        }
+            const info = this.ctx.tender.info;
+            updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+            updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
 
-        /**
-         * 保存部位明细数据
-         * @param data
-         * @param {Number} tid - 标段id
-         * @returns {Promise<{ledger: {}, pos: null}>}
-         */
-        async savePosData(tid, rid, data) {
             const transaction = await this.db.beginTransaction();
             try {
-                if (data.posPostType === 'add') {
-                    if (data.updateData instanceof Array) {
-                        for (const d of data.postData) {
-                            this._insertPosData(transaction, d, tid, rid);
-                        }
-                    } else {
-                        this._insertPosData(transaction, data.postData, tid, rid);
-                    }
-                } else if (data.posPostType === 'update') {
-                    const datas = data.postData instanceof Array ? data.postData : [data.postData];
-                    result.ledger.update = [];
-                    const orgPos = await this.getPosData({tid: tid, id: this._.map(datas, 'id')});
-                    for (const d of datas) {
-                        const op = this._.find(orgPos, function (p) { return p.id = d.id; });
-                        if (d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined) {
-                            const bills = await this.ctx.service.reviseBills.getDataById(op.lid);
-                            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
-                            if (d.sgfh_qty !== undefined) {
-                                d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
-                            } else if (op) {
-                                d.sgfh_qty = op.sgfh_qty;
-                            }
-                            if (d.sjcl_qty !== undefined) {
-                                d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
-                            } else if (op) {
-                                d.sjcl_qty = op.sjcl_qty;
-                            }
-                            if (d.qtcl_qty !== undefined) {
-                                d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
-                            } else if (op) {
-                                d.qtcl_qty = op.qtcl_qty;
-                            }
-                            d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
-                        }
-                        await transaction.update(this.tableName, d, {tid: tid, id: d.id});
-                        if (d.quantity !== undefined && op && (result.ledger.update.indexOf(op.lid) === -1)) {
-                            result.ledger.update.push(op.lid);
-                        }
-                    }
-                    for (const lid of result.ledger.update) {
-                        await this.ctx.service.reviseBills.calc(tid, lid, transaction);
-                    }
-                } else if (data.posPostType === 'delete') {
-                    if (!data.postData || data.postData.length === 0) {
-                        throw '提交数据错误';
-                    }
-                    const pos = await this.getPosData({tid: tid, id: data.postData});
-                    const ledgerIds = this._.map(pos, 'lid');
-                    await transaction.delete(this.tableName, {tid: tid, id: data.postData});
-                    for (const lid of ledgerIds) {
-                        await this.ctx.service.reviseBills.calc(tid, lid, transaction);
-                    }
-                    result.ledger.update = ledgerIds;
-                } else {
-                    throw '提交数据错误';
-                }
+                await transaction.delete(this.tableName, {tid: tid, id: data});
+                await transaction.update(this.ctx.service.ledger.tableName, updateBills);
                 await transaction.commit();
-            } catch (err) {
+                updateBills.ledger_id = bills.ledger_id;
+                return { ledger: { update: [updateBills] }, pos: data };
+            } catch(err) {
                 await transaction.rollback();
                 throw err;
             }
-            result.pos = data.postData;
-            result.ledger.update = await this.ctx.service.reviseBills.getDataById(result.ledger.update);
-            return result;
         }
 
         /**

+ 7 - 2
app/service/stage.js

@@ -77,7 +77,6 @@ module.exports = app => {
          * @returns {Promise<void>}
          */
         async getValidStagesShort(tenderId) {
-            // console.log('tenderId: ' + tenderId);
             const sql = 'select * from zh_stage where tid = ? order by zh_stage.order';
             const sqlParam = [tenderId];
             return await this.db.query(sql, sqlParam);
@@ -96,7 +95,7 @@ module.exports = app => {
 
         async checkStageGatherData(stage) {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
-            if (stage.status !== auditConst.status.checked) {
+            if (stage.status !== auditConst.status.checked && stage.check_calc) {
                 const curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
                 const isActive = curAuditor ? curAuditor.id === this.ctx.session.sessionUser.accountId : stage.user_id === this.ctx.session.sessionUser.accountId;
                 stage.curTimes = stage.status === auditConst.status.checkNo ? stage.times - 1 : stage.times;
@@ -178,6 +177,7 @@ module.exports = app => {
                 status: auditConst.status.uncheck,
                 user_id: this.ctx.session.sessionUser.accountId,
                 check_detail: true,
+                check_calc: false,
             };
             if (preStage) {
                 newStage.im_type = preStage.im_type;
@@ -303,6 +303,11 @@ module.exports = app => {
             return result.affectedRows === 1;
         }
 
+        async updateCheckCalcFlag(sid, check) {
+            const result = await this.db.update(this.tableName, {id: sid, check_calc: check});
+            return result.affectedRows === 1;
+        }
+
         /**
          * 删除计量期
          *

+ 2 - 1
app/service/stage_audit.js

@@ -185,7 +185,7 @@ module.exports = app => {
             try {
                 await transaction.update(this.tableName, {id: audit.id, status: auditConst.status.checking, begin_time: new Date()});
                 // 计算原报最终数据
-                await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
+                const yfPay = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
                 // 复制一份下一审核人数据
                 await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, 1, transaction);
                 // 更新期数据
@@ -194,6 +194,7 @@ module.exports = app => {
                     id: stageId, status: auditConst.status.checking,
                     contract_tp: tpData.contract_tp,
                     qc_tp: tpData.qc_tp,
+                    yf_tp: yfPay.tp,
                 });
 
                 // 添加短信通知-需要审批提醒功能

+ 15 - 9
app/service/stage_pos.js

@@ -230,7 +230,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 for (const d of datas) {
-                    if (d.sgfh_qty || d.qtcl_qty || d.sjcl_qty || d.contract_qty || d.qc_qty) {
+                    if (d.sgfh_qty !== undefined || d.qtcl_qty !== undefined || d.sjcl_qty !== undefined || d.contract_qty !== undefined || d.qc_qty !== undefined) {
                         if (!bills || bills.id !== data.lid) {
                             bills = await this.ctx.service.ledger.getDataById(d.lid);
                             precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
@@ -275,13 +275,19 @@ module.exports = app => {
                                 said: this.ctx.session.sessionUser.accountId,
                                 times: this.ctx.stage.curTimes, order: this.ctx.stage.curOrder
                             };
-                            sp.contract_qty = d.contract_qty === undefined && osp
-                                ? osp.contract_qty
-                                : this.ctx.helper.round(d.contract_qty, precision.value);
-                            sp.qc_qty = d.qc_qty === undefined && osp
-                                ? osp.qc_qty
-                                : this.ctx.helper.round(d.qc_qty, precision.value);
-                            sp.postil = d.postil === undefined && osp ? osp.postil : d.postil;
+                            if (d.contract_qty !== undefined || osp) {
+                                sp.contract_qty = d.contract_qty === undefined && osp
+                                    ? osp.contract_qty
+                                    : this.ctx.helper.round(d.contract_qty, precision.value);
+                            }
+                            if (d.qc_qty || osp) {
+                                sp.qc_qty = d.qc_qty === undefined && osp
+                                    ? osp.qc_qty
+                                    : this.ctx.helper.round(d.qc_qty, precision.value);
+                            }
+                            if (d.postil || osp) {
+                                sp.postil = d.postil === undefined && osp ? osp.postil : d.postil;
+                            }
                             await transaction.insert(this.tableName, sp);
                         }
                     }
@@ -386,7 +392,7 @@ module.exports = app => {
                     if (refreshData.isDeletePos) {
                         result.pos.pos = refreshData.pos;
                     } else if (refreshData.pos.length > 0) {
-                        result.pos.pos = await this.ctx.service.pos.getPosData({id: refreshData.pos});
+                        result.pos.pos = await this.ctx.service.pos.getPosDataWithAddStageOrder({id: refreshData.pos});
                         if (refreshData.stageUpdate) {
                             result.pos.curStageData = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, {pid: refreshData.pos});
                         }

+ 3 - 0
app/view/layout/layout.ejs

@@ -37,6 +37,9 @@
     <i class="icon fa"></i>
     <span class="message"></span>
 </div>
+<!-- 遮罩层 -->
+<div id="hide-all" style="display:none;position: absolute;width: 100%;height: 100%;top:0;left: 0;z-index:10000000">
+</div>
 <%- modal %>
 <script type="text/javascript">
     toastr.options = {

+ 11 - 1
app/view/ledger/explode.ejs

@@ -36,7 +36,7 @@
                         <div class="input-group-prepend">
                             <span class="input-group-text" id="basic-addon1">表达式</span>
                         </div>
-                        <input type="text" class="form-control m-0">
+                        <input type="text" class="form-control m-0" id="bills-expr" readonly="">
                     </div>
                 </div>
             </div>
@@ -76,6 +76,16 @@
                             </li>
                             <li class="nav-item" id="pos-search">
                             </li>
+                            <li class="nav-item">
+                                <div class="d-inline-block">
+                                    <div class="input-group input-group-sm ml-2 mt-1">
+                                        <div class="input-group-prepend">
+                                            <span class="input-group-text" id="basic-addon1">表达式</span>
+                                        </div>
+                                        <input type="text" class="form-control m-0" id="pos-expr" readonly="">
+                                    </div>
+                                </div>
+                            </li>
                         </ul>
                     </div>
                     <div class="sp-wrap" id="pos-spread">

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

@@ -198,7 +198,7 @@
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
-                <input type="hidden" name="checkType" value="<%= auditConst.status.checked %>" />
+                <input type="hidden" name="checkType" value="<%= auditConst.status.checkNo %>" />
                 <button type="submit" class="btn btn-warning" >确认退回</button>
             </div>
         </form>

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

@@ -52,7 +52,7 @@
                     <% } else if (m.status === auditConst.status.checking && m.curAuditor && m.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
                         <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                     <% } else { %>
-                        <span class="<%- auditConst.auditStringClass[m.status] %>"><%- auditConst.auditString[m.status] %></span>
+                        <span class="<%- auditConst.auditProgressClass[m.status] %>"><%- auditConst.auditProgress[m.status] %></span>
                     <% } %>
                     <% if (m.user_id === ctx.session.sessionUser.accountId && m.order === materials.length) { %>
                         <a href="#del-qi" class="btn btn-outline-danger btn-sm ml-1" data-toggle="modal" data-target="#del-qi">删除</a>

+ 11 - 2
app/view/material/info.ejs

@@ -33,14 +33,22 @@
                             <div class="input-group-prepend">
                                 <span class="input-group-text" id="basic-addon1">增税税率</span>
                             </div>
-                            <select class="form-control col-1"><option>9%</option><option>10%</option><option>11%</option></select>
+                            <select class="form-control col-1" id="changeRate">
+                                <% if (ctx.material.readOnly) { %>
+                                <option value="9" <% if(ctx.material.rate === 9) { %>selected<% } %>>9%</option>
+                                <option value="10" <% if(ctx.material.rate === 10) { %>selected<% } %>>10%</option>
+                                <option value="11" <% if(ctx.material.rate === 11) { %>selected<% } %>>11%</option>
+                                <% } else { %>
+                                <option value="<%= ctx.material.rate %>" selected><%= ctx.material.rate %>%</option>
+                                <% } %>
+                            </select>
                         </div>
                     </div>
                     <div class="sp-wrap">
                         <div class="col-4 p-0">
                             <table class="table table-sm table-bordered">
                                 <tr><th></th><th>本期金额</th><th>截止本期金额</th></tr>
-                                <tr><td>材料价差费用</td><td></td><td></td></tr>
+                                <tr><td>材料价差费用</td><td><%= ctx.material.m_tp %></td><td><%= ctx.material.pre_tp %></td></tr>
                                 <tr><td>材料价差费用(含税)</td><td></td><td></td></tr>
                             </table>
                         </div>
@@ -60,4 +68,5 @@
     const materialBillsData = JSON.parse('<%- JSON.stringify(materialBillsData) %>');
     const materialListData = JSON.parse('<%- JSON.stringify(materialListData) %>');
     const readOnly = <%- material.readOnly %>;
+    const materialID = <%- material.id %>;
 </script>

+ 3 - 2
app/view/material/list.ejs

@@ -10,8 +10,8 @@
                 <div class="d-inline-block ml-2">
                     <a class="btn btn-sm btn-light">
                         <div class="custom-control custom-checkbox">
-                            <input type="checkbox" class="custom-control-input" id="customCheckDisabled">
-                            <label class="custom-control-label text-primary" for="customCheckDisabled">显示有调差工料清单</label>
+                            <input type="checkbox" class="custom-control-input" id="show_material_gcl">
+                            <label class="custom-control-label text-primary" for="show_material_gcl">显示有调差工料清单</label>
                         </div>
                     </a>
                 </div>
@@ -76,4 +76,5 @@
     const pos = JSON.parse('<%- JSON.stringify(pos) %>');
     const curPosData = JSON.parse('<%- JSON.stringify(curPosData) %>');
     const readOnly = <%- material.readOnly %>;
+    const materialID = <%- material.id %>;
 </script>

+ 2 - 0
app/view/material/list_modal.ejs

@@ -20,6 +20,7 @@
                         </thead>
                         <tbody id="materialBills">
                         <% for (const [index,m] of materialBillsData.entries()) { %>
+                        <% if (m.code !== null && m.code !== '') { %>
                         <tr class="table-secondary">
                             <td><%= index+1 %></td>
                             <td><input type="checkbox" checked disabled value="<%= m.id %>"></td>
@@ -29,6 +30,7 @@
                             <td><%= m.spec %></td>
                         </tr>
                         <% } %>
+                        <% } %>
                         </tbody>
                     </table>
                 </div>

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

@@ -49,7 +49,7 @@
                 <h5>删除后,数据无法恢复,请谨慎操作。</h5>
             </div>
             <div class="modal-footer">
-                <input type="hidden" name="stage_id" value="<%= materials[0].id %>">
+                <input type="hidden" name="material_id" value="<%= materials[0].id %>">
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
                 <button type="submit" class="btn btn-danger">确定删除</button>

+ 1 - 1
app/view/measure/gather.ejs

@@ -41,4 +41,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 0 - 3
app/view/revise/history.ejs

@@ -59,9 +59,6 @@
                 <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
                 <div class="tab-content">
                     <div id="xd-content" class="tab-pane">
-                        <div class="sjs-bar-2">
-                            <div class="d-flex"><a href="javascirpt: void(0);" class="btn btn-sm btn-outline-success mb-1 ml-auto" id="save">保存</a></div>
-                        </div>
                         <div class="sjs-sh-2" style="overflow:auto">
                             <div class="form-group mt-2">
                                 <label >创建时间</label>

+ 13 - 1
app/view/revise/info.ejs

@@ -38,7 +38,7 @@
                         <div class="input-group-prepend">
                             <span class="input-group-text" id="basic-addon1">表达式</span>
                         </div>
-                        <input type="text" class="form-control m-0">
+                        <input type="text" class="form-control m-0" id="bills-expr" readonly="">
                     </div>
                 </div>
             </div>
@@ -84,6 +84,16 @@
                             </li>
                             <li class="nav-item" id="pos-search">
                             </li>
+                            <li class="nav-item">
+                                <div class="d-inline-block">
+                                    <div class="input-group input-group-sm ml-2 mt-1">
+                                        <div class="input-group-prepend">
+                                            <span class="input-group-text" id="basic-addon1">表达式</span>
+                                        </div>
+                                        <input type="text" class="form-control m-0" id="pos-expr" readonly="">
+                                    </div>
+                                </div>
+                            </li>
                         </ul>
                     </div>
                     <div class="sp-wrap" id="pos-spread">
@@ -95,9 +105,11 @@
                 <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
                 <div class="tab-content">
                     <div id="xd-content" class="tab-pane">
+                        <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && !readOnly) { %>
                         <div class="sjs-bar-2">
                             <div class="d-flex"><a href="javascirpt: void(0);" class="btn btn-sm btn-outline-success mb-1 ml-auto" id="save">保存</a></div>
                         </div>
+                        <% } %>
                         <div class="sjs-sh-2" style="overflow:auto">
                             <div class="form-group mt-2">
                                 <label >创建时间</label>

+ 7 - 1
app/view/revise/info_modal.ejs

@@ -477,9 +477,15 @@
 
     // 检查上报情况
     function checkAuditorFrom () {
+        const content = $('textarea').val();
+        if (!content || content === '') {
+            toastr.error('请先填写修订内容,再上报数据');
+            return false;
+        }
         if ($('#auditors li').length === 0) {
             toastr.error('请先选择审批人,再上报数据');
             return false;
         }
+        $('#hide-all').show();
     }
-</script>
+</script>

+ 2 - 2
app/view/stage/detail.ejs

@@ -45,7 +45,7 @@
                         <div class="sjs-sh-1" style="overflow: auto;">
                             <% if (!stage.readOnly) { %>
                             <div class="d-flex justify-content-end mt-1 mr-1">
-                                <a href="javascript: void(0);" class="btn btn-sm btn-outline-primary" id="edit-detail">编辑</a>
+                                <a href="javascript: void(0);" class="btn btn-sm btn-outline-primary" id="edit-detail" style="display: none;">编辑</a>
                                 <a href="javascript: void(0);" class="btn btn-sm btn-outline-success mr-1" id="save-detail" style="display: none;">保存</a>
                                 <a href="javascript: void(0);" class="btn btn-sm btn-outline-secondary" id="cancel-detail" style="display: none;">取消</a>
                             </div>
@@ -82,7 +82,7 @@
                                 <div class="d-flex justify-content-between my-3">
                                     <label>计算草图:</label>
                                     <% if (!stage.readOnly) { %>
-                                    <a href="#edit-img" data-toggle="modal" data-target="#edit-img" id="modify-img">添加草图</a>
+                                    <a href="#edit-img" data-toggle="modal" data-target="#edit-img" id="modify-img" style="display: none;">添加草图</a>
                                     <% } %>
                                 </div>
                                 <p id="calc-img"><img src="" class="d-100" width="100%"></p>

+ 3 - 3
app/view/stage/gather.ejs

@@ -55,9 +55,9 @@
                         <li class="nav-item">
                             <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">所属项目节</a>
                         </li>
-                        <li class="nav-item">
-                            <a class="nav-link " data-toggle="tab" href="#biangeng" role="tab">相关变更令</a>
-                        </li>
+                        <!--<li class="nav-item">-->
+                            <!--<a class="nav-link " data-toggle="tab" href="#biangeng" role="tab">相关变更令</a>-->
+                        <!--</li>-->
                     </ul>
                 </div>
                 <div class="tab-content">

+ 12 - 1
app/view/stage/index.ejs

@@ -29,7 +29,7 @@
                         <div class="input-group-prepend">
                             <span class="input-group-text" id="basic-addon1">表达式</span>
                         </div>
-                        <input type="text" class="form-control m-0" <% if (stage.readOnly) { %> readonly="" <% } %>>
+                        <input type="text" class="form-control m-0" id="bills-expr" readonly="">
                     </div>
                 </div>
             </div>
@@ -83,6 +83,17 @@
                                     </div>
                                 </div>
                             </li>
+
+                            <li class="nav-item">
+                                <div class="d-inline-block">
+                                    <div class="input-group input-group-sm ml-2 mt-1">
+                                        <div class="input-group-prepend">
+                                            <span class="input-group-text" id="basic-addon1">表达式</span>
+                                        </div>
+                                        <input type="text" class="form-control m-0" id="pos-expr" readonly="">
+                                    </div>
+                                </div>
+                            </li>
                         </ul>
                     </div>
                     <div class="sp-wrap" id="stage-pos">

+ 12 - 6
app/view/tender/detail.ejs

@@ -75,22 +75,28 @@
             <!--进度条-->
             <div class="mb-5">
                 <div class="progress">
+                    <% if (tender.pre_ratio > 0) { %>
                     <div class="progress-bar bg-success" style="width: <%- tender.pre_ratio %>%;" data-placement="bottom" data-toggle="tooltip" data-original-title="截止上期累计完成:¥<%- ctx.helper.formatMoney(tender.end_gather_tp) %>"><%- tender.pre_ratio %>%</div>
+                    <% } %>
+                    <% if (tender.cur_ratio > 0) { %>
                     <div class="progress-bar bg-info" style="width: <%- tender.cur_ratio %>%;" data-placement="bottom" data-toggle="tooltip" data-original-title="本期完成:¥<%- ctx.helper.formatMoney(tender.gather_tp) %>"><%- tender.cur_ratio %>%</div>
+                    <% } %>
+                    <% if (tender.other_ratio > 0) { %>
                     <div class="progress-bar bg-gray" style="width: <%- tender.other_ratio %>%;" data-placement="bottom" data-toggle="tooltip" data-original-title="未完成:¥<%- ctx.helper.formatMoney(tender.other_tp) %>"><%- tender.other_ratio %>%</div>
+                    <% } %>
                 </div>
             </div>
             <ul class="nav nav-tabs">
+                <!--<li class="nav-item">-->
+                    <!--<a class="nav-link active" data-toggle="tab" href="#tubiao" role="tab">进度表</a>-->
+                <!--</li>-->
                 <li class="nav-item">
-                    <a class="nav-link active" data-toggle="tab" href="#tubiao" role="tab">进度表</a>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="tab" href="#shuxing" role="tab">标段属性</a>
+                    <a class="nav-link active" data-toggle="tab" href="#shuxing" role="tab">标段属性</a>
                 </li>
             </ul>
             <div class="tab-content">
                 <!--进度表-->
-                <div id="tubiao" class="tab-pane active">
+                <div id="tubiao" class="tab-pane">
                     <!--月进度图表-->
                     <div id="chartContainer3" style="height: 300px; width: 100%;" class="my-5">
                     </div>
@@ -99,7 +105,7 @@
                     </div>
                 </div>
                 <!--标段属性-->
-                <div id="shuxing" class="tab-pane">
+                <div id="shuxing" class="tab-pane active">
                     <div class="row my-3">
                         <div class="col-2">
                             <div class="nav flex-column nav-pills" >

+ 3 - 3
app/view/tender/sub_menu.ejs

@@ -1,9 +1,9 @@
 <div class="panel-title fluid">
     <div class="title-main  d-flex justify-content-between">
         <div>
-            <div class="d-inline-block mr-2">
-                <button href="#cate-set" class="btn btn-sm btn-light" data-toggle="modal" data-target="#cate-set"><i class="fa fa-sitemap fa-rotate-270"></i> 分类</button>
-            </div>
+            <!--<div class="d-inline-block mr-2">-->
+                <!--<button href="#cate-set" class="btn btn-sm btn-light" data-toggle="modal" data-target="#cate-set"><i class="fa fa-sitemap fa-rotate-270"></i> 分类</button>-->
+            <!--</div>-->
             <div class="d-inline-block">
                 <div class="btn-group btn-group-toggle" data-toggle="buttons">
                     <label class="btn btn-sm btn-light <% if (ctx.url === '/list') { %>active<% } %>" onclick="window.location.href='/list'">

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

@@ -17,7 +17,7 @@
                 <% if (ctx.tender.data.ledger_status !== ctx.tender.auditLedgerConst.status.uncheck) { %>
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/ledger/audit') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/ledger/audit"><span>台帐审批</span></a></li>
                 <% } %>
-                <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/revise') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/revise"><span>台帐修订</span></a></li>
+                <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/revise') >= 0) { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/revise"><span>台帐修订</span></a></li>
             </ul>
         </div>
         <div class="nav-box">

+ 5 - 5
config/config.default.js

@@ -115,7 +115,7 @@ module.exports = appInfo => {
     // 是否压缩替换前端js
     config.min = true;
 
-    config.version = '1.0.0';
+    config.version = '1.0.1';
 
     // 压缩设置
     config.gzip = {
@@ -127,17 +127,17 @@ module.exports = appInfo => {
     config.customLogger = {
         // 操作失败日志
         fail: {
-            file: path.join(appInfo.root, 'logs/fail.log'),
+            file: path.join(appInfo.root, 'logs/' + appInfo.name + '-' + config.version + '/fail.log'),
         },
         // 以下为业务日志
         ledger: {
-            file: path.join(appInfo.root, 'logs/ledger.log'),
+            file: path.join(appInfo.root, 'logs/' + appInfo.name + '-' + config.version + '/ledger.log'),
         },
         stage: {
-            file: path.join(appInfo.root, 'logs/stage.log'),
+            file: path.join(appInfo.root, 'logs/' + appInfo.name + '-' + config.version + 'stage.log'),
         },
         mixed: {
-            file: path.join(appInfo.root, 'logs/mixed.log'),
+            file: path.join(appInfo.root, 'logs/' + appInfo.name + '-' + config.version + 'mixed.log'),
         }
     };
 

+ 6 - 6
config/menu.js

@@ -261,12 +261,12 @@ const settingMenu = {
         url: '/setting/user',
         caption: '账号设置',
     },
-    category: {
-        name: '标段自定义类别',
-        display: true,
-        url: '/setting/category',
-        caption: '标段自定义类别',
-    },
+    // category: {
+    //     name: '标段自定义类别',
+    //     display: true,
+    //     url: '/setting/category',
+    //     caption: '标段自定义类别',
+    // },
 };
 
 const profileMenu = {

+ 3 - 0
config/web.js

@@ -110,6 +110,7 @@ const JsFiles = {
                     "/public/js/js-xlsx/xlsx.utils.js",
                     "/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/math.min.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -161,6 +162,7 @@ const JsFiles = {
                 files: [
                     "/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/math.min.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -196,6 +198,7 @@ const JsFiles = {
                 files: [
                     "/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/math.min.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",