Quellcode durchsuchen

结算期变更功能提交

ellisran vor 1 Jahr
Ursprung
Commit
8386244293

+ 46 - 18
app/controller/change_controller.js

@@ -83,6 +83,7 @@ module.exports = app => {
                             auditStatus = c.uid === ctx.session.sessionUser.accountId ? 1 : 0;
                             const back_changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
                             c.stageChangeNum = this.ctx.helper.sum(back_changeUsedData.map(x => { return Math.abs(x.qty); }));
+                            c.isSettle = await ctx.service.changeSettleList.isSettle(c.cid);
                             break;
                         case 6:
                             changeAudit = await ctx.service.changeAudit.getLastBackUser(c.cid, c.times);
@@ -93,6 +94,7 @@ module.exports = app => {
                             auditStatus = c.uid === ctx.session.sessionUser.accountId ? 9 : 0;
                             const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
                             c.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
+                            c.isSettle = await ctx.service.changeSettleList.isSettle(c.cid);
                             break;
                         default:
                             break;
@@ -638,6 +640,14 @@ module.exports = app => {
                 if (change && change.status !== audit.flow.status.checked && ctx.session.sessionUser.is_admin) {
                     change.auditors2 = await ctx.service.changeAudit.getListGroupByWithoutYB(change.cid, change.times);
                 }
+                const settleBills = ctx.change.readySettle ? await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.change.readySettle.id } }) : [];
+                const settlePos = ctx.change.readySettle ? await ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.change.readySettle.id } }) : [];
+                // 判断并是否更新结算清单数据
+                await ctx.service.change.checkSettleUpdate(ctx.tender.id, ctx.change.readySettle);
+                // 获取清单
+                const changeList = await ctx.service.changeAuditList.getList(change.cid);
+                // 处理清单数据
+                const removeSettleNum = change.status !== audit.flow.status.checked ? await ctx.service.changeSettleList.updateChangeList(change.cid, settleBills, settlePos, changeList) : 0;
                 const renderData = {
                     tender,
                     change,
@@ -657,10 +667,14 @@ module.exports = app => {
                     upUnit: change.up_decimal ? change.up_decimal : ctx.tender.info.decimal.up,
                     authMobile: auth_mobile,
                     shenpiConst,
+                    settleStatus: ctx.service.settle.settleStatus,
                     unitList,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.information),
                     preUrl: '/tender/' + ctx.tender.id + '/change/' + ctx.change.cid + '/information',
                     precision: ctx.tender.info.precision,
+                    settleBills,
+                    settlePos,
+                    removeSettleNum,
                 };
                 // 获取是否已存在调用变更令
                 let changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
@@ -726,8 +740,6 @@ module.exports = app => {
                     // 获取公司列表
                     const companyList = await ctx.service.changeCompany.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                     renderData.companyList = companyList;
-                    // 获取已选清单
-                    const changeList = await ctx.service.changeAuditList.getList(change.cid);
                     renderData.changeLedgerList = await ctx.service.changeLedger.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
                     renderData.changePosList = await ctx.service.changePos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                     const stateInfo = ctx.helper._.find(fun_set.change_state, { order: ctx.change.state });
@@ -757,8 +769,6 @@ module.exports = app => {
                         if (updateList.length > 0) await ctx.service.changeAuditList.defaultUpdateRows(updateList);
                         await ctx.service.change.defaultUpdate({ delimit: stateInfo.value }, { where: { cid: change.cid } });
                     }
-                    renderData.changeList = changeList;
-
                     // 判断是否更新变更类别
                     if (ctx.helper._.findIndex(fun_set.change_class, { value: change.class, checked: true }) === -1) {
                         renderData.change.class = ctx.helper._.find(fun_set.change_class, { checked: true }).value;
@@ -789,12 +799,6 @@ module.exports = app => {
                         auditList3.push(auditTimeList);
                     }
                     renderData.auditList3 = auditList3;
-
-                    // 获取已选清单
-                    // let changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.params.cid } });
-                    const changeList = await ctx.service.changeAuditList.getList(change.cid);
-
-                    // changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
                     for (const cl of changeList) {
                         const audit_amount = cl.audit_amount !== null && cl.audit_amount !== '' ? cl.audit_amount.split(',') : '';
                         // 清单表页赋值
@@ -808,7 +812,6 @@ module.exports = app => {
                                 (ctx.helper._.indexOf([audit.flow.status.backnew, audit.flow.status.checking], change.status) !== -1 ? cl.camount : 0)) : cl.camount;
                         // cl.changed_amount = ctx.helper.add(cl.oamount ? parseFloat(cl.oamount) : 0, changed_amount ? parseFloat(changed_amount) : 0);
                     }
-                    renderData.changeList = changeList;
                     renderData.auditList2 = auditList2;
                 } else if (auditStatus === 6) {
                     // 展示页左侧审批流程列表和清单审批列表数据
@@ -842,12 +845,6 @@ module.exports = app => {
                         }
                     }
                     renderData.auditList4 = auditList4;
-
-                    // 获取已选清单
-                    // let changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.params.cid } });
-                    const changeList = await ctx.service.changeAuditList.getList(change.cid);
-
-                    // changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
                     for (const cl of changeList) {
                         const audit_amount = cl.audit_amount !== null && cl.audit_amount !== '' ? cl.audit_amount.split(',') : '';
                         // 清单表页赋值
@@ -861,10 +858,10 @@ module.exports = app => {
                                 (ctx.helper._.indexOf([audit.flow.status.backnew, audit.flow.status.checking], change.status) !== -1 ? cl.camount : 0)) : cl.camount;
                         // cl.changed_amount = ctx.helper.add(cl.oamount ? parseFloat(cl.oamount) : 0, changed_amount ? parseFloat(changed_amount) : 0);
                     }
-                    renderData.changeList = changeList;
                     renderData.changeLedgerList = await ctx.service.changeLedger.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
                     renderData.changePosList = await ctx.service.changePos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                 }
+                renderData.changeList = changeList;
                 renderData.auditList = auditList;
                 await this.layout('change/information.ejs', renderData, 'change/information_modal.ejs');
             } catch (err) {
@@ -1100,11 +1097,22 @@ module.exports = app => {
                 // 判断是否台账修订中,修订中则不获取changeLedger及changePos值
                 const lastRevise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
                 const data = JSON.parse(ctx.request.body.data);
+                const readySettle = await ctx.service.settle.getReadySettle(ctx.tender.id);
+                if (data.from === 'revise') await ctx.service.change.checkSettleUpdate(ctx.tender.id, readySettle);
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
                 const changeLedgerData = !ctx.session.sessionProject.page_show.openChangeRevise ? [] : (lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changeLedger.getData(ctx.tender.id));
                 const posData = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 const changePosData = !ctx.session.sessionProject.page_show.openChangeRevise ? [] : (lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changePos.getPosData({ tid: ctx.tender.id }));
                 const dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                // const settleStatus = ctx.service.settle.settleStatus;
+                const settleBills = readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
+                ctx.helper.assignRelaData(ledgerData, [
+                    { data: settleBills, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+                ]);
+                const settlePos = readySettle ? await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
+                this.ctx.helper.assignRelaData(posData, [
+                    { data: settlePos, fields: ['settle_status'], prefix: '', relaId: 'pid' },
+                ]);
                 // 标记ledger,搜索需求
                 if (changePosData.length > 0) {
                     const cplIdList = ctx.helper._.uniq(ctx.helper._.map(changePosData, 'lid'));
@@ -1174,6 +1182,10 @@ module.exports = app => {
                 if (!curAuditor || (curAuditor && curAuditor.uid !== ctx.session.sessionUser.accountId)) {
                     throw '该变更令当前您无权操作';
                 }
+                const readySettle = await ctx.service.settle.getReadySettle(changeData.tid);
+                if (readySettle && readySettle.settle_order !== ctx.tender.data.settle_order) {
+                    throw '结算数据发生变化,请刷新页面再提交';
+                }
                 let result = false;
                 const pid = this.ctx.session.sessionProject.id;
                 switch (status) {
@@ -1210,6 +1222,9 @@ module.exports = app => {
         async checkAuditCancel(ctx) {
             try {
                 if (!ctx.change.cancancel) throw '您无权进行该操作';
+                if (ctx.change.readySettle && ctx.change.readySettle.settle_order !== ctx.tender.data.settle_order) {
+                    throw '结算数据发生变化,请刷新页面再提交';
+                }
 
                 await ctx.service.changeAudit.checkCancel(ctx.change);
                 ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
@@ -1537,6 +1552,10 @@ module.exports = app => {
                 if (changeData.status !== audit.flow.status.checked || ctx.session.sessionUser.accountId !== auditInfo.uid) {
                     throw '您无权进行该操作';
                 }
+                const readySettle = await ctx.service.settle.getReadySettle(changeData.tid);
+                if (readySettle && readySettle.settle_order !== ctx.tender.data.settle_order) {
+                    throw '结算数据发生变化,请刷新页面再提交';
+                }
                 if (ctx.session.sessionUser.loginStatus === 0) {
                     const code = ctx.request.body.code;
                     const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
@@ -1586,6 +1605,10 @@ module.exports = app => {
                 if (changeData.status !== audit.flow.status.checked || ctx.session.sessionUser.accountId !== changeData.uid) {
                     throw '您无权进行该操作';
                 }
+                const readySettle = await ctx.service.settle.getReadySettle(changeData.tid);
+                if (readySettle && readySettle.settle_order !== ctx.tender.data.settle_order) {
+                    throw '结算数据发生变化,请刷新页面再提交';
+                }
                 if (ctx.session.sessionUser.loginStatus === 0) {
                     const code = ctx.request.body.code;
                     const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
@@ -1642,6 +1665,10 @@ module.exports = app => {
                 if (!(changeData.status === audit.flow.status.revise && (ctx.session.sessionUser.accountId === changeData.uid || ctx.session.sessionUser.accountId === ctx.session.sessionUser.is_admin))) {
                     throw '您无权进行该操作';
                 }
+                const readySettle = await ctx.service.settle.getReadySettle(changeData.tid);
+                if (readySettle && readySettle.settle_order !== ctx.tender.data.settle_order) {
+                    throw '结算数据发生变化,请刷新页面再提交';
+                }
                 // 重新审批
                 const result = await ctx.service.change.cancelRevise(changeData.cid, changeData.times);
                 if (!result) {
@@ -1816,6 +1843,7 @@ module.exports = app => {
                 audit: audit.flow,
                 stdChapters,
                 nodeType: stdConst.nodeType,
+                settleStatus: ctx.service.settle.settleStatus,
             };
         }
 

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
app/public/css/bootstrap/draw.css


+ 7 - 0
app/public/css/main.css

@@ -2165,7 +2165,14 @@ animation:shake 1s .2s ease both;}
     padding: .5rem;
 }
 .permission-title {
+    font-size: 14px;
     padding-bottom: .5rem;
     margin-bottom: .5rem;
     border-bottom: 1px solid rgba(0,0,0,.1);
 }
+.right-duiqi {
+    display: inline-block;
+    width: 70px;
+    text-align: right;
+    line-height: 25px;
+}

+ 9 - 0
app/public/js/change_information.js

@@ -723,3 +723,12 @@ function bytesToSize(bytes) {
     return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
 }
 
+// 判断是否是已结算清单
+function checkIsSettle(data) {
+    const info = data.mx_id ? _.find(settlePos, { lid: data.gcl_id, pid: data.mx_id }) : _.find(settleBills, { lid: data.gcl_id });
+    if (info && info.settle_status && info.settle_status === settleStatus.finish) {
+        return true;
+    }
+    return false;
+}
+

+ 9 - 2
app/public/js/change_information_approval.js

@@ -67,8 +67,7 @@ $(document).ready(() => {
             title: userinfo.name + ' 审批|数量',
             colSpan: '2|1', rowSpan: '1|1',
             field: 'audit_amount_' + aid,
-            hAlign: 2, width: 60, type: 'Number',
-            readOnly: aid !== parseInt(accountId)
+            hAlign: 2, width: 60, type: 'Number', readOnly: aid !== parseInt(accountId) ? true : 'readOnly.isSettle' ,
         };
         const newColTp = {
             title: '|金额',
@@ -82,6 +81,9 @@ $(document).ready(() => {
     }
     changeSpreadSetting.cols.push({title: '变更后|数量', colSpan: '2|1', rowSpan: '1|1', field: 'samount', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.changed_amount'});
     changeSpreadSetting.cols.push({title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sa_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.changed_tp'});
+    changeSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        return checkIsSettle(data) ? spreadColor.stage.settle : defaultColor;
+    }
 
     const changeCol = {
         getValue: {
@@ -111,6 +113,11 @@ $(document).ready(() => {
                 return data.bwmx ? data.bwmx : (data.xmj_jldy ? data.xmj_jldy : '');
             }
         },
+        readOnly: {
+            isSettle: function (data) {
+                return checkIsSettle(data);
+            }
+        }
     };
     const changeSpreadObj = {
         makeSjsFooter: function () {

+ 18 - 7
app/public/js/change_information_set.js

@@ -104,7 +104,7 @@ $(document).ready(() => {
             {title: '计量上限(%)', colSpan: '1', rowSpan: '2', field: 'delimit', hAlign: 2, width: 60, type: 'Number', readOnly: false, visible: openChangeState},
             {title: '原设计|数量', colSpan: '2|1', rowSpan: '1|1', field: 'oamount', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.oamount'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'oa_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.oa_tp'},
-            {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, type: 'Number', readOnly: false, getValue: 'getValue.camount'},
+            {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isSettle', getValue: 'getValue.camount'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'ca_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.ca_tp'},
             {title: '变更后|数量', colSpan: '2|1', rowSpan: '1|1', field: 'samount', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.changed_amount'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sa_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.changed_tp'},
@@ -140,6 +140,10 @@ $(document).ready(() => {
         }
     };
 
+    changeSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        return checkIsSettle(data) ? spreadColor.stage.settle : defaultColor;
+    }
+
     const changeCol = {
         getValue: {
             unit_price: function(data) {
@@ -165,7 +169,7 @@ $(document).ready(() => {
                     ZhCalc.round(ZhCalc.mul(ZhCalc.round(data.unit_price, unitPriceUnit), ZhCalc.round(data.camount, findDecimal(data.unit))), totalPriceUnit));
             },
             del_list: function (data) {
-                return !_.find(changeUsedData, { cbid: data.id }) ? '移除' : '';
+                return !_.find(changeUsedData, { cbid: data.id }) && !checkIsSettle(data) ? '移除' : '';
             }
         },
         readOnly: {
@@ -178,6 +182,9 @@ $(document).ready(() => {
             isEdit3: function (data) {
                 return !readOnly && _.findIndex(changeUsedData, { cbid: data.id }) === -1;
             },
+            isSettle: function (data) {
+                return !readOnly && checkIsSettle(data);
+            }
         },
     };
     // 数字只判断几个值(unit_price, oamount, camount)
@@ -415,7 +422,7 @@ $(document).ready(() => {
             const sel = info.sheet.getSelections()[0];
             const col = info.sheet.zh_setting.cols[sel.col];
             const data = SpreadJsObj.getSelectObject(info.sheet);
-            if (col && col.field === 'del_list' && data && !_.find(changeUsedData, { cbid: data.id })) {
+            if (col && col.field === 'del_list' && data && !_.find(changeUsedData, { cbid: data.id }) && !checkIsSettle(data)) {
                 changeSpreadObj.del();
             }
             if (col && col.field === 'camount' && data) {
@@ -940,6 +947,7 @@ $(document).ready(() => {
 
             if (hadcid !== 0) gclGatherData[ggd].cid = 1;
         }
+        console.log(gclGatherData);
         // 数组去重
         const dealBillList = result.dealBills;
         for (const db of gclGatherData) {
@@ -1381,8 +1389,9 @@ $(document).ready(() => {
                 const existGcl = _.find(changeList, {gcl_id: leaf.gcl_id, bwmx: (bwmx ? bwmx : leaf.jldy ? leaf.jldy : ''), oamount: leaf.quantity});
                 const isUsed = existGcl ? _.find(changeUsedData, { cbid: existGcl.id }) : null;
                 const isOldChaRu = changeOrder && isChecked && data_charu.indexOf(pushMsg) === -1;
-                const isDisabled = isUsed || isOldChaRu ? 'disabled ' : '';
-                codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '" mx_id="' + mx_id + '">' +
+                const isDisabled = isUsed || isOldChaRu || (leaf.settle_status === settleStatus.finish && !isChecked) ? 'disabled ' : '';
+                const bgColor = leaf.settle_status === settleStatus.finish ? 'style="background-color: ' + spreadColor.stage.settle + '"' : '';
+                codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '" mx_id="' + mx_id + '"' + bgColor + '>' +
                     '<td class="text-center">' + (index+1) + (leaf.cid ? '<i class="text-danger" style="font-weight: 900">*</i>' : '') + '</td>' +
                     '<td>' + leaf.code + '</td>' +
                     '<td>' + (leaf.jldy ? leaf.jldy: '') + '</td>' +
@@ -1398,7 +1407,7 @@ $(document).ready(() => {
         } else if (!isDeal && isCheck) {
             const pushMsg = '0*;*' + $(this).children('td').eq(5).text();
             const isChecked = data_bwmx.indexOf(pushMsg) !== -1 && isCheck ? 'checked ' : '';
-            const isDisabeld = changeOrder && isChecked && data_charu.indexOf(pushMsg) === -1 ? 'disabled ' : '';
+            const isDisabeld = (changeOrder && isChecked && data_charu.indexOf(pushMsg) === -1) ? 'disabled ' : '';
             codeHtml = '<tr quantity="'+ $(this).children('td').eq(5).text() +'" gcl_id="" mx_id=""><td class="text-center">1</td><td colspan="7" class="colspan_1">&nbsp;</td><td class="colspan_2 text-center"><input type="checkbox" ' + isChecked + isDisabeld +'></td></tr>';
         }
         $('#code-list').attr('data-index', parseInt($(this).children('td').eq(0).text()));
@@ -1515,9 +1524,11 @@ $(document).ready(() => {
                 })
             }
             $('#addlist').modal('hide');
+            tableDataRemake(changeListData);
         }, function () {
             $('#table-list-select tr').attr('data-charu', '');
             $('#addlist').modal('hide');
+            tableDataRemake(changeListData);
         });
     });
 
@@ -1763,7 +1774,7 @@ $(document).ready(() => {
         if (index) {
             if ($(this).is(':checked')){
                 $('#code-list tr').each(function () {
-                    if ($(this).css('display') !== 'none') {
+                    if ($(this).css('display') !== 'none' && $(this).find('input').prop('disabled') !== true) {
                         $(this).find('input').prop('checked', true);
                     }
                 })

+ 3 - 0
app/public/js/change_information_show.js

@@ -70,6 +70,9 @@ $(document).ready(() => {
     }
     changeSpreadSetting.cols.push({title: '变更后|数量', colSpan: '2|1', rowSpan: '1|1', field: 'samount', hAlign: 2, width: 60, type: 'Number', getValue: 'getValue.changed_amount'});
     changeSpreadSetting.cols.push({title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sa_tp', hAlign: 2, width: 80, type: 'Number', getValue: 'getValue.changed_tp'});
+    changeSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        return checkIsSettle(data) ? spreadColor.stage.settle : defaultColor;
+    }
 
     const changeCol = {
         getValue: {

+ 149 - 30
app/public/js/change_revise.js

@@ -75,7 +75,7 @@ $(document).ready(() => {
     const posCol = {
         readOnly: {
             isChangeAdd: function (data) {
-                return !readOnly && !data.formc;
+                return !readOnly && !data.formc && (data.settle_status && data.settle_status === settleStatus.finish);
             }
         }
     };
@@ -96,6 +96,12 @@ $(document).ready(() => {
             },
         },
     ];
+    billsSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (data && data.settle_status && data.settle_status === settleStatus.finish) {
+            return spreadColor.stage.settle;
+        }
+        return defaultColor;
+    };
     billsSpreadSetting.localCache = {
         key: 'changes-ledger-spread',
         colWidth: true,
@@ -121,6 +127,12 @@ $(document).ready(() => {
             },
         },
     ];
+    posSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (data && data.settle_status && data.settle_status === settleStatus.finish) {
+            return spreadColor.stage.settle;
+        }
+        return defaultColor;
+    };
     posSpreadSetting.localCache = {
         key: 'changes-pos-spread',
             colWidth: true,
@@ -313,7 +325,7 @@ $(document).ready(() => {
                 invalidAll();
                 return;
             }
-            let last = first, sameParent = true, nodeUsed = first.used;
+            let last = first, sameParent = true, nodeUsed = first.used, settleFinish = first.settle_status === settleStatus.finish;
             if (sel.rowCount > 1 && first) {
                 for (let r = 1; r < sel.rowCount; r++) {
                     const rNode = tree.nodes[sel.row + r];
@@ -326,6 +338,7 @@ $(document).ready(() => {
                         break;
                     }
                     nodeUsed = nodeUsed || rNode.used;
+                    settleFinish = settleFinish || rNode.settle_status === settleStatus.finish;
                     if (rNode.level > first.level) continue;
                     if ((rNode.level < first.level) || (rNode.level === first.level && rNode.pid !== first.pid)) {
                         sameParent = false;
@@ -345,16 +358,16 @@ $(document).ready(() => {
             }
             const valid = !sheet.zh_setting.readOnly;
 
-            setObjEnable($('a[name=base-opr][type=add]'), valid && first && first.level > 1);
-            setObjEnable($('a[name=base-opr][type=delete]'), valid && first && sameParent && first.level > 1 && !nodeUsed && first.formc);
+            setObjEnable($('a[name=base-opr][type=add]'), valid && first && first.level > 1 && first.settle_status !== settleStatus.finish);
+            setObjEnable($('a[name=base-opr][type=delete]'), valid && first && sameParent && first.level > 1 && !nodeUsed && first.formc && !settleFinish);
             setObjEnable($('a[name=base-opr][type=up-move]'), valid && first && sameParent && first.level > 1 && preNode && first.formc);
             setObjEnable($('a[name=base-opr][type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last) && first.formc);
             const posRange = last ? pos.getLedgerPos(last.id) : [];
             setObjEnable($('a[name=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed
-                && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)) && upPower && first.formc);
+                && first.level > 2 && ((!posRange || posRange.length === 0) || last.settle_status !== settleStatus.finish || tree.isLastSibling(last)) && upPower && first.formc);
             const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
             setObjEnable($('a[name=base-opr][type=down-level]'), valid && first && sameParent
-                && first.level > 1 && preNode && (preNode.children.length > 0 || (preNode.children.length === 0 && preNode.formc && (!preNodePosRange || preNodePosRange.length === 0))) && !preNode.used && first.formc);
+                && first.level > 1 && preNode && (preNode.children.length > 0 || (preNode.children.length === 0 && preNode.formc && (!preNodePosRange || preNodePosRange.length === 0))) && !preNode.used && first.formc && preNode.settle_status !== settleStatus.finish);
             setObjEnable($('#cut'), valid);
             setObjEnable($('#paste'), valid);
         },
@@ -446,32 +459,62 @@ $(document).ready(() => {
                         toastr.warning('选中的节点已计量,不可删除');
                         return;
                     }
+                    if (child.settle_status === settleStatus.finish) {
+                        toastr.warning('选中的节点已结算,不可删除');
+                        return;
+                    }
                 }
             } else if (type === 'up-level') {
                 const parent = tree.getParent(node);
                 const children = parent ? parent.children : tree.children;
                 const index = children.indexOf(node);
-                for (let i = index; i < children.length; i++) {
-                    const child = children[index];
-                    if (tree.checkNodeUsed(child, pos)) {
-                        if (i >= index + count) {
-                            toastr.warning('其后节点已计量,选中的节点不可升级');
-                        } else {
-                            toastr.warning('选中的节点已计量,不可升级');
-                        }
-                        return;
-                    }
+                const lastSelect = children[index + count - 1];
+                if (tree.checkNodeUsed(lastSelect, pos) && (!lastSelect.children || lastSelect.children.length === 0) && (children.length > index + count)) {
+                    toastr.warning('选中的节点已计量,不可升级');
+                    return;
+                }
+                if (lastSelect.settle_status === settleStatus.finish && (children.length > index + count)) {
+                    toastr.warning('选中的节点已结算,不可升级');
+                    return;
                 }
+                // for (let i = index; i < children.length; i++) {
+                //     const child = children[index];
+                //     if (tree.checkNodeUsed(child, pos)) {
+                //         if (i >= index + count) {
+                //             toastr.warning('其后节点已计量,选中的节点不可升级');
+                //         } else {
+                //             toastr.warning('选中的节点已计量,不可升级');
+                //         }
+                //         return;
+                //     }
+                // }
             } else if (type === 'down-level') {
                 const parent = tree.getParent(node);
                 const children = parent ? parent.children : tree.children;
                 const index = children.indexOf(node);
-                for (let i = 0; i < count; i++) {
+                if (index > 0 && children[index-1].used) {
+                    toastr.warning('其前节点已计量,选中的节点不可降级');
+                    return;
+                }
+                if (index > 0 && children[index-1].settle_status === settleStatus.finish) {
+                    toastr.warning('其前节点已结算,选中的节点不可降级');
+                    return;
+                }
+                for (let i = index; i < count; i++) {
                     const child = children[i+index];
                     if (tree.checkNodeUsed(child, pos)) {
                         toastr.warning('选中的节点已计量,不可降级');
                         return;
                     }
+                    if (tree.settle_status === settleStatus.finish) {
+                        toastr.warning('选中的节点已结算,不可降级');
+                        return;
+                    }
+                }
+            } else if (type === 'add') {
+                if (node.settle_status === settleStatus.finish) {
+                    toastr.warning('选中的节点已结算,不可新增子项');
+                    return;
                 }
             }
 
@@ -654,6 +697,7 @@ $(document).ready(() => {
                 gcl: {type: 'warning', msg: '工程量清单,不可粘贴项目节数量'},
                 posXmj: {type: 'warning', msg: '清单含有计量单元,不可粘贴项目节编号'},
                 // sameParent: {type: 'warning', msg: '仅可粘贴同层节点'},
+                settle: {type: 'warning', msg: '已结算节点,不可修改数量、单价、金额'},
             };
             const datas = [], filterNodes = [];
 
@@ -708,6 +752,10 @@ $(document).ready(() => {
                         toastMessageUniq (hint.usedUp);
                         continue;
                     }
+                    if (colSetting.type === 'Number' && node.settle_status === settleStatus.finish) {
+                        toastMessageUniq(hint.settle);
+                        continue;
+                    }
                     if (colSetting.type === 'Number') {
                         const num = _.toNumber(value);
                         if (num) {
@@ -873,6 +921,7 @@ $(document).ready(() => {
                     const col = sheet.zh_setting.cols[iCol];
                     const style = sheet.getStyle(iRow, iCol);
                     if (style.locked || (['dgn_qty1', 'dgn_qty2'].indexOf(col.field) >= 0 && node.b_code)) continue;
+                    if (col.type === 'Number' && node.settle_status === settleStatus.finish) continue;
 
                     if (['dgn_qty1', 'dgn_qty2'].indexOf(col.field) < 0 && sheet.zh_tree.checkNodeUsed(node, pos)) {
                         toastr.warning('"' + (node.code || '') + (node.b_code || '') + ' ' + node.name +'"已计量,请勿修改');
@@ -900,6 +949,10 @@ $(document).ready(() => {
             const self = this;
             const sheet = spread.getActiveSheet();
             const [tree, node] = this.getDefaultSelectInfo(spread.getActiveSheet());
+            if (node.settle_status === settleStatus.finish) {
+                toastr.warning('选中的节点已结算,不可粘贴整块');
+                return;
+            }
 
             postData(window.location.pathname + '/update', {
                 postType: 'paste-block',
@@ -944,7 +997,7 @@ $(document).ready(() => {
                     info.cancel = posRange && posRange.length > 0;
                     break;
                 case 'unit_price':
-                    info.cancel = (node.children && node.children.length > 0) || node.used;
+                    info.cancel = (node.children && node.children.length > 0) || node.used || node.settle_status === settleStatus.finish;
                     break;
                 case 'sgfh_qty':
                 case 'sgfh_tp':
@@ -954,11 +1007,11 @@ $(document).ready(() => {
                 case 'qtcl_tp':
                 case 'deal_qty':
                 case 'deal_tp':
-                    info.cancel = (node.children && node.children.length > 0);
+                    info.cancel = (node.children && node.children.length > 0) || node.settle_status === settleStatus.finish;
                     break;
                 case 'dgn_qty1':
                 case 'dgn_qty2':
-                    info.cancel = !_.isEmpty(node.b_code);
+                    info.cancel = !_.isEmpty(node.b_code || node.settle_status === settleStatus.finish);
                     break;
             }
         },
@@ -1168,7 +1221,7 @@ $(document).ready(() => {
                 if (!tree) return true;
                 const first = sheet.zh_tree.nodes[row];
                 const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && first.level > 1);
+                return !(valid && first && first.level > 1 && first.settle_status !== settleStatus.finish);
             }
         };
         billsContextMenuOptions.items.delete = {
@@ -1185,7 +1238,7 @@ $(document).ready(() => {
                 const tree = sheet.zh_tree;
                 if (!tree) return true;
                 const first = sheet.zh_tree.nodes[row];
-                let last = first, sameParent = true, nodeUsed = first.used;
+                let last = first, sameParent = true, nodeUsed = first.used, settleFinish = first.settle_status === settleStatus.finish;
                 if (sel.rowCount > 1 && first) {
                     for (let r = 1; r < sel.rowCount; r++) {
                         const rNode = tree.nodes[sel.row + r];
@@ -1197,6 +1250,7 @@ $(document).ready(() => {
                             return true;
                         }
                         nodeUsed = nodeUsed || rNode.used;
+                        settleFinish = settleFinish || rNode.settle_status === settleStatus.finish;
                         if (rNode.level > first.level) continue;
                         if ((rNode.level < first.level) || (rNode.level === first.level && rNode.pid !== first.pid)) {
                             sameParent = false;
@@ -1206,7 +1260,7 @@ $(document).ready(() => {
                     }
                 }
                 const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed && first.formc);
+                return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed && first.formc && !settleFinish);
             }
         };
         billsContextMenuOptions.items.sprBase = '----';
@@ -1348,7 +1402,7 @@ $(document).ready(() => {
                 if (!tree) return true;
                 const first = sheet.zh_tree.nodes[row];
                 const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && first.level > 1);
+                return !(valid && first && first.level > 1 && first.settle_status !== settleStatus.finish);
             }
         };
         billsContextMenuOptions.items.batchInsertBillsPos = {
@@ -1362,10 +1416,10 @@ $(document).ready(() => {
                 const select = SpreadJsObj.getSelectObject(billsSheet);
                 if (select) {
                     if (select.code && select.code !== '') {
-                        return !billsTree.isLeafXmj(select);
+                        return !billsTree.isLeafXmj(select) || select.settle_status === settleStatus.finish;
                     } else {
                         const parent = billsTree.getParent(select);
-                        return !(parent && billsTree.isLeafXmj(parent));
+                        return !(parent && billsTree.isLeafXmj(parent)) || !(parent && parent.settle_status === settleStatus.finish);
                     }
                 } else {
                     return false;
@@ -1447,7 +1501,7 @@ $(document).ready(() => {
                         ? (data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
                         : data[col.field];
                     $('#pos-expr').val(value).attr('field', col.field).attr('org', data[col.field])
-                        .attr('readOnly', readOnly || (!data.formc && cell.locked())).attr('data-row', sel.row);
+                        .attr('readOnly', readOnly || (!data.formc && cell.locked()) || data.settle_status === settleStatus.finish).attr('data-row', sel.row);
                 } else {
                     $('#pos-expr').val('').attr('readOnly', true);
                     $('#pos-expr').removeAttr('data-row');
@@ -1467,7 +1521,7 @@ $(document).ready(() => {
             if (node) {
                 const posData = pos.getLedgerPos(node.id) || [];
                 SpreadJsObj.loadSheetData(posSheet, 'data', posData);
-                posSheet.zh_setting.readOnly = readOnly || (node.used && posData.length === 0);
+                posSheet.zh_setting.readOnly = readOnly || (node.used && posData.length === 0) || (node.settle_status && node.settle_status === settleStatus.finish);
             } else {
                 SpreadJsObj.loadSheetData(posSheet, 'data', []);
                 posSheet.zh_setting.readOnly = true;
@@ -1502,6 +1556,10 @@ $(document).ready(() => {
                             toastr.error('"' + posData.name + '"已计量,请勿删除');
                             return;
                         }
+                        if (posData.settle_status) {
+                            toastr.error('"' + posData.name + '"已结算,请勿删除');
+                            return;
+                        }
                         data.postData.push(sheet.zh_data[iRow + row].id);
                     }
                 }
@@ -1525,7 +1583,12 @@ $(document).ready(() => {
                     }
                 }
                 data.postData.push(nextUpdate);
-
+            } else if (type === 'add') {
+                const billsNode = SpreadJsObj.getSelectObject(billsSheet);
+                if (billsNode.settle_status === settleStatus.finish) {
+                    toastr.warning('清单已结算,不可新增计量单元');
+                    return;
+                }
             }
             if (data.postData.length > 0) {
                 postData(window.location.pathname + '/update', data, function (result) {
@@ -1560,6 +1623,9 @@ $(document).ready(() => {
                     info.sheet.getCell(info.row, info.col).text(node[exprInfo.expr]);
                 }
             }
+            if (col.type === 'Number' && node.settle_status === settleStatus.finish) {
+                info.cancel = true;
+            }
         },
         /**
          * 编辑单元格响应事件
@@ -1605,12 +1671,22 @@ $(document).ready(() => {
                         const sortData = info.sheet.zh_data;
                         const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
                         data.postData = { name: newText, lid: node.id, porder: order};
+                        if (node.settle_status === settleStatus.finish) {
+                            toastr.error('已结算清单不可插入计量单元');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
                     } else {
                         return;
                     }
                 } else {
                     data.posPostType = 'update';
                     data.postData = {id: posData.id, name: newText};
+                    if (col.type === 'Number' && posData.settle_status === settleStatus.finish) {
+                        toastr.error('计量单元已结算,不可修改');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
                 }
             } else if (!posData) {
                 toastr.warning('新增计量单元请先输入名称');
@@ -1728,6 +1804,10 @@ $(document).ready(() => {
                         toastr.error('"' + posData.name + '"已计量,请勿删除');
                         return;
                     }
+                    if (posData.settle_status === settleStatus.finish) {
+                        toastr.error(`"${posData.name}"已计量,请勿删除`);
+                        return;
+                    }
                     data.postData.push(sortData[iRow + row].id);
                 }
             }
@@ -1762,6 +1842,11 @@ $(document).ready(() => {
                 posSpreadObj.loadCurPosData();
                 return;
             }
+            if (node.settle_status === settleStatus.finish) {
+                toastr.error('清单已结算,请勿修改计量单元数据');
+                posSpread.loadCurPosData();
+                return;
+            }
             if (!info.sheet.zh_setting) {
                 posSpreadObj.loadCurPosData();
                 return;
@@ -1769,6 +1854,10 @@ $(document).ready(() => {
 
             const data = [];
             const sortData = info.sheet.zh_data || [];
+            const hint = {
+                expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
+                settle: {type: 'warning', msg: '计量单元已结算,不可修改台账数据,已过滤'},
+            };
             if (sortData.length === 0 || info.cellRange.row + info.cellRange.rowCount > sortData.length) {
                 if (info.cellRange.col !== 0) {
                     toastr.warning('新增计量单元请先输入名称');
@@ -1788,6 +1877,11 @@ $(document).ready(() => {
                     if (!colSetting) continue;
 
                     posData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+                    if (posData.id && colSetting.type === 'Number' && sortData[curRow].settle_status === settleStatus.finish) {
+                        bPaste = false;
+                        toastMessageUniq(hint.settle);
+                        continue;
+                    }
                     if (colSetting.type === 'Number') {
                         const num = _.toNumber(posData[colSetting.field]);
                         if (num) {
@@ -1915,7 +2009,15 @@ $(document).ready(() => {
                     disabled: function (key, opt) {
                         if (posSheet.zh_data) {
                             const selection = posSheet.getSelections();
-                            return (posSheet.zh_data.length < selection[0].row + selection[0].rowCount) || (posSheet.zh_data[selection[0].row] && !posSheet.zh_data[selection[0].row].formc);
+                            const sel = selection ? selection[0] : sheet.getSelections()[0];
+                            const row = sel ? sel.row : -1;
+                            const rowCount = sel.rowCount;
+                            let settleFinish = false;
+                            for (let i = row; i < row + rowCount; i++) {
+                                if (!posSheet.zh_data[i]) continue;
+                                settleFinish = settleFinish || posSheet.zh_data[i].settle_status === settleStatus.finish;
+                            }
+                            return (posSheet.zh_data.length < selection[0].row + selection[0].rowCount) || (posSheet.zh_data[selection[0].row] && !posSheet.zh_data[selection[0].row].formc) || settleFinish;
                         } else {
                             return true;
                         }
@@ -1980,6 +2082,10 @@ $(document).ready(() => {
                         toastr.warning('非最底层项目下,不应添加节点');
                         return;
                     }
+                    if (mainNode.settle_status === settleStatus.finish) {
+                        toastr.warning('已结算节点下,不应添加签约清单');
+                        return;
+                    }
 
                     postData(window.location.pathname + '/update', {
                         postType: 'add-deal',
@@ -2386,6 +2492,19 @@ $(document).ready(() => {
                 toastr.warning('非最底层项目下,不应添加节点');
                 return;
             }
+            if (mainNode.settle_status === settleStatus.finish) {
+                toastr.warning('已结算节点下,不可添加工程量清单');
+                return;
+            }
+        } else {
+            const stdNodes = stdTree.getAllParents(stdNode);
+            for (const node of stdNodes) {
+                const parent = mainTree.datas.find(x => { return x.code === node.code && x.name === node.name; });
+                if (parent && parent.settle_status === settleStatus.finish) {
+                    toastr.warning('项目节父项已结算,不可添加节点');
+                    return;
+                }
+            }
         }
 
         postData(window.location.pathname + '/update', {

+ 117 - 20
app/service/change.js

@@ -1528,8 +1528,25 @@ module.exports = app => {
                 await this.ctx.service.changeHistory.saveHistory(this.transaction, changeData, changeList);
                 // 申请变更金额更新为上一次审批审批变更后数量,清空audit_amount值
                 const updateTpList = [];
+                const readySettle = await this.ctx.service.settle.getReadySettle(changeData.tid);
+                const settleBills = readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
+                const settlePos = readySettle ? await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
+                const insertSettleList = [];
                 for (const cl of changeList) {
-                    updateTpList.push({ id: cl.id, camount: cl.spamount, audit_amount: null, samount: '' });
+                    let settleInfo = null;
+                    if (cl.gcl_id) {
+                        settleInfo = cl.mx_id ? this._.find(settlePos, { lid: cl.gcl_id, pid: cl.mx_id }) : this._.find(settleBills, { lid: cl.gcl_id });
+                        if (settleInfo) {
+                            insertSettleList.push({
+                                tid: changeData.tid,
+                                cid: changeData.cid,
+                                gcl_id: cl.gcl_id,
+                                mx_id: cl.mx_id,
+                                amount: cl.checked_amount,
+                            });
+                        }
+                    }
+                    updateTpList.push({ id: cl.id, camount: cl.spamount, audit_amount: null, samount: settleInfo ? cl.spamount : '' });
                 }
                 if (updateTpList.length > 0) {
                     await this.transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateTpList);
@@ -1557,6 +1574,9 @@ module.exports = app => {
                     },
                 };
                 await this.transaction.update(this.tableName, change_update, options);
+                // 添加已结算清单到change_settle_list表中,防止删除
+                await this.transaction.delete(this.ctx.service.changeSettleList.tableName, { cid });
+                if (insertSettleList.length > 0) await this.transaction.insert(this.ctx.service.changeSettleList.tableName, insertSettleList);
 
                 await this.transaction.commit();
                 result = true;
@@ -1689,7 +1709,23 @@ module.exports = app => {
                     where: { cid: changeInfo.cid },
                 });
                 const updateChangeList = [];
+                const readySettle = await this.ctx.service.settle.getReadySettle(changeInfo.tid);
+                const settleBills = readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
+                const settlePos = readySettle ? await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
+                const insertSettleList = [];
                 for (const cl of changeList) {
+                    if (cl.gcl_id) {
+                        const settleInfo = cl.mx_id ? this._.find(settlePos, { lid: cl.gcl_id, pid: cl.mx_id }) : this._.find(settleBills, { lid: cl.gcl_id });
+                        if (settleInfo) {
+                            insertSettleList.push({
+                                tid: changeInfo.tid,
+                                cid: changeInfo.cid,
+                                gcl_id: cl.gcl_id,
+                                mx_id: cl.mx_id,
+                                amount: cl.checked_amount,
+                            });
+                        }
+                    }
                     const audit_amount = cl.audit_amount.split(',');
                     const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
                     audit_amount.splice(-1, 1);
@@ -1718,27 +1754,31 @@ module.exports = app => {
                     },
                 };
                 await this.transaction.update(this.tableName, change_update, options);
+
+                // 添加已结算清单到change_settle_list表中,防止删除
+                await this.transaction.delete(this.ctx.service.changeSettleList.tableName, { cid });
+                if (insertSettleList.length > 0) await this.transaction.insert(this.ctx.service.changeSettleList.tableName, insertSettleList);
                 await this.transaction.commit();
                 result = true;
-                const sms = new SMS(this.ctx);
-                const code = await sms.contentChange(changeInfo.code);
-                const shenpiUrl = await this.ctx.helper.urlToShort(
-                    this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + changeInfo.tid + '/change/' + changeInfo.cid + '/information#shenpi'
-                );
-                await this.ctx.helper.sendAliSms(zsAudit.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), SmsAliConst.template.change_check, {
-                    biangeng: code,
-                    code: shenpiUrl,
-                });
-
-                // 微信模板通知
-                const wechatData = {
-                    wap_url: shenpiUrl,
-                    status: wxConst.status.check,
-                    tips: wxConst.tips.check,
-                    code: this.ctx.session.sessionProject.code,
-                    c_name: changeInfo.name,
-                };
-                await this.ctx.helper.sendWechat(zsAudit.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                // const sms = new SMS(this.ctx);
+                // const code = await sms.contentChange(changeInfo.code);
+                // const shenpiUrl = await this.ctx.helper.urlToShort(
+                //     this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + changeInfo.tid + '/change/' + changeInfo.cid + '/information#shenpi'
+                // );
+                // await this.ctx.helper.sendAliSms(zsAudit.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), SmsAliConst.template.change_check, {
+                //     biangeng: code,
+                //     code: shenpiUrl,
+                // });
+                //
+                // // 微信模板通知
+                // const wechatData = {
+                //     wap_url: shenpiUrl,
+                //     status: wxConst.status.check,
+                //     tips: wxConst.tips.check,
+                //     code: this.ctx.session.sessionProject.code,
+                //     c_name: changeInfo.name,
+                // };
+                // await this.ctx.helper.sendWechat(zsAudit.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
 
             } catch (error) {
                 console.log(error);
@@ -1920,6 +1960,63 @@ module.exports = app => {
             }
         }
 
+        async checkSettleUpdate(tid, settleInfo = null) {
+            const readySettle = settleInfo ? settleInfo : await this.ctx.service.settle.getReadySettle(tid);
+            if (readySettle && readySettle.settle_order !== this.ctx.tender.data.settle_order) {
+                // 更新tender里的settle_order值且更新变更数据
+                const transaction = await this.db.beginTransaction();
+                try {
+                    const changeLedgerData = await this.ctx.service.changeLedger.getData(tid);
+                    const changePosData = await this.ctx.service.changePos.getPosData({ tid });
+                    // const changeSettleData = await this.ctx.service.changeSettleList.getAllDataByCondition({ where: { tid } });
+                    if (changeLedgerData.length > 0 || changePosData.length > 0) {
+                        const settleBills = await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: readySettle.id } });
+                        const removeLedgerList = [];
+                        const settleStatus = this.ctx.service.settle.settleStatus;
+                        for (const l of changeLedgerData) {
+                            const parent = this._.find(settleBills, { tree_id: l.ledger_pid });
+                            if (parent && parent.settle_status === settleStatus.finish) {
+                                removeLedgerList.push(l);
+                            }
+                        }
+                        // 移除计量单元
+                        const removePosList = [];
+                        for (const p of changePosData) {
+                            const parent = this._.find(settleBills, { lid: p.lid });
+                            if (parent && parent.settle_status === settleStatus.finish) {
+                                removePosList.push(p);
+                            }
+                        }
+                        // // 移除结算清单
+                        // const removeSettleIdList = [];
+                        // const settlePos = await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: readySettle.id } });
+                        // for (const s of changeSettleData) {
+                        //     const info = s.mx_id ? this._.find(settlePos, { lid: s.gcl_id, pid: s.mx_id }) : this._.find(settleBills, { lid: s.gcl_id });
+                        //     if (!info || info.settle_status !== settleStatus.finish) {
+                        //         removeSettleIdList.push(s.id);
+                        //     }
+                        // }
+                        if (removeLedgerList.length > 0) {
+                            await this.ctx.service.changeLedger.deleteBySettle(transaction, tid, removeLedgerList);
+                        }
+                        if (removePosList.length > 0) {
+                            await this.ctx.service.changePos.deleteBySettle(transaction, tid, removePosList);
+                        }
+                        // if (removeSettleIdList.length > 0) {
+                        //     await transaction.delete(this.ctx.service.changeSettleList.tableName, { id: removeSettleIdList });
+                        // }
+                    }
+                    await transaction.update(this.ctx.service.tender.tableName, { id: tid, settle_order: readySettle.settle_order });
+                    await transaction.commit();
+                    return true;
+                } catch (err) {
+                    console.log(err);
+                    await transaction.rollback();
+                    throw err;
+                }
+            }
+        }
+
     }
 
     return Change;

+ 25 - 9
app/service/change_ledger.js

@@ -106,7 +106,7 @@ module.exports = app => {
          * @returns {Promise<*>}
          * @private
          */
-        async _updateChildrenOrder(tableName, mid, pid, order, incre = 1) {
+        async _updateChildrenOrder(tableName, mid, pid, order, incre = 1, transaction = this.transaction) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere(this.setting.mid, {
                 value: mid,
@@ -125,7 +125,7 @@ module.exports = app => {
                 selfOperate: incre > 0 ? '+' : '-',
             });
             const [sql, sqlParam] = this.sqlBuilder.build(tableName, 'update');
-            const data = await this.transaction.query(sql, sqlParam);
+            const data = await transaction.query(sql, sqlParam);
 
             return data;
         }
@@ -410,12 +410,12 @@ module.exports = app => {
          * @return {Promise<void>}
          * @private
          */
-        async _deleteRelaData(mid, deleteData) {
-            await this.ctx.service.changePos.deletePosData(this.transaction, mid, this._.map(deleteData, 'id'));
+        async _deleteRelaData(mid, deleteData, transaction = this.transaction) {
+            await this.ctx.service.changePos.deletePosData(transaction, mid, this._.map(deleteData, 'id'));
         }
 
-        async _deleteChangeAuditListData(mid, deleteData) {
-            await this.ctx.service.changeAuditList.deleteDataByRevise(this.transaction, mid, this._.map(deleteData, 'id'));
+        async _deleteChangeAuditListData(mid, deleteData, transaction = this.transaction) {
+            await this.ctx.service.changeAuditList.deleteDataByRevise(transaction, mid, this._.map(deleteData, 'id'));
         }
 
         /**
@@ -425,7 +425,7 @@ module.exports = app => {
          * @return {Promise<*>}
          * @private
          */
-        async _deleteNodeData(mid, deleteNode) {
+        async _deleteNodeData(mid, deleteNode, transaction = this.transaction) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere(this.setting.mid, {
                 value: mid,
@@ -436,7 +436,7 @@ module.exports = app => {
                 operate: 'Like',
             });
             const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'delete');
-            const result = await this.transaction.query(sql, sqlParam);
+            const result = await transaction.query(sql, sqlParam);
 
             return result;
         }
@@ -570,7 +570,7 @@ module.exports = app => {
             }
         }
 
-        async delete(mid, kid, count) {
+        async delete(mid, kid, count = null) {
             if (count && count > 1) {
                 return await this.deleteNodes(mid, kid, count);
             } else {
@@ -578,6 +578,22 @@ module.exports = app => {
             }
         }
 
+        async deleteBySettle(transcation, mid, ledgerDatas) {
+            if (ledgerDatas.length <= 0) return;
+            const allDeleteData = [];
+            for (const l of ledgerDatas) {
+                // 删除部位明细
+                const deleteData = await this.getDataByFullPath(this.tableName, mid, l[this.setting.fullPath] + '%', transcation);
+                if (deleteData.length === 0) throw '删除节点数据错误';
+                // deleteData数组插入到allDeleteData后面
+                allDeleteData.push(...deleteData);
+                const operate = await this._deleteNodeData(mid, l, transcation);
+                await this._updateChildrenOrder(this.ctx.service.ledger.tableName, mid, l[this.setting.pid], l[this.setting.order] + 1, -1, transcation);
+            }
+            await this._deleteRelaData(mid, allDeleteData, transcation);
+            await this._deleteChangeAuditListData(mid, allDeleteData, transcation);
+        }
+
         /**
          * 上移节点
          *

+ 8 - 0
app/service/change_pos.js

@@ -285,6 +285,14 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        async deleteBySettle(transaction, tid, posDatas) {
+            if (!posDatas || posDatas.length === 0) {
+                return;
+            }
+            await transaction.delete(this.tableName, { id: this._.map(posDatas, 'id') });
+            await this.ctx.service.changeAuditList.deleteDataByRevise(transaction, tid, this._.map(posDatas, 'id'), 'mx_id');
+        }
         /**
          * 复制粘贴 部位明细数据
          * @param {Array} data - 复制粘贴的数据

+ 76 - 0
app/service/change_settle_list.js

@@ -0,0 +1,76 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/8/14
+ * @version
+ */
+module.exports = app => {
+    class ChangeSettleList extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'change_settle_list';
+        }
+
+        async isSettle(cid) {
+            const result = await this.getDataByCondition({ cid });
+            return result.length > 0;
+        }
+
+        async updateChangeList(cid, settleBills, settlePos, list) {
+            const removeList = [];
+            const updateList = [];
+            const changeSettleListData = await this.getAllDataByCondition({ where: { cid } });
+            const settleStatus = this.ctx.service.settle.settleStatus;
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const cl of list) {
+                    const settleInfo = cl.mx_id ? this._.find(settlePos, { lid: cl.gcl_id, pid: cl.mx_id }) : this._.find(settleBills, { lid: cl.gcl_id });
+                    if (settleInfo && settleInfo.settle_status === settleStatus.finish) {
+                        const changeSettleInfo = cl.mx_id ? this._.find(changeSettleListData, { gcl_id: cl.gcl_id, mx_id: cl.mx_id }) : this._.find(changeSettleListData, { gcl_id: cl.gcl_id });
+                        if (!changeSettleInfo) {
+                            removeList.push(cl.id);
+                        } else {
+                            if (changeSettleInfo.amount !== cl.spamount || changeSettleInfo.amount !== cl.camount) {
+                                cl.camount = changeSettleInfo.amount;
+                                cl.spamount = changeSettleInfo.amount;
+                                updateList.push({ id: cl.id, camount: cl.camount, spamount: cl.spamount });
+                            }
+                        }
+                    }
+                }
+                // list根据removeList对应删除
+                if (removeList.length > 0) {
+                    for (const id of removeList) {
+                        const index = this._.findIndex(list, { id });
+                        if (index !== -1) {
+                            list.splice(index, 1);
+                        }
+                    }
+                    await transaction.delete(this.ctx.service.changeAuditList.tableName, { id: removeList });
+                }
+                if (updateList.length > 0) await transaction.update(this.ctx.service.changeAuditList.tableName, updateList);
+                // 重算变更令金额
+                if (removeList.length > 0 || updateList.length > 0) {
+                    await this.ctx.service.changeAuditList.reCalcTp(transaction, cid);
+                }
+                await transaction.commit();
+            } catch (err) {
+                console.log(err);
+                await transaction.rollback();
+                throw err;
+            }
+            return removeList.length;
+        }
+    }
+
+    return ChangeSettleList;
+};

+ 1 - 1
app/service/tender.js

@@ -16,7 +16,7 @@ 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',
-    'build_status',
+    'build_status', 'settle_order',
 ];
 
 module.exports = app => {

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

@@ -144,7 +144,7 @@
                             <span class="<%- auditConst.auditStatusClass[c.changeAudit.status] %>"><%- auditConst.auditStatusString[c.changeAudit.status] %></span>
                         </td>
                         <% } else { %><td></td><% } %>
-                        <td><% if ((c.status === auditConst.status.uncheck || (((c.status === auditConst.status.back || c.status === auditConst.status.revise) && c.stageChangeNum === 0) && c.uid === uid)) && !ctx.tender.isTourist) { %><a href="#del-bg" cid="<%= c.cid %>" data-toggle="modal" data-target="#del-bg" class="btn btn-outline-danger btn-sm delete-cid-modal">删除</a><% } %></td>
+                        <td><% if (c.uid === uid && (c.status === auditConst.status.uncheck || ((c.status === auditConst.status.back || c.status === auditConst.status.revise) && c.stageChangeNum === 0 && !c.isSettle))) { %><a href="#del-bg" cid="<%= c.cid %>" data-toggle="modal" data-target="#del-bg" class="btn btn-outline-danger btn-sm delete-cid-modal">删除</a><% } %></td>
                     </tr>
                     <% } %>
                     <% } %>

+ 7 - 0
app/view/change/information.ejs

@@ -478,6 +478,8 @@
     let attData = JSON.parse(unescape('<%- escape(JSON.stringify(attList)) %>'));
     let currPageFileData = [];
     const change_uid = parseInt('<%- change.uid %>');;
+    const settleBills = JSON.parse(unescape('<%- escape(JSON.stringify(settleBills)) %>'));
+    const settlePos = JSON.parse(unescape('<%- escape(JSON.stringify(settlePos)) %>'));
     autoFlashHeight();
     $('a[href="#sub-ap"').click(function() {
         if (parseInt(ledgeStatus) === ledgerConsts.uncheck) {
@@ -507,6 +509,11 @@
     }
     const openChangeState = <%- ctx.session.sessionProject.page_show.openChangeState ? true : false %>;
     const startLimit = 9;
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
+    const removeSettleNum = <%- removeSettleNum %>;
+    if (removeSettleNum > 0) {
+        toastr.warning('结算发生变化,已移除 ' + removeSettleNum + ' 条已结算清单。');
+    }
 </script>
 <% if (auditStatus === 1 || auditStatus === 2 || auditStatus === 9) { %>
 <script>

+ 1 - 0
app/view/change/revise.ejs

@@ -151,4 +151,5 @@
     const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const decimal = JSON.parse('<%- JSON.stringify(ctx.tender.info.decimal) %>');
     const nodeType = JSON.parse('<%- JSON.stringify(nodeType) %>');
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
 </script>

+ 25 - 22
db_script/ledger_his.js

@@ -104,7 +104,9 @@ const doCompleteTender = async function(t) {
     if (revise.length > 0) {
         let his_id, withoutHisRevise = [];
         for (const r of revise) {
-            const preRevise = revise.find(x => { return x.status === audit.revise.status.checked && x.corder === r.corder - 1});
+            const preRevise = revise.find(x => {
+                return x.status === audit.revise.status.checked && x.corder === r.corder - 1
+            });
             if (preRevise) {
                 await querySql('Update zh_ledger_revise Set pre_his_id = ? Where id = ?', [preRevise.his_id, r.id]);
             }
@@ -115,17 +117,18 @@ const doCompleteTender = async function(t) {
             } else {
                 if (r.status !== 1) withoutHisRevise.push(r);
 
-        }
-        if (!his_id || t.measure_type === measureType.gcl.value) his_id = await saveLedgerHis(t);
-        await querySql('Update zh_tender Set his_id = ? Where id = ?', [his_id, t.id]);
-        const stages = await querySql('Select * From zh_stage where tid = ? and status = ?', [t.id, audit.stage.status.checked]);
-        for (const s of stages) {
-            if (s.status === audit.stage.status.checked) {
-                await querySql('Update zh_stage Set his_id = ? Where id = ?', [his_id, s.id]);
             }
-        }
-        for (const r of withoutHisRevise) {
-            await querySql('Update zh_ledger_revise Set pre_his_id = ?, his_id = ? Where id = ?', [his_id, his_id, r.id]);
+            if (!his_id || t.measure_type === measureType.gcl.value) his_id = await saveLedgerHis(t);
+            await querySql('Update zh_tender Set his_id = ? Where id = ?', [his_id, t.id]);
+            const stages = await querySql('Select * From zh_stage where tid = ? and status = ?', [t.id, audit.stage.status.checked]);
+            for (const s of stages) {
+                if (s.status === audit.stage.status.checked) {
+                    await querySql('Update zh_stage Set his_id = ? Where id = ?', [his_id, s.id]);
+                }
+            }
+            for (const r of withoutHisRevise) {
+                await querySql('Update zh_ledger_revise Set pre_his_id = ?, his_id = ? Where id = ?', [his_id, his_id, r.id]);
+            }
         }
     } else {
         const ledgerHis = await saveLedgerHis(t);
@@ -138,20 +141,20 @@ const doCompleteTender = async function(t) {
             }
         }
     }
-};
 
-const doComplete = async function() {
-    try {
-        const tenders = await querySql('Select * From zh_tender where ledger_status <> ?', [audit.ledger.status.uncheck]);
-        for (const t of tenders) {
-            await doCompleteTender(t);
+    const doComplete = async function () {
+        try {
+            const tenders = await querySql('Select * From zh_tender where ledger_status <> ?', [audit.ledger.status.uncheck]);
+            for (const t of tenders) {
+                await doCompleteTender(t);
+            }
+        } catch (err) {
+            console.log(err);
         }
-    } catch (err) {
-        console.log(err);
-    }
-    pool.end();
+        pool.end();
+    };
+    doComplete();
 };
-doComplete();
 
 // const doCompleteTender2 = async function(t) {
 //     const revise = await querySql('Select * From zh_ledger_revise where tid = ? order By in_time asc', [t.id]);