Преглед изворни кода

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

Tony Kang пре 1 месец
родитељ
комит
bde49cdcdc
63 измењених фајлова са 3116 додато и 344 уклоњено
  1. 9 9
      app/const/audit.js
  2. 1 1
      app/const/financial.js
  3. 1 1
      app/const/sp_page_show.js
  4. 11 1
      app/controller/change_controller.js
  5. 1 1
      app/controller/datacollect_controller.js
  6. 55 2
      app/controller/file_controller.js
  7. 116 17
      app/controller/financial_controller.js
  8. 9 0
      app/controller/profile_controller.js
  9. 93 1
      app/controller/setting_controller.js
  10. 30 4
      app/controller/stage_controller.js
  11. 1 1
      app/controller/sub_proj_setting_controller.js
  12. 10 5
      app/controller/tender_controller.js
  13. 17 2
      app/lib/bills_pos_convert.js
  14. 142 3
      app/lib/ledger.js
  15. 22 3
      app/lib/sum_load.js
  16. 497 147
      app/public/js/change_revise.js
  17. 109 7
      app/public/js/file_detail.js
  18. 11 0
      app/public/js/financial_pay.js
  19. 6 0
      app/public/js/global.js
  20. 150 2
      app/public/js/ledger_check.js
  21. 15 0
      app/public/js/profile.js
  22. 54 50
      app/public/js/revise.js
  23. 299 0
      app/public/js/setting_s2b.js
  24. 72 0
      app/public/js/setting_tender.js
  25. 2 1
      app/public/js/shares/cs_tools.js
  26. 14 6
      app/public/js/stage.js
  27. 55 4
      app/public/js/stage_im.js
  28. 7 0
      app/router.js
  29. 16 2
      app/service/financial_pay.js
  30. 63 0
      app/service/financial_pay_audit.js
  31. 30 2
      app/service/financial_pay_tender_audit.js
  32. 8 2
      app/service/financial_transfer.js
  33. 8 2
      app/service/financial_transfer_tender.js
  34. 9 0
      app/service/ledger_extra.js
  35. 126 0
      app/service/multi_limit.js
  36. 26 1
      app/service/shenpi_audit.js
  37. 47 13
      app/service/stage_bills.js
  38. 22 6
      app/service/stage_change.js
  39. 8 8
      app/service/stage_pos.js
  40. 43 8
      app/service/stage_stash.js
  41. 3 1
      app/service/tender.js
  42. 101 9
      app/service/tender_info.js
  43. 1 1
      app/view/file/file.ejs
  44. 4 2
      app/view/file/file_modal.ejs
  45. 5 0
      app/view/financial/pay.ejs
  46. 4 0
      app/view/financial/pay_detail.ejs
  47. 159 0
      app/view/financial/pay_detail_modal.ejs
  48. 48 2
      app/view/financial/pay_modal.ejs
  49. 2 2
      app/view/financial/sub_menu_list.ejs
  50. 3 1
      app/view/financial/transfer.ejs
  51. 2 0
      app/view/financial/transfer_tender.ejs
  52. 17 0
      app/view/profile/modal.ejs
  53. 12 11
      app/view/profile/sms.ejs
  54. 2 2
      app/view/profile/wechat.ejs
  55. 124 0
      app/view/setting/limit.ejs
  56. 61 0
      app/view/setting/s2b.ejs
  57. 74 0
      app/view/setting/s2b_modal.ejs
  58. 17 0
      app/view/setting/tender.ejs
  59. 3 1
      app/view/stage/bwtz.ejs
  60. 1 0
      app/view/stage/index.ejs
  61. 16 0
      app/view/tender/detail_modal.ejs
  62. 18 0
      config/web.js
  63. 224 0
      sql/update.sql

+ 9 - 9
app/const/audit.js

@@ -1269,7 +1269,7 @@ const financial = (function() {
         // checkNo: 4,     // 审批终止
         checkNo: 5, // 退回到原报人重新上报
         // checkNoPre: 6, // 退回到上一个审批人
-        // checkAgain: 7, // 重新审批
+        checkAgain: 7, // 重新审批
         checkSkip: 8, // 跳过
         // revise: 9, // 修订变更
         // cancelRevise: 10, // 撤销修订
@@ -1281,7 +1281,7 @@ const financial = (function() {
     statusString[status.checked] = '审批通过';
     statusString[status.checkNo] = '审批退回';
     // statusString[status.checkNoPre] = '审批退回';
-    // statusString[status.checkAgain] = '重新审批';
+    statusString[status.checkAgain] = '重新审批';
     // statusString[status.revise] = '修订';
     // statusString[status.cancelRevise] = '撤销修订';
     // statusString[status.checkCancel] = '撤回';
@@ -1292,7 +1292,7 @@ const financial = (function() {
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-warning';
     // statusClass[status.checkNoPre] = 'text-warning';
-    // statusClass[status.checkAgain] = 'text-warning';
+    statusClass[status.checkAgain] = 'text-warning';
     // statusClass[status.revise] = 'text-warning';
     // statusClass[status.cancelRevise] = 'text-success';
     // statusClass[status.checkCancel] = 'text-warning';
@@ -1305,7 +1305,7 @@ const financial = (function() {
     auditString[status.checked] = '审批通过';
     auditString[status.checkNo] = '审批退回';
     // auditString[status.checkNoPre] = '审批退回';
-    // auditString[status.checkAgain] = '重新审批';
+    auditString[status.checkAgain] = '重新审批';
     // auditString[status.revise] = '修订';
     // auditString[status.cancelRevise] = '撤销修订';
     // auditString[status.checkCancel] = '撤回';
@@ -1317,7 +1317,7 @@ const financial = (function() {
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
     // auditStringClass[status.checkNoPre] = 'text-warning';
-    // auditStringClass[status.checkAgain] = 'text-warning';
+    auditStringClass[status.checkAgain] = 'text-warning';
     // auditStringClass[status.revise] = 'text-warning';
     // auditStringClass[status.cancelRevise] = 'text-success';
     // auditStringClass[status.checkCancel] = 'text-warning';
@@ -1328,8 +1328,8 @@ const financial = (function() {
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '审批退回';
-    auditProgress[status.checkNoPre] = '审批退回';
-    // auditProgress[status.checkAgain] = '重新审批';
+    // auditProgress[status.checkNoPre] = '审批退回';
+    auditProgress[status.checkAgain] = '重新审批';
     // auditProgress[status.revise] = '修订中';
     // auditProgress[status.cancelRevise] = '撤销修订';
     // auditProgress[status.checkCancel] = '撤回';
@@ -1340,8 +1340,8 @@ const financial = (function() {
     auditProgressClass[status.checking] = 'text-warning';
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checkNo] = 'text-warning';
-    auditProgressClass[status.checkNoPre] = 'text-warning';
-    // auditProgressClass[status.checkAgain] = 'text-warning';
+    // auditProgressClass[status.checkNoPre] = 'text-warning';
+    auditProgressClass[status.checkAgain] = 'text-warning';
     // auditProgressClass[status.revise] = 'text-warning';
     // auditProgressClass[status.cancelRevise] = 'text-success';
     // auditProgressClass[status.checkCancel] = 'text-warning';

+ 1 - 1
app/const/financial.js

@@ -7,7 +7,7 @@
  * @date 2019/10/20
  * @version
  */
-const used = ['劳务工工资', '材料款', '机械设备', '劳务结算', '安全经费', '技术服务费', '备用金', '报销款', '电费', '水费', '税费', '零星材料', '内部往来款', '征迁款', '职工工资', '职工社保', '其它'];
+const used = ['工程款', '劳务工工资', '材料款', '机械设备', '劳务结算', '安全经费', '技术服务费', '备用金', '报销款', '电费', '水费', '税费', '零星材料', '内部往来款', '征迁款', '职工工资', '职工社保', '其它'];
 const pay_type = ['网上转账', '支付宝', '微信', '现金', '发票', '其他'];
 // 类型
 // const type = {

+ 1 - 1
app/const/sp_page_show.js

@@ -98,7 +98,7 @@ const defaultSetting = {
     openStageAudit: 0,
     stageAuditEarly: 3,
     stageAuditWorry: 8,
-    openMaterialChecklist: 0,
+    openMaterialChecklist: 1,
     openMaterialSelf: 0,
     openMaterialEditForAudit: 0,
     openChangeProject: 0,

+ 11 - 1
app/controller/change_controller.js

@@ -635,7 +635,17 @@ module.exports = app => {
                         await ctx.service.changeAuditList.save(data.updateData);
                         break;
                     case 'paste':
-                        await ctx.service.changeAuditList.saveDatas(data.updateData);
+                        if (ctx.change.status !== audit.change.status.uncheck && ctx.change.status !== audit.change.status.checkNo && ctx.change.status !== audit.change.status.revise) {
+                            throw '该变更令正在审批中或已完成,无法操作清单数据';
+                        }
+                        if (data.insertData && data.insertData.length > 0) {
+                            const fun_set2 = this.ctx.subProject.fun_set;
+                            const stateInfo2 = ctx.helper._.find(fun_set2.change_state, { order: ctx.change.state });
+                            await ctx.service.changeAuditList.adds(data.insertData, stateInfo2.value);
+                        }
+                        if (data.updateData && data.updateData.length > 0) {
+                            await ctx.service.changeAuditList.saveDatas(data.updateData);
+                        }
                         // 取所有工料表
                         responseData.data = await ctx.service.changeAuditList.getList(ctx.change.cid);
                         break;

+ 1 - 1
app/controller/datacollect_controller.js

@@ -67,7 +67,7 @@ module.exports = app => {
                 if (ctx.params.index) {
                     ctx.subProject.data_collect = parseInt(ctx.params.index);
                 }
-                const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103', 'KLG25'].indexOf(ctx.session.sessionProject.code) !== -1
+                const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103', 'KLG25', 'I2U5B', 'SEN48'].indexOf(ctx.session.sessionProject.code) !== -1
                     && ctx.subProject.data_collect_pages.includes('6') && ctx.subProject.data_collect === 6;
                 const renderData = {
                     projectData,

+ 55 - 2
app/controller/file_controller.js

@@ -320,6 +320,10 @@ module.exports = app => {
                         if (type) a.type_str = type.name;
                     });
                     r.stage = await ctx.service.stage.getAllDataByCondition({ columns: ['id', 'order'], where: { tid: r.id, status: auditConst.stage.status.checked } });
+                    r.change = await ctx.service.change.getAllDataByCondition({ columns: ['cid', 'code'], where: { tid: r.id, status: auditConst.flow.status.checked } });
+                    r.change_apply = await ctx.service.changeApply.getAllDataByCondition({ columns: ['id', 'code'], where: { tid: r.id, status: auditConst.flow.status.checked } });
+                    r.change_plan = await ctx.service.changePlan.getAllDataByCondition({ columns: ['id', 'code'], where: { tid: r.id, status: auditConst.flow.status.checked } });
+                    r.change_project = await ctx.service.changeProject.getAllDataByCondition({ columns: ['id', 'code'], where: { tid: r.id, status: auditConst.flow.status.checked } });
                 }
                 ctx.body = {err: 0, msg: '', data: result };
             } catch (error) {
@@ -332,11 +336,28 @@ module.exports = app => {
             return await this.ctx.service.ledgerAtt.getAllDataByCondition({ where: { tid: data.tender_id }, order: [['id', 'desc']]});
         }
         async _loadStageAtt(data) {
-            if (!data.tender_id || !data.stage) throw '参数错误';
+            if (!data.tender_id || !data.stage || !data.sub_type) throw '参数错误';
+            const stage = await this.ctx.service.stage.getDataById(data.stage);
             switch (data.sub_type) {
                 case 'att':
-                    const stage = await this.ctx.service.stage.getDataById(data.stage);
                     return await this.ctx.service.stageAtt.getAllDataByCondition({ where: { tid: data.tender_id, sid: stage.order }, order: [['id', 'desc']]});
+                case 'dealPay':
+                    const dpFiles = [];
+                    await this.ctx.service.stage.doCheckStage(stage, this.ctx.session.sessionUser.is_admin);
+                    const stagePays = await this.ctx.service.stagePay.getAllDataByCondition({ where: { sid: stage.id, stimes: stage.curTimes, sorder: stage.curOrder } });
+                    stagePays.forEach(x => {
+                        x.attachment = x.attachment ? JSON.parse(x.attachment) : [];
+                        if (x.attachment.length > 0) imFiles.push(...x.attachment);
+                    });
+                    return dpFiles;
+                case 'stageIm':
+                    const imFiles = [];
+                    const stageIm = await this.ctx.service.stageDetailAtt.getAllDataByCondition({ where: { sid: stage.id} });
+                    stageIm.forEach(x => {
+                        x.attachment = x.attachment ? JSON.parse(x.attachment) : [];
+                        if (x.attachment.length > 0) imFiles.push(...x.attachment);
+                    });
+                    return imFiles;
             }
         }
         async _loadAdvanceAtt(data) {
@@ -350,6 +371,26 @@ module.exports = app => {
             });
             return result;
         }
+        async _loadChangeAtt(data) {
+            if (!data.selectId) throw '参数错误';
+            const result = await this.ctx.service.changeAtt.getAllDataByCondition({ where: { cid: data.selectId }, order: [['id', 'desc']]});
+            return result;
+        }
+        async _loadChangePlanAtt(data) {
+            if (!data.selectId) throw '参数错误';
+            const result = await this.ctx.service.changeAtt.getAllDataByCondition({ where: { cpid: data.selectId }, order: [['id', 'desc']]});
+            return result;
+        }
+        async _loadChangeProjectAtt(data) {
+            if (!data.selectId) throw '参数错误';
+            const result = await this.ctx.service.changeAtt.getAllDataByCondition({ where: { cpid: data.selectId }, order: [['id', 'desc']]});
+            return result;
+        }
+        async _loadChangeApplyAtt(data) {
+            if (!data.selectId) throw '参数错误';
+            const result = await this.ctx.service.changeAtt.getAllDataByCondition({ where: { caid: data.selectId }, order: [['id', 'desc']]});
+            return result;
+        }
         async loadRelaFiles(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
@@ -366,6 +407,18 @@ module.exports = app => {
                     case 'advance':
                         files = await this._loadAdvanceAtt(data);
                         break;
+                    case 'change':
+                        files = await this._loadChangeAtt(data);
+                        break;
+                    case 'change_plan':
+                        files = await this._loadChangePlanAtt(data);
+                        break;
+                    case 'change_project':
+                        files = await this._loadChangeProjectAtt(data);
+                        break;
+                    case 'change_apply':
+                        files = await this._loadChangeApplyAtt(data);
+                        break;
                     default: throw '未知文件类型';
                 }
                 ctx.body = {err: 0, msg: '', data: files };

+ 116 - 17
app/controller/financial_controller.js

@@ -126,12 +126,25 @@ module.exports = app => {
                     throw '没有查看权限';
                 }
                 const transferList = await ctx.service.financialTransfer.getList(ctx.subProject.id);
+                const total = await ctx.service.financialTransfer.count({ spid: ctx.subProject.id });
+                // 分页相关
+                const page = ctx.page;
+                const pageSize = ctx.pageSize;
+                const pageInfo = {
+                    page,
+                    pageSizeSelect: 1,
+                    pageSize,
+                    total_num: total,
+                    total: Math.ceil(total / pageSize),
+                    queryData: JSON.stringify(ctx.urlInfo.query),
+                };
                 const renderData = {
                     financialPermission,
                     transferList,
                     moment,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.transfer),
                     whiteList: ctx.app.config.multipart.whitelist,
+                    pageInfo,
                 };
                 await this.layout('financial/transfer.ejs', renderData, 'financial/transfer_modal.ejs');
             } catch (err) {
@@ -176,7 +189,19 @@ module.exports = app => {
                 if (!transferInfo) {
                     throw '该资金划拨信息不存在';
                 }
-                const transferTenderList = await ctx.service.financialTransferTender.getList(trid);
+                const transferTenderList = await ctx.service.financialTransferTender.getList(trid, true);
+                const total = await ctx.service.financialTransferTender.count({ trid });
+                // 分页相关
+                const page = ctx.page;
+                const pageSize = ctx.pageSize;
+                const pageInfo = {
+                    page,
+                    pageSizeSelect: 1,
+                    pageSize,
+                    total_num: total,
+                    total: Math.ceil(total / pageSize),
+                    queryData: JSON.stringify(ctx.urlInfo.query),
+                };
                 const tenders = await ctx.service.tender.getAllDataByCondition({ where: { spid: ctx.subProject.id, filter_fund: 0 } });
                 for (const tt of transferTenderList) {
                     const tenderIndex = ctx.helper._.findIndex(tenders, { id: tt.tid });
@@ -211,6 +236,7 @@ module.exports = app => {
                     preUrl: '/sp/' + ctx.subProject.id + '/financial/transfer',
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.transferTender),
                     whiteList: ctx.app.config.multipart.whitelist,
+                    pageInfo,
                 };
                 await this.layout('financial/transfer_tender.ejs', renderData, 'financial/transfer_tender_modal.ejs');
             } catch (err) {
@@ -614,22 +640,38 @@ module.exports = app => {
             filter.count[filter.status.uncheck] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.uncheck, filterTids, used);// await ctx.service.change.checkingDatas(tender.id, ctx.session.sessionUser.accountId);
             filter.count[filter.status.checking] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.checking, filterTids, used);// await ctx.service.change.checkedDatas(tender.id, ctx.session.sessionUser.accountId);
             filter.count[filter.status.checked] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.checked, filterTids, used);// await ctx.service.change.pendingDatas(tender.id, ctx.session.sessionUser.accountId);
-            const payList = await ctx.service.financialPay.getListByStatus(ctx.subProject.id, status, filterTids, used);
+            const payList = await ctx.service.financialPay.getListByStatus(ctx.subProject.id, status, filterTids, used, 1);
+            const total = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, status, filterTids, used);
+            // 分页相关
+            const page = ctx.page;
+            const pageSize = ctx.pageSize;
+            const pageInfo = {
+                page,
+                pageSizeSelect: 1,
+                pageSize,
+                total_num: total,
+                total: Math.ceil(total / pageSize),
+                queryData: JSON.stringify(ctx.urlInfo.query),
+            };
             // 获取所有项目参与者
             const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject, ['id', 'account', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile']);
             const payTenders = await ctx.service.financialPayTender.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
-            for (const t of tenders) {
-                const info = await ctx.service.tenderInfo.getDataByCondition({ tid: t.id });
-                t.pay_account = info && info.pay_account ? JSON.parse(info.pay_account).project : '';
-                const pt = ctx.helper._.find(payTenders, { tid: t.id });
-                t.pt = pt ? pt : { id: 0, tid: t.id, name: '', bank: '', bank_account: '', contact: '', phone: '' };
-                if (ctx.session.sessionUser.is_admin || ctx.helper._.includes(fptReportTids, t.id)) {
-                    t.dealCode = info && info.deal_info ? JSON.parse(info.deal_info).dealCode : '';
-                    const lastPay = await ctx.service.financialPay.getAllDataByCondition({ where: { spid: ctx.subProject.id, tid: t.id }, columns: ['code'], orders: [['id', 'desc']], limit: 1 });
-                    t.startNum = 1;
-                    if (lastPay.length > 0) {
-                        const startArray = lastPay[0].code.split('-');
-                        t.startNum = parseInt(startArray[startArray.length - 1]) + 1;
+            if (tenders.length > 0) {
+                const allLastPay = await ctx.service.financialPay.getAllDataByCondition({ where: { spid: ctx.subProject.id, tid: ctx.helper._.map(tenders, 'id') }, columns: ['id', 'code'], orders: [['id', 'desc']] });
+                const tenderInfos = await ctx.service.tenderInfo.getAllDataByCondition({ where: { tid: ctx.helper._.map(tenders, 'id') }, columns: ['tid', 'pay_account', 'deal_info'] });
+                for (const t of tenders) {
+                    const info = ctx.helper._.find(tenderInfos, { tid: t.id });
+                    t.pay_account = info && info.pay_account ? JSON.parse(info.pay_account).project : '';
+                    const pt = ctx.helper._.find(payTenders, { tid: t.id });
+                    t.pt = pt ? pt : { id: 0, tid: t.id, name: '', bank: '', bank_account: '', contact: '', phone: '' };
+                    if (ctx.session.sessionUser.is_admin || ctx.helper._.includes(fptReportTids, t.id)) {
+                        t.dealCode = info && info.deal_info ? JSON.parse(info.deal_info).dealCode : '';
+                        const lastPay = ctx.helper._.filter(allLastPay, { tid: t.id });
+                        t.startNum = 1;
+                        if (lastPay.length > 0) {
+                            const startArray = lastPay[0].code.split('-');
+                            t.startNum = parseInt(startArray[startArray.length - 1]) + 1;
+                        }
                     }
                 }
             }
@@ -651,10 +693,13 @@ module.exports = app => {
                     pay.entities = await ctx.service.financialPayContract.getEntities(pay.id);
                 }
             }
-            if (ctx.session.sessionUser.is_admin) {
+            if (ctx.session.sessionUser.is_admin && tenders.length > 0) {
+                const allPermissionList = await ctx.service.financialPayTenderAudit.getAllList(ctx.subProject.id, ctx.helper._.map(tenders, 'id'), accountList);
+                const allAuditGroupList = await ctx.service.shenpiAudit.getAllAuditGroupList(ctx.helper._.map(tenders, 'id'), shenpiConst.sp_other_type.financial, shenpiConst.sp_status.gdspl);
                 for (const t of tenders) {
-                    t.permissionList = await ctx.service.financialPayTenderAudit.getList(ctx.subProject.id, t.id, accountList);
-                    t.auditGroupList = await ctx.service.shenpiAudit.getAuditGroupList(t.id, shenpiConst.sp_other_type.financial, shenpiConst.sp_status.gdspl);
+                    t.permissionList = ctx.helper._.filter(allPermissionList, { tid: t.id }) || [];
+                    // t.auditGroupList = await ctx.service.shenpiAudit.getAuditGroupList(t.id, shenpiConst.sp_other_type.financial, shenpiConst.sp_status.gdspl);
+                    t.auditGroupList = ctx.helper._.find(allAuditGroupList, { tid: t.id }) ? ctx.helper._.find(allAuditGroupList, { tid: t.id }).audits : [];
                 }
             }
             const renderData = {
@@ -673,6 +718,7 @@ module.exports = app => {
                 auditType: auditConst.auditType,
                 // preUrl: '/financial',
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.pay),
+                pageInfo,
             };
             if (ctx.session.sessionUser.is_admin) {
                 const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
@@ -781,6 +827,8 @@ module.exports = app => {
                 await this._getFinancialAuditViewData(ctx);
                 // 获取附件列表
                 const fileList = await ctx.service.financialPayAtt.getAtt(ctx.financialPay.id);
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const auth_mobile = pa.auth_mobile;
                 const renderData = {
                     financialPermission: ctx.financialPay.permission,
                     financialPay: ctx.financialPay,
@@ -791,6 +839,7 @@ module.exports = app => {
                     whiteList: ctx.app.config.multipart.whitelist,
                     moment,
                     fileList,
+                    authMobile: auth_mobile,
                     preUrl: `/sp/${ctx.subProject.id}/financial/pay`,
                     preUrl2: `/sp/${ctx.subProject.id}/financial/pay/` + ctx.financialPay.id,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.payDetail),
@@ -860,6 +909,56 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 变更立项重新审批
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async checkPayAgain(ctx) {
+            try {
+                // 获取终审
+                // const auditInfo = await this.ctx.service.changeProjectAudit.getAuditorByStatus(ctx.change.id, audit.changeProject.status.checked);
+                // if (ctx.change.status !== audit.changeProject.status.checked || ctx.session.sessionUser.accountId !== auditInfo.aid) {
+                if (ctx.financialPay.status !== auditConst.financial.status.checked) {
+                    throw '您无权进行该操作';
+                }
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const code = ctx.request.body.code;
+                    const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!pa.auth_mobile) {
+                        throw '未绑定手机号';
+                    }
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    // console.log(cacheCode);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+                if ((ctx.financialPay.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) && ctx.financialPay.status === auditConst.financial.status.checked) {
+                    // 重新审批
+                    const result = await ctx.service.financialPayAudit.checkAgain(ctx.financialPay);
+                    if (!result) {
+                        throw '重新审批失败';
+                    }
+                } else {
+                    throw '您无权进行该操作';
+                }
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
+
         async payDetailSave(ctx) {
             try {
                 const responseData = {

+ 9 - 0
app/controller/profile_controller.js

@@ -414,6 +414,15 @@ module.exports = app => {
                             response.data = JSON.parse(dskAccount.dsk_account);
                         }
                         break;
+                    case 'unbindMobile':
+                        if (!dskAccount.auth_mobile) {
+                            throw '未绑定认证手机';
+                        }
+                        if (dskAccount.dsk_account) {
+                            throw '未解绑大司空账号无法解绑认证手机';
+                        }
+                        response.data = await ctx.service.projectAccount.defaultUpdate({ id: dskAccount.id, auth_mobile: '' });
+                        break;
                     case 'compilation':
                         if (!dskAccount.dsk_account) {
                             throw '未绑定大司空账号';

+ 93 - 1
app/controller/setting_controller.js

@@ -979,6 +979,8 @@ module.exports = app => {
                 if (projectData === null) throw '没有对应的项目数据';
                 if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
 
+                const limitList = await ctx.service.multiLimit.getLimits(ctx.session.sessionProject.id);
+
                 const tenders = await ctx.service.tender.getAllDataByCondition({where: {project_id: projectId}});
                 for (const t of tenders) {
                     t.measure_type_str = t.measure_type === measureType.tz.value ? measureType.tz.title : measureType.gcl.title;
@@ -991,7 +993,9 @@ module.exports = app => {
                 await this.layout('setting/s2b.ejs', {
                     projectData,
                     tenders,
-                });
+                    limitList,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.s2b),
+                }, 'setting/s2b_modal.ejs');
             } catch (error) {
                 ctx.helper.log(error);
                 ctx.redirect('/dashboard');
@@ -1013,6 +1017,8 @@ module.exports = app => {
                 if (data.gxby_check !== undefined) updateData.s2b_gxby_check = data.gxby_check;
                 if (data.dagl_limit !== undefined) updateData.s2b_dagl_limit = data.dagl_limit;
                 if (data.dagl_check !== undefined) updateData.s2b_dagl_check = data.dagl_check;
+                if (data.multi_limit !== undefined) updateData.s2b_multi_limit = data.multi_limit;
+                if (data.multi_check !== undefined) updateData.s2b_multi_check = data.multi_check;
                 await ctx.service.tender.saveApiRela(data.tid, updateData);
                 ctx.body = {err: 0, msg: '', data: null};
             } catch (error) {
@@ -1050,6 +1056,92 @@ module.exports = app => {
             }
         }
 
+        async saveLimit(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.multiLimit.saveLimit(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '修改数据失败');
+            }
+        }
+
+        async saveLimitOption(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.multiLimit.saveLimitOption(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '修改数据失败');
+            }
+        }
+
+        async tender(ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) throw '没有对应的项目数据';
+                if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
+
+                const limits = await this.ctx.service.multiLimit.getLimitList(this.ctx.session.sessionProject.id);
+                const renderData = {
+                    projectData,
+                    tender: ctx.tender.data,
+                    limits,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.tender),
+                };
+                await this.layout('setting/tender.ejs', renderData);
+            } catch (err) {
+                ctx.log(err);
+            }
+        }
+
+        async tenderLoad(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.filter) throw '请求参数错误';
+
+                const filter = data.filter.split(';');
+                const result = {};
+                for (const f of filter) {
+                    switch (f) {
+                        case 'xmj':
+                            result[f] = await this.ctx.service.ledger.getAllDataByCondition({
+                                columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'name', 'unit'],
+                                where: { tender_id: ctx.tender.id },
+                            });
+                            break;
+                        case 'limit':
+                            result[f] = await this.ctx.service.ledgerExtra.getAllDataByCondition({
+                                columns: ['id', 'multi_limit'],
+                                where: { tid: ctx.tender.id },
+                            });
+                            break;
+                        default:
+                            throw '未知数据类型';
+                    }
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '加载数据错误');
+            }
+        }
+
+        async tenderUpdate(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                await this.ctx.service.ledgerExtra.updateMultiLimit(data.id, data.multi_limit);
+                ctx.body = {err: 0, msg: '', data };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存数据错误');
+            }
+        }
+
         async spread(ctx) {
             try {
                 // 获取项目数据

+ 30 - 4
app/controller/stage_controller.js

@@ -184,6 +184,7 @@ module.exports = app => {
                 renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.findIndex(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1;
                 renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver;
                 renderData.hintMinusCb = projectFunInfo.banMinusChangeBills && ctx.tender.info.ledger_check.banMinusChangeBills;
+                renderData.limits = await this.ctx.service.multiLimit.getLimits(ctx.session.sessionProject.id);
                 renderData.categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
                 renderData.settleStatus = ctx.service.settle.settleStatus;
                 renderData.deleteFilePermission = PermissionCheck.delFile(this.ctx.session.sessionUser.permission);
@@ -224,6 +225,8 @@ module.exports = app => {
             this.ledgerExtraColumn = ['is_tp'];
             if (this.ctx.session.sessionProject.gxby) this.ledgerExtraColumn.push('gxby_status', 'gxby_url', 'gxby_limit');
             if (this.ctx.session.sessionProject.dagl) this.ledgerExtraColumn.push('dagl_status', 'dagl_url', 'dagl_limit');
+            // if (tender.data.s2b_multi_check) this.ledgerExtraColumn.push('multi_limit', 'gxby_date');
+            if (tender.data.s2b_multi_check) this.ledgerExtraColumn.push('multi_limit');
             this.ledgerMemoColumn = ['memo'];
 
             this.posColumn = ['id', 'tid', 'lid', 'name', 'position', 'porder', 'sgfh_qty', 'quantity', 'ex_qty1', 'add_stage_order', 'drawing_code'];
@@ -232,6 +235,7 @@ module.exports = app => {
             this.posExtraColumn = [];
             if (this.ctx.session.sessionProject.gxby) this.posExtraColumn.push('gxby_status', 'gxby_url', 'gxby_limit');
             if (this.ctx.session.sessionProject.dagl) this.posExtraColumn.push('dagl_status', 'dagl_url', 'dagl_limit');
+            // if (tender.data.s2b_multi_check) this.posExtraColumn.push('gxby_date');
             this.posMemoColumn = [];
 
             const extraFields = await spreadSetting.getExtraFields(this.ctx, tender.id, 'stage');
@@ -375,11 +379,18 @@ module.exports = app => {
                         c.bills.push(b);
                     }
                     b.checked_amount = this.ctx.helper.add(b.checked_amount, cb.checked_amount);
+                    if (cb.is_valuation) {
+                        b.qty = this.ctx.helper.add(b.qty, cb.checked_amount);
+                    } else {
+                        b.minus_qty = this.ctx.helper.add(b.minus_qty, cb.checked_amount);
+                    }
                     b.pos.push(cb);
                 } else {
                     c.bills.push({
                         code: cb.code, name: cb.name, unit: cb.unit, unit_price: cb.unit_price,
                         gcl_id: '', checked_amount: cb.checked_amount, checked_price: cb.checked_price,
+                        qty: cb.is_valuation ? cb.checked_amount : 0, tp: cb.is_valuation ? cb.checked_price : 0,
+                        minus_qty: !cb.is_valuation ? cb.checked_amount : 0, minus_tp: !cb.is_valuation ? cb.checked_price : 0,
                         pos: [cb],
                     });
                 }
@@ -387,7 +398,12 @@ module.exports = app => {
             const helper = ctx.helper;
             change.forEach(x => {
                 const tpDecimal = x.tp_decimal || ctx.tender.info.decimal.tp;
-                x.bills.forEach(b => { b.checked_price = helper.mul(b.unit_price, b.checked_amount, tpDecimal); });
+                x.bills.forEach(b => {
+                    if (!b.gcl_id) return;
+                    b.checked_price = helper.mul(b.unit_price, b.checked_amount, tpDecimal);
+                    b.tp = helper.mul(b.unit_price, b.qty, tpDecimal);
+                    b.minus_tp = helper.mul(b.unit_price, b.minus_qty, tpDecimal);
+                });
                 x.bills.sort((a, b) => { return helper.compareCode(a.code, b.code)});
             });
             return change;
@@ -514,13 +530,21 @@ module.exports = app => {
                 checkData.loadData(ledgerData, posData);
                 await this.ctx.service.s2bProj.refreshSessionS2b();
                 checkData.check3fLimit(ctx.tender.data);
+                const limits = await this.ctx.service.multiLimit.getLimits(this.ctx.session.sessionProject.id);
+                checkData.check3fMultiLimit(ctx.tender.data, limits);
 
                 if (projRela.banOver && ctx.tender.info.ledger_check.over) {
                     checkData.checkOverRange(ctx.tender.info.checkOverInfo);
                 }
-                checkData.checkBillsTp([
-                    { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' },
-                ], this.ctx.tender.info.decimal, x => { return x.is_tp; });
+                if (this.ctx.tender.info.calc_type === 'up') {
+                    checkData.checkBillsTp([
+                        { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' },
+                    ], this.ctx.tender.info.decimal, x => { return x.is_tp; });
+                } else {
+                    checkData.checkBillsTp([
+                        { qty: 'qc_qty', tp: 'qc_tp' },
+                    ], this.ctx.tender.info.decimal, x => { return x.is_tp; });
+                }
                 checkData.checkBillsQty(['contract_qty', 'qc_qty']);
                 if (projRela.banMinusChangeBills && ctx.tender.info.ledger_check.banMinusChangeBills) {
                     const change = await this.ctx.service.change.getAllChangeHasMinus(ctx.tender.id);
@@ -1628,6 +1652,8 @@ module.exports = app => {
                 renderData.ex_memo1 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo1'; });
                 renderData.ex_memo2 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo2'; });
                 renderData.ex_memo3 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo3'; });
+                const projectFunInfo = this.ctx.subProject.fun_rela;
+                renderData.minusNoValue = projectFunInfo.minusNoValue && ctx.tender.info.fun_rela.stage_change.minusNoValue;
                 await this.layout('stage/bwtz.ejs', renderData, 'stage/audit_modal.ejs');
             } catch (err) {
                 this.log(err);

+ 1 - 1
app/controller/sub_proj_setting_controller.js

@@ -432,7 +432,7 @@ module.exports = app => {
                 const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
                 const tenders = await ctx.service.tender.getList('', null, 1);
                 const dcTenders = await ctx.service.datacollectTender.getList(ctx.subProject.project_id, ctx.subProject.id);
-                const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103', 'KLG25'].indexOf(ctx.session.sessionProject.code) !== -1 ? 6 : false;
+                const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103', 'KLG25', 'I2U5B', 'SEN48'].indexOf(ctx.session.sessionProject.code) !== -1 ? 6 : false;
                 const renderData = {
                     dataCollectAudits,
                     accountList,

+ 10 - 5
app/controller/tender_controller.js

@@ -707,13 +707,18 @@ module.exports = app => {
         async saveTenderInfo2(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
-                if (!data || (!data.ledger_check && !data.fun_rela && !data.s_type && !data.over_range_check)) throw '提交数据错误';
+                if (!data || (!data.ledger_check && !data.fun_rela && !data.s_type && !data.calc_type && !data.over_range_check)) throw '提交数据错误';
                 if (!ctx.session.sessionUser.is_admin) throw '您无权修改该内容';
 
-                const updateData = {};
-                if (data.ledger_check) updateData.ledger_check = data.ledger_check;
-                if (data.fun_rela) updateData.fun_rela = data.fun_rela;
-                await ctx.service.tenderInfo.saveTenderInfo(ctx.tender.id, data);
+                if (data.ledger_check || data.fun_rela || data.s_type || data.over_range_check) {
+                    const updateData = {};
+                    if (data.ledger_check) updateData.ledger_check = data.ledger_check;
+                    if (data.fun_rela) updateData.fun_rela = data.fun_rela;
+                    if (data.over_range_check) updateData.over_range_check = data.over_range_check;
+                    if (data.s_type) updateData.s_type = data.s_type;
+                    await ctx.service.tenderInfo.saveTenderInfo(ctx.tender.id, updateData);
+                }
+                if (data.calc_type !== undefined) await ctx.service.tenderInfo.saveCalcType(ctx.tender.id, data.calc_type);
 
                 ctx.body = { err: 0, msg: '', data: JSON.parse(ctx.request.body.data) };
             } catch (err) {

+ 17 - 2
app/lib/bills_pos_convert.js

@@ -79,16 +79,29 @@ class BillsPosConvert {
             if (fields.indexOf(prop) >= 0) node[prop] = data[prop];
         }
     }
+    _calcContractTp(node, data) {
+        const info = this.ctx.tender.info;
+        if (info.calc_type === 'tp') {
+            let activeQty = this.ctx.helper.add(data.quantity, data.qc_minus_qty);
+            let end_contract_qty = data.contract_qty;
+            activeQty = this.ctx.helper.add(activeQty, data.pre_qc_minus_qty);
+            end_contract_qty = this.ctx.helper.add(end_contract_qty, data.pre_contract_qty);
+            const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), data.total_price, info.decimal.tp);
+            return this.ctx.helper.sub(end_contract_tp, data.pre_contract_tp);
+        } else {
+            return this.ctx.helper.mul(node.unit_price, data.contract_qty, info.decimal.tp);
+        }
+    }
     // v2
     _loadPosCalcFields(node, data) {
         const tpDecimal = this.ctx.tender.info.decimal.tp;
         node.quantity = this.ctx.helper.add(node.quantity, data.quantity);
-        node.total_price = this.ctx.helper.add(node.total_price, this.ctx.helper.mul(node.unit_price, data.quantity, tpDecimal));
+        data.total_price = this.ctx.helper.mul(node.unit_price, data.quantity, tpDecimal);
+        node.total_price = this.ctx.helper.add(node.total_price, data.total_price);
 
         const org_price = node.org_price || node.unit_price;
 
         node.contract_qty = this.ctx.helper.add(node.contract_qty, data.contract_qty);
-        node.contract_tp = this.ctx.helper.add(node.contract_tp, this.ctx.helper.mul(node.unit_price, data.contract_qty, tpDecimal));
         node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
         node.qc_tp = this.ctx.helper.add(node.qc_tp, this.ctx.helper.mul(node.unit_price, data.qc_qty, tpDecimal));
         node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
@@ -103,6 +116,8 @@ class BillsPosConvert {
 
         node.qc_minus_qty = this.ctx.helper.add(node.qc_minus_qty, data.qc_minus_qty);
         node.pre_qc_minus_qty = this.ctx.helper.add(node.pre_qc_minus_qty, data.pre_qc_minus_qty);
+
+        node.contract_tp = this.ctx.helper.add(node.contract_tp, this._calcContractTp(node, data));
     }
     _loadBillsCalcFields(node, data) {
         node.quantity = this.ctx.helper.add(node.quantity, data.quantity);

+ 142 - 3
app/lib/ledger.js

@@ -10,6 +10,51 @@
 
 const itemsPre = 'id_';
 
+class ValueCheck {
+    constructor(ctx) {
+        this.moment = ctx.moment;
+        this._ = ctx.helper._;
+    }
+    num(value, checkValue, operation) {
+        switch (operation) {
+            case '=':
+                return !this._.isNil(value) ? value === checkValue : false;
+            case '>':
+                return !this._.isNil(value) ? value > checkValue : false;
+            case '<':
+                return !this._.isNil(value) ? value < checkValue : false;
+            case '>=':
+                return !this._.isNil(value) ? value >= checkValue : false;
+            case '<=':
+                return !this._.isNil(value) ? value <= checkValue : false;
+            default:
+                return true;
+        }
+    }
+    date(value, range, operation) {
+        const curDate = new Date();
+        switch (operation) {
+            case '=':
+                return this.moment(value).add({ days: range }).isSame(curDate, 'day');
+            case '>':
+                return this.moment(value).add({ days: range }).isBefore(curDate);
+            case '<':
+                return this.moment(value).add({ days: range }).isAfter(curDate);
+            default:
+                return true;
+        }
+    }
+    gxby(value, checkValue, operation) {
+        return this.num(value, checkValue, operation);
+    }
+    dagl(value, checkValue, operation) {
+        return this.num(value, checkValue, operation);
+    }
+    gxbyDate(checkDate, operation) {
+        return this.date(checkDate, operation);
+    }
+}
+
 class baseTree {
     /**
      * 构造函数
@@ -748,13 +793,11 @@ class checkData {
         }
         return 0; // 合法
     }
-
     _getRatio(type, status) {
         const statusConst = type === 'gxby' ? this.ctx.session.sessionProject.gxby_status : this.ctx.session.sessionProject.dagl_status;
         const sc = statusConst.find(x => { return x.value === status });
         return sc ? sc.ratio : null;
     }
-
     _getValid = function (type, status, limit) {
         if (limit) {
             const statusConst = type === 'gxby' ? this.ctx.session.sessionProject.gxby_status : this.ctx.session.sessionProject.dagl_status;
@@ -764,7 +807,6 @@ class checkData {
             return -1;
         }
     };
-
     _checkLeafBills3fLimit(checkType, bills, checkInfo) {
         const over = [], lost = [];
         const posRange = this.checkPos.getLedgerPos(bills.id);
@@ -838,6 +880,94 @@ class checkData {
         }
     }
 
+    _checkMultiCondition(data, condition) {
+        let result = true;
+        for (const c of condition) {
+            result = c.rela && c.rela === 'or'
+                ? (result || this.valueCheck[c.check](data[c.field], c.value, c.operation))
+                : (result && this.valueCheck[c.check](data[c.field], c.value, c.operation));
+        }
+        return result;
+    }
+    _check3fMultiTp(data, limit, range) {
+        if (limit === 0) {
+            if (data.contract_tp || data.pre_contract_tp) return 1; // 违规
+        }
+        if (limit === 1) {
+            const lower = this.ctx.helper.mul(data.total_price, this.ctx.helper.div(range.lower, 100, 4), this.ctx.tender.info.decimal.tp);
+            const upper = this.ctx.helper.mul(data.total_price, this.ctx.helper.div(range.upper, 100, 4), this.ctx.tender.info.decimal.tp);
+            const checkTp = this.ctx.helper.add(data.contract_tp, data.pre_contract_tp) || 0;
+            if (checkTp > upper) return 1; // 违规
+            if (checkTp < lower) return 2; // 漏计
+        }
+        return 0; // 合法
+    }
+    _check3fMultiQty(data, limit, range, unit) {
+        if (limit === 0) {
+            if (data.contract_qty || data.qc_qty || data.pre_contract_qty || data.pre_qc_qty) return 1; // 违规
+        }
+        if (limit === 1) {
+            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, unit);
+            const lower = this.ctx.helper.mul(data.final_1_qty, this.ctx.helper.div(range.lower, 100, 4), precision.value);
+            const upper = this.ctx.helper.mul(data.final_1_qty, this.ctx.helper.div(range.upper, 100, 4), precision.value);
+            const checkQty = this.ctx.helper.add(data.contract_qty, data.pre_contract_qty) || 0;
+            if (checkQty > upper) return 1; // 违规
+            if (checkQty < lower) return 2; // 漏计
+        }
+        return 0; // 合法
+    }
+    _checkMulti3f(bills, checkData, statusData, limitOption){
+        if (!this._checkMultiCondition(statusData, limitOption.condition)) return;
+
+        if (bills.is_tp) return this._check3fMultiTp(checkData, limitOption.limit, limitOption);
+        return this._check3fMultiQty(checkData, limitOption.limit, limitOption, bills.unit);
+    }
+    _getMulti3fErrorInfo(bills, lo, checkResult, pos = null) {
+        if (!checkResult || checkResult <= 0) return;
+        const errorType = checkResult === 1 ? '超计' : '漏计';
+        if (!lo.rangeHint) lo.rangeHint = lo.limit ? (lo.lower === lo.upper ? `${lo.lower}%` : `${lo.lower}%~${lo.upper}%`) : '不允许计量';
+        if (pos) {
+            this.checkResult.error.push({
+                ledger_id: bills.ledger_id, pid: pos.id, code: pos.code, b_code: '', name: pos.name, data: pos, errorType: 's2b_multi', memo: `${errorType}:${lo.hint}(${lo.rangeHint})`
+            });
+        } else {
+            this.checkResult.error.push({
+                ledger_id: bills.ledger_id, pid: '', code: bills.code, b_code: bills.b_code, name: bills.name, data: bills, errorType: 's2b_multi', memo: `${errorType}:${lo.hint}(${lo.rangeHint})`
+            });
+        }
+
+    }
+    _checkLeafBills3fMultiLimit(bills, multiLimit, leafXmj) {
+        if (!multiLimit) return;
+        const limitOption = this.limits.find(x => { return x.limit_id === multiLimit; });
+        if (!limitOption) return;
+
+        const posRange = this.checkPos.getLedgerPos(bills.id);
+        if (posRange && posRange.length > 0) {
+            for (const p of posRange) {
+                for (const lo of limitOption.options) {
+                    const checkResult = this._checkMulti3f(bills, p, p, lo);
+                    this._getMulti3fErrorInfo(bills, lo, checkResult, p);
+                }
+            }
+        } else {
+            for (const lo of limitOption.options) {
+                const checkResult = this._checkMulti3f(bills, bills, leafXmj, lo);
+                this._getMulti3fErrorInfo(bills, lo, checkResult);
+            }
+        }
+    }
+    _recursiveCheckBills3fMultiLimit(bills, parentLimit = '', leafXmj = null) {
+        const limit = bills.multi_limit || parentLimit;
+        if (bills.children && bills.children.length > 0) {
+            for (const c of bills.children) {
+                this._recursiveCheckBills3fMultiLimit(c, limit, c.b_code ? (bills.b_code ? leafXmj : bills) : c);
+            }
+        } else {
+            this._checkLeafBills3fMultiLimit(bills, limit, bills.b_code ? leafXmj : bills);
+        }
+    }
+
     loadData(bills, pos) {
         this.checkBills.loadDatas(bills);
         this.checkPos.loadDatas(pos);
@@ -919,6 +1049,15 @@ class checkData {
             this._recursiveCheckBills3fLimit(check, b, {});
         }
     }
+    check3fMultiLimit(tender, limits) {
+        this.limits = limits;
+        if (!tender.s2b_multi_limit) return;
+        this.valueCheck = new ValueCheck(this.ctx);
+
+        for (const b of this.checkBills.children) {
+            this._recursiveCheckBills3fMultiLimit(b);
+        }
+    }
     checkBillsQty(fields) {
         for (const b of this.checkBills.nodes) {
             if (b.children && b.children.length > 0) continue;

+ 22 - 3
app/lib/sum_load.js

@@ -384,6 +384,9 @@ class gatherStageGclTree extends loadGclBaseTree {
                 qc_tp: 0,
                 is_tp: d.is_tp,
                 hasPos: relaPos.length > 0,
+                pre_contract_qty: d.pre_contract_qty || 0,
+                pre_contract_tp: d.pre_contract_tp || 0,
+                pre_qc_minus_qty: d.pre_qc_minus_qty || 0,
             };
             parent.children.push(baseNode);
             Index[baseNode.ledger_id] = baseNode;
@@ -399,6 +402,18 @@ class gatherStageGclTree extends loadGclBaseTree {
             node.change_detail.push(cd);
         }
     }
+    calcContractTp(info, node) {
+        if (info.calc_type === 'tp') {
+            let activeQty = this.ctx.helper.add(node.quantity, node.qc_minus_qty);
+            let end_contract_qty = node.contract_qty;
+            activeQty = this.ctx.helper.add(activeQty, node.pre_qc_minus_qty);
+            end_contract_qty = this.ctx.helper.add(end_contract_qty, node.pre_contract_qty);
+            const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, decimal.tp);
+            return this.ctx.helper.sub(end_contract_tp, node.pre_contract_tp);
+        } else if (info.calc_type === 'up') {
+            return this.ctx.helper.mul(node.unit_price, node.contract_qty, info.decimal.tp);
+        }
+    }
     gather(source, parent) {
         parent = parent ? parent : this.parent;
         const checkFun = function (node, source) {
@@ -410,9 +425,6 @@ class gatherStageGclTree extends loadGclBaseTree {
         if (node.is_tp) {
             node.contract_tp = this.ctx.helper.add(node.contract_tp, source.contract_tp);
         } else {
-            node.contract_qty = this.ctx.helper.add(node.contract_qty, source.contract_qty);
-            node.contract_tp = this.ctx.helper.mul(node.unit_price, node.contract_qty, this.ctx.tender.info.decimal.tp);
-
             if (this.loadChange) {
                 node.qc_qty = this.ctx.helper.add(node.qc_qty, source.qc_qty);
                 node.qc_tp = this.ctx.helper.mul(node.unit_price, node.qc_qty, this.ctx.tender.info.decimal.tp);
@@ -421,7 +433,12 @@ class gatherStageGclTree extends loadGclBaseTree {
                 node.positive_qc_tp = this.ctx.helper.mul(node.unit_price, node.positive_qc_qty, this.ctx.tender.info.decimal.tp);
                 node.negative_qc_qty = this.ctx.helper.add(node.negative_qc_qty, source.negative_qc_qty);
                 node.negative_qc_tp = this.ctx.helper.mul(node.unit_price, node.negative_qc_qty, this.ctx.tender.info.decimal.tp);
+            } else {
+                node.qc_minus_qty = node.org_qc_minus_qty;
             }
+            node.contract_qty = this.ctx.helper.add(node.contract_qty, source.contract_qty);
+            node.contract_tp = this.calcContractTp(this.ctx.tender.info, node);
+
         }
         if (this.loadChange) this._gatherChange(node, source);
         return node;
@@ -595,11 +612,13 @@ class sumLoad {
         const stageBills = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
         const importLid = await this.ctx.service.stageImportChange.getLeafXmjImportLid(this.ctx.stage.id, select.id);
         const settleStatusBills = this.ctx.stage.readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: this.ctx.stage.readySettle.id }}) : [];
+        const preStageBills = await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender.data, this.ctx.stage.preCheckedStage.order);
         this.ctx.helper.assignRelaData(posterity, [
             { data: extraData, fields: ['is_tp'], prefix: '', relaId: 'id' },
             { data: importLid, fields: ['is_import'], prefix: '', relaId: 'lid' },
             { data: stageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'positive_qc_qty', 'positive_qc_tp', 'negative_qc_qty', 'negative_qc_tp'], prefix: '', relaId: 'lid' },
             { data: settleStatusBills, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+            { data: preStageBills, fields: ['contract_qty', 'contract_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid'},
         ]);
         const pos = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
         const settleStatusPos = this.ctx.stage.readySettle ? await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: this.ctx.stage.readySettle.id }}) : [];

+ 497 - 147
app/public/js/change_revise.js

@@ -56,7 +56,7 @@ $(document).ready(() => {
     let stdXmj, stdGcl, searchLedger;
     autoFlashHeight();
     billsSpreadSetting.cols.unshift({ title: '变更清单', colSpan: '1', rowSpan: '2', field: 'is_change', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isChangeList', });
-    const billsIndex = _.findIndex(billsSpreadSetting.cols, { field: 'drawing_code' }) !== -1 ? _.findIndex(billsSpreadSetting.cols, { field: 'drawing_code' }) : (_.findIndex(billsSpreadSetting.cols, { field: 'sgfh_tp' }) !== -1 ? _.findIndex(billsSpreadSetting.cols, { field: 'sgfh_tp' }) + 1 : billsSpreadSetting.cols.length - 1);
+    const billsIndex = _.findIndex(billsSpreadSetting.cols, { field: 'drawing_code' }) !== -1 ? _.findIndex(billsSpreadSetting.cols, { field: 'drawing_code' }) : (_.findIndex(billsSpreadSetting.cols, { field: 'ex_tp1' }) !== -1 ? _.findIndex(billsSpreadSetting.cols, { field: 'ex_tp1' }) + 1 : billsSpreadSetting.cols.length - 1);
     console.log(billsSpreadSetting.cols);
     billsSpreadSetting.cols.splice(billsIndex, 0, { title: '计价', colSpan: '1', rowSpan: '2', field: 'is_valuation', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isValuation', getValue: 'getValue.isValuation' });
     billsSpreadSetting.cols.splice(billsIndex + 1, 0, {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, readOnly: 'readOnly.isSettle', getValue: 'getValue.camount'},);
@@ -118,7 +118,7 @@ $(document).ready(() => {
             },
             isSettle: function (data) {
                 if (!data) return true;
-                return !readOnly && !(data && data.name && _.findIndex(changeList, { gcl_id: data.id, mx_id: '' }) !== -1 && (!data.settle_status || (data.settle_status && data.settle_status !== settleStatus.finish)));
+                return !readOnly && !(!billsCol.readOnly.isChangeList(data) && (!data.settle_status || (data.settle_status && data.settle_status !== settleStatus.finish)));
             }
         }
     };
@@ -158,7 +158,7 @@ $(document).ready(() => {
             },
             isSettle: function (data) {
                 if (!data) return true;
-                return !readOnly && !(data && data.name && _.findIndex(changeList, { gcl_id: data.lid, mx_id: data.id }) !== -1 && (!data.settle_status || (data.settle_status && data.settle_status !== settleStatus.finish)));
+                return !readOnly && !(!posCol.readOnly.isChangeList(data) && (!data.settle_status || (data.settle_status && data.settle_status !== settleStatus.finish)));
             }
         }
     };
@@ -352,8 +352,13 @@ $(document).ready(() => {
                 if (data) {
                     const cInfo = _.find(changeList, { gcl_id: data.id, mx_id: '' });
                     if (!cInfo) {
-                        $('#bills-expr').val('').attr('readOnly', true);
-                        $('#bills-expr').removeAttr('data-row');
+                        if (billsCol.readOnly.isChangeList(data)) {
+                            $('#bills-expr').val('').attr('readOnly', true);
+                            $('#bills-expr').removeAttr('data-row');
+                        } else {
+                            $('#bills-expr').val('').attr('field', col.field).attr('org', '')
+                                .attr('readOnly', false).attr('data-row', sel.row);
+                        }
                     } else {
                         const value = cInfo.camount_expr ? cInfo.camount_expr : ZhCalc.round(cInfo.camount, findDecimal(cInfo.unit));
                         $('#bills-expr').val(value).attr('field', col.field).attr('org', ZhCalc.round(cInfo.camount, findDecimal(cInfo.unit)))
@@ -839,11 +844,50 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
-                    const cInfo = _.find(changeList, { gcl_id: node.id, mx_id: '' });
+                    let cInfo = _.find(changeList, { gcl_id: node.id, mx_id: '' });
                     if (!cInfo) {
-                        toastr.error('未勾选变更清单,无法编辑申请数量');
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
+                        if (billsCol.readOnly.isSettle(node)) {
+                            toastr.error('清单数据未完善,无法编辑申请数量');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                        makeGclGatherData();
+                        const gclInfo = _.find(gclGatherData, function (item) {
+                            return item.leafXmjs && _.find(item.leafXmjs, {gcl_id: node.id });
+                        });
+                        const xmjInfo = gclInfo.leafXmjs.find(function (item) {
+                            return item.gcl_id === node.id;
+                        });
+                        const oldCInfo = _.find(oldChangeList, { gcl_id: node.id, mx_id: '' });
+                        const data = {
+                            lid: gclInfo.leafXmjs[0].gcl_id || node.id,
+                            code: gclInfo.b_code,
+                            name: gclInfo.name || '',
+                            unit: gclInfo.unit || '',
+                            unit_price: gclInfo.unit_price,
+                            oamount: xmjInfo.quantity,
+                            oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
+                            bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                            xmj_code: xmjInfo.code || '',
+                            xmj_jldy: xmjInfo.jldy || '',
+                            xmj_dwgc: xmjInfo.dwgc || '',
+                            xmj_fbgc: xmjInfo.fbgc || '',
+                            xmj_fxgc: xmjInfo.fxgc || '',
+                            gcl_id: node.id,
+                            mx_id: '',
+                        };
+                        if (oldCInfo) {
+                            data.detail = oldCInfo.detail;
+                            data.camount = oldCInfo.camount;
+                            data.camount_expr = oldCInfo.camount_expr;
+                            data.spamount = oldCInfo.spamount;
+                            data.is_valuation = oldCInfo.is_valuation;
+                            data.delimit = oldCInfo.delimit;
+                        }
+                        cInfo = data;
+                        // toastr.error('未勾选变更清单,无法编辑申请数量');
+                        // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        // return;
                     }
                     let validText = info.editingText ? trimInvalidChar(info.editingText) : '';
                     const orgValue = validText && validText !== ''
@@ -869,35 +913,59 @@ $(document).ready(() => {
                         return;
                     }
                     validText = parseFloat(exprQuantity.quantity);
-                    // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                    const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
-                    if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
-                        toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
-                    } else if (usedInfo && usedInfo.qty < 0  && validText > usedInfo.qty) {
-                        toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
+                    if (cInfo.id) {
+                        // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                        const usedInfo = _.find(changeUsedData, {cbid: cInfo.id});
+                        if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
+                            toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        } else if (usedInfo && usedInfo.qty < 0 && validText > usedInfo.qty) {
+                            toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
                     }
                     cInfo[col.field] = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
                     cInfo.spamount = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
                     cInfo.camount_expr = exprQuantity.expr;
                     node[col.field] = cInfo[col.field];
                     delete cInfo.waitingLoading;
-                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'update', updateData: cInfo }, function (result) {
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        const billsNode = node;
-                        billsTreeSpreadObj.reCalcCamount(billsNode);
-                        const loadResult = { update: [billsNode] };
-                        const refreshNode = billsTree.loadPostData(loadResult);
-                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
-                    }, function () {
-                        cInfo[col.field] = orgValue;
-                        cInfo.spamount = orgValue;
-                        node[col.field] = orgValue;
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    });
+                    if (cInfo.id) {
+                        postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', {
+                            type: 'update',
+                            updateData: cInfo
+                        }, function (result) {
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            const billsNode = node;
+                            billsTreeSpreadObj.reCalcCamount(billsNode);
+                            const loadResult = {update: [billsNode]};
+                            const refreshNode = billsTree.loadPostData(loadResult);
+                            billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                        }, function () {
+                            cInfo[col.field] = orgValue;
+                            cInfo.spamount = orgValue;
+                            node[col.field] = orgValue;
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        });
+                    } else {
+                        // 更新至服务器
+                        postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', {type: 'add-change-list', postData: [cInfo]}, function (result) {
+                            changeList = result.changeList;
+                            node.is_change = 1;
+                            node.is_valuation = cInfo.is_valuation !== undefined ? cInfo.is_valuation : 1;
+                            node.camount = cInfo.camount !== undefined ? cInfo.camount : null;
+                            billsTreeSpreadObj.reCalcCamount(node);
+                            const refreshNode = billsTree.loadPostData({ update: [node] });
+                            billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        }, function () {
+                            cInfo[col.field] = orgValue;
+                            cInfo.spamount = orgValue;
+                            node[col.field] = orgValue;
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        });
+                    }
                     return;
                 }
 
@@ -1031,9 +1099,10 @@ $(document).ready(() => {
                 // sameParent: {type: 'warning', msg: '仅可粘贴同层节点'},
                 settle: {type: 'warning', msg: '已结算节点,不可修改数量、单价、金额'},
             };
-            const datas = [], filterNodes = [], camountData = [];
+            const datas = [], filterNodes = [], camountData = [], insertCamountData = [];
 
             let pid, level, filterRow = 0;
+            makeGclGatherData();
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
                 const curRow = info.cellRange.row + iRow;
                 const node = tree.nodes[curRow];
@@ -1096,10 +1165,43 @@ $(document).ready(() => {
                     if (colSetting.field === 'camount') {
                         bPaste = true;
                         let validText = value;
-                        const cInfo = _.find(changeList, { gcl_id: node.id, mx_id: '' });
+                        let cInfo = _.find(changeList, { gcl_id: node.id, mx_id: '' });
                         if (!cInfo) {
-                            bPaste = false;
-                            continue;
+                            const gclInfo = _.find(gclGatherData, function (item) {
+                                return item.leafXmjs && _.find(item.leafXmjs, {gcl_id: node.id });
+                            });
+                            const xmjInfo = gclInfo.leafXmjs.find(function (item) {
+                                return item.gcl_id === node.id;
+                            });
+                            const oldCInfo = _.find(oldChangeList, { gcl_id: node.id, mx_id: '' });
+                            const data = {
+                                lid: gclInfo.leafXmjs[0].gcl_id || node.id,
+                                code: gclInfo.b_code,
+                                name: gclInfo.name || '',
+                                unit: gclInfo.unit || '',
+                                unit_price: gclInfo.unit_price,
+                                oamount: xmjInfo.quantity,
+                                oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
+                                bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                                xmj_code: xmjInfo.code || '',
+                                xmj_jldy: xmjInfo.jldy || '',
+                                xmj_dwgc: xmjInfo.dwgc || '',
+                                xmj_fbgc: xmjInfo.fbgc || '',
+                                xmj_fxgc: xmjInfo.fxgc || '',
+                                gcl_id: node.id,
+                                mx_id: '',
+                            };
+                            if (oldCInfo) {
+                                data.detail = oldCInfo.detail;
+                                data.camount = oldCInfo.camount;
+                                data.camount_expr = oldCInfo.camount_expr;
+                                data.spamount = oldCInfo.spamount;
+                                data.is_valuation = oldCInfo.is_valuation;
+                                data.delimit = oldCInfo.delimit;
+                            }
+                            cInfo = data;
+                            // bPaste = false;
+                            // continue;
                         }
                         const exprQuantity = {
                             expr: '',
@@ -1117,21 +1219,30 @@ $(document).ready(() => {
                             continue;
                         }
                         validText = parseFloat(exprQuantity.quantity);
-                        // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                        const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
-                        if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
-                            toastr.error(curRow ? '台账第' + (curRow+1) + '行变更数值必须大于等于已调用值 ' + usedInfo.qty : '清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
-                            bPaste = false;
-                            continue;
-                        } else if (usedInfo && usedInfo.qty < 0 && validText > usedInfo.qty) {
-                            toastr.error(curRow ? '台账第' + (curRow+1) + '行变更数值必须小于等于已调用值 ' + usedInfo.qty : '清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
-                            bPaste = false;
-                            continue;
+                        if (cInfo.id) {
+                            // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                            const usedInfo = _.find(changeUsedData, {cbid: cInfo.id});
+                            if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
+                                toastr.error(curRow ? '台账第' + (curRow + 1) + '行变更数值必须大于等于已调用值 ' + usedInfo.qty : '清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
+                                bPaste = false;
+                                continue;
+                            } else if (usedInfo && usedInfo.qty < 0 && validText > usedInfo.qty) {
+                                toastr.error(curRow ? '台账第' + (curRow + 1) + '行变更数值必须小于等于已调用值 ' + usedInfo.qty : '清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
+                                bPaste = false;
+                                continue;
+                            }
                         }
                         if (bPaste) {
                             node.camount = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
                             filterNodes.push(node);
-                            camountData.push({id: cInfo.id, camount: node.camount, camount_expr: exprQuantity.expr, spamount: ZhCalc.round(validText, findDecimal(node.unit)) || 0 });
+                            if (cInfo.id) {
+                                camountData.push({id: cInfo.id, camount: node.camount, camount_expr: exprQuantity.expr, spamount: ZhCalc.round(validText, findDecimal(node.unit)) || 0 });
+                            } else {
+                                cInfo.camount = node.camount;
+                                cInfo.camount_expr = exprQuantity.expr;
+                                cInfo.spamount = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
+                                insertCamountData.push(cInfo);
+                            }
                         } else {
                             filterNodes.push(node);
                             SpreadJsObj.reLoadRowData(info.sheet, curRow);
@@ -1183,9 +1294,13 @@ $(document).ready(() => {
                     filterNodes.push(node);
                 }
             }
-            if (camountData.length > 0) {
-                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'paste', updateData: camountData }, function (result) {
+            if (camountData.length > 0 || insertCamountData.length > 0) {
+                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'paste', insertData: insertCamountData, updateData: camountData }, function (result) {
                     changeList = result;
+                    // 给所有filterNodes 加上is_change=1
+                    filterNodes.forEach(function (item) {
+                        item.is_change = 1;
+                    });
                     const loadResult = { update: filterNodes };
                     const refreshNode = billsTree.loadPostData(loadResult);
                     billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
@@ -1474,8 +1589,11 @@ $(document).ready(() => {
             if (col.field === 'camount') {
                 const cInfo = _.find(changeList, { gcl_id: node.id, mx_id: '' });
                 if (!cInfo) {
-                    toastr.error('未勾选变更清单,无法编辑申请数量');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    if (billsCol.readOnly.isSettle(node)) {
+                        toastr.error('清单数据未完善,无法编辑申请数量');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return
+                    }
                     return;
                 }
                 if (cInfo.camount_expr && cInfo.camount_expr !== '') {
@@ -1773,10 +1891,49 @@ $(document).ready(() => {
             if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
 
             if (field === 'camount') {
-                const cInfo = _.find(changeList, { gcl_id: select.id, mx_id: '' });
+                let cInfo = _.find(changeList, { gcl_id: select.id, mx_id: '' });
                 if (!cInfo) {
-                    toastr.error('未勾选变更清单,无法编辑申请数量');
-                    return;
+                    if (billsCol.readOnly.isChangeList(select)) {
+                        toastr.error('清单数据未完善,无法编辑申请数量');
+                        SpreadJsObj.reLoadRowData(billsSheet, row);
+                        return;
+                    }
+                    makeGclGatherData();
+                    const gclInfo = _.find(gclGatherData, function (item) {
+                        return item.leafXmjs && _.find(item.leafXmjs, {gcl_id: select.id });
+                    });
+                    const xmjInfo = gclInfo.leafXmjs.find(function (item) {
+                        return item.gcl_id === select.id;
+                    });
+                    const oldCInfo = _.find(oldChangeList, { gcl_id: select.id, mx_id: '' });
+                    const data = {
+                        lid: gclInfo.leafXmjs[0].gcl_id || select.id,
+                        code: gclInfo.b_code,
+                        name: gclInfo.name || '',
+                        unit: gclInfo.unit || '',
+                        unit_price: gclInfo.unit_price,
+                        oamount: xmjInfo.quantity,
+                        oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
+                        bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                        xmj_code: xmjInfo.code || '',
+                        xmj_jldy: xmjInfo.jldy || '',
+                        xmj_dwgc: xmjInfo.dwgc || '',
+                        xmj_fbgc: xmjInfo.fbgc || '',
+                        xmj_fxgc: xmjInfo.fxgc || '',
+                        gcl_id: select.id,
+                        mx_id: '',
+                    };
+                    if (oldCInfo) {
+                        data.detail = oldCInfo.detail;
+                        data.camount = oldCInfo.camount;
+                        data.camount_expr = oldCInfo.camount_expr;
+                        data.spamount = oldCInfo.spamount;
+                        data.is_valuation = oldCInfo.is_valuation;
+                        data.delimit = oldCInfo.delimit;
+                    }
+                    cInfo = data;
+                    // toastr.error('未勾选变更清单,无法编辑申请数量');
+                    // return;
                 }
                 const exprQuantity = {
                     expr: '',
@@ -1792,33 +1949,54 @@ $(document).ready(() => {
                     return;
                 }
                 const camount = parseFloat(exprQuantity.quantity);
-                // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
-                if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
-                    toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
-                    return;
-                } else if (usedInfo && usedInfo.qty < 0  && validText > usedInfo.qty) {
-                    toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
-                    return;
+                if (cInfo.id) {
+                    // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                    const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
+                    if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
+                        toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
+                        return;
+                    } else if (usedInfo && usedInfo.qty < 0  && validText > usedInfo.qty) {
+                        toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
+                        return;
+                    }
                 }
                 cInfo.camount = ZhCalc.round(camount, findDecimal(select.unit)) || 0;
                 select.camount = ZhCalc.round(camount, findDecimal(select.unit)) || 0;
                 cInfo.spamount = ZhCalc.round(camount, findDecimal(select.unit)) || 0;
                 cInfo.camount_expr = exprQuantity.expr;
                 delete cInfo.waitingLoading;
-                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'update', updateData: cInfo }, function (result) {
-                    SpreadJsObj.reLoadRowData(billsSheet, row);
-                    billsTreeSpreadObj.reCalcCamount(select);
-                    const loadResult = { update: [select] };
-                    const refreshNode = billsTree.loadPostData(loadResult);
-                    billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
-                    billsTreeSpreadObj.loadExprToInput(billsSheet);
-                }, function () {
-                    cInfo.camount = orgValue;
-                    cInfo.spamount = orgValue;
-                    select.camount = orgValue;
-                    SpreadJsObj.reLoadRowData(billsSheet, row);
-                });
+                if (cInfo.id) {
+                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'update', updateData: cInfo }, function (result) {
+                        SpreadJsObj.reLoadRowData(billsSheet, row);
+                        billsTreeSpreadObj.reCalcCamount(select);
+                        const loadResult = { update: [select] };
+                        const refreshNode = billsTree.loadPostData(loadResult);
+                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                        billsTreeSpreadObj.loadExprToInput(billsSheet);
+                    }, function () {
+                        cInfo.camount = orgValue;
+                        cInfo.spamount = orgValue;
+                        select.camount = orgValue;
+                        SpreadJsObj.reLoadRowData(billsSheet, row);
+                    });
+                } else {
+                    // 更新至服务器
+                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', {type: 'add-change-list', postData: [cInfo]}, function (result) {
+                        changeList = result.changeList;
+                        select.is_change = 1;
+                        select.is_valuation = cInfo.is_valuation !== undefined ? cInfo.is_valuation : 1;
+                        select.camount = cInfo.camount !== undefined ? cInfo.camount : null;
+                        billsTreeSpreadObj.reCalcCamount(select);
+                        const refreshNode = billsTree.loadPostData({ update: [select] });
+                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                        SpreadJsObj.reLoadRowData(billsSheet, row);
+                    }, function () {
+                        cInfo[col.field] = orgValue;
+                        cInfo.spamount = orgValue;
+                        node[col.field] = orgValue;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
                 return;
             }
 
@@ -2201,8 +2379,13 @@ $(document).ready(() => {
                 if (data) {
                     const cInfo = _.find(changeList, { gcl_id: data.lid, mx_id: data.id });
                     if (!cInfo) {
-                        $('#pos-expr').val('').attr('readOnly', true);
-                        $('#pos-expr').removeAttr('data-row');
+                        if (posCol.readOnly.isChangeList(data)) {
+                            $('#pos-expr').val('').attr('readOnly', true);
+                            $('#pos-expr').removeAttr('data-row');
+                        } else {
+                            $('#pos-expr').val('').attr('field', col.field).attr('org', '')
+                                .attr('readOnly', false).attr('data-row', sel.row);
+                        }
                     } else {
                         const value = cInfo.camount_expr ? cInfo.camount_expr : cInfo.camount;
                         $('#pos-expr').val(value).attr('field', col.field).attr('org', cInfo.camount)
@@ -2353,9 +2536,12 @@ $(document).ready(() => {
             if (col.field === 'camount') {
                 const cInfo = _.find(changeList, { gcl_id: node.lid, mx_id: node.id });
                 if (!cInfo) {
-                    toastr.error('未勾选变更清单,无法编辑申请数量');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
+                    if (posCol.readOnly.isSettle(node)) {
+                        toastr.error('清单数据未完善,无法编辑申请数量');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return
+                    }
+                    return
                 }
                 if (cInfo.camount_expr && cInfo.camount_expr !== '') {
                     info.sheet.getCell(info.row, info.col).text(cInfo.camount_expr);
@@ -2524,11 +2710,48 @@ $(document).ready(() => {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
                 }
-                const cInfo = _.find(changeList, { gcl_id: posData.lid, mx_id: posData.id });
+                let cInfo = _.find(changeList, { gcl_id: posData.lid, mx_id: posData.id });
                 if (!cInfo) {
-                    toastr.error('未勾选变更清单,无法编辑申请数量');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
+                    if (posCol.readOnly.isSettle(posData)) {
+                        toastr.error('清单数据未完善,无法编辑申请数量');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return
+                    }
+                    makeGclGatherData();
+                    const gclInfo = _.find(gclGatherData, function (item) {
+                        return item.leafXmjs && _.find(item.leafXmjs, {gcl_id: posData.lid, mx_id: posData.id });
+                    });
+                    const xmjInfo = _.find(gclInfo.leafXmjs, { mx_id: posData.id });
+                    const oldCInfo = _.find(oldChangeList, { gcl_id: posData.lid, mx_id: posData.id });
+                    const data = {
+                        lid: posData.lid,
+                        code: gclInfo.b_code,
+                        name: gclInfo.name || '',
+                        unit: gclInfo.unit || '',
+                        unit_price: gclInfo.unit_price,
+                        oamount: xmjInfo.quantity,
+                        oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
+                        bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
+                        xmj_code: xmjInfo.code || '',
+                        xmj_jldy: xmjInfo.jldy || '',
+                        xmj_dwgc: xmjInfo.dwgc || '',
+                        xmj_fbgc: xmjInfo.fbgc || '',
+                        xmj_fxgc: xmjInfo.fxgc || '',
+                        gcl_id: posData.lid,
+                        mx_id: posData.id,
+                    }
+                    if (oldCInfo) {
+                        data.detail = oldCInfo.detail;
+                        data.camount = oldCInfo.camount;
+                        data.camount_expr = oldCInfo.camount_expr;
+                        data.spamount = oldCInfo.spamount;
+                        data.is_valuation = oldCInfo.is_valuation;
+                        data.delimit = oldCInfo.delimit;
+                    }
+                    cInfo = data;
+                    // toastr.error('未勾选变更清单,无法编辑申请数量');
+                    // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    // return;
                 }
                 let validText = info.editingText ? trimInvalidChar(info.editingText) : '';
                 const orgValue = validText && validText !== ''
@@ -2554,33 +2777,57 @@ $(document).ready(() => {
                     return;
                 }
                 validText = parseFloat(exprQuantity.quantity);
-                // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
-                if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
-                    toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
-                } else if (usedInfo && usedInfo.qty < 0  && validText > usedInfo.qty) {
-                    toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
+                if (cInfo.id) {
+                    // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                    const usedInfo = _.find(changeUsedData, {cbid: cInfo.id});
+                    if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
+                        toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    } else if (usedInfo && usedInfo.qty < 0 && validText > usedInfo.qty) {
+                        toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
                 }
                 cInfo[col.field] = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
                 cInfo.spamount = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
                 cInfo.camount_expr = exprQuantity.expr;
                 delete cInfo.waitingLoading;
-                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'update', updateData: cInfo }, function (result) {
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    const billsNode = node;
-                    billsTreeSpreadObj.reCalcCamount(billsNode);
-                    const loadResult = { update: [billsNode] };
-                    const refreshNode = billsTree.loadPostData(loadResult);
-                    billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
-                }, function () {
-                    cInfo[col.field] = orgValue;
-                    cInfo.spamount = orgValue;
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                });
+                if (cInfo.id) {
+                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type: 'update', updateData: cInfo }, function (result) {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        const billsNode = node;
+                        billsTreeSpreadObj.reCalcCamount(billsNode);
+                        const loadResult = {update: [billsNode]};
+                        const refreshNode = billsTree.loadPostData(loadResult);
+                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                    }, function () {
+                        cInfo[col.field] = orgValue;
+                        cInfo.spamount = orgValue;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                } else {
+                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', {type: 'add-change-list', postData: [cInfo]}, function (result) {
+                        changeList = result.changeList;
+                        posData.is_change = 1;
+                        posData.is_valuation = cInfo.is_valuation !== undefined ? cInfo.is_valuation : 1;
+                        posData.camount = cInfo.camount !== undefined ? cInfo.camount : null;
+                        posData.ca_tp = cInfo.camount !== undefined ? ZhCalc.mul(cInfo.camount, cInfo.unit_price, decimal.tp) : null;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        const billsNode = SpreadJsObj.getSelectObject(billsSheet);
+                        // info.sheet.setValue(info.row, valutaionCol, data.is_valuation !== undefined ? data.is_valuation : 1);
+                        if (_.findIndex(changeList, { gcl_id: posData.lid }) !== -1) {
+                            billsNode.is_change = 1;
+                        }
+                        billsTreeSpreadObj.reCalcCamount(billsNode);
+                        const loadResult = { update: [billsNode] };
+                        const refreshNode = billsTree.loadPostData(loadResult);
+                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                    }, function () {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
                 return;
             }
 
@@ -2881,7 +3128,7 @@ $(document).ready(() => {
             }
 
             const data = [];
-            const camountData = [];
+            const camountData = [], insertCamountData = [];
             const sortData = info.sheet.zh_data || [];
             const hint = {
                 expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
@@ -2897,6 +3144,7 @@ $(document).ready(() => {
             }
             let bHint = false;
             const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
+            makeGclGatherData();
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                 let bPaste = true;
                 const curRow = info.cellRange.row + iRow;
@@ -2915,10 +3163,41 @@ $(document).ready(() => {
                     if (colSetting.field === 'camount') {
                         bPaste = true;
                         let validText = posData[colSetting.field];
-                        const cInfo = _.find(changeList, { gcl_id: posData.lid, mx_id: posData.id });
+                        let cInfo = _.find(changeList, { gcl_id: posData.lid, mx_id: posData.id });
                         if (!cInfo) {
-                            bPaste = false;
-                            continue;
+                            const gclInfo = _.find(gclGatherData, function (item) {
+                                return item.leafXmjs && _.find(item.leafXmjs, {gcl_id: posData.lid, mx_id: posData.id });
+                            });
+                            const xmjInfo = _.find(gclInfo.leafXmjs, { mx_id: posData.id });
+                            const oldCInfo = _.find(oldChangeList, { gcl_id: posData.lid, mx_id: posData.id });
+                            const data = {
+                                lid: posData.lid,
+                                code: gclInfo.b_code,
+                                name: gclInfo.name || '',
+                                unit: gclInfo.unit || '',
+                                unit_price: gclInfo.unit_price,
+                                oamount: xmjInfo.quantity,
+                                oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
+                                bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
+                                xmj_code: xmjInfo.code || '',
+                                xmj_jldy: xmjInfo.jldy || '',
+                                xmj_dwgc: xmjInfo.dwgc || '',
+                                xmj_fbgc: xmjInfo.fbgc || '',
+                                xmj_fxgc: xmjInfo.fxgc || '',
+                                gcl_id: posData.lid,
+                                mx_id: posData.id,
+                            }
+                            if (oldCInfo) {
+                                data.detail = oldCInfo.detail;
+                                data.camount = oldCInfo.camount;
+                                data.camount_expr = oldCInfo.camount_expr;
+                                data.spamount = oldCInfo.spamount;
+                                data.is_valuation = oldCInfo.is_valuation;
+                                data.delimit = oldCInfo.delimit;
+                            }
+                            cInfo = data;
+                            // bPaste = false;
+                            // continue;
                         }
                         const exprQuantity = {
                             expr: '',
@@ -2936,19 +3215,28 @@ $(document).ready(() => {
                             continue;
                         }
                         validText = parseFloat(exprQuantity.quantity);
-                        // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                        const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
-                        if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
-                            toastr.error(curRow ? '计量单元第' + (curRow+1) + '行变更数值必须大于等于已调用值 ' + usedInfo.qty : '清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
-                            bPaste = false;
-                            continue;
-                        } else if (usedInfo && usedInfo.qty < 0 && validText > usedInfo.qty) {
-                            toastr.error(curRow ? '计量单元第' + (curRow+1) + '行变更数值必须小于等于已调用值 ' + usedInfo.qty : '清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
-                            bPaste = false;
-                            continue;
+                        if (cInfo.id) {
+                            // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                            const usedInfo = _.find(changeUsedData, {cbid: cInfo.id});
+                            if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
+                                toastr.error(curRow ? '计量单元第' + (curRow + 1) + '行变更数值必须大于等于已调用值 ' + usedInfo.qty : '清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
+                                bPaste = false;
+                                continue;
+                            } else if (usedInfo && usedInfo.qty < 0 && validText > usedInfo.qty) {
+                                toastr.error(curRow ? '计量单元第' + (curRow + 1) + '行变更数值必须小于等于已调用值 ' + usedInfo.qty : '清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
+                                bPaste = false;
+                                continue;
+                            }
                         }
                         if (bPaste) {
-                            camountData.push({id: cInfo.id, camount: ZhCalc.round(validText, findDecimal(node.unit)) || 0, camount_expr: exprQuantity.expr, spamount: ZhCalc.round(validText, findDecimal(node.unit)) || 0 });
+                            if (cInfo.id) {
+                                camountData.push({id: cInfo.id, camount: ZhCalc.round(validText, findDecimal(node.unit)) || 0, camount_expr: exprQuantity.expr, spamount: ZhCalc.round(validText, findDecimal(node.unit)) || 0 });
+                            } else {
+                                cInfo.camount = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
+                                cInfo.camount_expr = exprQuantity.expr;
+                                cInfo.spamount = ZhCalc.round(validText, findDecimal(node.unit)) || 0;
+                                insertCamountData.push(cInfo);
+                            }
                         } else {
                             SpreadJsObj.reLoadRowData(info.sheet, curRow);
                         }
@@ -2992,8 +3280,8 @@ $(document).ready(() => {
                     data.push(posData);
                 }
             }
-            if (camountData.length > 0) {
-                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'paste', updateData: camountData }, function (result) {
+            if (camountData.length > 0 || insertCamountData.length > 0) {
+                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'paste', insertData: insertCamountData, updateData: camountData }, function (result) {
                     changeList = result;
                     const billsNode = SpreadJsObj.getSelectObject(billsSheet);
                     billsTreeSpreadObj.reCalcCamount(billsNode);
@@ -3027,8 +3315,9 @@ $(document).ready(() => {
                     billsNode.camount = null;
                     billsNode.ca_tp = null;
                 }
-                if (loadResult.update) {
+                if (loadResult.update && loadResult.update.length > 0) {
                     const r = _.find(loadResult.update, { id: billsNode.id });
+                    console.log(r, billsNode, loadResult.update);
                     r.is_change = billsNode.is_change;
                 } else {
                     loadResult.update = [billsNode];
@@ -3082,10 +3371,47 @@ $(document).ready(() => {
             if (orgValue === newValue || (!orgValue && newValue == '')) return;
 
             if (field === 'camount') {
-                const cInfo = _.find(changeList, { gcl_id: select.lid, mx_id: select.id });
+                let cInfo = _.find(changeList, { gcl_id: select.lid, mx_id: select.id });
                 if (!cInfo) {
-                    toastr.error('未勾选变更清单,无法编辑申请数量');
-                    return;
+                    if (posCol.readOnly.isSettle(select)) {
+                        toastr.error('清单数据未完善,无法编辑申请数量');
+                        SpreadJsObj.reLoadRowData(posSheet, row);
+                        return
+                    }
+                    makeGclGatherData();
+                    const gclInfo = _.find(gclGatherData, function (item) {
+                        return item.leafXmjs && _.find(item.leafXmjs, {gcl_id: select.lid, mx_id: select.id });
+                    });
+                    const xmjInfo = _.find(gclInfo.leafXmjs, { mx_id: select.id });
+                    const oldCInfo = _.find(oldChangeList, { gcl_id: select.lid, mx_id: select.id });
+                    const data = {
+                        lid: select.lid,
+                        code: gclInfo.b_code,
+                        name: gclInfo.name || '',
+                        unit: gclInfo.unit || '',
+                        unit_price: gclInfo.unit_price,
+                        oamount: xmjInfo.quantity,
+                        oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
+                        bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
+                        xmj_code: xmjInfo.code || '',
+                        xmj_jldy: xmjInfo.jldy || '',
+                        xmj_dwgc: xmjInfo.dwgc || '',
+                        xmj_fbgc: xmjInfo.fbgc || '',
+                        xmj_fxgc: xmjInfo.fxgc || '',
+                        gcl_id: select.lid,
+                        mx_id: select.id,
+                    }
+                    if (oldCInfo) {
+                        data.detail = oldCInfo.detail;
+                        data.camount = oldCInfo.camount;
+                        data.camount_expr = oldCInfo.camount_expr;
+                        data.spamount = oldCInfo.spamount;
+                        data.is_valuation = oldCInfo.is_valuation;
+                        data.delimit = oldCInfo.delimit;
+                    }
+                    cInfo = data;
+                    // toastr.error('未勾选变更清单,无法编辑申请数量');
+                    // return;
                 }
                 const exprQuantity = {
                     expr: '',
@@ -3101,31 +3427,55 @@ $(document).ready(() => {
                     return;
                 }
                 const camount = parseFloat(exprQuantity.quantity);
-                // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
-                if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
-                    toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
-                    return;
-                } else if (usedInfo && usedInfo.qty < 0  && validText > usedInfo.qty) {
-                    toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
-                    return;
+                if (cInfo.id) {
+                    // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                    const usedInfo = _.find(changeUsedData, { cbid: cInfo.id });
+                    if (usedInfo && usedInfo.qty >= 0 && validText < usedInfo.qty) {
+                        toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.qty);
+                        return;
+                    } else if (usedInfo && usedInfo.qty < 0  && validText > usedInfo.qty) {
+                        toastr.error('清单变更数值必须小于等于已调用值 ' + usedInfo.qty);
+                        return;
+                    }
                 }
                 cInfo.camount = ZhCalc.round(camount, findDecimal(billsNode.unit)) || 0;
                 cInfo.spamount = ZhCalc.round(camount, findDecimal(billsNode.unit)) || 0;
                 cInfo.camount_expr = exprQuantity.expr;
                 delete cInfo.waitingLoading;
-                postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'update', updateData: cInfo }, function (result) {
-                    SpreadJsObj.reLoadRowData(posSheet, row);
-                    billsTreeSpreadObj.reCalcCamount(billsNode);
-                    const loadResult = { update: [billsNode] };
-                    const refreshNode = billsTree.loadPostData(loadResult);
-                    billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
-                    posSpreadObj.loadExprToInput(posSheet);
-                }, function () {
-                    cInfo.camount = orgValue;
-                    cInfo.spamount = orgValue;
-                    SpreadJsObj.reLoadRowData(posSheet, row);
-                });
+                if (cInfo.id) {
+                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', { type:'update', updateData: cInfo }, function (result) {
+                        SpreadJsObj.reLoadRowData(posSheet, row);
+                        billsTreeSpreadObj.reCalcCamount(billsNode);
+                        const loadResult = { update: [billsNode] };
+                        const refreshNode = billsTree.loadPostData(loadResult);
+                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                        posSpreadObj.loadExprToInput(posSheet);
+                    }, function () {
+                        cInfo.camount = orgValue;
+                        cInfo.spamount = orgValue;
+                        SpreadJsObj.reLoadRowData(posSheet, row);
+                    });
+                } else {
+                    postData('/tender/' + window.location.pathname.split('/')[2] + '/change/' + window.location.pathname.split('/')[4] + '/information/save', {type: 'add-change-list', postData: [cInfo]}, function (result) {
+                        changeList = result.changeList;
+                        select.is_change = 1;
+                        select.is_valuation = cInfo.is_valuation !== undefined ? cInfo.is_valuation : 1;
+                        select.camount = cInfo.camount !== undefined ? cInfo.camount : null;
+                        select.ca_tp = cInfo.camount !== undefined ? ZhCalc.mul(cInfo.camount, cInfo.unit_price, decimal.tp) : null;
+                        SpreadJsObj.reLoadRowData(posSheet, row);
+                        const billsNode = SpreadJsObj.getSelectObject(billsSheet);
+                        // info.sheet.setValue(info.row, valutaionCol, data.is_valuation !== undefined ? data.is_valuation : 1);
+                        if (_.findIndex(changeList, { gcl_id: select.lid }) !== -1) {
+                            billsNode.is_change = 1;
+                        }
+                        billsTreeSpreadObj.reCalcCamount(billsNode);
+                        const loadResult = { update: [billsNode] };
+                        const refreshNode = billsTree.loadPostData(loadResult);
+                        billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+                    }, function () {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
                 return;
             }
 

+ 109 - 7
app/public/js/file_detail.js

@@ -371,11 +371,11 @@ $(document).ready(function() {
                 await filingObj.loadFiles(node, 1);
                 filingObj.refreshFilesTable();
             }
-            if (filingObj.curFiling.source_node.filing_type === 5) {
-                $('#rela-file-btn').show();
-            } else {
-                $('#rela-file-btn').hide();
-            }
+            // if (filingObj.curFiling.source_node.filing_type === 5) {
+            //     $('#rela-file-btn').show();
+            // } else {
+            //     $('#rela-file-btn').hide();
+            // }
             setLocalCache(this.curFilingKey, filingObj.curFiling.id);
         }
         findFiling(id) {
@@ -969,6 +969,9 @@ $(document).ready(function() {
             $('#tf-stage').change(function() {
                 self.selectTfStage(this.value);
             });
+            $('#tf-select').change(function() {
+                self.selectTfId(this.value);
+            });
             $('#rela-file-ok').click(function() {
                 const selectFiles = self.getSelectRelaFile();
                 filingObj.relaFiles(selectFiles, function() {
@@ -1041,9 +1044,19 @@ $(document).ready(function() {
                 await this.setCurTender(firstNode);
             }
         }
+        refreshTenderFileSelectId() {
+            if (['change', 'change_apply', 'change_plan', 'change_project'].indexOf(this.rfType.type) >= 0) {
+                const type = this.tenderFileType.find(x => { return x.value === this.rfType.type; });
+                const html = type.select.map(x => { return `<option value="${x.value}">${x.text}</option>`});
+                $('#tf-select').html(html.join('')).show();
+                this.rfType.selectId = type.select[0].value;
+            } else {
+                $('#tf-select').html('').hide();
+            }
+        }
         refreshTenderFileStage() {
             if (this.rfType.sub_type) {
-                const type = this.tenderFileType.find(x => { return x.value === this.rfType.type});
+                const type = this.tenderFileType.find(x => { return x.value === this.rfType.type; });
                 const subType = type.subType ? type.subType.find(x => { return x.value === this.rfType.sub_type; }) : null;
                 if (subType) {
                     this.rfType.stage = subType.stage[0].value;
@@ -1088,6 +1101,8 @@ $(document).ready(function() {
                     value: 'stage', text: '计量期',
                     subType: [
                         { value: 'att', text: '计量附件', stage: JSON.parse(JSON.stringify(stages)) },
+                        { value: 'dealPay', text: '合同支付', stage: JSON.parse(JSON.stringify(stages)) },
+                        { value: 'stageIm', text: '中间计量', stage: JSON.parse(JSON.stringify(stages)) },
                     ],
                 });
             }
@@ -1105,10 +1120,44 @@ $(document).ready(function() {
                     value: 'advance', text: '预付款', subType: advanceType
                 });
             }
+            if (tender.change && tender.change.length > 0) {
+                const selects = [];
+                tender.change.forEach(x => {
+                    selects.push({ value: x.cid, text: x.code })
+                });
+                this.tenderFileType.push({ value: 'change', text: '变更令', select: selects });
+            }
+            if (tender.change_plan && tender.change_plan.length > 0) {
+                const selects = [];
+                tender.change_plan.forEach(x => {
+                    selects.push({ value: x.cpid, text: x.code })
+                });
+                this.tenderFileType.push({ value: 'change_plan', text: '变更方案', select: selects });
+            }
+            if (tender.change_project && tender.change_project.length > 0) {
+                const selects = [];
+                tender.change_project.forEach(x => {
+                    selects.push({ value: x.cid, text: x.code })
+                });
+                this.tenderFileType.push({ value: 'change_project', text: '变更立项', select: selects });
+            }
+            if (tender.change_apply && tender.change_apply.length > 0) {
+                const selects = [];
+                tender.change_apply.forEach(x => {
+                    selects.push({ value: x.cid, text: x.code })
+                });
+                this.tenderFileType.push({ value: 'change_apply', text: '变更申请', select: selects });
+            }
             this.rfType = { type: this.tenderFileType[0].value };
             this.refreshTenderFileType();
             this.refreshTenderFileSubType();
             this.refreshTenderFileStage();
+            this.refreshTenderFileSelectId();
+        }
+        async selectTfId(id) {
+            this.rfType.selectId = id;
+            await this.loadFiles();
+            this.refreshFileTable();
         }
         async selectTfStage(stage){
             this.rfType.stage = stage;
@@ -1125,13 +1174,14 @@ $(document).ready(function() {
             this.rfType.type = type;
             this.refreshTenderFileSubType();
             this.refreshTenderFileStage();
+            this.refreshTenderFileSelectId();
             await this.loadFiles();
             this.refreshFileTable();
         }
         refreshFileTable() {
             const html = [];
             const typeStr = [];
-            const selectOption = $('option:selected');
+            const selectOption = $('option:selected', '#rela-file');
             selectOption.each((i, x) => {
                typeStr.push(x.innerText);
             });
@@ -1186,11 +1236,63 @@ $(document).ready(function() {
             if (!advance.files) advance.files = await this._loadRelaFiles(rfType);
             this.curFiles = advance.files;
         }
+        async _loadChangeFile() {
+            const rfType = this.rfType;
+            const change = this.curTender.source_node.change.find(x => {
+                return x.cid === rfType.selectId;
+            });
+            if (!change) {
+                this.curFiles = [];
+                return;
+            }
+            if (!change.files) change.files = await this._loadRelaFiles(rfType);
+            this.curFiles = change.files;
+        }
+        async _loadChangePlanFile() {
+            const rfType = this.rfType;
+            const change = this.curTender.source_node.change_plan.find(x => {
+                return x.id === rfType.selectId;
+            });
+            if (!change) {
+                this.curFiles = [];
+                return;
+            }
+            if (!change.files) change.files = await this._loadRelaFiles(rfType);
+            this.curFiles = change.files;
+        }
+        async _loadChangeProjectFile() {
+            const rfType = this.rfType;
+            const change = this.curTender.source_node.change_project.find(x => {
+                return x.id === rfType.selectId;
+            });
+            if (!change) {
+                this.curFiles = [];
+                return;
+            }
+            if (!change.files) change.files = await this._loadRelaFiles(rfType);
+            this.curFiles = change.files;
+        }
+        async _loadChangeApplyFile() {
+            const rfType = this.rfType;
+            const change = this.curTender.source_node.change_apply.find(x => {
+                return x.id === rfType.selectId;
+            });
+            if (!change) {
+                this.curFiles = [];
+                return;
+            }
+            if (!change.files) change.files = await this._loadRelaFiles(rfType);
+            this.curFiles = change.files;
+        }
         async loadFiles() {
             switch (this.rfType.type) {
                 case 'ledger': await this._loadLedgerFile(); break;
                 case 'stage': await this._loadStageFile(); break;
                 case 'advance': await this._loadAdvanceFile(); break;
+                case 'change': await this._loadChangeFile(); break;
+                case 'change_plan': await this._loadChangePlanFile(); break;
+                case 'change_project': await this._loadChangeProjectFile(); break;
+                case 'change_apply': await this._loadChangeApplyFile(); break;
             }
             this.initFilesId(this.curFiles);
         }

+ 11 - 0
app/public/js/financial_pay.js

@@ -2,6 +2,13 @@ let auditUtils;
 $(function () {
     autoFlashHeight();
 
+    $('#tid_select').select2({
+        language: 'zh-CN',
+        theme: 'bootstrap4',
+        selectOnClose: true,
+        // width: '150',
+    });
+
     $('#tid_select').change(function () {
         const tid = parseInt($(this).val()) || 0;
         setSelectValue('tid', tid);
@@ -853,6 +860,8 @@ $(function () {
                         historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
                     } else if (group.status === auditConst.status.checking) {
                         historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
+                    } else if (group.status === auditConst.status.checkAgain) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>');
                     } else {
                         historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
                     }
@@ -878,6 +887,8 @@ $(function () {
                             historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
                         } else if (auditor.status === auditConst.status.checking) {
                             historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>');
+                        } else if (auditor.status === auditConst.status.checkAgain) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-check"></i></span>');
                         }
                         historyHTML.push('</div>');
                         if (auditor.opinion) {

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

@@ -122,6 +122,12 @@ $(function(){
         }
     });
 
+    $('.account-page-size').each(function () {
+        if (getLocalCache('account-pageSize')) {
+            $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
+        }
+    });
+
     $('.change_sort_link').each(function () {
         const tender_id = $(this).attr('href').split('/')[2];
         const filterString = setChangeFilterData('change-'+ tender_id +'-list-order');

+ 150 - 2
app/public/js/ledger_check.js

@@ -28,8 +28,11 @@ const ledgerCheckType = {
             { value: 11, text: '遗漏计量(档案管理)', key: 'daglLost', type: 'dagl', },
         ]
     },
-    minus_cb: { value: 12, text: '负变更清单漏计', url: window.location.pathname + '/stageCheck?type=minus_cb' },
-    change_over: { value: 13, text: '变更令超计', url: window.location.pathname + '/stageCheck?type=change_over'},
+    multiLimit3f: {
+        value: 12, text: '联动计量', fun: 'check3fMultiLimit'
+    },
+    minus_cb: { value: 13, text: '负变更清单漏计', url: window.location.pathname + '/stageCheck?type=minus_cb' },
+    change_over: { value: 14, text: '变更令超计', url: window.location.pathname + '/stageCheck?type=change_over'},
 };
 const ledgerCheckUtil = {
     checkSibling: function (ledgerTree, ledgerPos, decimal, option) {
@@ -337,6 +340,149 @@ const ledgerCheckUtil = {
         }
         return error;
     },
+    check3fMultiLimit: function(ledgerTree, ledgerPos, decimal, option) {
+        const error = [];
+        if (option.limits.length === 0) return error;
+
+        const findPrecision = function (list, unit) {
+            if (unit) {
+                for (const p in list) {
+                    if (list[p].unit && list[p].unit === unit) {
+                        return list[p];
+                    }
+                }
+            }
+            return list.other;
+        };
+        const check3fTp = function (data, limit, range) {
+            if (limit === 0) {
+                if (data.contract_tp || data.pre_contract_tp) return 1; // 违规
+            }
+            if (limit === 1) {
+                const lower = ZhCalc.mul(data.total_price, ZhCalc.div(range.lower, 100, 4), decimal.tp);
+                const upper = ZhCalc.mul(data.total_price, ZhCalc.div(range.upper, 100, 4), decimal.tp);
+                const checkTp = ZhCalc.add(data.contract_tp, data.pre_contract_tp) || 0;
+                if (checkTp > upper) return 1; // 违规
+                if (checkTp < lower) return 2; // 漏计
+            }
+            return 0; // 合法
+        };
+        const check3fQty = function (data, limit, range, unit) {
+            if (limit === 0) {
+                if (data.contract_qty || data.qc_qty || data.pre_contract_qty || data.pre_qc_qty) return 1; // 违规
+            }
+            if (limit === 1) {
+                const precision = findPrecision(tenderInfo.precision, unit);
+                const lower = ZhCalc.mul(data.final_1_qty, ZhCalc.div(range.lower, 100, 4), precision.value);
+                const upper = ZhCalc.mul(data.final_1_qty, ZhCalc.div(range.upper, 100, 4), precision.value);
+                const checkQty = ZhCalc.add(data.contract_qty, data.pre_contract_qty) || 0;
+                if (checkQty > upper) return 1; // 违规
+                if (checkQty < lower) return 2; // 漏计
+            }
+            return 0; // 合法
+        };
+        const valueCheck = {
+            num(value, checkValue, operation) {
+                switch (operation) {
+                    case '=':
+                        return !_.isNil(value) ? value === checkValue : false;
+                    case '>':
+                        return !_.isNil(value) ? value > checkValue : false;
+                    case '<':
+                        return !_.isNil(value) ? value < checkValue : false;
+                    case '>=':
+                        return !_.isNil(value) ? value >= checkValue : false;
+                    case '<=':
+                        return !_.isNil(value) ? value <= checkValue : false;
+                    default:
+                        return true;
+                }
+            },
+            date(value, range, operation) {
+                const curDate = new Date();
+                switch (operation) {
+                    case '=':
+                        return moment(value).add({ days: range }).isSame(curDate, 'day');
+                    case '>':
+                        return moment(value).add({ days: range }).isBefore(curDate);
+                    case '<':
+                        return moment(value).add({ days: range }).isAfter(curDate);
+                    default:
+                        return true;
+                }
+            },
+            gxby(value, checkValue, operation) {
+                return valueCheck.num(value, checkValue, operation);
+            },
+            dagl(value, checkValue, operation) {
+                return valueCheck.num(value, checkValue, operation);
+            },
+            gxbyDate(checkDate, operation) {
+                return valueCheck.date(checkDate, operation);
+            }
+        };
+        const checkMultiCondition = function (data, condition) {
+            let result = true;
+            for (const c of condition) {
+                result = c.rela && c.rela === 'or'
+                    ? (result || valueCheck[c.check](data[c.field], c.value, c.operation))
+                    : (result && valueCheck[c.check](data[c.field], c.value, c.operation));
+            }
+            return result;
+        };
+        const checkMulti3f = function(bills, checkData, statusData, limitOption) {
+            if (!checkMultiCondition(statusData, limitOption.condition)) return;
+
+            if (bills.is_tp) return check3fTp(checkData, limitOption.limit, limitOption);
+            return check3fQty(checkData, limitOption.limit, limitOption, bills.unit);
+        };
+
+        const getErrorInfo = function(bills, lo, checkResult, pos = null) {
+            if (!checkResult || checkResult <= 0) return;
+            const errorType = checkResult === 1 ? '超计' : '漏计';
+            if (!lo.rangeHint) lo.rangeHint = lo.limit ? (lo.lower === lo.upper ? `${lo.lower}%` : `${lo.lower}%~${lo.upper}%`) : '不允许计量';
+            if (pos) {
+                error.push({ledger_id: ledgerTree.getNodeKey(bills), pid: pos.id, code: pos.code, b_code: '', name: pos.name, data: pos, errorType: 's2b_multi', memo: `${errorType}:${lo.hint}(${lo.rangeHint})` });
+            } else {
+                error.push({ledger_id: ledgerTree.getNodeKey(bills), pid: '', code: bills.code, b_code: bills.b_code, name: bills.name, data: bills, errorType: 's2b_multi', memo: `${errorType}:${lo.hint}(${lo.rangeHint})` });
+            }
+        };
+        const checkLeafBills3fMultiLimit = function(bills, multiLimit, leafXmj) {
+            if (!multiLimit) return;
+            const limitOption = option.limits.find(x => { return x.limit_id === multiLimit });
+            if (!limitOption) return;
+
+            const posRange = ledgerPos.getLedgerPos(bills.id);
+            if (posRange && posRange.length > 0) {
+                for (const p of posRange) {
+                    for (const lo of limitOption.options) {
+                        const checkResult = checkMulti3f(bills, p, p, lo);
+                        getErrorInfo(bills, lo, checkResult, p);
+                    }
+                }
+            } else {
+                for (const lo of limitOption) {
+                    const checkResult = checkMulti3f(bills, bills, leafXmj, lo);
+                    getErrorInfo(bills, lo, checkResult);
+                }
+            }
+        };
+        const recursiveCheckBills3fMultiLimit = function (bills, parentLimit = '', leafXmj = null) {
+            const limit = bills.multi_limit || parentLimit;
+            if (bills.children && bills.children.length > 0) {
+                for (const c of bills.children) {
+                    recursiveCheckBills3fMultiLimit(c, limit, c.b_code ? (bills.b_code ? leafXmj : bills) : c);
+                }
+            } else {
+                checkLeafBills3fMultiLimit(bills, limit, bills.b_code ? leafXmj : bills);
+            }
+        };
+
+        for (const x of ledgerTree.children) {
+            recursiveCheckBills3fMultiLimit(x);
+        }
+        return error;
+    },
 };
 
 const ledgerCheck2 = async function (setting) {
@@ -348,9 +494,11 @@ const ledgerCheck2 = async function (setting) {
             warningData.push({
                 type: checkType,
                 ledger_id: node.ledger_id,
+                pid: node.pid,
                 code: node.code,
                 b_code: node.b_code,
                 name: node.name,
+                memo: node.memo,
             })
         }
     };

+ 15 - 0
app/public/js/profile.js

@@ -7,6 +7,7 @@
  */
 $(document).ready(function() {
     autoFlashHeight();
+    $('[data-toggle="popover"]').popover();
     try {
         if (user !== '') {
             $(".sidebar-title").text(user);
@@ -111,6 +112,20 @@ $(document).ready(function() {
             $('#mobile-form').show();
         });
 
+        $('#hide-bind-btn').click(function () {
+            $('#mobile-form').hide();
+            $('#mobile-form').siblings('.form-group').show();
+        });
+
+        $('#del-mobile-btn').click(function () {
+            postData('/profile/dsk/api', { type: 'unbindMobile' }, function (response) {
+                toastr.success('解绑认证手机成功');
+                setTimeout(function () {
+                    location.reload();
+                }, 1000);
+            });
+        })
+
         // 移除签名
         $('#delete-sign').click(function () {
             postData('/profile/sign/delete', {}, function (result) {

+ 54 - 50
app/public/js/revise.js

@@ -536,55 +536,56 @@ $(document).ready(() => {
          * @param {Object} info
          */
         editEnded: function (e, info) {
-            if (info.sheet.zh_setting) {
-                const col = info.sheet.zh_setting.cols[info.col];
-                const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
-                const node = sortData[info.row];
-                const data = {
-                    id: node.id,
-                    tender_id: node.tender_id,
-                    ledger_id: node.ledger_id
-                };
-                // 未改变值则不提交
-                const orgValue = node[col.field];
-                if (info.editingText === null) info.editingText = '';
-                const newValue = col.wordWrap ? info.editingText : trimInvalidChar(info.editingText);
-                if (orgValue == info.editingText || ((!orgValue || orgValue === '') && (newValue === ''))) {
-                    return;
-                }
-                // 台账模式,检查计量单元相关
-                // if (isTz) {
-                    if (col.field === 'sgfh_qty' || col.field === 'sgfh_tp' ||
-                        col.field === 'sjcl_qty' || col.field === 'sjcl_tp' ||
-                        col.field === 'qtcl_qty' || col.field === 'qtcl_tp'||
-                        col.field === 'ex_qty1' || col.field === 'ex_tp1') {
-                        if (!node.children || node.children.length ===0) {
-                            const lPos = pos.getLedgerPos(node.id);
-                            if (lPos && lPos.length > 0) {
-                                toastr.error('清单含有计量单元,不可修改施工图复核数量');
-                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                                return;
-                            }
-                        }
-                    }
-                    if (col.field === 'b_code' && (newValue === '' || !newValue)) {
-                        const lPos = pos.getLedgerPos(node.id);
-                        if (lPos && lPos.length > 0) {
-                            toastr.error('清单含有计量单元,请先删除计量单元,再删除清单编号');
-                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                            return;
-                        }
-                    }
-                // }
-                if (col.field === 'node_type' && newValue && newValue !== '0' && newValue !== '19') {
-                    const sameNodeType = sortData.find(x => { return x.node_type == newValue; });
-                    if (sameNodeType) {
-                        toastr.error('已存在该费用类别,请勿重复选择');
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
+            const node = sortData[info.row];
+            const data = {
+                id: node.id,
+                tender_id: node.tender_id,
+                ledger_id: node.ledger_id
+            };
+            // 未改变值则不提交
+            const orgValue = node[col.field];
+            if (info.editingText === null) info.editingText = '';
+            const newValue = col.wordWrap ? info.editingText : trimInvalidChar(info.editingText);
+            if (orgValue == info.editingText || ((!orgValue || orgValue === '') && (newValue === ''))) {
+                return;
+            }
+            // 台账模式,检查计量单元相关
+            if (col.field === 'sgfh_qty' || col.field === 'sgfh_tp' ||
+                col.field === 'sjcl_qty' || col.field === 'sjcl_tp' ||
+                col.field === 'qtcl_qty' || col.field === 'qtcl_tp'||
+                col.field === 'ex_qty1' || col.field === 'ex_tp1') {
+                if (!node.children || node.children.length ===0) {
+                    const lPos = pos.getLedgerPos(node.id);
+                    if (lPos && lPos.length > 0) {
+                        toastr.error('清单含有计量单元,不可修改施工图复核数量');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
                 }
-                // 获取更新数据
+            }
+            if (col.field === 'b_code' && (newValue === '' || !newValue)) {
+                const lPos = pos.getLedgerPos(node.id);
+                if (lPos && lPos.length > 0) {
+                    toastr.error('清单含有计量单元,请先删除计量单元,再删除清单编号');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+            }
+
+            if (col.field === 'node_type' && newValue && newValue !== '0' && newValue !== '19') {
+                const sameNodeType = sortData.find(x => { return x.node_type == newValue; });
+                if (sameNodeType) {
+                    toastr.error('已存在该费用类别,请勿重复选择');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+            }
+            // 获取更新数据
+            if (col.type === 'Number') {
                 const exprInfo = getExprInfo(col.field);
                 if (info.editingText) {
                     const text = newValue;
@@ -626,12 +627,15 @@ $(document).ready(() => {
                         data[exprInfo.expr] = '';
                     }
                 }
-                // 更新至服务器
-                postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
-                    const refreshNode = billsTree.loadPostData(result);
-                    billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
-                });
+            } else {
+                data[col.field] = newValue;
             }
+
+            // 更新至服务器
+            postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
+                const refreshNode = billsTree.loadPostData(result);
+                billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
+            });
         },
         clipboardPasting: function (e, info) {
             const tree = info.sheet.zh_tree, setting = info.sheet.zh_setting;

+ 299 - 0
app/public/js/setting_s2b.js

@@ -0,0 +1,299 @@
+'use strict';
+$(() => {
+    autoFlashHeight();
+
+    const limitObj = (function(list){
+        const limits = list;
+        let curLimit;
+
+        const getConditionHtml = function(condition) {
+            const html = [];
+            for (const c of condition) {
+                if (html.length > 0) html.push('</br>');
+                html.push(`${c.alias} ${c.operation} ${c.value}`);
+            }
+            return html.join('');
+        };
+        const getOptionHtml = function(option) {
+            const html = [];
+            html.push('<td>', option.limit ? '<i class="fa fa-check"></i>' : '','</td>');
+            html.push(`<td>`, getConditionHtml(option.condition), '</td>');
+            html.push(`<td>${ (!option.limit ? '--' : option.lower + '%') } </td>`);
+            html.push(`<td>${ (!option.limit ? '--' : option.upper + '%') } </td>`);
+            html.push(`<td>${option.hint}</td>`);
+            html.push(`<td><a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="编辑" name="editLimitOption"><i class="fa fa-pencil fa-fw"></i></a>
+                <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="delLimitOption"><i class="fa fa-trash-o fa-fw text-danger"></i></a></td>`);
+            return html.join('');
+        };
+        const loadLimitOption = function(limit) {
+            const html = [];
+            for (const option of limit.options) {
+                html.push(`<tr class="text-center" optionId = "${option.id}">`, getOptionHtml(option), '</tr>');
+            }
+            $('#limitOptions').html(html.join(''));
+        };
+        const setCurLimit = function(limit) {
+            curLimit = limit;
+            if (!limit) return;
+            loadLimitOption(limit);
+            $('dd[limitId]').removeClass('bg-warning');
+            $(`dd[limitId=${curLimit.limit_id}]`).addClass('bg-warning');
+        };
+        const getCurLimit = function() {
+            return curLimit;
+        };
+        const getLimitCaptionHtml = function(limit) {
+            return `<div class="d-flex justify-content-between align-items-center table-file" limitId="${limit.limit_id}"><div>${limit.name}</div>` +
+                '    <div class="btn-group-table" style="display: none;">\n' +
+                '    <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="编辑" name="renameLimit"><i class="fa fa-pencil fa-fw"></i></a>\n' +
+                '    <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="delLimit"><i class="fa fa-trash-o fa-fw text-danger"></i></a>\n' +
+                '</div></div>';
+        };
+        const getLimitHtml = function(limit) {
+            const html = [];
+            html.push(`<dd class="list-group-item" limitId="${limit.limit_id}">`, getLimitCaptionHtml(limit), '</dd>');
+            return html.join('');
+        };
+
+        const addLimit = function() {
+            postData('/setting/limit/save', {add: 1}, function(result) {
+                limits.push(result.add);
+                $('#limit-list').append(getLimitHtml(result.add));
+            });
+        };
+        const renameLimit = function(limit_id, name) {
+            postData('/setting/limit/save', { update: { limit_id, name }}, function(result){
+                const limit = limits.find(x => { return x.limit_id === result.update.limit_id; });
+                limit.name = result.update.name;
+                limit.options.forEach(x => { x.name === limit.name; });
+                $(`dd[limitId=${limit.limit_id}]`).html(getLimitCaptionHtml(limit));
+            });
+        };
+        const delLimit = function(limit_id){
+            postData('/setting/limit/save', {del: limit_id}, function(result) {
+                $(`dd[limitId=${result.del}]`).remove();
+            });
+        };
+
+        const addOption = function(option) {
+            const limit = limits.find(x => { return x.limit_id === option.limit_id; });
+            limit.options.push(option);
+            loadLimitOption(curLimit);
+        };
+        const updateOption = function(option) {
+            const limit = limits.find(x => { return x.limit_id === option.limit_id; });
+            const orgOption = limit.options.find(x => { return x.id === option.id; });
+            if (!orgOption) return;
+            _.assignIn(orgOption, option);
+            loadLimitOption(curLimit);
+        };
+        const delOption = function (option) {
+            const limit = limits.find(x => { return x.limit_id === option.limit_id; });
+            const index = limit.options.findIndex(x => { return x.id === option.id; });
+            limit.options.splice(index, 1);
+            loadLimitOption(curLimit);
+        };
+
+        if (limits.length > 0) setCurLimit(limits[0]);
+        return { setCurLimit, getCurLimit, addLimit, delLimit, renameLimit, addOption, updateOption, delOption, }
+    })(limitList);
+
+
+    const optionSaveObj = (function(){
+        let limit_id, limit_name;
+        const addCheckHtml = function(check) {
+            const html = [];
+            html.push('<tr>', `<td locInfo="${check.alias}&^&${check.field}&^&${check.check}&^&${check.operation}&^&${check.value}">${check.alias} ${check.operation} ${check.value}</td>`,
+                '<td><a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="loc-del"><i class="fa fa-trash-o fa-fw text-danger"></i></a></td>', '</tr>');
+            $('#loc-list').append(html.join(''));
+        };
+
+        $('#loc-add').click(() => {
+            const field = $('#loc-field').val();
+            const locType = locInfo[field];
+            if (!locType) {
+                toastr.warning('未知判断条件');
+                return;
+            }
+
+            const data = JSON.parse(JSON.stringify(locType));
+            data.operation = $('#loc-operation').val();
+            data.value = $('#loc-value').val();
+            if (!data.value) {
+                toastr.warning('请输入判断值');
+                return;
+            }
+            data.value = _.toInteger(data.value);
+            if (_.isNil(data.value) || data.value < 0) {
+                toastr.warning('判断值仅限0、正整数');
+                return;
+            }
+            addCheckHtml(data);
+        });
+
+        $('body').on('click', 'a[name=loc-del]', function() {
+            $(this).parent().parent().remove();
+        });
+
+        const showOptionModal = function(option, limit) {
+            limit_id = option ? option.limit_id : limit.limit_id;
+            limit_name = option ? option.name : limit.name;
+            $('#lo-id').val(option ? option.id : '');
+            $('#lo-limit')[0].checked = option && option.limit;
+            $('#lo-lower').val(option ? option.lower : 0);
+            $('#lo-upper').val(option ? option.upper : 100);
+            $('#lo-hint').val(option ? option.hint : '');
+            $('#loc-list').html('');
+            if (option) {
+                for (const c of option.condition) {
+                    addCheckHtml(c);
+                }
+            }
+            $('#save-limit-option').modal('show');
+        };
+
+        const getOptionData = function() {
+            const data = {};
+            try {
+                data.limit = $('#lo-limit')[0].checked;
+                data.lower = parseInt($('#lo-lower').val());
+                data.upper = parseInt($('#lo-upper').val());
+                data.hint = $('#lo-hint').val();
+                data.condition = [];
+                const clist = $('td[locinfo]');
+                for (const c of clist) {
+                    const info = c.getAttribute('locinfo').split('&^&');
+                    data.condition.push({
+                        alias: info[0],
+                        field: info[1],
+                        check: info[2],
+                        operation: info[3],
+                        value: parseInt(info[4]),
+                    });
+                }
+                const id = $('#lo-id').val();
+                if (id) {
+                    data.id = id;
+                } else {
+                    data.limit_id = limit_id;
+                }
+                return data;
+            } catch(err) {
+                if (!data.lower) {
+                    toastr.warning('计量下限请输入0-100的整数');
+                    return;
+                }
+                if (!data.upper) {
+                    toastr.warning('计量上限请输入0-100的整数');
+                    return;
+                }
+                if (data.hint.length > 20) {
+                    toastr.warning('超限提示过长,请再精简');
+                    return;
+                }
+                toastr.warning('判断条件错误');
+                return;
+            }
+        };
+
+        return { show: showOptionModal, data: getOptionData, }
+    })();
+    $('#add-lo').click(() => {
+        optionSaveObj.show(null, limitObj.getCurLimit());
+    });
+    $('body').on('click', 'a[name=editLimitOption]', function() {
+        const optionId = $(this).parent().parent().attr('optionId');
+        const curLimit = limitObj.getCurLimit();
+        if (!curLimit) return;
+        const option = curLimit.options.find(x => { return x.id == optionId; });
+        optionSaveObj.show(option, curLimit);
+    });
+    $('body').on('click', 'a[name=delLimitOption]', function() {
+        const optionId = $(this).parent().parent().attr('optionId');
+        const curLimit = limitObj.getCurLimit();
+        if (!curLimit) return;
+        const option = curLimit.options.find(x => { return x.id == optionId; });
+        if (curLimit.options.length === 1) {
+            toastr.warning('当前配置,仅剩最后一个判断,如需删除,请直接删除配置');
+            return;
+        }
+
+        postData('/setting/limit/saveOption', { del: option }, function (result) {
+            if (result.del) limitObj.delOption(result.del);
+            $('#save-limit-option').modal('hide');
+        });
+    });
+
+    $('#save-lo-ok').click(function() {
+        const loData = optionSaveObj.data();
+        const updateData = {};
+        if (loData.id) {
+            updateData.update = loData;
+        } else {
+            updateData.add = loData;
+        }
+        postData('/setting/limit/saveOption', updateData, function (result) {
+            if (result.add) limitObj.addOption(result.add);
+            if (result.update) limitObj.updateOption(result.update);
+            $('#save-limit-option').modal('hide');
+        })
+    });
+
+    $('body').on('click', '.table-file', function(e) {
+        if (this.getAttribute('renaming') === '1') return;
+        if (e.target.tagName === 'A' || e.target.tagName === 'I' || e.target.tagName === 'INPUT') return;
+        const limitId = this.getAttribute('limitId');
+        const limit = limitList.find(x => { return x.limit_id === limitId; });
+        limitObj.setCurLimit(limit);
+    });
+    $('body').on('mouseenter', ".table-file", function(){
+        $(this).children(".btn-group-table").css("display","block");
+    });
+    $('body').on('mouseleave', ".table-file", function(){
+        $(this).children(".btn-group-table").css("display","none");
+    });
+
+    $('body').on('click', 'a[name=renameLimit]', function(e){
+        $(this).parents('.table-file').attr('renaming', '1');
+        $(`#${this.getAttribute('aria-describedby')}`).remove();
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        const limit = limitList.find(x => { return x.limit_id === limitId; });
+        if (!limit) return;
+
+        const html = [];
+        html.push(`<div><input type="text" class="form-control form-control-sm" style="width: 160px" value="${limit.name}"/></div>`);
+        html.push('<div class="btn-group-table" style="display: none;">',
+            `<a href="javascript: void(0)" name="renameOk" class="mr-1"><i class="fa fa-check fa-fw"></i></a>`,
+            `<a href="javascript: void(0)" class="mr-1" name="renameCancel"><i class="fa fa-remove fa-fw text-danger"></i></a>`, '</div>');
+        $(`.table-file[limitId=${limitId}]`).html(html.join(''));
+        e.stopPropagation();
+    });
+    $('body').on('click', 'a[name=renameOk]', function(){
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        const newName = $(this).parents('.table-file').find('input').val();
+        limitObj.renameLimit(limitId, newName);
+        $(this).parents('.table-file').attr('renaming', '0');
+    });
+    $('body').on('click', 'a[name=renameCancel]', function() {
+        $(this).parents('.table-file').attr('renaming', '0');
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        const limit = limitList.find(x => { return x.limit_id === limitId; });
+        if (!limit) return;
+
+        const html = [];
+        html.push(`<div>${limit.name}</div>`);
+        html.push('<div class="btn-group-table" style="display: none;">',
+            '<a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="编辑" name="renameLimit"><i class="fa fa-pencil fa-fw"></i></a>',
+            '<a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="delLimit"><i class="fa fa-trash-o fa-fw text-danger"></i></a>',
+            '</div>');
+        $(`.table-file[limitId=${limitId}]`).html(html.join(''));
+    });
+    $('body').on('click', 'a[name=delLimit]', function(e){
+        e.stopPropagation();
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        limitObj.delLimit(limitId);
+    });
+    $('#addLimit').click(function() {
+        limitObj.addLimit();
+    });
+});

+ 72 - 0
app/public/js/setting_tender.js

@@ -0,0 +1,72 @@
+$(document).ready(() => {
+    autoFlashHeight();
+    const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
+    const xmjSheet = xmjSpread.getActiveSheet();
+    const limitItems = limits.map(l => {
+        return { value: l.limit_id, text: l.name };
+    });
+    limitItems.unshift({ value: '', text: '' });
+    const setting = {
+        cols: [
+            { title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 180, formatter: '@', cellType: 'tree', readOnly: true, },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 360, formatter: '@', readOnly: true, },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit', readOnly: true, },
+            { title: '联动计量配置', colSpan: '1', rowSpan: '2', field: 'multi_limit', hAlign: 0, width: 150, cellType: 'customizeCombo', comboItems: limitItems },
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+    };
+    sjsSettingObj.setFxTreeStyle(setting, sjsSettingObj.FxTreeStyle.jz);
+    SpreadJsObj.initSheet(xmjSheet, setting);
+    const xmjTree = createNewPathTree('ledger', {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        keys: ['id', 'tender_id', 'ledger_id'],
+        autoExpand: 3,
+    });
+    const xmjTreeObj = {
+        /**
+         * 编辑单元格响应事件
+         * @param {Object} e
+         * @param {Object} info
+         */
+        editEnded: function (e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field !== 'multi_limit') return;
+
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            const updateData = { id: node.id, multi_limit: info.editingText || '' };
+            if (updateData.multi_limit === node.multi_limit || (!updateData.multi_limit && !node.multi_limit)) return;
+
+            // 更新至服务器
+            postData(window.location.pathname + '/update', updateData, function (result) {
+                const xmj = xmjTree.nodes.find(x => { return x.id === result.id });
+                if (xmj) xmj.multi_limit = result.multi_limit;
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+            }, function() {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+            });
+        },
+    };
+    xmjSpread.bind(spreadNS.Events.EditEnded, xmjTreeObj.editEnded);
+
+    postData(window.location.pathname + '/load', {filter: 'xmj;limit'}, function(result) {
+        result.limit.forEach(l => {
+            const rx = result.xmj.find(x => { return x.id === l.id; });
+            if (rx) rx.multi_limit = l.multi_limit;
+        });
+        xmjTree.loadDatas(result.xmj);
+        SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
+    });
+
+
+});

+ 2 - 1
app/public/js/shares/cs_tools.js

@@ -83,6 +83,7 @@ const showSelectTab = function(select, spread, afterShow) {
                                 case 's2b_over_dagl': return '违规计量(档案管理)';
                                 case 's2b_lost_gxby': return '遗漏计量(工序报验)';
                                 case 's2b_lost_dagl': return '遗漏计量(档案管理)';
+                                case 's2b_multi': return x.memo || '联动计量超限';
                                 case 'minus_cb': return '负变更清单漏计';
                                 case 'change_over': return '变更令超计';
                                 case 'settle': return '结算清单';
@@ -226,7 +227,7 @@ const showSelectTab = function(select, spread, afterShow) {
                         title: '类型', field: 'type', width: 150, formatter: '@',
                         getValue: function (data){
                             if (setting.checkType) {
-                                return checkTypeText[data.type] || '';
+                                return data.memo || checkTypeText[data.type] || '';
                             } else {
                                 return '';
                             }

+ 14 - 6
app/public/js/stage.js

@@ -232,6 +232,8 @@ $(document).ready(() => {
         font: '10px 微软雅黑'
     };
     let detail, searchLedger, checkedChanges;
+    const tpCalcFields = [{qty: 'qc_qty', tp: 'qc_tp'}];
+    if (tenderInfo.calc_type === 'up') tpCalcFields.shift({qty: 'contract_qty', tp: 'contract_tp'});
     const checkOption = {
         sibling: { enable: 0 },
         empty_code: { enable: 0 },
@@ -242,10 +244,7 @@ $(document).ready(() => {
         zero: { enable: 0 },
         tp: {
             enable: 1,
-            fields: [
-                {qty: 'contract_qty', tp: 'contract_tp'},
-                {qty: 'qc_qty', tp: 'qc_tp'},
-            ],
+            fields: tpCalcFields,
             filter: function (node) {
                 return node.is_tp;
             }
@@ -257,6 +256,7 @@ $(document).ready(() => {
     };
     if (tender.s2b_gxby_check) checkOption.limit3f.checkType.push('gxby');
     if (tender.s2b_dagl_check) checkOption.limit3f.checkType.push('dagl');
+    if (tender.s2b_multi_check) checkOption.multiLimit3f = { enable: 1, limits };
     // 界面布局
     autoFlashHeight();
     let featureDisplay;
@@ -339,7 +339,11 @@ $(document).ready(() => {
                 node.end_correct_tp = node.end_gather_tp;
             }
         }
-        node.tz2_tp = ZhCalc.sum([node.total_price, node.tz_qc_tp, node.tz_qc_minus_tp]);
+        if (tenderInfo.calc_type === 'tp') {
+            node.tz2_tp = ZhCalc.sum([node.total_price, node.tz_qc_tp]);
+        } else if (tenderInfo.calc_type === 'up') {
+            node.tz2_tp = ZhCalc.sum([node.total_price, node.tz_qc_tp, node.tz_qc_minus_tp]);
+        }
         node.end_gather_percent = ZhCalc.mul(ZhCalc.div(node.end_gather_tp, node.end_final_tp), 100, 2);
         node.end_correct_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_tp), 100, 2);
         node.final_dgn_price = ZhCalc.round(ZhCalc.div(node.end_gather_tp, ZhCalc.add(node.deal_dgn_qty1, node.c_dgn_qty1)), tenderInfo.decimal.up);
@@ -1559,7 +1563,11 @@ $(document).ready(() => {
 
                     if (!node) continue;
                     node.tz_qc_qty = ZhCalc.add(node.tz_qc_qty, changeBills.checked_amount);
-                    node.tz_qc_tp = ZhCalc.add(node.tz_qc_tp, changeBills.checked_price);
+                    if (tenderInfo.calc_type === 'up') {
+                        node.tz_qc_tp = ZhCalc.add(node.tz_qc_tp, changeBills.checked_price);
+                    } else {
+                        node.tz_qc_tp = ZhCalc.add(node.tz_qc_tp, changeBills.tp);
+                    }
 
                     const posData = pos.getLedgerPos(node.id);
                     if (posData && posData.length > 0) {

+ 55 - 4
app/public/js/stage_im.js

@@ -3,6 +3,13 @@
 /**
  *
  *
+ * const imType = {
+     zl: { value: 0, name: '总量控制' },
+     tz: { value: 1, name: '项目节-清单' },
+     bw: { value: 2, name: '清单-计量单元' },
+     bb: { value: 3, name: '计量单元-清单' },
+   };
+ *
  * @author Mai
  * @date
  * @version
@@ -789,7 +796,9 @@ const stageIm = (function () {
             lx = {
                 lxid: leafXmj.id,
                 code: leafXmj.code,
-                name: leafXmj.name
+                name: leafXmj.name,
+                contract_expr: [],
+                qc_expr: [],
             };
             im.leafXmjs.push(lx);
         }
@@ -797,6 +806,8 @@ const stageIm = (function () {
         lx.contract_jl = ZhCalc.add(lx.contract_jl, node.contract_qty);
         lx.qc_jl = ZhCalc.add(lx.qc_jl, node.qc_qty);
         lx.qc_minus_jl = ZhCalc.add(lx.qc_minus_jl, node.qc_minus_qty);
+        lx.contract_expr.push(node.contract_expr);
+        lx.qc_expr.push(node.qc_qty);
         generateZlPosData(node, lx);
     }
     function generateZlChangeData(node, im) {
@@ -901,13 +912,33 @@ const stageIm = (function () {
                         im_type: imType.bw.value,
                         visible: !node.filter,
                     };
-                    im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + (im.qc_minus_jl ? (` (不计价 ${im.qc_minus_jl}) `) : ' ') + im.unit;
                     ImData.push(im);
                     for (const c of changes) {
                         if (c.lid === p.id && c.pid === pp.id && c.qty && c.qty !== 0) {
                             im.changes.push(c);
                         }
                     }
+                    const calc_memo = [];
+                    calc_memo.push('本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + (im.qc_minus_jl ? (` (不计价 ${im.qc_minus_jl}) `) : ' ') + im.unit);
+                    const contract_expr = pp.contract_expr ? pp.contract_qty + '=' + pp.contract_expr : (pp.contract_qty || 0);
+                    let qc_expr = '', minus_qc_expr = '';
+                    im.changes.forEach(x => {
+                        if (x.no_value) {
+                            if (minus_qc_expr) {
+                                minus_qc_expr = minus_qc_expr + (x.qty > 0 ? '+' + x.qty : x.qty);
+                            } else {
+                                minus_qc_expr = minus_qc_expr + x.qty;
+                            }
+                        } else {
+                            if (qc_expr) {
+                                qc_expr = qc_expr + (x.qty > 0 ? '+' + x.qty : x.qty);
+                            } else {
+                                qc_expr = qc_expr + x.qty;
+                            }
+                        }
+                    });
+                    calc_memo.push(`其中合同计量:${contract_expr};变更计量:${qc_expr || '0'};变更不计价:${minus_qc_expr || '0'}`);
+                    im.calc_memo =  calc_memo.join('\n');
                 }
             } else {
                 if (checkZero(p.gather_qty) && checkZero(p.gather_tp) && checkZero(p.qc_minus_qty)) { continue }
@@ -928,14 +959,34 @@ const stageIm = (function () {
                     im_type: imType.bw.value,
                     visible: !node.filter,
                 };
-                im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + (im.qc_minus_jl ? (` (不计价 ${im.qc_minus_jl}) `) : ' ') + im.unit;
                 ImData.push(im);
                 for (const c of changes) {
                     if (c.lid === p.id && c.pid == -1 && c.qty && c.qty !== 0) {
                         im.changes.push(c);
                     }
                 }
-                importChanges.forEach(x => { if (x.lid === p.id) im.changes.push(x); });
+                importChanges.forEach(x => { if (x.lid === p.id && x.qty) im.changes.push(x); });
+                const calc_memo = [];
+                calc_memo.push('本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + (im.qc_minus_jl ? (` (不计价 ${im.qc_minus_jl}) `) : ' ') + im.unit);
+                const contract_expr = p.contract_expr ? p.contract_qty + '=' + p.contract_expr : (p.contract_qty || 0);
+                let qc_expr = '', minus_qc_expr = '';
+                im.changes.forEach(x => {
+                    if (x.no_value) {
+                        if (minus_qc_expr) {
+                            minus_qc_expr = minus_qc_expr + (x.qty > 0 ? '+' + x.qty : x.qty);
+                        } else {
+                            minus_qc_expr = minus_qc_expr + x.qty;
+                        }
+                    } else {
+                        if (qc_expr) {
+                            qc_expr = qc_expr + (x.qty > 0 ? '+' + x.qty : x.qty);
+                        } else {
+                            qc_expr = qc_expr + x.qty;
+                        }
+                    }
+                });
+                calc_memo.push(`其中合同计量:${contract_expr};变更计量:${qc_expr || '0'};变更不计价:${minus_qc_expr || '0'}`);
+                im.calc_memo = calc_memo.join('\n');
             }
         }
     }

+ 7 - 0
app/router.js

@@ -184,6 +184,12 @@ module.exports = app => {
     app.get('/setting/api', sessionAuth, 'settingController.s2b');
     app.post('/setting/api/update', sessionAuth, 'settingController.s2bUpdate');
     app.post('/setting/api/update-status', sessionAuth, 'settingController.s2bUpdateStatus');
+    // 联动计量
+    app.post('/setting/limit/save', sessionAuth, projectManagerCheck, 'settingController.saveLimit');
+    app.post('/setting/limit/saveOption', sessionAuth, projectManagerCheck, 'settingController.saveLimitOption');
+    app.get('/setting/api/tender/:id', sessionAuth, projectManagerCheck, tenderCheck, 'settingController.tender');
+    app.post('/setting/api/tender/:id/load', sessionAuth, projectManagerCheck, tenderCheck, 'settingController.tenderLoad');
+    app.post('/setting/api/tender/:id/update', sessionAuth, projectManagerCheck, tenderCheck, 'settingController.tenderUpdate');
     // 显示设置
     app.get('/setting/show', sessionAuth, 'settingController.show');
     app.post('/setting/show/update', sessionAuth, 'settingController.showListUpdate');
@@ -440,6 +446,7 @@ module.exports = app => {
     app.get('/sp/:id/financial/pay/:fpid/file/:fid/download', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, 'financialController.payDownloadFile');
     app.post('/sp/:id/financial/pay/:fpid/audit/start', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, financialPayAuditCheck, 'financialController.startPayAudit');
     app.post('/sp/:id/financial/pay/:fpid/audit/check', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, 'financialController.checkPayAudit');
+    app.post('/sp/:id/financial/pay/:fpid/audit/check/again', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, 'financialController.checkPayAgain');
     app.get('/sp/:id/financial/summary', sessionAuth, subProjectCheck, financialCheck, 'financialController.summary');
     app.post('/sp/:id/financial/summary/load', sessionAuth, subProjectCheck, financialCheck, 'financialController.summaryLoad');
 

+ 16 - 2
app/service/financial_pay.js

@@ -38,7 +38,7 @@ module.exports = app => {
          * @param {int} hadlimit - 分页
          * @return {object} list - 列表
          */
-        async getListByStatus(spid, status = 0, tid = null, used = null) {
+        async getListByStatus(spid, status = 0, tid = null, used = null, hadlimit = 0, sortBy = '', orderBy = '') {
             let addSql = '';
             if (tid !== null) {
                 if (tid.length === 0) {
@@ -85,7 +85,21 @@ module.exports = app => {
                 default:
                     break;
             }
-            sql += ' ORDER BY a.create_time DESC';
+            if (sortBy && orderBy) {
+                if (sortBy === 'code') {
+                    sql += ' ORDER BY CHAR_LENGTH(a.code) ' + orderBy + ',convert(a.code using gbk) ' + orderBy;
+                } else {
+                    sql += ' ORDER BY a.create_time ' + orderBy;
+                }
+            } else {
+                sql += ' ORDER BY a.create_time DESC';
+            }
+            if (hadlimit) {
+                const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
+                const offset = limit * (this.ctx.page - 1);
+                const limitString = offset >= 0 ? offset + ',' + limit : limit;
+                sql += ' LIMIT ' + limitString;
+            }
             const list = await this.db.query(sql, sqlParam);
             return list;
         }

+ 63 - 0
app/service/financial_pay_audit.js

@@ -458,6 +458,69 @@ module.exports = app => {
         }
 
         /**
+         * 重新审批变更申请
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async checkAgain(fp) {
+            const accountId = this.ctx.session.sessionUser.accountId;
+            // 初始化事务
+            const time = new Date();
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const noYbAuditors = fp.auditors.filter(x => { return x.audit_order !== 0; });
+                const noYbMaxOrder = noYbAuditors[noYbAuditors.length - 1].order;
+                const maxOrder = fp.auditors[fp.auditors.length - 1].order;
+                const audits = fp.auditors.filter(x => { return x.order === noYbMaxOrder; });
+                if (!audits || audits.length === 0 || maxOrder < 1) throw '审核数据错误';
+                const selfAudit = audits.find(x => { return x.aid === accountId; });
+                if (!selfAudit) throw '当前标段您无权审批';
+                // 当前审批人2次添加至流程中
+                const checkAgainAuditors = [];
+                audits.forEach(x => {
+                    checkAgainAuditors.push({
+                        spid: fp.spid, tid: fp.tid, fpid: fp.id, aid: x.aid,
+                        times: x.times, order: maxOrder + 1,
+                        status: !selfAudit || x.aid === selfAudit.aid ? auditConst.status.checkAgain : auditConst.status.checkSkip,
+                        begin_time: time, end_time: time, opinion: '',
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                    });
+                });
+                const checkingAuditors = [];
+                audits.forEach(x => {
+                    checkingAuditors.push({
+                        spid: fp.spid, tid: fp.tid, fpid: fp.id, aid: x.aid,
+                        times: x.times, order: maxOrder + 2,
+                        status: auditConst.status.checking,
+                        begin_time: time,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                    });
+                });
+                await transaction.insert(this.tableName, checkAgainAuditors);
+                const checkingAuditors_result = await transaction.insert(this.tableName, checkingAuditors);
+                // 获取刚批量添加的所有list
+                // for (let j = 0; j < checkingAuditors.length; j++) {
+                //     checkingAuditors[j].id = checkingAuditors_result.insertId + j;
+                // }
+
+                // 设置审批中
+                await transaction.update(this.ctx.service.financialPay.tableName, {
+                    id: fp.id,
+                    status: auditConst.status.checking,
+                    entities: '',
+                    final_auditor_str: '',
+                });
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
+
+        /**
          * 获取审核人需要审核的期列表
          *
          * @param auditorId

+ 30 - 2
app/service/financial_pay_tender_audit.js

@@ -24,6 +24,32 @@ module.exports = app => {
             this.dataId = 'id';
         }
 
+        async getAllList(spid, tids, accountList = null) {
+            if (accountList === null) {
+                accountList = await this.ctx.service.projectAccount.getAllDataByCondition({
+                    where: { project_id: this.ctx.session.sessionProject.id, enable: 1 },
+                    columns: ['id', 'account', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
+                });
+            }
+            const list = await this.db.select(this.tableName, { where: { spid, tid: tids }, orders: [['id', 'asc']] });
+            const removeList = [];
+            for (const l of list) {
+                const accountInfo = this._.find(accountList, { id: l.uid });
+                if (!accountInfo) {
+                    removeList.push(l.id);
+                    continue;
+                }
+                l.name = accountInfo.name;
+                l.company = accountInfo.company;
+            }
+            if (removeList.length > 0) {
+                for (const r of removeList) {
+                    list.splice(this._.findIndex(list, { id: r }), 1);
+                }
+            }
+            return list;
+        }
+
         async getList(spid, tid, accountList = null) {
             if (accountList === null) {
                 accountList = await this.ctx.service.projectAccount.getAllDataByCondition({
@@ -84,12 +110,14 @@ module.exports = app => {
                         subProjPermission.fund_pay_permission = subProjPermission.fund_pay_permission ? this._.map(subProjPermission.fund_pay_permission.split(','), this._.toInteger) : [];
                         const financialPermission = await this.ctx.service.subProjPermission.getFinancailPermission(subProjPermission.fund_trans_permission, subProjPermission.fund_pay_permission);
                         if (!financialPermission.pay_show) {
+                            subProjPermission.fund_pay_permission.push(1);
                             const upPermission = {
                                 id: subProjPermission.id,
-                                fund_pay_permission: subProjPermission.fund_pay_permission.push(1).join(','),
+                                fund_pay_permission: subProjPermission.fund_pay_permission.join(','),
                             };
                             if (!financialPermission.transfer_show) {
-                                upPermission.fund_trans_permission = subProjPermission.fund_trans_permission.push(1).join(',');
+                                subProjPermission.fund_trans_permission.push(1);
+                                upPermission.fund_trans_permission = subProjPermission.fund_trans_permission.join(',');
                             }
                             updateSubProjPermissionData.push(upPermission);
                         }

+ 8 - 2
app/service/financial_transfer.js

@@ -21,8 +21,14 @@ module.exports = app => {
             this.tableName = 'financial_transfer';
         }
 
-        async getList(spid) {
-            const sql = 'SELECT ft.*, pa.`name` as username, pa.`company` FROM ?? as ft LEFT JOIN ?? as pa ON ft.`uid` = pa.`id` WHERE ft.`spid` = ? ORDER BY ft.`id` DESC';
+        async getList(spid, pageLimit = 0) {
+            let sql = 'SELECT ft.*, pa.`name` as username, pa.`company` FROM ?? as ft LEFT JOIN ?? as pa ON ft.`uid` = pa.`id` WHERE ft.`spid` = ? ORDER BY ft.`id` DESC';
+            if (pageLimit) {
+                const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
+                const offset = limit * (this.ctx.page - 1);
+                const limitString = offset >= 0 ? offset + ',' + limit : limit;
+                sql += ' LIMIT ' + limitString;
+            }
             const sqlParams = [this.tableName, this.ctx.service.projectAccount.tableName, spid];
             const list = await this.db.query(sql, sqlParams);
             for (const l of list) {

+ 8 - 2
app/service/financial_transfer_tender.js

@@ -21,8 +21,14 @@ module.exports = app => {
             this.tableName = 'financial_transfer_tender';
         }
 
-        async getList(trid) {
-            const sql = 'SELECT ftt.*, t.`name` as name FROM ?? as ftt LEFT JOIN ?? as t ON ftt.`tid` = t.`id` WHERE ftt.`trid` = ?';
+        async getList(trid, pageLimit = 0) {
+            let sql = 'SELECT ftt.*, t.`name` as name FROM ?? as ftt LEFT JOIN ?? as t ON ftt.`tid` = t.`id` WHERE ftt.`trid` = ?';
+            if (pageLimit) {
+                const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
+                const offset = limit * (this.ctx.page - 1);
+                const limitString = offset >= 0 ? offset + ',' + limit : limit;
+                sql += ' LIMIT ' + limitString;
+            }
             const sqlParams = [this.tableName, this.ctx.service.tender.tableName, trid];
             const list = await this.db.query(sql, sqlParams);
             for (const l of list) {

+ 9 - 0
app/service/ledger_extra.js

@@ -31,6 +31,15 @@ module.exports = app => {
                 columns: ['id', ...columns],
             });
         }
+
+        async updateMultiLimit(id, multi_limit) {
+            const exist = await this.getDataById(id);
+            if (exist) {
+                await this.defaultUpdate({ id, multi_limit });
+            } else {
+                await this.db.insert(this.tableName, { id, tid: this.ctx.tender.id, multi_limit });
+            }
+        }
     }
 
     return LedgerExtra;

+ 126 - 0
app/service/multi_limit.js

@@ -0,0 +1,126 @@
+'use strict';
+
+/**
+ * 联合限制计量
+ *
+ * @author Mai
+ * @date 2024/6/27
+ * @version
+ */
+
+module.exports = app => {
+
+    class MultiLimit extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'multi_limit';
+        }
+
+        async getLimitList(pid) {
+            const sql = `SELECT limit_id, name FROM ${this.tableName} WHERE pid = ? GROUP BY limit_id`;
+            return await this.db.query(sql, [pid]);
+        }
+
+        analysisLimits(datas) {
+            const result = [];
+            datas.forEach(x => {
+                x.condition = x.condition ? JSON.parse(x.condition) : [];
+                let limit = result.find(r => { return x.limit_id === r.limit_id; });
+                if (!limit) {
+                    limit = { limit_id: x.limit_id, name: x.name, options: [] };
+                    result.push(limit);
+                }
+                limit.options.push(x);
+            });
+            return result;
+        }
+
+        async getLimits(pid) {
+            const result = await this.getAllDataByCondition({
+                columns: ['id', 'limit_id', 'name', 'limit', 'lower', 'upper', 'condition', 'hint'],
+                where: { pid },
+            });
+            return this.analysisLimits(result);
+        }
+
+        async getLimitOptions(limit_id) {
+            const result = await this.getAllDataByCondition({
+                columns: ['limit_id', 'name', 'limit', 'lower', 'upper', 'condition', 'hint'],
+                where: { limit_id },
+            });
+            result.forEach(x => { x.condition = x.condition ? JSON.parse(x.condition) : []; });
+            return result;
+        }
+
+        async getLimitOption(id) {
+            const result = await this.getDataById(id);
+            result.condition = result.condition ? JSON.parse(result.condition) : [];
+            return result;
+        }
+
+        async _delLimit(id) {
+            await this.db.delete(this.tableName, { limit_id: id });
+            return id;
+        }
+
+        async _saveLimit(id, name) {
+            if (id) {
+                await this.db.update(this.tableName, { name }, { where: { limit_id: id } });
+                return { limit_id: id, name };
+            }
+
+            const newLimitOption = {
+                limit_id: this.uuid.v4(), pid: this.ctx.session.sessionProject.id,
+                user_id: this.ctx.session.sessionUser.accountId,
+                name: name || '新增计量配置', condition: '[]', limit: 1,
+            };
+            const result = await this.db.insert(this.tableName, newLimitOption);
+            const lo = await this.getLimitOption(result.insertId);
+            return { limit_id: lo.limit_id, name: lo.name, options: [lo] };
+        }
+
+        async saveLimit(data) {
+            const result = {};
+            if (data.add) result.add = await this._saveLimit();
+            if (data.del) result.del = await this._delLimit(data.del);
+            if (data.update) result.update = await this._saveLimit(data.update.limit_id, data.update.name);
+            return result;
+        }
+
+        async _saveLimitOption(data) {
+            if (data.id) {
+                data.condition = JSON.stringify(data.condition);
+                await this.db.update(this.tableName, data);
+            } else {
+                data.pid = this.ctx.session.sessionProject.id;
+                data.user_id = this.ctx.session.sessionUser.accountId;
+                data.condition = JSON.stringify(data.condition);
+                const result = await this.db.insert(this.tableName, data);
+                data.id = result.insertId;
+            }
+            return await this.getLimitOption(data.id);
+        }
+
+        async _delLimitOption(data) {
+            await this.deleteById(data.id);
+            return data;
+        }
+
+        async saveLimitOption(data) {
+            const result = {};
+            if (data.add) result.add = await this._saveLimitOption(data.add);
+            if (data.del) result.del = await this._delLimitOption(data.del);
+            if (data.update) result.update = await this._saveLimitOption(data.update);
+            return result;
+        }
+    }
+
+    return MultiLimit;
+};

+ 26 - 1
app/service/shenpi_audit.js

@@ -68,6 +68,31 @@ module.exports = app => {
             return await this.db.query(sql, sqlParam);
         }
 
+        async getAllAuditGroupList(tids, type, status, group_id = 0) {
+            const sql = 'SELECT sp.tid, sp.audit_id, sp.audit_type, sp.audit_order, sp.sp_group, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
+                ' WHERE sp.tid in (' + this.ctx.helper.getInArrStrSqlFilter(tids) + ') AND sp.sp_type = ? AND sp.sp_status = ? AND sp.sp_group = ? ORDER BY sp.audit_order ASC, sp.id ASC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, type, status, group_id];
+            const audits = await this.db.query(sql, sqlParam);
+            const result = [];
+            for (const t of tids) {
+                const oneResult = [];
+                const tidAudits = audits.filter(a => a.tid === t);
+                for (const a of tidAudits) {
+                    if (a.audit_order > 0) {
+                        if (oneResult[a.audit_order - 1]) {
+                            oneResult[a.audit_order - 1].push(a);
+                        } else {
+                            oneResult.push([a]);
+                        }
+                    } else {
+                        oneResult.push([a]);
+                    }
+                }
+                result.push({ tid: t, audits: oneResult });
+            }
+            return result;
+        }
+
         async getAuditGroupList(tid, type, status, group_id = 0) {
             const sql = 'SELECT sp.audit_id, sp.audit_type, sp.audit_order, sp.sp_group, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
                 ' WHERE sp.tid = ? AND sp.sp_type = ? AND sp.sp_status = ? AND sp.sp_group = ? ORDER BY sp.audit_order ASC, sp.id ASC';
@@ -443,7 +468,7 @@ module.exports = app => {
                     audit_id: a.aid || a.uid || a.audit_id,
                     audit_order: a.audit_order,
                     audit_type: a.audit_type,
-                    audit_ledger_id: a.audit_ledger_id,
+                    audit_ledger_id: a.audit_ledger_id || '',
                     sp_group,
                 });
             }

+ 47 - 13
app/service/stage_bills.js

@@ -171,12 +171,38 @@ module.exports = app => {
             return await this.db.queryOne(sql, sqlParam);
         }
 
-        _calcStageBillsData(data, orgData, ledgerData) {
+        _calcContractTpByTp(bills, preStageBills, stageBills, decimal) {
+            let activeQty = this.ctx.helper.add(bills.quantity, stageBills.qc_minus_qty);
+            let end_contract_qty = stageBills.contract_qty;
+            if (preStageBills) {
+                activeQty = this.ctx.helper.add(activeQty, preStageBills.qc_minus_qty);
+                end_contract_qty = this.ctx.helper.add(end_contract_qty, preStageBills.contract_qty);
+            }
+            const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), bills.total_price, decimal.tp);
+            return preStageBills ? this.ctx.helper.sub(end_contract_tp, preStageBills.contract_tp) : end_contract_tp;
+        }
+        async calcContractTp(info, stage, bills, stageBills) {
+            if (info.calc_type === 'tp') {
+                const preStageBills = await this.ctx.service.stageBillsFinal.getDataByCondition({ tid: stage.tid, sorder: stage.order - 1, lid: bills.id });
+                return this._calcContractTpByTp(bills, preStageBills, stageBills, info.decimal);
+            } else if (info.calc_type === 'up') {
+                return this.ctx.helper.mul(stageBills.contract_qty, bills.unit_price, info.decimal.tp);
+            }
+        }
+        async _calcStageBillsData(data, orgData, ledgerData) {
             const info = this.ctx.tender.info;
             const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, ledgerData.unit);
+            if (data.qc_minus_qty !== undefined) {
+                data.qc_minus_qty = data.qc_minus_qty ? this.round(data.qc_minus_qty, precision.value) : 0;
+            } else if (data.contract_qty !== undefined) {
+                data.qc_minus_qty = orgData.qc_minus_qty;
+            }
             if (data.contract_qty !== undefined) {
                 data.contract_qty = this.round(data.contract_qty, precision.value);
-                data.contract_tp = this.ctx.helper.mul(data.contract_qty, ledgerData.unit_price, info.decimal.tp);
+                data.contract_tp = await this.calcContractTp(info, this.ctx.stage, ledgerData, data);
+            } else if (data.qc_minus_qty !== undefined) {
+                data.contract_qty = orgData.contract_qty;
+                data.contract_tp = await this.calcContractTp(info, this.ctx.stage, ledgerData, data);
             }
             if (data.qc_qty !== undefined) {
                 data.qc_qty = this.round(data.qc_qty, precision.value);
@@ -224,9 +250,12 @@ module.exports = app => {
             }
             const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, ledgerData.unit);
 
+            if (insertData.qc_minus_qty !== undefined) {
+                d.qc_minus_qty = insertData.qc_minus_qty ? this.round(insertData.qc_minus_qty, precision.value) : 0;
+            }
             if (insertData.contract_qty !== undefined) {
                 d.contract_qty = this.round(insertData.contract_qty, precision.value);
-                d.contract_tp = this.ctx.helper.mul(d.contract_qty, ledgerData.unit_price, info.decimal.tp);
+                d.contract_tp = await this.calcContractTp(info, this.ctx.stage, ledgerData, d);
             }
             if (insertData.contract_expr !== undefined) d.contract_expr = insertData.contract_expr;
             if (insertData.qc_qty !== undefined) {
@@ -241,9 +270,6 @@ module.exports = app => {
                 d.negative_qc_qty = this.round(insertData.negative_qc_qty, precision.value);
                 d.negative_qc_tp = this.ctx.helper.mul(d.negative_qc_qty, ledgerData.unit_price, info.decimal.tp);
             }
-            if (insertData.qc_minus_qty !== undefined) {
-                d.qc_minus_qty = insertData.qc_minus_qty ? this.round(insertData.qc_minus_qty, precision.value) : 0;
-            }
             if (insertData.ex_stage_qty1 !== undefined) {
                 d.ex_stage_qty1 = this.round(insertData.ex_stage_qty1, precision.value);
                 d.ex_stage_tp1 = this.ctx.helper.mul(d.ex_stage_qty1, ledgerData.unit_price, info.decimal.tp);
@@ -275,7 +301,7 @@ module.exports = app => {
                     } else {
                         d.id = stageBills.id;
                         d.said = this.ctx.session.sessionUser.accountId;
-                        this._calcStageBillsData(d, stageBills, ledgerBills);
+                        await this._calcStageBillsData(d, stageBills, ledgerBills);
                         await transaction.update(this.tableName, d);
                     }
                 }
@@ -300,12 +326,13 @@ module.exports = app => {
             if (stageBills) {
                 if ((data.contract_qty === undefined || stageBills.contract_qty !== data.contract_qty) ||
                     (data.qc_qty === undefined || stageBills.qc_qty !== data.qc_qty) ||
+                    (data.qc_minus_qty === undefined || stageBills.qc_minus_qty !== data.qc_minus_qty) ||
                     (data.ex_stage_qty1 === undefined || stageBills.ex_stage_qty1 !== data.ex_stage_qty1)
                 ) {
                     if (stageBills.times === this.ctx.stage.curTimes && stageBills.order === this.ctx.stage.curOrder) {
                         data.id = stageBills.id;
                         data.said = this.ctx.session.sessionUser.accountId;
-                        this._calcStageBillsData(data, stageBills, ledgerBills);
+                        await this._calcStageBillsData(data, stageBills, ledgerBills);
                         await transaction.update(this.tableName, data);
                     } else {
                         await this._insertStageBillsData(transaction, data, stageBills, ledgerBills);
@@ -323,22 +350,29 @@ module.exports = app => {
          * @param {Object} transaction - 操作所属事务
          * @return {Promise<void>}
          */
-        async calc(tid, sid, lid, transaction) {
+        async calc(tid, stage, lid, transaction) {
             const info = this.ctx.tender.info;
-            const stageBills = await this.getLastestStageData2(tid, sid, lid);
+            const stageBills = await this.getLastestStageData2(tid, stage.id, lid);
             const ledgerBills = await this.ctx.service.ledger.getCompleteDataById(lid);
             if (!ledgerBills) {
                 throw '提交数据错误';
             }
-            const posGather = await this.ctx.service.stagePos.getPosGather(tid, sid, lid, transaction);
+            const posGather = await this.ctx.service.stagePos.getPosGather(tid, stage.id, lid, transaction);
             if (!posGather) { return; }
-            posGather.qc_minus_qty = posGather.qc_minus_qty || 0;
+            if (posGather.qc_minus_qty !== undefined) {
+                posGather.qc_minus_qty = posGather.qc_minus_qty || 0;
+            } else if (posGather.contract_qty !== undefined) {
+                posGather.qc_minus_qty = stageBills.qc_minus_qty;
+            }
 
             const precision = this.ctx.helper.findPrecision(info.precision, ledgerBills.unit);
             // 计算
             if (posGather.contract_qty !== undefined) {
                 posGather.contract_qty = this.round(posGather.contract_qty, precision.value);
-                posGather.contract_tp = this.ctx.helper.mul(posGather.contract_qty, ledgerBills.unit_price, info.decimal.tp);
+                posGather.contract_tp = await this.calcContractTp(info, stage, ledgerBills, posGather);
+            } else if (posGather.qc_minus_qty) {
+                posGather.contract_qty = stageBills.contract_qty;
+                posGather.contract_tp = await this.calcContractTp(info, stage, ledgerBills, posGather);
             }
             if (posGather.qc_qty !== undefined) {
                 posGather.qc_qty = this.round(posGather.qc_qty, precision.value);

+ 22 - 6
app/service/stage_change.js

@@ -16,6 +16,7 @@ const changeConst = require('../const/change');
 class autoUseChange {
     constructor(helper, info, settleStatus) {
         this.helper = helper;
+        this.info = info;
         this.precision = info.precision;
         this.decimal = info.decimal;
         this.settleStatus = settleStatus;
@@ -46,6 +47,7 @@ class autoUseChange {
         this.stageBills = source.stageBills;
         this.stagePos = source.stagePos;
         this.stageChange = source.stageChange || [];
+        this.preStageBills = source.preStageBills || [];
     }
 
     findBillsPos(changeBills){
@@ -154,7 +156,17 @@ class autoUseChange {
             this._calculateQty(cb, minus, bills);
         }
     }
-
+    calcContractTp(bills, contract_qty, qc_minus_qty) {
+        const preSb = this.preStageBills.find(x => { return x.lid === bills.id; });
+        let activeQty = this.helper.add(bills.quantity, qc_minus_qty);
+        let end_contract_qty = contract_qty;
+        if (preSb) {
+            activeQty = this.helper.add(activeQty, preSb.qc_minus_qty);
+            end_contract_qty = this.helper.add(end_contract_qty, preSb.contract_qty);
+        }
+        const end_contract_tp = this.helper.mul(this.helper.div(end_contract_qty, activeQty), bills.total_price, this.decimal.tp);
+        return preSb ? this.helper.sub(end_contract_tp, preSb.pre_contract_tp) : end_contract_tp;
+    }
     calculateAll() {
         for (const cd of this.changeDetail) {
             const sci = this.stageChange.findIndex(x => {
@@ -218,8 +230,10 @@ class autoUseChange {
             const negative_qc_tp = this.helper.mul(negative_qc_qty, cb.bills.unit_price, this.decimal.tp);
             const qc_minus_qty = this.helper.round(cb.qc_minus_qty || 0, precision.value);
             const sb = this.stageBills.find(x => {return x.lid === lid});
+
             if (sb) {
-                this.updateBills.push({ id: sb.id, qc_qty, qc_tp, positive_qc_qty, positive_qc_tp, negative_qc_qty, negative_qc_tp, qc_minus_qty });
+                const contract_tp = qc_minus_qty && this.info.calc_type === 'tp' && sb.contract_qty ? this.calcContractTp(cb.bills, sb.contract_qty, qc_minus_qty) : 0;
+                this.updateBills.push({ id: sb.id, qc_qty, qc_tp, positive_qc_qty, positive_qc_tp, negative_qc_qty, negative_qc_tp, qc_minus_qty, contract_tp });
             } else {
                 this.insertBills.push({
                     tid: this.default.tid, sid: this.default.sid, said: this.default.said,
@@ -815,7 +829,7 @@ module.exports = app => {
                 x.billsPos = data.find(y => { return x.id === y.cbid; });
             });
             const ledgerData = await this.ctx.service.ledger.getAllDataByCondition({
-                columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price'],
+                columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price'],
                 where: { id: lid },
             });
             const extraData = await this.ctx.service.ledgerExtra.getData(this.ctx.tender.id, ['is_tp']);
@@ -829,11 +843,12 @@ module.exports = app => {
             const stageBills = await this.ctx.service.stageBills.getAllDataByCondition({ where: { sid: stage.id } });
             const stagePos = await this.ctx.service.stagePos.getAllDataByCondition({ where: { sid: stage.id } });
             const stageChange = await this.ctx.service.stageChange.getAllDataByCondition({ where: { sid: stage.id, lid }});
+            const preStageBills = stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData(stage.tid, stage.preCheckedStage.order) : [];
 
             const useModal = new autoUseChange(this.ctx.helper, tender.info);
             const projectFunInfo = this.ctx.subProject.fun_rela;
             const minusNoValue = projectFunInfo.minusNoValue && tender.info.fun_rela.stage_change.minusNoValue;
-            useModal.use({ledgerData, posData, stageBills, stagePos, stageChange, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } }, validChangeBills, minusNoValue);
+            useModal.use({ledgerData, posData, stageBills, stagePos, stageChange, preStageBills, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } }, validChangeBills, minusNoValue);
 
             const conn = await this.db.beginTransaction();
             try {
@@ -859,7 +874,7 @@ module.exports = app => {
         async autoUseAllChange(tender, stage) {
             const validChangeBills = await this.ctx.service.stageChangeFinal.getAllChangeBillsValidQty(tender.id);
             const ledgerData = await this.ctx.service.ledger.getAllDataByCondition({
-                columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price'],
+                columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price'],
                 where: { tender_id: stage.tid },
             });
             const extraData = await this.ctx.service.ledgerExtra.getData(this.ctx.tender.id, ['is_tp']);
@@ -879,10 +894,11 @@ module.exports = app => {
             ]);
             const stageBills = await this.ctx.service.stageBills.getAllDataByCondition({ where: { sid: stage.id } });
             const stagePos = await this.ctx.service.stagePos.getAllDataByCondition({ where: { sid: stage.id } });
+            const preStageBills = stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData(stage.tid, stage.preCheckedStage.order) : [];
             const useModal = new autoUseChange(this.ctx.helper, tender.info, this.ctx.service.settle.settleStatus);
             const projectFunInfo = this.ctx.subProject.fun_rela;
             const minusNoValue = projectFunInfo.minusNoValue && tender.info.fun_rela.stage_change.minusNoValue;
-            useModal.use({ledgerData, posData, stageBills, stagePos, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } }, validChangeBills, minusNoValue);
+            useModal.use({ledgerData, posData, stageBills, stagePos, preStageBills, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } }, validChangeBills, minusNoValue);
 
             // if (useModal.insertChange.length === 0) return '无可调用的清单或计量单元';
 

+ 8 - 8
app/service/stage_pos.js

@@ -190,7 +190,7 @@ module.exports = app => {
                 if (insertPosStage.length > 0) await transaction.insert(this.tableName, insertPosStage);
 
                 for (const lid of calcStageBills) {
-                    await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, lid, transaction);
+                    await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage, lid, transaction);
                 }
                 await transaction.commit();
                 return result;
@@ -332,7 +332,7 @@ module.exports = app => {
                 if (updateBills) await transaction.update(this.ctx.service.ledger.tableName, updateBills);
                 if (updatePosStage.length > 0) await transaction.updateRows(this.tableName, updatePosStage);
                 if (insertPosStage.length > 0) await transaction.insert(this.tableName, insertPosStage);
-                if (bills) await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, bills.id, transaction);
+                if (bills) await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage, bills.id, transaction);
                 await transaction.commit();
                 return result;
             } catch (err) {
@@ -414,13 +414,14 @@ module.exports = app => {
                 }
                 if (stageBills && stageBills.times === this.ctx.stage.curTimes && stageBills.order === this.ctx.stage.curOrder) {
                     if (contract_qty === stageBills.contract_qty) continue;
+                    const contract_tp = await this.ctx.service.stageBills.calcContractTp(this.ctx.tender.info, this.ctx.stage, b, { contract_qty, qc_minus_qty: stageBills.qc_minus_qty });
                     updateBillsStage.push({
                         id: stageBills.id,
                         said: this.ctx.session.sessionUser.accountId,
-                        contract_qty: contract_qty,
-                        contract_tp: this.ctx.helper.mul(contract_qty, b.unit_price, info.decimal.tp),
+                        contract_qty: contract_qty, contract_tp,
                     });
                 } else {
+                    const contract_tp = await this.ctx.service.stageBills.calcContractTp(this.ctx.tender.info, this.ctx.stage, b, { contract_qty, qc_minus_qty: 0 });
                     insertBillsStage.push({
                         tid: this.ctx.tender.id,
                         lid: b.id,
@@ -428,8 +429,7 @@ module.exports = app => {
                         times: this.ctx.stage.curTimes,
                         order: this.ctx.stage.curOrder,
                         said: this.ctx.session.sessionUser.accountId,
-                        contract_qty: contract_qty,
-                        contract_tp: this.ctx.helper.mul(contract_qty, b.unit_price, info.decimal.tp),
+                        contract_qty: contract_qty, contract_tp
                     })
                 }
             }
@@ -478,7 +478,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.pos.tableName, {tid: this.ctx.tender.id, id: data});
                 // 删除部位明细计量数据
                 await transaction.delete(this.tableName, {tid: this.ctx.tender.id, lid: data});
-                await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, pos[0].lid, transaction);
+                await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage, pos[0].lid, transaction);
                 await transaction.commit();
                 // 获取需要更新的数据
                 result.ledger = [pos[0].lid];
@@ -569,7 +569,7 @@ module.exports = app => {
                     ex_stage_qty1: orgPos ? orgPos.ex_stage_qty1 || 0 : 0,
                 });
             }
-            await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, pos.lid, transaction);
+            await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage, pos.lid, transaction);
         }
 
         /**

+ 43 - 8
app/service/stage_stash.js

@@ -14,6 +14,7 @@ class loadStageExcelTree {
     constructor(ctx) {
         this.ctx = ctx;
 
+        this.info = ctx.tender.info;
         this.decimal = ctx.tender.info.decimal;
         this.settleStatus = ctx.service.settle.settleStatus;
 
@@ -39,6 +40,8 @@ class loadStageExcelTree {
         this.stagePos = source.stagePos;
 
         this.stageBillsDgn = source.stageBillsDgn;
+
+        this.preStageBills = source.preStageBills;
     }
     findNode(node, parent) {
         const _ = this.ctx.helper._;
@@ -59,6 +62,21 @@ class loadStageExcelTree {
             }
         });
     }
+    calcContractTp(node, contract_qty, qc_minus_qty) {
+        if (this.info.calc_type === 'tp') {
+            const preSb = this.preStageBills.find(x => { return x.lid === node.id; });
+            let activeQty = this.ctx.helper.add(node.quantity, qc_minus_qty);
+            let end_contract_qty = contract_qty;
+            if (preSb) {
+                activeQty = this.ctx.helper.add(activeQty, preSb.qc_minus_qty);
+                end_contract_qty = this.ctx.helper.add(end_contract_qty, preSb.contract_qty);
+            }
+            const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, this.decimal.tp);
+            return preSb ? this.ctx.helper.sub(end_contract_tp, preSb.pre_contract_tp) : end_contract_tp;
+        } else if (this.info.calc_type === 'up') {
+            return this.ctx.helper.mul(contract_qty, node.unit_price, this.decimal.tp);
+        }
+    }
     loadLeaf(node, source) {
         const curStageBills = this.stageBills.find(csb => { return csb.lid === source.id; });
         if (node.has_pos) {
@@ -86,7 +104,7 @@ class loadStageExcelTree {
             for (const ssp of sourceStagePos) {
                 contract_qty = this.ctx.helper.add(contract_qty, ssp.contract_qty);
             }
-            const contract_tp = this.ctx.helper.mul(contract_qty, source.unit_price, this.decimal.tp);
+            const contract_tp = this.calcContractTp(source, contract_qty, curStageBills ? curStageBills.qc_minus_qty : 0);
             if (curStageBills) {
                 this.updateBills.push({ id: curStageBills.id, contract_qty, contract_tp, postil: node.postil || curStageBills.postil || '' });
             } else {
@@ -99,7 +117,7 @@ class loadStageExcelTree {
             if (!node.contract_qty && !node.contract_tp) return;
             const contract_qty = source.is_tp ? 0 : node.contract_qty;
             const contract_tp = contract_qty
-                ? this.ctx.helper.mul(contract_qty, source.unit_price, this.decimal.tp)
+                ? this.calcContractTp(source, contract_qty, curStageBills ? curStageBills.qc_minus_qty : 0)
                 : source.is_tp ? this.ctx.helper.round(node.contract_tp, this.decimal.tp) : 0;
 
             if (curStageBills) {
@@ -249,7 +267,20 @@ module.exports = app => {
             return data.bills;
         }
 
+
         async reCalcStashData(stage, data) {
+            const calcContractTp = function(helper, info, stageBills, bills) {
+                if (info.calc_type === 'tp') {
+                    let activeQty = helper.add(bills.quantity, stageBills.qc_minus_qty);
+                    let end_contract_qty = stageBills.contract_qty;
+                    activeQty = helper.add(activeQty, bills.pre_qc_minus_qty);
+                    end_contract_qty = helper.add(end_contract_qty, bills.pre_contract_qty);
+                    const end_contract_tp = helper.mul(helper.div(end_contract_qty, activeQty), bills.total_price, decimal.tp);
+                    return helper.sub(end_contract_tp, bills.pre_contract_tp);
+                } else if (info.calc_type === 'up') {
+                    return helper.mul(bills.unit_price, stageBills.contract_qty, info.decimal.tp);
+                }
+            };
             const decimal = this.ctx.tender.info.decimal;
             const insertBillsData = [], insertPosData = [], insertChangeData = [];
             const settleStatus = this.ctx.service.settle.settleStatus;
@@ -257,9 +288,11 @@ module.exports = app => {
             const bills = await this.ctx.service.ledger.getAllDataByCondition({ where: { tender_id: stage.tid, is_leaf: true } });
             const extraData = await this.ctx.service.ledgerExtra.getData(stage.tid, ['id', 'is_tp']);
             const settleStatusBills = stage.readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: stage.readySettle.id }}) : [];
+            const preStageBills = stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData(stage.tid, this.ctx.stage.preCheckedStage.order) : [];
             this.ctx.helper.assignRelaData(bills, [
                 { data: extraData, fields: ['is_tp'], prefix: '', relaId: 'id' },
                 { data: settleStatusBills, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+                { data: preStageBills, field: ['contract_qty', 'contract_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid'},
             ]);
             const pos = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: stage.tid } });
             const settleStatusPos = stage.readySettle ? await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: stage.readySettle.id }}) : [];
@@ -324,7 +357,7 @@ module.exports = app => {
                         nbs.positive_qc_qty = this.ctx.helper.add(nbs.positive_qc_qty, nps.positive_qc_qty);
                         nbs.negative_qc_qty = this.ctx.helper.add(nbs.negative_qc_qty, nps.negative_qc_qty);
                     }
-                    nbs.contract_tp = this.ctx.helper.mul(nbs.contract_qty, b.unit_price, decimal.tp);
+                    nbs.contract_tp = calcContractTp(this.ctx.helper, this.ctx.tender.info, nbs, b);
                     nbs.qc_tp = this.ctx.helper.mul(nbs.qc_qty, b.unit_price, decimal.tp);
                     nbs.positive_qc_tp = this.ctx.helper.mul(nbs.positive_qc_qty, b.unit_price, decimal.tp);
                     nbs.negative_qc_tp = this.ctx.helper.mul(nbs.negative_qc_qty, b.unit_price, decimal.tp);
@@ -333,7 +366,6 @@ module.exports = app => {
                         nbs.contract_tp = this.ctx.helper.round(d.contract_tp, decimal.tp);
                     } else {
                         nbs.contract_qty = this.ctx.helper.round(d.contract_qty, decimal.qty);
-                        nbs.contract_tp = this.ctx.helper.mul(nbs.contract_qty, b.unit_price, decimal.tp);
                         if (d.change) {
                             for (const c of d.change) {
                                 if (!c.qty) continue;
@@ -353,6 +385,7 @@ module.exports = app => {
                                 }
                             }
                         }
+                        nbs.contract_tp = calcContractTp(this.ctx.helper, this.ctx.tender.info, nbs, b);
                         nbs.qc_tp = this.ctx.helper.mul(nbs.qc_qty, b.unit_price, decimal.tp);
                         nbs.positive_qc_tp = this.ctx.helper.mul(nbs.positive_qc_qty, b.unit_price, decimal.tp);
                         nbs.negative_qc_tp = this.ctx.helper.mul(nbs.negative_qc_qty, b.unit_price, decimal.tp);
@@ -405,7 +438,7 @@ module.exports = app => {
                 const cacheTree = analysisExcel.analysisData(excelData, tempData, { filterZeroGcl: false });
 
                 const ledgerData = await this.ctx.service.ledger.getAllDataByCondition({
-                    columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price'],
+                    columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price'],
                     where: { tender_id: stage.tid},
                 });
                 const extraData = await this.ctx.service.ledgerExtra.getData(this.ctx.tender.id, ['is_tp']);
@@ -425,9 +458,10 @@ module.exports = app => {
                 const stageBills = await this.ctx.service.stageBills.getAllDataByCondition({ where: { sid: stage.id } });
                 const stagePos = await this.ctx.service.stagePos.getAllDataByCondition({ where: { sid: stage.id } });
                 const stageBillsDgn = await this.ctx.service.stageBillsDgn.getAllDataByCondition({ where: { tid: stage.tid } });
+                const preStageBills = stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData(stage.tid, this.ctx.stage.preCheckedStage.order) : [];
 
                 const loadModal = new loadStageExcelTree(this.ctx);
-                loadModal.load(cacheTree, { ledgerData, posData, stageBills, stagePos, stageBillsDgn, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } });
+                loadModal.load(cacheTree, { ledgerData, posData, stageBills, stagePos, stageBillsDgn, preStageBills, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } });
 
                 const conn = await this.db.beginTransaction();
                 try {
@@ -463,7 +497,7 @@ module.exports = app => {
                 dealPos.loadDatas(dealData.pos);
 
                 const ledgerData = await this.ctx.service.ledger.getAllDataByCondition({
-                    columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price'],
+                    columns: ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price'],
                     where: { tender_id: stage.tid},
                 });
                 const extraData = await this.ctx.service.ledgerExtra.getData(this.ctx.tender.id, ['is_tp']);
@@ -483,9 +517,10 @@ module.exports = app => {
                 const stageBills = await this.ctx.service.stageBills.getAllDataByCondition({ where: { sid: stage.id } });
                 const stagePos = await this.ctx.service.stagePos.getAllDataByCondition({ where: { sid: stage.id } });
                 const stageBillsDgn = await this.ctx.service.stageBillsDgn.getAllDataByCondition({ where: { tid: stage.tid } });
+                const preStageBills = stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData(stage.tid, this.ctx.stage.preCheckedStage.order) : [];
 
                 const loadModal = new loadStageExcelTree(this.ctx);
-                loadModal.loadDeal(dealTree, dealPos, { ledgerData, posData, stageBills, stagePos, stageBillsDgn, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } });
+                loadModal.loadDeal(dealTree, dealPos, { ledgerData, posData, stageBills, stagePos, stageBillsDgn, preStageBills, default: { tid: stage.tid, sid: stage.id, said: this.ctx.session.sessionUser.accountId } });
 
                 const conn = await this.db.beginTransaction();
                 try {

+ 3 - 1
app/service/tender.js

@@ -15,7 +15,9 @@ const fs = require('fs');
 const path = require('path');
 const commonQueryColumns = [
     'id', 'project_id', 'name', 'status', 'category', 'ledger_times', 'ledger_status', 'measure_type', 'user_id', 'valuation', 'create_time',
-    'total_price', 'deal_tp', 'copy_id', 's2b_gxby_check', 's2b_gxby_limit', 's2b_dagl_check', 's2b_dagl_limit', 'has_rela', 'his_id', 'rpt_show_level',
+    'total_price', 'deal_tp', 'copy_id',
+    's2b_gxby_check', 's2b_gxby_limit', 's2b_dagl_check', 's2b_dagl_limit', 's2b_multi_check', 's2b_multi_limit',
+    'has_rela', 'his_id', 'rpt_show_level',
     'build_status', 'settle_order', 'spid',
 ];
 

+ 101 - 9
app/service/tender_info.js

@@ -250,23 +250,19 @@ module.exports = app => {
             return changeBills;
         }
 
-        async _reCalcStageBills(tenderId, newDecimal, oldDecimal) {
-            const updateStageBills = [], insertStageBills = [];
-            const stages = await this.ctx.service.stage.getUnCompleteStages(tenderId);
-            if (stages.length === 0 || newDecimal.tp === oldDecimal.tp) return [updateStageBills, insertStageBills];
-
+        async _reCalcStageBillsUp(stages, updateStageBills, insertStageBills, decimal) {
             for (const stage of stages) {
                 const stageBills = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
                 const bills = await this.ctx.service.ledger.getAllDataByCondition({
                     columns: ['id', 'unit_price'],
-                    where: { tender_id: tenderId, is_leaf: true },
+                    where: { tender_id: stage.tid, is_leaf: true },
                 });
                 for (const sb of stageBills) {
                     const b = bills.find(x => {return x.id === sb.lid});
                     if (!b) continue;
-                    const contract_tp = this.ctx.helper.mul(b.unit_price, sb.contract_qty, newDecimal.tp);
-                    const qc_tp = this.ctx.helper.mul(b.unit_price, sb.qc_qty, newDecimal.tp);
-                    const ex_stage_tp1 = this.ctx.helper.mul(b.unit_price, sb.ex_stage_qty1, newDecimal.tp);
+                    const contract_tp = this.ctx.helper.mul(b.unit_price, sb.contract_qty, decimal.tp);
+                    const qc_tp = this.ctx.helper.mul(b.unit_price, sb.qc_qty, decimal.tp);
+                    const ex_stage_tp1 = this.ctx.helper.mul(b.unit_price, sb.ex_stage_qty1, decimal.tp);
                     if (contract_tp == sb.contract_tp && qc_tp === sb.qc_tp && ex_stage_tp1 === sb.ex_stage_tp1) continue;
 
                     if (sb.times === stage.times && sb.order === 0) {
@@ -285,6 +281,67 @@ module.exports = app => {
                     }
                 }
             }
+        }
+
+        async _reCalcStageBillsTp(stages, updateStageBills, insertStageBills, decimal) {
+            stages.sort((x, y) => { return x.order - y.order; });
+            const preStage = stages[0].order > 1 ? await this.ctx.service.stage.getDataByCondition({ tid: stages[0].tid, order: stages[0].order - 1 }) : null;
+            const preStageBills = preStage ? await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: preStage.tid, sid: preStage.id } }) : [];
+            for (const stage of stages) {
+                const stageBills = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
+                const bills = await this.ctx.service.ledger.getAllDataByCondition({
+                    columns: ['id', 'unit_price', 'quantity', 'total_price'],
+                    where: { tender_id: stage.tid, is_leaf: true },
+                });
+                for (const sb of stageBills) {
+                    const preSb = preStageBills.find(x => { return x.lid === sb.lid; });
+                    const b = bills.find(x => {return x.id === sb.lid});
+                    if (!b) continue;
+                    let activeQty = this.ctx.helper.add(b.quantity, sb.qc_minus_qty);
+                    let end_contract_qty = sb.contract_qty;
+                    if (preSb) {
+                        activeQty = this.ctx.helper.add(activeQty, preSb.qc_minus_qty);
+                        end_contract_qty = this.ctx.helper.add(end_contract_qty, preSb.contract_qty);
+                    }
+                    const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), b.total_price, decimal.tp);
+                    const contract_tp = preSb ? this.ctx.helper.sub(end_contract_tp, preSb.contract_tp) : end_contract_tp;
+                    const qc_tp = this.ctx.helper.mul(b.unit_price, sb.qc_qty, decimal.tp);
+                    const ex_stage_tp1 = this.ctx.helper.mul(b.unit_price, sb.ex_stage_qty1, decimal.tp);
+                    if (contract_tp == sb.contract_tp && qc_tp === sb.qc_tp && ex_stage_tp1 === sb.ex_stage_tp1) continue;
+
+                    if (sb.times === stage.times && sb.order === 0) {
+                        updateStageBills.push({
+                            id: sb.id, contract_tp, qc_tp, ex_stage_tp1,
+                        });
+                    } else {
+                        insertStageBills.push({
+                            tid: stage.tid, lid: sb.lid, sid: stage.id, said: this.ctx.session.sessionUser.accountId,
+                            times: stage.times, order: 0,
+                            contract_qty: sb.contract_qty, contract_expr: sb.contract_expr, contract_tp,
+                            qc_qty: sb.qc_qty, qc_tp,
+                            ex_stage_qty1: sb.ex_stage_qty1, ex_stage_tp1,
+                            postil: sb.postil,
+                        });
+                    }
+                }
+            }
+        }
+
+        async _reCalcStageBills(tenderId, newDecimal, oldDecimal) {
+            const updateStageBills = [], insertStageBills = [];
+            const stages = await this.ctx.service.stage.getUnCompleteStages(tenderId);
+            if (stages.length === 0 || newDecimal.tp === oldDecimal.tp) return [updateStageBills, insertStageBills];
+            switch (this.ctx.tender.info.calc_type) {
+                case 'up':
+                    await this._reCalcStageBillsUp(stages, updateStageBills, insertStageBills, newDecimal);
+                    break;
+                case 'tp':
+                    await this._reCalcStageBillsTp(stages, updateStageBills, insertStageBills, newDecimal);
+                    break;
+                default:
+                    await this._reCalcStageBillsUp(stages, updateStageBills, insertStageBills, newDecimal);
+                    break;
+            }
             return [updateStageBills, insertStageBills];
         }
 
@@ -386,6 +443,41 @@ module.exports = app => {
             }
         }
 
+        async _reCalcStageBills2(tenderId, calcType) {
+            const updateStageBills = [], insertStageBills = [];
+            const stages = await this.ctx.service.stage.getUnCompleteStages(tenderId);
+            if (stages.length === 0) return [updateStageBills, insertStageBills];
+            switch (calcType) {
+                case 'up':
+                    await this._reCalcStageBillsUp(stages, updateStageBills, insertStageBills, this.ctx.tender.info.decimal);
+                    break;
+                case 'tp':
+                    await this._reCalcStageBillsTp(stages, updateStageBills, insertStageBills, this.ctx.tender.info.decimal);
+                    break;
+                default:
+                    await this._reCalcStageBillsUp(stages, updateStageBills, insertStageBills, this.ctx.tender.info.decimal);
+                    break;
+            }
+            return [updateStageBills, insertStageBills];
+        }
+
+        async saveCalcType(tenderId, calcType) {
+            if (!this.ctx.tender.info.calc_type && calcType === 'up') return;
+            if (this.ctx.tender.info.calc_type && calcType === this.ctx.tender.info.calc_type) return;
+
+            const [updateStageBills, insertStageBills] = await this._reCalcStageBills2(tenderId, calcType);
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, { calc_type: calcType }, { where: { tid: tenderId } });
+                if (updateStageBills.length > 0) await transaction.updateRows(this.ctx.service.stageBills.tableName, updateStageBills);
+                if (insertStageBills.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, insertStageBills);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw '保存计价类型失败';
+            }
+        }
+
         /**
          * 获取标段审批相关信息 (审批设置用)
          * @param tenderId

+ 1 - 1
app/view/file/file.ejs

@@ -38,7 +38,7 @@
                             <% if (canUpload) { %>
                             <div class="py-2 pr-2"><a href="#add-file" data-toggle="modal" data-target="#add-file">上传文件</a></div>
                             <div class="py-2 pr-2"><a href="#add-big-file" data-toggle="modal" data-target="#add-big-file">大文件上传</a></div>
-                            <div class="p-2" id="rela-file-btn"><a href="#rela-file" data-toggle="modal" data-target="#rela-file">导入文件</a></div>
+                            <div class="p-2" id="rela-file-btn"><a href="#rela-file" data-toggle="modal" data-target="#rela-file">引用文件</a></div>
                             <div class="p-2"><a href="javascript: void(0)" id="batch-del-file-btn">批量删除</a></div>
                             <% } %>
                             <div class="p-2"><a href="javascript: void(0)" id="batch-download">批量下载</a></div>

+ 4 - 2
app/view/file/file_modal.ejs

@@ -142,9 +142,11 @@
                                 <div class="d-flex flex-row">
                                     <select class="form-control form-control-sm mr-2" id="tf-type" style="width: 100px">
                                     </select>
-                                    <select class="form-control form-control-sm mr-2" id="tf-sub-type" style="width: 120px">
+                                    <select class="form-control form-control-sm mr-2" id="tf-sub-type" style="width: 120px; display: none">
                                     </select>
-                                    <select class="form-control form-control-sm" id="tf-stage" style="width: 80px">
+                                    <select class="form-control form-control-sm mr-2" id="tf-stage" style="width: 80px; display: none">
+                                    </select>
+                                    <select class="form-control form-control-sm mr-2" id="tf-select" style="width: 120px; display: none">
                                     </select>
                                 </div>
                             </div>

+ 5 - 0
app/view/financial/pay.ejs

@@ -119,10 +119,15 @@
                     <% } %>
                     </tbody>
                 </table>
+                <!--翻页-->
+                <% include ../layout/page.ejs %>
             </div>
         </div>
     </div>
 </div>
+<link href="/public/css/bootstrap/select2.min.css" rel="stylesheet" />
+<link rel="stylesheet" href="/public/css/bootstrap/select2-bootstrap4.min.css">
+<script src="/public/js/bootstrap/select2.min.js"></script>
 <script>
     const user_id = <%- ctx.session.sessionUser.accountId %>;
     const is_admin = <%- ctx.session.sessionUser.is_admin %>;

+ 4 - 0
app/view/financial/pay_detail.ejs

@@ -34,6 +34,10 @@
                     <% } %>
                 <% } else if (financialPay.status === auditConst.status.checked) { %>
                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm">审批完成</a>
+                    <% if (financialPay.auditors !== undefined && financialPay.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
+                        <!--重新审批-->
+                        <a href="#sp-down-back" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm ml-2">重新审批</a>
+                    <% } %>
                 <% } else if (financialPay.status === auditConst.status.checkNo) { %>
                     <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批<% if (financialPay.status === auditConst.status.checkNo) { %>退回<% } %></a>
                     <% if (ctx.session.sessionUser.accountId === financialPay.uid) { %>

+ 159 - 0
app/view/financial/pay_detail_modal.ejs

@@ -285,6 +285,8 @@
                                                     <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>
                                                 <% } else if (group.status === auditConst.status.checking) { %>
                                                     <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>
+                                                <% } else if (group.status === auditConst.status.checkAgain) {%>
+                                                    <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>
                                                 <% } else { %>
                                                     <div class="timeline-item-icon bg-secondary text-light"></div>
                                                 <% } %>
@@ -311,6 +313,8 @@
                                                                             <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
                                                                         <% } else if (ctx.helper._.includes([auditConst.status.checkNo], auditor.status)) { %>
                                                                             <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
+                                                                        <% } else if(auditor.status === auditConst.status.checkAgain) {%>
+                                                                            <span class="pull-right text-warning"><i class="fa fa-check-circle"></i></span>
                                                                         <% } else if (auditor.status === auditConst.status.checking) { %>
                                                                             <span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>
                                                                         <% } %>
@@ -462,6 +466,8 @@
                                                         <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>
                                                     <% } else if (group.status === auditConst.status.checking) { %>
                                                         <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>
+                                                    <% } else if(group.status === auditConst.status.checkAgain) {%>
+                                                        <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>
                                                     <% } else { %>
                                                         <div class="timeline-item-icon bg-secondary text-light"></div>
                                                     <% } %>
@@ -488,6 +494,8 @@
                                                                                 <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
                                                                             <% } else if (ctx.helper._.includes([auditConst.status.checkNo], auditor.status)) { %>
                                                                                 <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
+                                                                            <% } else if (auditor.status === auditConst.status.checkAgain) {%>
+                                                                                <span class="pull-right text-warning"><i class="fa fa-check-circle"></i></span>
                                                                             <% } else if (auditor.status === auditConst.status.checking) { %>
                                                                                 <span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>
                                                                             <% } %>
@@ -640,6 +648,8 @@
                                                         <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>
                                                     <% } else if (group.status === auditConst.status.checking) { %>
                                                         <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>
+                                                    <% } else if(group.status === auditConst.status.checkAgain) {%>
+                                                        <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>
                                                     <% } else { %>
                                                         <div class="timeline-item-icon bg-secondary text-light"></div>
                                                     <% } %>
@@ -666,6 +676,8 @@
                                                                                 <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
                                                                             <% } else if (ctx.helper._.includes([auditConst.status.checkNo], auditor.status)) { %>
                                                                                 <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
+                                                                            <% } else if(auditor.status === auditConst.status.checkAgain) {%>
+                                                                                <span class="pull-right text-warning"><i class="fa fa-check-circle"></i></span>
                                                                             <% } else if (auditor.status === auditConst.status.checking) { %>
                                                                                 <span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>
                                                                             <% } %>
@@ -707,7 +719,62 @@
         </div>
     <% } %>
 <% } %>
+<% if (financialPay.status === auditConst.status.checked && financialPay.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
+    <% if (!authMobile && ctx.session.sessionUser.loginStatus === 0) { %>
+        <!--终审重新审批-->
+        <div class="modal fade" id="sp-down-back" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h5 class="modal-title">重新审批</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>重新审批需要您的手机短信验证</h5>
+                        <h5>您目前还没设置认证手机,请先设置。</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                        <a href="/profile/sms" class="btn btn-sm btn-primary">去设置</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    <% } else { %>
+        <!--重新审批-->
+        <div class="modal fade" id="sp-down-back" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <form id="againForm" class="modal-content" method="post" action="<%- preUrl2 %>/audit/check/again" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title">重新审批</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>确认重新审批「<%= financialPay.code %>」?</h5>
+                        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+                            <div class="form-group">
+                                <label>重审需要验证码确认,验证码将发送至尾号<%- authMobile.slice(-4) %>的手机</label>
+                                <div class="input-group input-group-sm mb-3">
+                                    <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
+                                    <div class="input-group-append">
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
+                                    </div>
+                                </div>
+                            </div>
+                        <% } %>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="fpid" value="<%= financialPay.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="re-shenpi-btn" class="btn btn-warning btn-sm" <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %>>确定重审</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
+<% } %>
 <script>
+    const csrf = '<%= ctx.csrf %>';
+    const authMobile = '<%= authMobile %>';
     $(function () {
         $('.sp-location-list').on('shown.bs.modal', function () {
             const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
@@ -754,5 +821,97 @@
                 $('.modal-title').text('重新上报')
             }
         });
+
+        $('#re-shenpi-btn').click(function () {
+            const data = {
+                fpid: parseInt('<%- financialPay.id %>'),
+            };
+            <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+            const code = $("#againForm input[name='code']").val();
+            if ($(this).hasClass('disabled')) {
+                return false;
+            }
+            if (code.length < 6) {
+                // alert('请填写正确的验证码');
+                toastr.error('请填写正确的验证码');
+                return false;
+            }
+            data.code = code;
+            <% } %>
+            $.ajax({
+                url: '<%- preUrl2 %>/audit/check/again?_csrf_j=' + csrf,
+                type: 'post',
+                data: data,
+                dataTye: 'json',
+                success: function(response) {
+                    if (response.err === 0) {
+                        window.location.href = response.url;
+                    } else {
+                        toastr.error(response.msg);
+                    }
+                }
+            });
+        })
+
+        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+        // 重新审批获取手机验证码
+        // 获取验证码
+        let isPosting = false;
+        $(".get-code").on('click', function() {
+            if (isPosting) {
+                return false;
+            }
+            const btn = $(this);
+
+            $.ajax({
+                url: '/profile/code?_csrf_j=' + csrf,
+                type: 'post',
+                data: { mobile: authMobile, type: 'shenpi' },
+                dataTye: 'json',
+                error: function() {
+                    isPosting = false;
+                },
+                beforeSend: function() {
+                    isPosting = true;
+                },
+                success: function(response) {
+                    isPosting = false;
+                    if (response.err === 0) {
+                        codeSuccess(btn);
+                        $("input[name='code']").removeAttr('readonly');
+                        $("#re-shenpi-btn").removeAttr('disabled');
+                        // $("#re-shenpi-btn2").removeAttr('disabled');
+                    } else {
+                        toastr.error(response.msg);
+                    }
+                }
+            });
+        });
+        <% } %>
     });
+    /**
+     * 获取成功后的操作
+     *
+     * @param {Object} btn - 点击的按钮
+     * @return {void}
+     */
+    function codeSuccess(btn) {
+        let counter = 60;
+        btn.addClass('disabled').text('重新获取 ' + counter + 'S');
+        btn.parent().siblings('input').removeAttr('readonly').attr('placeholder', '输入短信中的6位验证码');
+        const bindBtn = $("#bind-btn");
+        bindBtn.removeClass('btn-secondary disabled').addClass('btn-primary');
+
+        const countDown = setInterval(function () {
+            const countString = counter - 1 <= 0 ? '' : ' ' + (counter - 1) + 'S';
+            // 倒数结束后
+            if (countString === '') {
+                clearInterval(countDown);
+                btn.removeClass('disabled');
+            }
+            const text = '重新获取' + countString;
+            btn.text(text);
+            counter -= 1;
+        }, 1000);
+    }
 </script>

+ 48 - 2
app/view/financial/pay_modal.ejs

@@ -8,6 +8,42 @@
                 <h5 class="modal-title">申请支付</h5>
             </div>
             <div class="modal-body">
+                <style>
+                    .select2-container {
+                        /*display: inline-block!important;*/
+                        /*height: 27px;*/
+                        width: 100% !important;
+                    }
+                    .select2-container--bootstrap4 .select2-selection--single {
+                        height: calc(1.1em + .75rem + 2px) !important;
+                    }
+                    /*.select2-container--bootstrap4 .select2-selection {*/
+                    /*    background-color: #f8f9fa;*/
+                    /*    !*border-color: #f8f9fa;*!*/
+                    /*    border: 1px solid #f8f9fa;*/
+                    /*}*/
+                    /*.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered:focus{*/
+                    /*    box-shadow: 0 0 0 0.2rem rgba(216,217,219,.5);*/
+                    /*}*/
+                    /*.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered:hover {*/
+                    /*    background-color: #e2e6ea;*/
+                    /*    border-color: #dae0e5;*/
+                    /*}*/
+                    .select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered {
+                        line-height: calc(1.1em + .75rem);
+                        /*color: #007bff!important;*/
+                        border-radius: 0.2rem;
+                        /*background-color: #f8f9fa;*/
+                        /*border-color: #f8f9fa;*/
+                    }
+                    .select2-container--bootstrap4 .select2-selection--single .select2-selection__arrow b {
+                        border-color: #007bff transparent transparent transparent;
+                        border-width: 4px 3.7px 0;
+                    }
+                    .select2-search--dropdown .select2-search__field {
+                        padding: 0.175rem 0.5rem;
+                    }
+                </style>
                 <div class="form-group mb-2">
                     <label>支付标段<strong class="text-danger">*</strong></label>
                     <select class="form-control form-control-sm" id="add-pay-tender">
@@ -39,6 +75,16 @@
         </div>
     </div>
 </div>
+<script>
+    $(function () {
+        $('#add-pay-tender').select2({
+            language: 'zh-CN',
+            theme: 'bootstrap4',
+            selectOnClose: true,
+            // width: '150',
+        });
+    });
+</script>
 <% } %>
 <!--批量审批-->
 <div class="modal fade" id="batch-sp" data-backdrop="static">
@@ -123,8 +169,8 @@
                 <h5 class="modal-title">审批流程</h5>
             </div>
             <div class="modal-body pt-0">
-                <div class="row" style="min-height: 400px;max-height: 700px;overflow:auto;">
-                    <div class="col-3">
+                <div class="row" style="min-height: 400px;max-height: 700px;">
+                    <div class="col-3" style="min-height: 400px;max-height: 700px;overflow:auto;">
                         <div class="p-2">标段列表</div>
                         <table class="table table-bordered">
                             <thead>

+ 2 - 2
app/view/financial/sub_menu_list.ejs

@@ -2,7 +2,7 @@
 <div class="nav-box">
     <ul class="nav-list list-unstyled">
         <li class="<% if (ctx.url.indexOf('/transfer') !== -1) { %>active<% } %>">
-            <a href="/sp/<%- ctx.subProject.id %>/financial/transfer"><span class="ml-3">资金划拨</span></a>
+            <a href="/sp/<%- ctx.subProject.id %>/financial/transfer" class="account-page-size"><span class="ml-3">资金划拨</span></a>
         </li>
     </ul>
 </div>
@@ -11,7 +11,7 @@
 <div class="nav-box">
     <ul class="nav-list list-unstyled">
         <li class="<% if (ctx.url.indexOf('/pay') !== -1) { %>active<% } %>">
-            <a href="/sp/<%- ctx.subProject.id %>/financial/pay"><span class="ml-3">资金支付</span></a>
+            <a href="/sp/<%- ctx.subProject.id %>/financial/pay" class="account-page-size"><span class="ml-3">资金支付</span></a>
         </li>
     </ul>
 </div>

+ 3 - 1
app/view/financial/transfer.ejs

@@ -33,7 +33,7 @@
                     <% for (const [i, t] of transferList.entries()) { %>
                     <tr class="text-center" data-id="<%- t.id %>">
                         <td class=""><%- (transferList.length - i) %></td>
-                        <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/transfer/<%- t.id %>/tender"><%- t.t_time %></a></td>
+                        <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/transfer/<%- t.id %>/tender" class="account-page-size"><%- t.t_time %></a></td>
                         <td class="text-right"><%- t.total_price %></td>
                         <td class=""><%- t.username %></td>
                         <td class="text-left" ><%- t.company %></td>
@@ -46,6 +46,8 @@
                     <% } %>
                     </tbody>
                 </table>
+                <!--翻页-->
+                <% include ../layout/page.ejs %>
             </div>
         </div>
     </div>

+ 2 - 0
app/view/financial/transfer_tender.ejs

@@ -60,6 +60,8 @@
                     <% } %>
                     </tbody>
                 </table>
+                <!--翻页-->
+                <% include ../layout/page.ejs %>
             </div>
         </div>
     </div>

+ 17 - 0
app/view/profile/modal.ejs

@@ -15,6 +15,23 @@
     </div>
 </div>
 
+<div class="modal fade" id="remove-auth-mobile" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">解绑认证手机</h5>
+            </div>
+            <div class="modal-body">
+                <h5>确认解绑认证手机?</h5>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                <button type="submit" class="btn btn-danger btn-sm" id="del-mobile-btn">确定解绑</button>
+            </div>
+        </div>
+    </div>
+</div>
+
 <div class="modal fade" id="remove-dsk-account" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">

+ 12 - 11
app/view/profile/sms.ejs

@@ -14,16 +14,16 @@
                             <div class="card-body pt-3">
                                 <h6 class="card-title">认证手机</h6>
                                 <% if (accountData.auth_mobile !== '') { %>
-                                    <!--已绑定手机-->
-                                    <div class="form-group">
-                                        <label>已认证手机</label>
-                                        <div class="input-group mb-3">
-                                            <input class="form-control form-control-sm" readonly="" value="<%= accountData.auth_mobile %>">
-                                            <div class="input-group-append">
-                                                <button class="btn btn-outline-secondary btn-sm" id="change-mobile">修改手机</button>
-                                            </div>
-                                        </div>
-                                    </div>
+                                <!--已绑定手机-->
+                                <div class="form-group">
+                                    <label><%- accountData.auth_mobile %></label>
+                                    <% if (accountData.dsk_account) { %>
+                                    <a href="javascript:void(0);" class="btn btn-sm btn-outline-secondary" data-container="body" data-toggle="popover" data-placement="bottom" data-content="先解绑大司空账号才能解绑认证手机" data-trigger="focus">解绑</a>
+                                    <% } else { %>
+                                    <a href="#remove-auth-mobile" class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#remove-auth-mobile">解绑</a>
+                                    <% } %>
+                                    <a href="javascript:void(0);" class="btn btn-sm btn-outline-warning ml-2" id="change-mobile">修改手机</a>
+                                </div>
                                 <% } %>
                                 <!--绑定手机-->
                                 <% if (accountData.auth_mobile === '') { %><div class="alert alert-warning">认证手机用户找回密码操作,请优先设置。</div><% } %>
@@ -44,6 +44,7 @@
                                         </div>
                                     </div>
                                     <button type="button" class="btn btn-secondary btn-sm disabled" id="bind-btn">确认绑定</button>
+                                    <% if (accountData.auth_mobile !== '') { %><button type="button" class="btn btn-outline-secondary btn-sm" id="hide-bind-btn">取消</button><% } %>
                                 </form>
                                 <% if (accountData.auth_mobile !== '' && false) { %>
                                     <!--短信通知开关(已有认证手机后显示)-->
@@ -243,6 +244,6 @@
                     location.reload();
                 }, 1000);
             });
-        })
+        });
     });
 </script>

+ 2 - 2
app/view/profile/wechat.ejs

@@ -17,8 +17,8 @@
                                     <% if (accountData.wx_openid !== null && accountData.wx_openid !== '') { %>
                                         <!--已绑定手机-->
                                         <div class="form-group">
-                                            <label>微信账号</label>
-                                            <input class="form-control-plaintext" disabled="" value="<%= accountData.wx_name %>">
+                                            <label>微信ID</label>
+                                            <input class="form-control-plaintext" disabled="" value="<%= accountData.wx_openid %>">
                                             <a href="#remove-wechat" class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#remove-wechat">解绑</a>
                                         </div>
                                     <% } else { %>

+ 124 - 0
app/view/setting/limit.ejs

@@ -0,0 +1,124 @@
+<div class="panel-content">
+    <div class="panel-title fluid">
+        <div class="title-main  d-flex justify-content-between">
+            <div>联动计量检查配置</div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0 row">
+                <div class="col-2">
+                    <div class="d-flex flex-row">
+                        <form class="ml-2 p-2" method="POST" action="/setting/limit/save">
+                            <input type="hidden" name="_csrf_j2" value="<%= ctx.csrf %>" />
+                            <button class="btn btn-sm btn-light text-primary"><i class="fa fa-plus" aria-hidden="true" type="submit"></i> 新增配置</button>
+                        </form>
+                    </div>
+                    <div>
+                        <dl class="list-group">
+                            <% for (const limit of limitList) { %>
+                            <dd class="list-group-item <%- (limit.limit_id === curLimit.limit_id ? 'bg-warning' : '')%>">
+                                <div class="d-flex justify-content-between align-items-center table-file" limitId="<%- limit.limit_id %>">
+                                    <div><%- limit.name %>%></div>
+                                    <div class="btn-group-table" style="display: none;">
+                                        <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="编辑" name="renameLimit"><i class="fa fa-pencil fa-fw"></i></a>
+                                        <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="delLimit"><i class="fa fa-trash-o fa-fw text-danger"></i></a>
+                                    </div>
+                                </div>
+                            </dd>
+                            <% } %>
+                        </dl>
+                    </div>
+                </div>
+                <div class="col-10">
+                    <div class="d-flex flex-row">
+                        <div class="p-2">
+                            <button class="btn btn-sm btn-light text-primary"><i class="fa fa-plus" aria-hidden="true" type="submit"></i> 新增判断</button>
+                        </div>
+                    </div>
+                    <div>
+                        <table class="table table-sm table-bordered">
+                            <tr class="text-center"><th>序号</th><th>允许计量</th><th>条件</th><th>计量下限</th><th>计量上限</th><th>超限提示</th></tr>
+                            <tbody>
+
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div stype="display: none">
+        <form id="hiddenForm" action="" method="POST">
+            <input type="hidden" name="_csrf_j2" value="<%= ctx.csrf %>" />
+            <input type="hidden" id="extra" name="" value="">
+        </form>
+    </div>
+</div>
+<script>
+    const limitList = JSON.parse('<%- JSON.stringify(limitList) %>');
+    const limitOptions = JSON.parse('<%- JSON.stringify(limitOptions) %>');
+    $('.table-file').click(function(e) {
+        if (this.getAttribute('renaming') === '1') return;
+        if (e.target.tagName === 'A' || e.target.tagName === 'I' || e.target.tagName === 'INPUT') return;
+        window.location.href = '/setting/limit?id=' +  this.getAttribute('limitId');
+    });
+    $('body').on('mouseenter', ".table-file", function(){
+        $(this).children(".btn-group-table").css("display","block");
+    });
+    $('body').on('mouseleave', ".table-file", function(){
+        $(this).children(".btn-group-table").css("display","none");
+    });
+
+    const hiddenSubmit = function(action, extraName, extraValue) {
+        $('#hiddenForm').attr('action', action);
+        if (extraName) {
+            $('#extra').attr('name', extraName);
+            $('#extra').val(extraValue);
+        };
+        $('#hiddenForm').submit();
+    };
+    $('body').on('click', 'a[name=renameLimit]', function(e){
+        $(this).parents('.table-file').attr('renaming', '1');
+        $(`#${this.getAttribute('aria-describedby')}`).remove();
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        const limit = limitList.find(x => { return x.limit_id === limitId; });
+        if (!limit) return;
+
+        const html = [];
+        html.push(`<div><input type="text" class="form-control form-control-sm" style="width: 160px" value="${limit.name}"/></div>`);
+        html.push('<div class="btn-group-table" style="display: none;">',
+            `<a href="javascript: void(0)" name="renameOk" class="mr-1"><i class="fa fa-check fa-fw"></i></a>`,
+            `<a href="javascript: void(0)" class="mr-1" name="renameCancel"><i class="fa fa-remove fa-fw text-danger"></i></a>`, '</div>');
+        $(`.table-file[limitId=${limitId}]`).html(html.join(''));
+        e.stopPropagation();
+    });
+    $('body').on('click', 'a[name=renameOk]', function(){
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        const newName = $(this).parents('.table-file').find('input').val();
+        hiddenSubmit('/setting/limit/save?id='+limitId, 'name', newName);
+        $(this).parents('.table-file').attr('renaming', '0');
+    });
+    $('body').on('click', 'a[name=renameCancel]', function() {
+        $(this).parents('.table-file').attr('renaming', '0');
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        const limit = limitList.find(x => { return x.limit_id === limitId; });
+        if (!limit) return;
+
+        const html = [];
+        html.push(`<div>${limit.name}</div>`);
+        html.push('<div class="btn-group-table" style="display: none;">',
+            '<a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="编辑" name="renameLimit"><i class="fa fa-pencil fa-fw"></i></a>',
+            '<a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="delLimit"><i class="fa fa-trash-o fa-fw text-danger"></i></a>',
+            '</div>');
+        $(`.table-file[limitId=${limitId}]`).html(html.join(''));
+    });
+    $('body').on('click', 'a[name=delLimit]', function(e){
+        e.stopPropagation();
+        const limitId = $(this).parents('.table-file').attr('limitId');
+        hiddenSubmit('/setting/limit/del?id='+limitId);
+    });
+    $(document).ready(() => {
+        autoFlashHeight();
+    });
+</script>

+ 61 - 0
app/view/setting/s2b.ejs

@@ -10,6 +10,7 @@
             <div class="sjs-height-0">
                 <nav class="nav nav-tabs m-3" role="tablist">
                     <a class="nav-item nav-link active" data-toggle="tab" href="#user-purview" role="tab">业务配置</a>
+                    <a class="nav-item nav-link" data-toggle="tab" href="#multi-limit" role="tab">联动计量配置</a>
                     <a class="nav-item nav-link" data-toggle="tab" href="#user-list" role="tab">标段列表</a>
                 </nav>
                 <div class="m-3">
@@ -79,6 +80,44 @@
                                 </table>
                             </div>
                         </div>
+                        <div class="tab-pane" id="multi-limit">
+                            <div class="row">
+                                <div class="col-3">
+                                    <div class="d-flex flex-row">
+                                        <button class="btn btn-sm btn-light text-primary" id="addLimit"><i class="fa fa-plus" aria-hidden="true"></i> 新增配置</button>
+                                    </div>
+                                    <div>
+                                        <dl class="list-group" id="limit-list">
+                                            <% for (const limit of limitList) { %>
+                                            <dd class="list-group-item" limitId="<%- limit.limit_id %>">
+                                                <div class="d-flex justify-content-between align-items-center table-file" limitId="<%- limit.limit_id %>">
+                                                    <div><%- limit.name %>%></div>
+                                                    <div class="btn-group-table" style="display: none;">
+                                                        <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="编辑" name="renameLimit"><i class="fa fa-pencil fa-fw"></i></a>
+                                                        <a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="delLimit"><i class="fa fa-trash-o fa-fw text-danger"></i></a>
+                                                    </div>
+                                                </div>
+                                            </dd>
+                                            <% } %>
+                                        </dl>
+                                    </div>
+                                </div>
+                                <div class="col-9">
+                                    <div class="d-flex flex-row">
+                                        <div class="p-2">
+                                            <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="add-lo"><i class="fa fa-plus" aria-hidden="true"></i> 新增判断</a>
+                                        </div>
+                                    </div>
+                                    <div>
+                                        <table class="table table-sm table-bordered">
+                                            <tr class="text-center"><th width="10%">允许计量</th><th width="40%">条件</th><th width="10%">计量下限</th><th width="10%">计量上限</th><th width="20%">超限提示</th><th width="10%">操作</th></tr>
+                                            <tbody id="limitOptions">
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                         <div class="tab-pane" id="user-list">
                             <!--没有标段数据-->
                             <% if (tenders.length === 0) { %>
@@ -96,6 +135,7 @@
                                     <th class="text-center" >标段创建时间/期审批时间</th>
                                     <th class="text-center">工序报验</th>
                                     <th class="text-center">档案管理</th>
+                                    <th class="text-center">联动计量</th>
                                 </tr>
                                 </thead>
                                 <% for (const t of tenders) { %>
@@ -129,6 +169,21 @@
                                             <label class="form-check-label" for="inlineCheckbox4">限制上报</label>
                                         </div>
                                     </td>
+                                    <td class="text-center">
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="inlineCheckbox5" <% if (t.s2b_multi_check) { %>checked<% } %> name="multi_check" onchange="updateS2bSetting(this);" value="<%- t.s2b_multi_check %>">
+                                            <input type="hidden" name="_csrf_j2" value="<%= ctx.csrf %>">
+                                            <label class="form-check-label" for="inlineCheckbox5">检查计量</label>
+                                        </div>
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="inlineCheckbox6" <% if (t.s2b_multi_limit) { %>checked<% } %> name="multi_limit" onchange="updateS2bSetting(this);" value="<%- t.s2b_multi_limit %>">
+                                            <input type="hidden" name="_csrf_j2" value="<%= ctx.csrf %>">
+                                            <label class="form-check-label" for="inlineCheckbox6">限制上报</label>
+                                        </div>
+                                        <div class="form-check form-check-inline">
+                                            <a class="btn btn-sm btn-primary" href="/setting/api/tender/<%- t.id %>" target="_blank">详细配置</a>
+                                        </div>
+                                    </td>
                                 </tr>
                                 <% } %>
                             </table>
@@ -141,6 +196,12 @@
     </div>
 </div>
 <script>
+    const limitList = JSON.parse('<%- JSON.stringify(limitList) %>');
+    const locInfo = {
+        gxby_status: { check: 'num', field: 'gxby_status', alias: '工序报验'},
+        dagl_status: { check: 'num', field: 'dagl_status', alias: '档案管理'},
+        gxby_date_ago: { check: 'date', field: 'gxby_date', alias: '完工日期 距今'},
+    }
     const updateStatusLimit = function (obj, type) {
         const data = { type };
         data.status = parseInt(obj.getAttribute('status'));

+ 74 - 0
app/view/setting/s2b_modal.ejs

@@ -0,0 +1,74 @@
+<!--弹出添加类别-->
+<div class="modal fade" id="save-limit-option" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">新增/修改判断</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>计量相关</label>
+                    <div class="ml-2 row">
+                        <div class="form-check form-check-inline col-10 ml-3 mb-2">
+                            <input class="form-check-input" type="checkbox" id="lo-limit">
+                            <label class="form-check-label" for="loc-limit">允许计量</label>
+                            <lable class="text-warning ml-2">不允许计量时,上下限无效</lable>
+                        </div>
+                        <div class="input-group input-group-sm col-5">
+                            <label class="mr-2">计量下限 </label>
+                            <input type="number" class="form-control" max="100" min="0" step="2" value="0" id="lo-lower">
+                            <div class="input-group-append">
+                                <span class="input-group-text">%</span>
+                            </div>
+                        </div>
+                        <div class="input-group input-group-sm col-5">
+                            <label class="mr-2">计量上限 </label>
+                            <input type="number" class="form-control" max="100" min="0" step="2" value="100" id="lo-upper">
+                            <div class="input-group-append">
+                                <span class="input-group-text">%</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>超限提示</label>
+                    <input class="form-control form-control-sm col-11 ml-3"  placeholder="请使用精简的提示" type="text" id="lo-hint">
+                </div>
+                <div class="form-group">
+                    <label>判断条件</label>
+                    <div class="ml-4 mb-2">
+                        <div class="d-inline-block">
+                            <select class="form-control form-control-sm" id="loc-field">
+                                <option value="gxby_status">工序报验</option>
+                                <option value="dagl_status">档案管理</option>
+                                <!--<option value="gxby_date_ago">完工日期</option>-->
+                            </select>
+                        </div>
+                        <div class="d-inline-block ml-2">
+                            <select class="form-control form-control-sm" id="loc-operation">
+                                <option value="="> = </option>
+                                <option value=">"> &gt; </option>
+                                <option value="<"> &lt; </option>
+                            </select>
+                        </div>
+                        <div class="d-inline-block ml-2">
+                            <input class="form-control form-control-sm"  placeholder="请输入判断值" type="text" id="loc-value">
+                        </div>
+                        <div class="d-inline-block ml-2">
+                            <button class="btn btn-sm btn-primary" id="loc-add">新增判断</button>
+                        </div>
+                    </div>
+                    <table class="table table-sm table-bordered col-11 ml-3">
+                        <tr class="text-center"><th width="80%">判断条件</th><th width="20%">操作</th></tr>
+                        <tbody id="loc-list"></tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" id="lo-id">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="save-lo-ok">确认</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 17 - 0
app/view/setting/tender.ejs

@@ -0,0 +1,17 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>标段【<%- tender.name %>】联动计量配置</h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0" id="xmj-spread">
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const limits = JSON.parse('<%- JSON.stringify(limits) %>');
+</script>

+ 3 - 1
app/view/stage/bwtz.ejs

@@ -236,6 +236,7 @@
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number', visible: <%- minusNoValue %> },
             {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'bgl_code', hAlign: 2, width: 60, formatter: '@', cellType: 'ellipsisAutoTip'},
             <% if ( ctx.tender.info.display.stage.priceDiff ) { %>
             { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', getValue(data) { return data.pc_tp ? data.org_price : null},  },
@@ -245,8 +246,9 @@
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, type: 'Number'},
-            {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '截止本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number', visible: <%- minusNoValue %>},
             {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, type: 'Number'},

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

@@ -578,6 +578,7 @@
     const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
     const contractExpr = <%- !!ctx.subProject.page_show.openContractExpr %>;
     const deleteFilePermission = <%- deleteFilePermission %>;
+    const limits = JSON.parse('<%- JSON.stringify(limits) %>');
 </script>
 <style>
 

+ 16 - 0
app/view/tender/detail_modal.ejs

@@ -1894,6 +1894,19 @@
                             </div>
                         </div>
                     </div>
+                    <div class="row">
+                        <div class="col-6 mb-2">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text" style="width:90px">计价类型</span>
+                                </div>
+                                <select class="form-control" id="tender-calc-type">
+                                    <option value="up">单价合同</option>
+                                    <option value="tp">总价合同</option>
+                                </select>
+                            </div>
+                        </div>
+                    </div>
                 </div>
                 <div class="modal-footer">
                     <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
@@ -2167,6 +2180,7 @@
 
         const loadSTypeProperty = function () {
             $('#tender-s-type').val(property.s_type);
+            $('#tender-calc-type').val(property.calc_type);
         };
 
         $('#bd-set-13').on('show.bs.modal', function () {
@@ -2175,10 +2189,12 @@
         function post13() {
             const prop = {
                 s_type: $('#tender-s-type').val(),
+                calc_type: $('#tender-calc-type').val(),
             }
             const tenderId = window.location.pathname.split('/')[2];
             postData('/tender/' + tenderId + '/save2', prop, function (data) {
                 property.s_type = data.s_type;
+                property.calc_type = data.calc_type;
                 $('#bd-set-13').modal('hide');
             });
         }

+ 18 - 0
config/web.js

@@ -1662,6 +1662,24 @@ const JsFiles = {
                 mergeFiles: [],
                 mergeFile: 'setting_user',
             },
+            s2b: {
+                files: [],
+                mergeFiles: ['/public/js/setting_s2b.js'],
+                mergeFile: 'setting_s2b',
+            },
+            tender: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/setting_tender.js',
+                ],
+                mergeFile: 'setting_tender',
+            },
             sp_permission: {
                 files: [],
                 mergeFiles: [

+ 224 - 0
sql/update.sql

@@ -22,6 +22,230 @@ ADD COLUMN `file_company` varchar(100) NOT NULL DEFAULT '' COMMENT '归档单位
 ALTER TABLE `zh_sub_project_permission`
 ADD COLUMN `last_time` timestamp NULL DEFAULT NULL COMMENT '最近使用时间' AFTER `tp_cache_time`;
 
+ALTER TABLE `zh_tender_info`
+ADD COLUMN `calc_type` varchar(20) NOT NULL DEFAULT 'up' COMMENT '计价类型(单价up,总价tp)' AFTER `s_type`;
+
+CREATE TABLE `zh_multi_limit`  (
+  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+  `pid` int(11) UNSIGNED NOT NULL COMMENT '项目id',
+  `limit_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
+  `user_id` int(11) NOT NULL COMMENT '创建人',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
+  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
+  `limit` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '限制计量',
+  `lower` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '计量下限',
+  `upper` tinyint(4) UNSIGNED NOT NULL DEFAULT 100 COMMENT '计量上限',
+  `condition` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '判断条件',
+  `hint` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '提示文本',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+
+ALTER TABLE `zh_tender`
+ADD COLUMN `s2b_multi_check` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '联动计量-检查计量' AFTER `map_pic`,
+ADD COLUMN `s2b_multi_limit` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '联动计量-限制上报' AFTER `s2b_multi_check`;
+
+ALTER TABLE `zh_ledger_extra_0`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_1`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_2`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_3`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_4`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_5`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_6`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_7`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_8`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_9`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_10`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_11`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_12`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_13`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_14`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_15`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_16`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_17`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_18`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_19`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_20`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_21`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_22`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_23`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_24`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_25`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_26`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_27`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_28`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_29`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_30`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_31`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_32`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_33`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_34`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_35`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_36`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_37`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_38`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_39`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_40`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_41`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_42`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_43`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_44`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_45`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_46`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_47`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_48`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_49`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_50`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_51`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_52`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_53`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_54`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_55`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_56`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_57`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_58`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_59`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_60`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_61`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_62`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_63`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_64`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_65`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_66`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_67`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_68`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_69`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_70`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_71`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_72`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_73`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_74`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_75`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_76`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_77`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_78`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_79`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_80`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_81`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_82`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_83`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_84`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_85`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_86`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_87`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_88`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_89`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_90`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_91`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_92`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_93`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_94`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_95`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_96`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_97`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_98`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+ALTER TABLE `zh_ledger_extra_99`
+ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
+
 ------------------------------------
 -- 表数据
 ------------------------------------