MaiXinRong 1 år sedan
förälder
incheckning
6b143769a7

+ 36 - 5
app/controller/revise_controller.js

@@ -302,6 +302,7 @@ module.exports = app => {
                 auditHistory,
                 shenpiConst,
                 nodeType: stdConst.nodeType,
+                settleStatus: ctx.service.settle.settleStatus,
             };
         }
 
@@ -405,6 +406,7 @@ module.exports = app => {
                     ? await ctx.helper.loadLedgerDataFromOss(revise.curHis.pos_file)
                     : await ctx.service.revisePos.getData(ctx.tender.id);
 
+                // 已计量
                 if (revise.uid === ctx.session.sessionUser.accountId &&
                     (revise.status === audit.revise.status.uncheck || revise.status === audit.revise.status.checkNo)) {
                     const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true);
@@ -432,6 +434,15 @@ module.exports = app => {
                         }
                     }
                 }
+                // 结算状态
+                const settleStatusBills = ctx.revise.readySettle ? await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.revise.readySettle.id }}): [];
+                this.ctx.helper.assignRelaData(reviseBills, [
+                    { data: settleStatusBills, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+                ]);
+                const settleStatusPos = ctx.revise.readySettle ? await ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.revise.readySettle.id }}) : [];
+                this.ctx.helper.assignRelaData(revisePos, [
+                    { data: settleStatusPos, fields: ['settle_status'], prefix: '', relaId: 'pid' },
+                ]);
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
                 const price = !revise.readOnly ? await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } }) : [];
                 ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags, price } };
@@ -957,19 +968,39 @@ module.exports = app => {
         async _loadDataByFilter(ctx, filter) {
             switch(filter) {
                 case 'bills':
-                    return ctx.revise.preHis ? await this.ctx.helper.loadLedgerDataFromOss(ctx.revise.preHis.bills_file) : [];
+                    const billsData = ctx.revise.preHis ? await this.ctx.helper.loadLedgerDataFromOss(ctx.revise.preHis.bills_file) : [];
+                    const settleStatusBills = ctx.revise.readySettle ? await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.revise.readySettle.id }}): [];
+                    this.ctx.helper.assignRelaData(billsData, [
+                        { data: settleStatusBills, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+                    ]);
+                    return billsData;
                 case 'pos':
                     if (ctx.tender.data.measure_type === measureType.gcl.value) return [];
-                    return ctx.revise.preHis ? await this.ctx.helper.loadLedgerDataFromOss(ctx.revise.preHis.pos_file) : [];
+                    const posData = ctx.revise.preHis ? await this.ctx.helper.loadLedgerDataFromOss(ctx.revise.preHis.pos_file) : [];
+                    const settleStatusPos = ctx.revise.readySettle ? await ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.revise.readySettle.id }}) : [];
+                    this.ctx.helper.assignRelaData(posData, [
+                        { data: settleStatusPos, fields: ['settle_status'], prefix: '', relaId: 'pid' },
+                    ]);
+                    return posData;
                 case 'reviseBills':
-                    return ctx.revise.readOnly && ctx.revise.curHis
+                    const reviseBillsData = ctx.revise.readOnly && ctx.revise.curHis
                         ? await this.ctx.helper.loadLedgerDataFromOss(ctx.revise.curHis.bills_file)
                         : await ctx.service.reviseBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                    const settleBills = ctx.revise.readySettle ? await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.revise.readySettle.id }}): [];
+                    this.ctx.helper.assignRelaData(reviseBillsData, [
+                        { data: settleBills, fields: ['settle_status'], prefix: '', relaId: 'lid' },
+                    ]);
+                    return reviseBillsData;
                 case 'revisePos':
                     if (ctx.tender.data.measure_type === measureType.gcl.value) return [];
-                    return ctx.revise.readOnly && ctx.revise.curHis
+                    const revisePosData = ctx.revise.readOnly && ctx.revise.curHis
                         ? await this.ctx.helper.loadLedgerDataFromOss(ctx.revise.curHis.pos_file)
                         : await ctx.service.revisePos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                    const settlePos = ctx.revise.readySettle ? await ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.revise.readySettle.id }}) : [];
+                    this.ctx.helper.assignRelaData(revisePosData, [
+                        { data: settlePos, fields: ['settle_status'], prefix: '', relaId: 'pid' },
+                    ]);
+                    return revisePosData;
                 case 'stageBills':
                 case 'stagePos':
                     if (!ctx.lastStage) ctx.lastStage = await this._getLastStage(ctx);
@@ -1027,13 +1058,13 @@ module.exports = app => {
             await this.layout('revise/gcl_compare.ejs', renderData);
         }
 
-
         async price(ctx) {
             if (!ctx.revise) throw '台账修订数据有误';
             const renderData = {
                 preUrl: ctx.url.replace('/price', ''),
                 revise: ctx.revise,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.price),
+                settleStatus: ctx.service.settle.settleStatus,
             };
             await this.layout('revise/price.ejs', renderData, 'revise/price_modal.ejs');
         }

+ 61 - 7
app/controller/settle_controller.js

@@ -53,16 +53,17 @@ module.exports = app => {
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.settle.list);
                 renderData.settles = await ctx.service.settle.getValidSettles(ctx.tender.id);
                 for (const s of renderData.settles) {
-                    if (s.status === auditConst.settle.status.uncheck) {
+                    if (s.audit_status === auditConst.settle.status.uncheck) {
                         s.curAuditors = [];
-                    } else if (s.status === auditConst.settle.status.checkNo) {
-                        s.curAuditors = await ctx.service.settleAudit.getAuditorsByStatus(sid, s.status, s.audit_times - 1);
+                    } else if (s.audit_status === auditConst.settle.status.checkNo) {
+                        s.curAuditors = await ctx.service.settleAudit.getAuditorsByStatus(sid, s.audit_status, s.audit_times - 1);
                     } else {
-                        s.curAuditors = await ctx.service.settleAudit.getAuditorsByStatus(s.id, s.status, s.audit_times);
+                        s.curAuditors = await ctx.service.settleAudit.getAuditorsByStatus(s.id, s.audit_status, s.audit_times);
                     }
-                    if (s.status === auditConst.settle.status.checkNoPre) {
+                    if (s.audit_status === auditConst.settle.status.checkNoPre) {
                         s.curAuditorsPre = await ctx.service.settleAudit.getAuditorsByStatus(s.id, auditConst.settle.status.checking, s.audit_times);
                     }
+                    s.end_tp = ctx.helper.add(s.tp, s.pre_tp);
                 }
                 renderData.checkedStageCount = await ctx.service.stage.count({ tid: ctx.tender.id, status: auditConst.stage.status.checked });
                 await this.layout('settle/list.ejs', renderData, 'settle/list_modal.ejs');
@@ -118,9 +119,9 @@ module.exports = app => {
                 const settle = await ctx.service.settle.getDataByCondition({ tid: ctx.tender.id, settle_order: data.order });
                 if (!settle) throw '修改的结算期不存在';
 
-                if (ctx.session.sessionUser.accountId !== stage.user_id) throw '您无权修改该数据';
+                if (ctx.session.sessionUser.accountId !== settle.user_id) throw '您无权修改该数据';
 
-                await this.ctx.service.stage.saveSettle(ctx.tender.id, data.order, data.date, data.period);
+                await this.ctx.service.settle.saveSettle(ctx.tender.id, data.order, data.date, data.period);
                 ctx.redirect('/tender/' + ctx.tender.id + '/settle');
             } catch (err) {
                 ctx.log(err);
@@ -153,6 +154,9 @@ module.exports = app => {
 
         async _getDefaultRenderData(ctx) {
             const data = {
+                settleStatusHint: ctx.service.settle.statusArray,
+                settleStatusColor: ctx.service.settle.statusColor,
+                settleStatus: ctx.service.settle.settleStatus,
                 tender: ctx.tender.data,
                 auditConst: auditConst.settle,
                 measureType,
@@ -238,9 +242,13 @@ module.exports = app => {
             const ledgerData = await ctx.service.ledger.getAllDataByCondition({ columns: this.ledgerColumn, where: { tender_id: ctx.tender.id } });
             const endStageData = await ctx.service.stageBillsFinal.getAllDataByCondition({ where: { sid: ctx.settle.latestStage.id } });
             const extraData = this.ledgerExtraColumn.length > 0 ? await ctx.service.ledgerExtra.getData(ctx.tender.id, this.ledgerExtraColumn) : [];
+            const settleStatus = ctx.settle.settle_order > 1
+                ? await ctx.service.settleBills.getAllDataByCondition({ column: ['lid', 'settle_status'], where: { settle_order: ctx.settle.settle_order - 1, settle_status: [1, 2]} })
+                : [];
             this.ctx.helper.assignRelaData(ledgerData, [
                 { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'end_', relaId: 'lid' },
                 { data: extraData, fields: this.ledgerExtraColumn, prefix: '', relaId: 'id' },
+                { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'lid' },
             ]);
             return ledgerData;
         }
@@ -252,9 +260,13 @@ module.exports = app => {
             const posData = await ctx.service.pos.getAllDataByCondition({ columns: this.posColumn, where: { tid: ctx.tender.id } });
             const endStageData = await ctx.service.stagePosFinal.getAllDataByCondition({ where: { sid: ctx.settle.latestStage.id } });
             const extraData = this.posExtraColumn.length > 0 ? await ctx.service.posExtra.getData(ctx.tender.id, this.posExtraColumn) : [];
+            const settleStatus = ctx.settle.settle_order > 1
+                ? await ctx.service.settlePos.getAllDataByCondition({ column: ['pid', 'settle_status'], where: { settle_order: ctx.settle.settle_order - 1, settle_status: [1, 2]} })
+                : [];
             this.ctx.helper.assignRelaData(posData, [
                 { data: endStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'end_', relaId: 'pid' },
                 { data: extraData, fields: this.posExtraColumn, prefix: '', relaId: 'id'},
+                { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'pid' },
             ]);
             return posData;
         }
@@ -540,6 +552,7 @@ module.exports = app => {
         async auditStart(ctx) {
             try {
                 if (ctx.settle.user_id !== ctx.session.sessionUser.accountId) throw '您无权上报该期数据';
+                if (ctx.settle.revising) throw '台账修订中,不可上报';
                 if (ctx.settle.audit_status !== auditConst.settle.status.uncheck && ctx.settle.audit_status !== auditConst.settle.status.checkNo) throw '该期数据当前无法上报';
                 await this._checkSettleSelect(ctx);
 
@@ -560,6 +573,7 @@ module.exports = app => {
                 if (ctx.settle.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0) {
                     throw '您无权进行该操作';
                 }
+                if (ctx.settle.revising) throw '台账修订中,不可审批';
 
                 const checkType = parseInt(ctx.request.body.checkType);
                 const opinion = ctx.request.body.opinion.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
@@ -570,6 +584,46 @@ module.exports = app => {
             }
             ctx.redirect(ctx.request.header.referer);
         }
+        async auditCheckAgain(ctx) {
+            try {
+                if (ctx.settle.settle_order !== ctx.settle.highOrder) throw '非最新一期,不可重新审批';
+                if (ctx.settle.audit_status !== auditConst.settle.status.checked) throw '未审批完成,不可重新审批';
+                if (ctx.settle.revising) throw '台账修订中,不可重审';
+
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const user = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!user.auth_mobile) throw '未绑定手机号';
+
+                    const code = ctx.request.body.code;
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+
+                const adminCheckAgain = ctx.request.body.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin;
+                if (ctx.settle.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0 && !adminCheckAgain) throw '您无权重新审批';
+
+                await ctx.service.settleAudit.checkAgain(ctx.settle, adminCheckAgain);
+            } catch (err) {
+                this.log(err);
+                ctx.postError(err, '重新审批失败');
+            }
+            ctx.redirect(ctx.request.header.referer);
+        }
+        async auditCheckCancel(ctx) {
+            try {
+                if (ctx.settle.revising) throw '台账修订中,不可撤回';
+                if (!ctx.settle.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.settleAudit.checkCancel(ctx.settle);
+            } catch (err) {
+                this.log(err);
+                ctx.postError(err, '撤回失败');
+            }
+            ctx.redirect(ctx.request.header.referer);
+        }
 
         async loadGatherData(ctx) {
             try {

+ 9 - 3
app/controller/stage_controller.js

@@ -166,7 +166,7 @@ module.exports = app => {
                     this.ctx.stage.readOnly || this.ctx.stage.revising, {minusNoValue: renderData.minusNoValue});
                 renderData.changeConst = changeConst;
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.index);
-                renderData.whiteList = this.ctx.app.config.multipart.whitelist;
+                renderData.whiteList = ctx.app.config.multipart.whitelist;
                 renderData.imType = tenderConst.imType;
                 renderData.auditConst = auditConst;
                 // 获取附件列表
@@ -186,7 +186,8 @@ 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.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.settleStatus = ctx.service.settle.settleStatus;
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
             } catch (err) {
                 this.log(err);
@@ -268,7 +269,8 @@ module.exports = app => {
                 }
             }
             const changeData = await ctx.service.changeAuditList.getBillsSum(ctx.tender.id);
-
+            // 结算状态
+            const settleStatus = ctx.stage.readySettle ? await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.stage.readySettle.id }}) : [];
             // 查询截止上期数据
             const preStageData = ctx.stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(ledgerData, [
@@ -280,6 +282,7 @@ module.exports = app => {
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used'], prefix: 'pre_', relaId: 'lid' },
                 { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
                 { data: changeData, fields: ['qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'due_', relaId: 'gcl_id' },
+                { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'lid' },
             ]);
             return ledgerData;
         }
@@ -302,6 +305,8 @@ module.exports = app => {
                     await ctx.service.stagePos.deleteById(surplus);
                 }
             }
+            // 结算状态
+            const settleStatus = ctx.stage.readySettle ? await ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.stage.readySettle.id }}) : [];
             // 查询截止上期数据
             const preStageData = ctx.stage.order > 1 ? await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(posData, [
@@ -309,6 +314,7 @@ module.exports = app => {
                 { data: extraData, fields: this.posExtraColumn, prefix: '', relaId: 'id'},
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'pid' },
                 { data: preStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'pre_', relaId: 'pid' },
+                { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'pid' },
             ]);
             return posData;
         }

+ 18 - 0
app/lib/ledger.js

@@ -331,6 +331,24 @@ class baseTree {
             }
         }
     }
+    initNodeData(field, defaultValue, calcFun) {
+        if (!field || !calcFun) return;
+
+        const initNode = function (node) {
+            if (node[field] === undefined) node[field] = defaultValue;
+            if (node.children && node.children.length > 0) {
+                const values = [];
+                for (const child of node.children) {
+                    initNode(child);
+                    if (values.indexOf(child[field]) < 0) values.push(child[field]);
+                }
+                node[field] = calcFun(values, defaultValue);
+            }
+        };
+        for (const node of this.children) {
+            initNode(node);
+        }
+    }
 }
 
 class billsTree extends baseTree {

+ 25 - 17
app/lib/settle.js

@@ -13,6 +13,7 @@ const Ledger = require('./ledger');
 class Settle {
     constructor (ctx) {
         this.ctx = ctx;
+        this.settleStatus = ctx.service.settle.settleStatus;
     }
 
     async _loadLatestStageData() {
@@ -53,6 +54,7 @@ class Settle {
                 if (!sp) continue;
                 sp.is_settle = true;
                 sp.selected = true;
+                sp.is_org_settle = 1;
                 const sb = this.stageTree.nodes.find(x => { return x.id === sp.lid });
                 sb.is_settle = true;
                 const parents = this.stageTree.getAllParents(sb);
@@ -62,6 +64,7 @@ class Settle {
                 const sb = this.stageTree.nodes.find(x => { return x.id === s.lid });
                 sb.is_settle = true;
                 sb.selected = true;
+                sb.is_org_settle = 1;
                 const parents = this.stageTree.getAllParents(sb);
                 parents.forEach(p => { p.is_settle = true; });
                 const posterity = this.stageTree.getPosterity(sb);
@@ -95,9 +98,10 @@ class Settle {
                 sp.pre_settle = true;
                 sp.pre_contract_qty = pp.end_contract_qty;
                 sp.pre_qc_qty = pp.end_qc_qty;
-                sp.pre_qc_minus_qty = pp.pre_qc_minus_qty;
+                sp.pre_qc_minus_qty = pp.end_qc_minus_qty;
                 sp.settle_status = pp.settle_status;
                 sp.settle_done_order = pp.settle_done_order;
+                sp.is_org_settle = pp.is_org_settle;
             }
         }
         const preBills = await this.ctx.service.settleBills.getAllDataByCondition({ where: { tid: this.settle.tid, settle_order: this.settle.settle_order - 1 } });
@@ -105,20 +109,23 @@ class Settle {
             const sb = this.stageTree.nodes.find(x => { return x.id === pb.lid });
             if (sb) {
                 sb.pre_settle = true;
-                sb.pre_contract_qty = pb.pre_contract_qty;
-                sb.pre_contract_tp = pb.pre_contract_tp;
-                sb.pre_qc_qty = pb.pre_qc_qty;
-                sb.pre_qc_tp = pb.pre_qc_tp;
-                sb.pre_qc_minus_qty = pb.pre_qc_minus_qty;
+                sb.pre_contract_qty = pb.end_contract_qty;
+                sb.pre_contract_tp = pb.end_contract_tp;
+                sb.pre_qc_qty = pb.end_qc_qty;
+                sb.pre_qc_tp = pb.end_qc_tp;
+                sb.pre_qc_minus_qty = pb.end_qc_minus_qty;
                 sb.settle_status = pb.settle_status;
                 sb.settle_done_order = pb.settle_done_order;
+                sb.is_org_settle = pb.is_org_settle;
             }
         }
+        this.stageTree.initNodeData('settle_status', this.settleStatus.non, values => { return values.length === 1 ? values[0] : this.settleStatus.part});
     }
 
     calculateSettle() {
         const helper = this.ctx.helper;
         const settle = this.settle;
+        const settleStatus = this.settleStatus;
         this.stagePos.calculateAll(function(p) {
             if (p.is_settle || !p.pre_settle) {
                 p.cur_contract_qty = p.settle_contract_qty;
@@ -131,7 +138,7 @@ class Settle {
                 p.end_qc_minus_qty = helper.add(p.cur_qc_minus_qty, p.pre_qc_minus_qty);
             }
             if (helper.numEqual(p.end_contract_qty, p.quantity)) {
-                p.settle_status = 2;
+                p.settle_status = settleStatus.finish;
                 p.settle_done_order = p.settle_done_order || settle.settle_order;
             }
         });
@@ -164,22 +171,23 @@ class Settle {
                 b.end_qc_tp = helper.add(b.cur_qc_tp, b.pre_qc_tp);
                 b.end_qc_minus_qty = helper.add(b.cur_qc_minus_qty, b.pre_qc_minus_qty);
             }
-            if (b.is_tp) {
+            //if (b.is_tp) {
+            if (!b.end_contract_qty) {
                 if (helper.numEqual(b.end_contract_tp, b.total_price)) {
-                    b.settle_status = 2;
+                    b.settle_status = settleStatus.finish;
                     b.settle_done_order = b.settle_done_order || settle.settle_order;
                 } else {
-                    b.settle_status = 1;
+                    b.settle_status = settleStatus.part;
                 }
             } else {
                 if (helper.numEqual(b.end_contract_qty, b.quantity)) {
-                    b.settle_status = 2;
+                    b.settle_status = settleStatus.finish;
                     b.settle_done_order = b.settle_done_order || settle.settle_order;
                 } else {
-                    b.settle_status = 1;
+                    b.settle_status = settleStatus.part;
                 }
             }
-        })
+        });
     }
 
     getSettleData() {
@@ -200,8 +208,8 @@ class Settle {
                 pre_qc_qty: node.pre_qc_qty || 0, pre_qc_tp: node.pre_qc_tp || 0, pre_qc_minus_qty: node.pre_qc_minus_qty || 0,
                 end_contract_qty: node.end_contract_qty || 0, end_contract_tp: node.end_contract_tp || 0,
                 end_qc_qty: node.end_qc_qty || 0, end_qc_tp: node.end_qc_tp || 0, end_qc_minus_qty: node.end_qc_minus_qty || 0,
-                is_settle: node.is_settle ? 1 : 0, pre_settle: node.pre_settle ? 1 : 0,
-                settle_status: node.settle_status || 0, settle_done_order: node.settle_done_order || 0,
+                is_settle: node.is_settle ? 1 : 0, is_org_settle: node.is_org_settle || 0, pre_settle: node.pre_settle ? 1 : 0,
+                settle_status: node.settle_status || this.settleStatus.non, settle_done_order: node.settle_done_order || 0,
             });
             sum.contract_tp = this.ctx.helper.add(sum.contract_tp, node.cur_contract_tp);
             sum.qc_tp = this.ctx.helper.add(sum.qc_tp, node.cur_qc_tp);
@@ -217,8 +225,8 @@ class Settle {
                 cur_contract_qty: pos.cur_contract_qty || 0, cur_qc_qty: pos.cur_qc_qty || 0, cur_qc_minus_qty: pos.cur_qc_minus_qty || 0,
                 pre_contract_qty: pos.pre_contract_qty || 0, pre_qc_qty: pos.pre_qc_qty || 0, pre_qc_minus_qty: pos.pre_qc_minus_qty || 0,
                 end_contract_qty: pos.end_contract_qty || 0, end_qc_qty: pos.end_qc_qty || 0, end_qc_minus_qty: pos.end_qc_minus_qty || 0,
-                is_settle: pos.is_settle ? 1 : 0, pre_settle: pos.pre_settle ? 1 : 0,
-                settle_status: pos.settle_status || 0, settle_done_order: pos.settle_done_order || 0,
+                is_settle: pos.is_settle ? 1 : 0, is_org_settle: pos.is_org_settle || 0, pre_settle: pos.pre_settle ? 1 : 0,
+                settle_status: pos.settle_status || this.settleStatus.non, settle_done_order: pos.settle_done_order || 0,
             });
         }
         return [settleBills, settlePos, sum];

+ 1 - 0
app/middleware/change_check.js

@@ -68,6 +68,7 @@ module.exports = options => {
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }
+            change.readySettle = yield this.service.settle.getReadySettle(revise.tid);
             this.change = change;
             if ((change.status === status.uncheck || change.status === status.back || change.status === status.revise) && change.tp_decimal !== this.tender.info.decimal.tp) {
                 this.change.tp_decimal = this.tender.info.decimal.tp;

+ 1 - 0
app/middleware/revise_check.js

@@ -38,6 +38,7 @@ module.exports = options => {
             revise.readOnly = revise.uid !== this.session.sessionUser.accountId ||
                 revise.status === auditConst.status.checking || revise.status === auditConst.status.checked;
             revise.priceCount = yield this.service.revisePrice.count({ rid: revise.id });
+            revise.readySettle = yield this.service.settle.getReadySettle(revise.tid);
             this.revise = revise;
             yield next;
         } catch (err) {

+ 4 - 0
app/middleware/settle_check.js

@@ -6,6 +6,7 @@
  * @date
  * @version
  */
+const reviseStatus = require('../const/audit').revise.status;
 
 module.exports = options => {
     /**
@@ -28,6 +29,9 @@ module.exports = options => {
             // 获取最新的期数
             settle.highOrder = yield this.service.settle.count({ tid: this.tender.id });
             yield this.service.settle.checkSettleShenpi(settle);
+            // 是否台账修订中
+            const lastRevise = yield this.service.ledgerRevise.getLastestRevise(this.tender.id);
+            settle.revising = (lastRevise && lastRevise.status !== reviseStatus.checked) || false;
             this.settle = settle;
             yield next;
         } catch (err) {

+ 3 - 1
app/middleware/stage_check.js

@@ -128,11 +128,12 @@ module.exports = options => {
             // 判断stage流程可否撤回,是哪一种撤回
             yield this.service.stage.doCheckStageCanCancel(stage);
 
-            // 是否台账修订中
+            // 是否台账修订中
             const lastRevise = yield this.service.ledgerRevise.getLastestRevise(this.tender.id);
             stage.revising = (lastRevise && lastRevise.status !== reviseStatus.checked) || false;
             // 根据状态判断是否需要更新审批人列表
             yield this.service.stage.doCheckStageCanCancel(stage);
+            stage.readySettle = yield this.service.settle.getReadySettle(stage.tid);
             this.stage = stage;
             // 根据状态判断是否需要更新审批人列表
             if ((stage.status === status.uncheck || stage.status === status.checkNo) && this.tender.info.shenpi.stage !== shenpiConst.sp_status.sqspr) {
@@ -169,6 +170,7 @@ module.exports = options => {
                     }
                 }
             }
+
             yield next;
         } catch (err) {
             this.helper.log(err);

+ 5 - 0
app/public/js/gcl_gather.js

@@ -312,6 +312,7 @@ const gclGatherModel = (function () {
             if (d.drawing_code) {
                 dx.drawing_code = d.drawing_code;
             }
+            dx.settle_status = (posRange && posRange.length > 0 ? d.settle_status : node.settle_status) || 0 ;
             gcl.leafXmjs.push(dx);
         }
     }
@@ -398,7 +399,10 @@ const gclGatherModel = (function () {
             gcl.final_percent = gcl.final_qty && gcl.end_gather_qty
                 ? ZhCalc.mul(ZhCalc.div(gcl.end_gather_qty, gcl.final_qty), 100, 2)
                 : ZhCalc.mul(ZhCalc.div(gcl.end_gather_tp, gcl.final_tp), 100, 2);
+
+            let settle_status = [];
             for (const xmj of gcl.leafXmjs) {
+                if (settle_status.indexOf(xmj.settle_status) < 0) settle_status.push(xmj.settle_status);
                 xmj.pre_gather_qty = ZhCalc.add(xmj.pre_contract_qty, xmj.pre_qc_qty);
                 xmj.gather_qty = ZhCalc.add(xmj.contract_qty, xmj.qc_qty);
                 xmj.end_contract_qty = ZhCalc.add(xmj.pre_contract_qty, xmj.contract_qty);
@@ -411,6 +415,7 @@ const gclGatherModel = (function () {
                 xmj.end_gather_percent = ZhCalc.mul(ZhCalc.div(xmj.end_gather_qty, xmj.end_final_qty), 100, 2);
                 xmj.end_final_1_percent = ZhCalc.mul(ZhCalc.div(xmj.end_gather_qty, xmj.end_final_1_qty), 100, 2);
             }
+            gcl.settle_status = settle_status.length === 1 ? settle_status[0] : 1;
         }
     }
 

+ 20 - 0
app/public/js/path_tree.js

@@ -1127,6 +1127,24 @@ const createNewPathTree = function (type, setting) {
         getCodeSplit() {
             return this.checkCodeType() === '07' ? '-' : '0';
         }
+        initNodeData(field, defaultValue, calcFun) {
+            if (!field || !calcFun) return;
+
+            const initNode = function (node) {
+                if (node[field] === undefined) node[field] = defaultValue;
+                if (node.children && node.children.length > 0) {
+                    const values = [];
+                    for (const child of node.children) {
+                        initNode(child);
+                        if (values.indexOf(child[field]) < 0) values.push(child[field]);
+                    }
+                    node[field] = calcFun(values, defaultValue);
+                }
+            };
+            for (const node of this.children) {
+                initNode(node);
+            }
+        }
     }
 
     class LedgerTree extends FxTree {
@@ -1239,6 +1257,8 @@ const createNewPathTree = function (type, setting) {
             });
         }
         checkRevisePrice(d) {
+            if (d.b_code === '203-3-2') console.log(d);
+            if (d.settle_status) return false;
             if (!this.price || this.price.length === 0) return false;
             const setting = this.setting;
             const pid = this.getAllParents(d).map(x => { return x[setting.id] + ''; });

+ 127 - 24
app/public/js/revise.js

@@ -192,25 +192,26 @@ $(document).ready(() => {
             if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
+                    const settleFinish = data.settle_status === settleStatus.finish;
                     const exprInfo = getExprInfo(col.field);
                     if (exprInfo) {
                         $('#bills-expr').val(data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
-                            .attr('field', col.field).attr('org', data[col.field]);
+                            .attr('field', col.field).attr('org', data[col.field]).attr('readOnly', readOnly || cell.locked() || settleFinish);
                     } else {
-                        $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
+                        $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]).attr('readOnly', readOnly || cell.locked() || settleFinish);
                     }
 
                     if (col.field.indexOf('dgn') >= 0) {
-                        $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (_.isString(data.b_code) && data.b_code !== ''));
+                        $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (_.isString(data.b_code) && data.b_code !== '') || settleFinish);
                     } else if (col.field === 'unit_price') {
-                        $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (data.children && data.children.length > 0) || (_.isBoolean(data.used) && data.used === true));
+                        $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (data.children && data.children.length > 0) || (_.isBoolean(data.used) && data.used === true) || settleFinish);
                     } else {
                         const nodePos = pos.getLedgerPos(data.id);
                         if (nodePos && nodePos.length > 0) {
                             $('#bills-expr').val('').attr('readOnly', true);
                             $('#bills-expr').removeAttr('data-row');
                         } else {
-                            $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (data.children && data.children.length > 0));
+                            $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (data.children && data.children.length > 0) || settleFinish);
                         }
                     }
                     $('#bills-expr').attr('data-row', sel.row);
@@ -276,7 +277,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];
@@ -285,6 +286,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;
@@ -296,16 +298,16 @@ $(document).ready(() => {
             const preNode = tree.getPreSiblingNode(first);
             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);
+            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 && !settleFinish);
             setObjEnable($('a[name=base-opr][type=up-move]'), valid && first && sameParent && first.level > 1 && preNode);
             setObjEnable($('a[name=base-opr][type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
             const posRange = last ? pos.getLedgerPos(last.id) : [];
             setObjEnable($('a[name=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first)
-                && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
+                && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)) && last.settle_status !== settleStatus.finish);
             const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
             setObjEnable($('a[name=base-opr][type=down-level]'), valid && first && sameParent
-                && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0) && !preNode.used);
+                && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0) && !preNode.used && preNode.settle_status !== settleStatus.finish);
             setObjEnable($('#cut'), valid);
             setObjEnable($('#paste'), valid);
         },
@@ -398,6 +400,10 @@ $(document).ready(() => {
                         toastr.warning('选中的节点已计量,不可删除');
                         return;
                     }
+                    if (child.settle_status === settleStatus.finish) {
+                        toastr.warning('选中的节点已结算,不可删除');
+                        return;
+                    }
                 }
             } else if (type === 'up-level') {
                 const parent = tree.getParent(node);
@@ -408,6 +414,10 @@ $(document).ready(() => {
                     toastr.warning('选中的节点已计量,不可升级');
                     return;
                 }
+                if (lastSelect.settle_status === settleStatus.finish) {
+                    toastr.warning('选中的节点已结算,不可升级');
+                    return;
+                }
                 // for (let i = index; i < children.length; i++) {
                 //     const child = children[index];
                 //     if (tree.checkNodeUsed(child, pos)) {
@@ -427,12 +437,25 @@ $(document).ready(() => {
                     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;
                 }
             }
 
@@ -611,6 +634,7 @@ $(document).ready(() => {
                 gcl: {type: 'warning', msg: '工程量清单,不可粘贴项目节数量'},
                 posXmj: {type: 'warning', msg: '清单含有计量单元,不可粘贴项目节编号'},
                 // sameParent: {type: 'warning', msg: '仅可粘贴同层节点'},
+                settle: {type: 'warning', msg: '已结算节点,不可修改数量、单价、金额'},
             };
             const datas = [], filterNodes = [];
 
@@ -667,6 +691,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) {
@@ -810,6 +838,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 +'"已计量,请勿修改');
@@ -837,6 +866,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',
@@ -881,7 +914,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':
@@ -891,11 +924,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;
             }
         },
@@ -1107,7 +1140,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 = {
@@ -1124,7 +1157,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];
@@ -1133,6 +1166,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;
@@ -1142,7 +1176,7 @@ $(document).ready(() => {
                     }
                 }
                 const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed);
+                return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed && !settleFinish);
             }
         };
         billsContextMenuOptions.items.sprBase = '----';
@@ -1172,7 +1206,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 = {
@@ -1183,10 +1217,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;
@@ -1338,7 +1372,8 @@ $(document).ready(() => {
                 return readOnly
                     || (node.children && node.children.length > 0)
                     || (!_.isNil(node.b_code) && node.b_code !== '')
-                    || billsTree.checkNodeUsed(node, pos);
+                    || billsTree.checkNodeUsed(node, pos)
+                    || node.settle_status === settleStatus.finish;
             },
             callback: function (key, opt) {
                 const node = SpreadJsObj.getSelectObject(billsSheet);
@@ -1388,7 +1423,7 @@ $(document).ready(() => {
             icon: 'fa-link',
             disabled: function (key, opt) {
                 const node = SpreadJsObj.getSelectObject(billsSheet);
-                return readOnly || !node || !billsTree.isLeafXmj(node);
+                return readOnly || !node || !billsTree.isLeafXmj(node) || node.settle_status === settleStatus.finish;
             },
             callback: function (key, opt) {
                 tenderSelect.showSelect(SpreadJsObj.getSelectObject(billsSheet));
@@ -1473,7 +1508,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 || cell.locked()).attr('data-row', sel.row);
+                        .attr('readOnly', readOnly || cell.locked() || data.settle_status === settleStatus.finish).attr('data-row', sel.row);
                 } else {
                     $('#pos-expr').val('').attr('readOnly', true);
                     $('#pos-expr').removeAttr('data-row');
@@ -1517,6 +1552,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);
                     }
                 }
@@ -1541,6 +1580,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) {
@@ -1575,6 +1620,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;
+            }
         },
         /**
          * 编辑单元格响应事件
@@ -1614,9 +1662,19 @@ $(document).ready(() => {
                 const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
                 data.posPostType = 'add';
                 data.postData = { lid: node.id, porder: order };
+                if (node.settle_status === settleStatus.finish) {
+                    toastr.error('已结算清单不可插入计量单元');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
             } else {
                 data.posPostType = 'update';
                 data.postData = { id: posData.id };
+                if (col.type === 'Number' && posData.settle_status === settleStatus.finish) {
+                    toastr.error('计量单元已结算,不可修改');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
             }
             if (col.type === 'Number') {
                 const exprInfo = getExprInfo(col.field);
@@ -1707,6 +1765,10 @@ $(document).ready(() => {
             const sortData = sheet.zh_data;
             const row = selection[0].row;
             const node = SpreadJsObj.getSelectObject(billsSheet);
+            if (node.settle_status === settleStatus.finish) {
+                toastr.warning('清单已结算,不可新增计量单元');
+                return;
+            }
             const select = SpreadJsObj.getSelectObject(sheet);
             const porder = select ? select.porder : sortData.length + 1;
             const data = {
@@ -1741,6 +1803,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);
                 }
             }
@@ -1775,6 +1841,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;
@@ -1784,6 +1855,7 @@ $(document).ready(() => {
             const sortData = info.sheet.zh_data || [];
             const hint = {
                 expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
+                settle: {type: 'warning', msg: '计量单元已结算,不可修改台账数据,已过滤'},
             };
             const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
@@ -1796,6 +1868,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) {
@@ -1920,7 +1997,7 @@ $(document).ready(() => {
                     icon: 'fa-plus',
                     disabled: function (key, opt) {
                         const node = SpreadJsObj.getSelectObject(billsSheet);
-                        return node && node.children && node.children.length > 0;
+                        return node && node.children && node.children.length > 0 && node.settle_status !== settleStatus.finish;
                     },
                     callback: function (key, opt) {
                         posSpreadObj.insertPos(posSheet);
@@ -1932,7 +2009,12 @@ $(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;
+                            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 || settleFinish;
                         } else {
                             return true;
                         }
@@ -2014,6 +2096,10 @@ $(document).ready(() => {
                         toastr.warning('非最底层项目下,不应添加节点');
                         return;
                     }
+                    if (mainNode.settle_status === settleStatus.finish) {
+                        toastr.warning('已结算节点下,不应添加签约清单');
+                        return;
+                    }
 
                     postData(window.location.pathname + '/update', {
                         postType: 'add-deal',
@@ -2129,6 +2215,10 @@ $(document).ready(() => {
                         toastr.warning('非最底层项目下,不应添加变更清单');
                         return;
                     }
+                    if (mainNode.settle_status === settleStatus.finish) {
+                        toastr.warning('已结算节点下,不应添加变更清单');
+                        return;
+                    }
 
                     postData(window.location.pathname + '/update', {
                         postType: 'add-bg',
@@ -2585,6 +2675,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', {

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

@@ -264,6 +264,7 @@ $(document).ready(() => {
         reviseLedger.billsTree.loadRevisePrice(result.price, decimal);
         reviseLedger.billsTree.loadDatas(result.reviseBills);
         reviseLedger.pos.loadDatas(result.revisePos);
+
         const orgLedger = {
             billsTree: createNewPathTree('ledger', tenderTreeSetting),
             pos: new PosData({ id: 'id', ledgerId: 'lid', }),

+ 19 - 5
app/public/js/revise_price.js

@@ -199,9 +199,6 @@ $(document).ready(() => {
                 }
                 this.tree.loadFilter(invalid.join(','), 'filter');
             }
-            console.log(this.tree.nodes.find(x => { return x.code === '1'}));
-            console.log(this.tree.nodes.find(x => { return x.code === '1-2'}));
-            console.log(this.tree.nodes.find(x => { return x.code === '1-2-2'}));
         }
         refreshChangeRela(price, samePrice) {
             if (!samePrice) samePrice = this.getSamePrice(price);
@@ -611,6 +608,10 @@ $(document).ready(() => {
             if (!readOnly) {
                 this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
                     const gcl = SpreadJsObj.getSelectObject(info.sheet);
+                    if (gcl.settle_status === settleStatus.finish) {
+                        toastr.warning('该清单已结算,不可进行单价调整');
+                        return;
+                    }
                     priceOprObj.addRevisePrice(gcl);
                 });
 
@@ -621,20 +622,27 @@ $(document).ready(() => {
                         return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
                     },
                     items: {
-                        del: {
+                        add: {
                             name: '添加',
                             icon: 'fa-sign-in',
                             callback: function (key, opt) {
                                 const datas = [];
                                 const sel = self.sheet.getSelections()[0];
                                 const node = self.sheet.zh_data[sel.row];
-                                datas.push(node);
+                                if (node.settleStatus !== settleStatus.finish) datas.push(node);
                                 if (sel.rowCount > 1) {
                                     for (let r = 1; r < sel.rowCount; r++) {
                                         const rNode = self.sheet.zh_data[sel.row + r];
+                                        if (rNode.settle_status === settleStatus.finish) continue;
                                         if (rNode) datas.push(rNode);
                                     }
                                 }
+                                if (datas.length === 0) {
+                                    toastr.warning('选中清单已结算,不可进行单价调整');
+                                    return;
+                                } else if (datas.length < sel.rowCount) {
+                                    toastr.warning('部分选中清单已结算,已过滤');
+                                }
                                 priceOprObj.addRevisePrices(datas);
                             },
                         },
@@ -814,6 +822,11 @@ $(document).ready(() => {
                     return;
                 }
 
+                if (!node.check && node.settle_status === settleStatus.finish) {
+                    toastr.warning('该部位已结算,不可选择');
+                    return;
+                }
+
                 if (!node.check) {
                     const invalid = checkInvalid(node);
                     const invalidHint = ['该部位已被选择,请勿重复选择', '该部位的父项已被选择,请勿选择', '该部位的子项已被选择,请勿在其子项中选择'];
@@ -924,6 +937,7 @@ $(document).ready(() => {
         }
         loadTree(data) {
             this.tree.loadDatas(data);
+            this.tree.initNodeData('settle_status', settleStatus.non, values => { return values.length === 1 ? values[0] : settleStatus.part});
         }
         show(price, samePrice){
             this.price = price;

+ 15 - 2
app/public/js/settle_ledger.js

@@ -39,10 +39,23 @@ $(document).ready(() => {
                 } else {
                     node.cur_correct_tp = node.cur_gather_tp;
                 }
+                node.pre_gather_qty = ZhCalc.add(node.pre_contract_qty, node.pre_qc_qty);
+                node.end_gather_qty = ZhCalc.add(node.end_contract_qty, node.end_qc_qty);
+                if (node.end_contract_qty) {
+                    node.end_correct_tp = ZhCalc.add(node.end_qc_tp, ZhCalc.mul(node.end_contract_qty, node.unit_price, tenderInfo.decimal.tp));
+                } else {
+                    node.end_correct_tp = node.end_gather_tp;
+                }
             }
             node.cur_gather_tp = ZhCalc.add(node.cur_contract_tp, node.cur_qc_tp);
             node.cur_gather_percent = ZhCalc.mul(ZhCalc.div(node.cur_gather_tp, node.cur_final_tp), 100, 2);
             node.cur_correct_percent = ZhCalc.mul(ZhCalc.div(node.cur_correct_tp, node.cur_final_tp), 100, 2);
+            node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
+            node.pre_gather_percent = ZhCalc.mul(ZhCalc.div(node.pre_gather_tp, node.pre_final_tp), 100, 2);
+            node.pre_correct_percent = ZhCalc.mul(ZhCalc.div(node.pre_correct_tp, node.pre_final_tp), 100, 2);
+            node.end_gather_tp = ZhCalc.add(node.end_contract_tp, node.end_qc_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);
         }
     };
     const settleTree = createNewPathTree('stage', settleTreeSetting);
@@ -50,8 +63,8 @@ $(document).ready(() => {
         id: 'id', ledgerId: 'lid',
         calcFun: function(pos) {
             pos.cur_gather_qty = ZhCalc.add(pos.cur_contract_qty, pos.cur_qc_qty);
-            pos.sum = ZhCalc.add(pos.end_qc_qty, pos.quantity);
-            pos.cur_gather_percent = ZhCalc.mul(ZhCalc.div(pos.cur_gather_qty, pos.sum), 100, 2);
+            pos.pre_gather_qty = ZhCalc.add(pos.pre_contract_qty, pos.pre_qc_qty);
+            pos.end_gather_qty = ZhCalc.add(pos.end_contract_qty, pos.end_qc_qty);
         }
     };
     const settlePos = new StagePosData(settlePosSetting);

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

@@ -147,19 +147,24 @@ $(document).ready(() => {
 
     const settleCheck = {
         _analysisPos(pos) {
+            if (pos.settle_status === undefined) pos.settle_status = 0;
             pos.undoneDeal = pos.quantity ? !checkZero(ZhCalc.sub(pos.end_contract_qty, pos.quantity)) : false;
             pos.undone = !!pos.undoneDeal || !!pos.undoneChange;
         },
         _analysisNode(node) {
             if (node.undoneDeal === undefined) node.undoneDeal = false;
             if (node.undoneChange === undefined) node.undoneChange = false;
+            if (node.settle_status === undefined) node.settle_status = 0;
+            const status = [];
             if (node.children && node.children.length > 0) {
                 for (const child of node.children) {
                     this._analysisNode(child);
                     if (child.undoneDeal) node.undoneDeal = true;
                     if (child.undoneChange) node.undoneChange = true;
+                    if (status.indexOf(child.settle_status) < 0) status.push(child.settle_status);
                 }
                 node.undone = !!node.undoneDeal || !!node.undoneChange;
+                node.settle_status = status.length === 1 ? status[0] : 1;
             } else {
                 const posRange = settlePos.getLedgerPos(node.id);
                 if (posRange && posRange.length > 0) {
@@ -167,8 +172,10 @@ $(document).ready(() => {
                         this._analysisPos(pos);
                         if (pos.undoneDeal) node.undoneDeal = true;
                         if (pos.undoneChange) node.undoneChange = true;
+                        if (status.indexOf(pos.settle_status) < 0) status.push(pos.settle_status);
                     }
                     node.undone = !!node.undoneDeal || !!node.undoneChange;
+                    node.settle_status = status.length === 1 ? status[0] : 1;
                 } else {
                     node.undoneDeal = node.end_contract_qty
                         ? !checkZero(ZhCalc.sub(node.end_contract_qty, node.quantity))
@@ -213,6 +220,11 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
                 return;
             }
+            if (node.settle_status === 2) {
+                toastr.warning('已结算数据,请勿重复结算');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
             if (!node.selected) {
                 const parents = settleTree.getAllParents(node);
                 for (const p of parents) {
@@ -307,6 +319,11 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
                 return;
             }
+            if (node.settle_status === 2) {
+                toastr.warning('已结算数据,请勿重复结算');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
             if (!node.selected) {
                 const billsNode = settleTree.nodes.find(x => { return x.id === node.lid });
                 const parents = settleTree.getAllParents(billsNode);

+ 25 - 11
app/public/js/stage.js

@@ -710,7 +710,7 @@ $(document).ready(() => {
             case 'gxby': data.gxby_url && window.open(data.gxby_url); break;
             case 'qc_qty':
             case 'qc_minus_qty':
-                if (data.children && data.children.length > 0 || data.lock || data.is_import) return;
+                if (data.children && data.children.length > 0 || data.lock || data.is_import || data.settle_status === settleStatus.finish) return;
                 const nodePos = stagePos.getLedgerPos(data.id);
                 if (nodePos && nodePos.length > 0) return;
                 const minus = col.field === 'qc_minus_qty' ? 1 : 0;
@@ -804,7 +804,7 @@ $(document).ready(() => {
             case 'qc_qty':
             case 'qc_minus_qty':
                 const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
-                if (node.lock) return;
+                if (node.lock || node.settle_status === settleStatus.finish) return;
                 const minus = col.field === 'qc_minus_qty' ? 1 : 0;
                 changesObj.loadChanges({bills: node, pos: data, minus, noValue: minus});
                 break;
@@ -1064,9 +1064,9 @@ $(document).ready(() => {
                                 if (node.b_code && node.b_code !== '') continue;
                             } else if (['postil', 'memo', 'ex_memo1', 'ex_memo2', 'ex_memo3'].indexOf(colSetting.field) < 0) {
                                 if (colSetting.field === 'contract_qty') {
-                                    if (node.is_tp) continue;
+                                    if (node.is_tp || node.settle_status !== settleStatus.finish) continue;
                                 } else if (colSetting.field === 'contract_tp') {
-                                    if (!node.is_tp) continue;
+                                    if (!node.is_tp || node.settle_status !== settleStatus.finish) continue;
                                 }
                                 if (node.children && node.children.length > 0) {
                                     continue;
@@ -1148,9 +1148,9 @@ $(document).ready(() => {
                         const curCol = info.cellRange.col + iCol;
                         const col = info.sheet.zh_setting.cols[curCol];
                         if (col.field === 'contract_tp') {
-                            if (!node.is_tp) continue;
+                            if (!node.is_tp || node.settle_status !== settleStatus.finish) continue;
                         } else if (col.field === 'contract_qty') {
-                            if (node.is_tp) continue;
+                            if (node.is_tp || node.settle_status !== settleStatus.finish) continue;
                         }
 
                         if (setting.dgnUpFields.indexOf(col.field) !== -1) {
@@ -1229,8 +1229,7 @@ $(document).ready(() => {
                         }
                         stageTreeSpreadObj.loadExprToInput(sheet);
                     }, function () {
-                        // todo
-                        //stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), filterNodes);
+                        stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), filterNodes);
                     });
                 } else {
                     stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), filterNodes);
@@ -1301,10 +1300,10 @@ $(document).ready(() => {
                 case 'contract_qty':
                 case 'qc_qty':
                 case 'qc_minus_qty':
-                    info.cancel = node.is_tp;
+                    info.cancel = node.is_tp || node.settle_status !== settleStatus.finish;
                     break;
                 case 'contract_tp':
-                    info.cancel = !node.is_tp;
+                    info.cancel = !node.is_tp || node.settle_status !== settleStatus.finish;
                     break;
                 case 'is_tp':
                     info.cancel = true;
@@ -1972,6 +1971,13 @@ $(document).ready(() => {
                     info.sheet.getCell(info.row, info.col).text(node[exprInfo.expr]);
                 }
             }
+            switch (col.field) {
+                case 'contract_qty':
+                case 'qc_qty':
+                case 'qc_minus_qty':
+                    info.cancel = node.settle_status !== settleStatus.finish;
+                    break;
+            }
         },
         clipboardPasting: function (e, info) {
             if (info.sheet.zh_setting) {
@@ -2077,6 +2083,10 @@ $(document).ready(() => {
                                 const colSetting = info.sheet.zh_setting.cols[curCol];
                                 const newValue = trimInvalidChar(info.sheet.getText(curRow, curCol));
                                 if (colSetting.type === 'Number') {
+                                    if (curPos.settle_status === settleStatus.finish) {
+                                        newData.error = 1;
+                                        continue;
+                                    }
                                     const exprInfo = getExprInfo(colSetting.field);
                                     if (!newValue) {
                                         newData[colSetting.field] = newValue;
@@ -2103,7 +2113,7 @@ $(document).ready(() => {
                                     newData[colSetting.field] = newValue;
                                 }
                             }
-                            data.updateData.push(newData);
+                            if (newData.error === undefined) data.updateData.push(newData);
                         }
                     }
                 }
@@ -2153,6 +2163,10 @@ $(document).ready(() => {
                                 toastr.error('部位名称不能为空');
                                 return;
                             }
+                            if (colSetting.Type === 'Number' && node.settle_status !== settleStatus.finish) {
+                                toastr.error('已结算节点不可计量');
+                                return;
+                            }
                             data[colSetting.field] = null;
                             const exprInfo = getExprInfo(colSetting.field);
                             if (exprInfo) {

+ 2 - 0
app/router.js

@@ -442,6 +442,8 @@ module.exports = app => {
     app.post('/tender/:id/settle/:sorder/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.deleteAudit');
     app.post('/tender/:id/settle/:sorder/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.auditStart');
     app.post('/tender/:id/settle/:sorder/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.auditCheck');
+    app.post('/tender/:id/settle/:sorder/audit/checkAgain', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.auditCheckAgain');
+    app.post('/tender/:id/settle/:sorder/audit/checkCancel', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.auditCheckCancel');
     // 结算汇总
     app.get('/tender/:id/settle/gather', sessionAuth, tenderCheck, uncheckTenderCheck, 'settleController.gather');
     app.get('/tender/:id/settle/gather/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'settleController.loadGatherData');

+ 50 - 10
app/service/settle.js

@@ -11,6 +11,11 @@
 const auditConst = require('../const/audit');
 const projectLogConst = require('../const/project_log');
 const shenpiConst = require('../const/shenpi');
+const settleStatus = [
+    { key: 'non', value: 0, name: '未结算', short: '', color: '' },
+    { key: 'part', value: 1, name: '部分结算', short: '部分', color: '#da9500' },
+    { key: 'finish', value: 2, name: '已结算', short: '结', color: '#28a745' },
+];
 
 module.exports = app => {
     class Settle extends app.BaseService {
@@ -23,6 +28,32 @@ module.exports = app => {
         constructor(ctx) {
             super(ctx);
             this.tableName = 'settle';
+
+        }
+
+        get statusArray() {
+            if (!this._statusArray) {
+                const arr = [];
+                settleStatus.forEach(x => { arr[x.value] = x.short; });
+                this._statusArray = arr;
+            }
+            return this._statusArray;
+        }
+        get statusColor() {
+            if (!this._statusColor) {
+                const arr = [];
+                settleStatus.forEach(x => { arr[x.value] = x.color; });
+                this._statusColor = arr;
+            }
+            return this._statusColor;
+        }
+        get settleStatus() {
+            if (!this._settleStatus) {
+                const status = {};
+                settleStatus.forEach(x => { status[x.key] = x.value; });
+                this._settleStatus = status;
+            }
+            return this._settleStatus;
         }
 
         /**
@@ -47,12 +78,21 @@ module.exports = app => {
         }
         async getLatestCompleteSettle(tenderId) {
             const settles = await this.getAllDataByCondition({
-                where: { tid: tenderId, status: auditConst.settle.status.checked },
+                where: { tid: tenderId, audit_status: auditConst.settle.status.checked },
                 order: [['settle_order', 'desc']],
                 limit: 1, offset: 0
             });
             return settles[0];
         }
+        async getReadySettle(tenderId) {
+            const settles = await this.db.select(this.tableName, {
+                where: { tid: tenderId },
+                orders: [['settle_order', 'desc']],
+            });
+            if (settles.length === 0) return null;
+            if (settles[0].audit_status !== auditConst.settle.status.uncheck && settles[0].audit_status !== auditConst.settle.status.checkNo) return settles[0];
+            return settles.length > 1 ? settles[1] : null;
+        }
 
         /**
          * 新增结算期
@@ -64,7 +104,7 @@ module.exports = app => {
         async addSettle(tenderId, date, period) {
             const settles = await this.getAllDataByCondition({
                 where: { tid: tenderId },
-                order: ['order'],
+                order: ['settle_order', 'asc'],
             });
             const pre = settles[settles.length - 1];
             if (settles.length > 0 && pre.audit_status !== auditConst.settle.status.checked) {
@@ -75,7 +115,7 @@ module.exports = app => {
 
             const newSettle = {
                 tid: tenderId,
-                add_sid: checkedStage.order, add_sorder: checkedStage.order,
+                add_sid: checkedStage.id, add_sorder: checkedStage.order,
                 user_id: this.ctx.session.sessionUser.accountId,
                 settle_order: settles.length + 1, settle_time: date, settle_period: period,
                 audit_times: 1, audit_status: auditConst.settle.status.uncheck,
@@ -113,16 +153,16 @@ module.exports = app => {
          * 编辑结算期
          *
          * @param {Number} tenderId - 标段Id
-         * @param {Number} order - 第N期
+         * @param {Number} settleOrder - 第N期
          * @param {String} date - 结算年月
          * @param {String} period - 结算周期
          * @return {Promise<void>}
          */
-        async saveSettle(tenderId, order, date, period) {
+        async saveSettle(tenderId, settleOrder, date, period) {
             await this.db.update(this.tableName, {
                 settle_time: date,
                 settle_period: period,
-            }, { where: { tid: tenderId, settle_order: order } });
+            }, { where: { tid: tenderId, settle_order: settleOrder } });
         }
         /**
          * 删除结算期
@@ -137,7 +177,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.settleAudit.tableName, { sid: id });
                 // await transaction.delete(this.ctx.service.settleAuditAss.tableName, { sid: id });
                 // 记录删除日志
-                await this.ctx.service.projectLog.addProjectLog(transaction, projectLogConst.type.settle, projectLogConst.status.delete, `第${settleInfo.order}期`);
+                await this.ctx.service.projectLog.addProjectLog(transaction, projectLogConst.type.settle, projectLogConst.status.delete, `第${settleInfo.settle_order}期`);
                 await transaction.commit();
                 return true;
             } catch (err) {
@@ -222,7 +262,7 @@ module.exports = app => {
                     return;
                 }
 
-                const preAuditors = settle.curAuditors[0] && settle.curAuditors[0].order !== 1 ? settle.auditors.filter(x => { return x.order === settle.curAuditors[0].order - 1; }) : [];
+                const preAuditors = settle.curAuditors[0] && settle.curAuditors[0].active_order !== 1 ? settle.auditors.filter(x => { return x.active_order === settle.curAuditors[0].active_order - 1; }) : [];
                 const preAuditorCheckAgain = preAuditors.find(pa => { return pa.audit_status === status.checkAgain; });
                 const preAuditorCheckCancel = preAuditors.find(pa => { return pa.audit_status === status.checkCancel; });
                 const preAuditorHasOld = preAuditors.find(pa => { return pa.is_old === 1; });
@@ -248,7 +288,7 @@ module.exports = app => {
                 const onAuditor = this._.findLast(lastAuditors, { audit_status: status.checkNo });
                 if (onAuditor.audit_id === accountId) {
                     settle.cancancel = 4;// 审批人撤回退回原报
-                    settle.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order });
+                    settle.preAuditors = lastAuditors.filter(x => { return x.active_order === onAuditor.active_order });
                 }
             }
         }
@@ -342,7 +382,7 @@ module.exports = app => {
                 } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
                     const shenpiInfo = await this.ctx.service.shenpiAudit.getDataByCondition({ tid: settle.tid, sp_type: shenpiConst.sp_type.settle, sp_status: shenpi_status });
                     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
-                    const lastAuditors = auditList.filter(x => { x.order === auditList.order; });
+                    const lastAuditors = auditList.filter(x => { x.active_order === auditList.active_order; });
                     if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].audit_id))) {
                         await this.ctx.service.settleAudit.updateLastAudit(settle, auditList, shenpiInfo.audit_id);
                         await this.loadRelaUser(settle);

+ 328 - 7
app/service/settle_audit.js

@@ -55,10 +55,10 @@ module.exports = app => {
         }
         // 获取某个状态的审批人
         async getAuditorsByStatus(settleId, auditStatus, auditTimes) {
-            const cur = await this.db.queryOne(`SELECT * From ${this.tableName} where settle_id = ? AND audit_times = ? AND audit_status = ? ORDER By audit_times DESC, audit_order DESC `, [settleId, auditTimes, auditStatus]);
+            const cur = await this.db.queryOne(`SELECT * From ${this.tableName} where settle_id = ? AND audit_times = ? AND audit_status = ? ORDER By audit_times DESC, active_order DESC `, [settleId, auditTimes, auditStatus]);
             if (!cur) return [];
 
-            return await this.getAllDataByCondition({ where: { settle_id: settleId, audit_times: auditTimes, audit_order: cur.audit_order}});
+            return await this.getAllDataByCondition({ where: { settle_id: settleId, audit_times: auditTimes, audit_order: cur.audit_order, audit_status: auditStatus}});
         }
         // 获取全部审批历史
         async getAuditorHistory(settleId, auditTimes, reverse = false) {
@@ -108,10 +108,9 @@ module.exports = app => {
         }
         async getAllAuditors(tenderId) {
             const sql =
-                'SELECT sa.audit_id, sa.tid FROM ' + this.tableName + ' sa' +
-                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On sa.tid = t.id' +
-                '  WHERE t.id = ?' +
-                '  GROUP BY sa.audit_id';
+                'SELECT audit_id, tid FROM ' + this.tableName +
+                '  WHERE tid = ?' +
+                '  GROUP BY audit_id';
             const sqlParam = [tenderId];
             return this.db.query(sql, sqlParam);
         }
@@ -243,7 +242,7 @@ module.exports = app => {
                     tid: newSettle.tid, settle_id: newSettle.id,
                     audit_id: a.id,
                     audit_times: 1, audit_order: a.audit_order, audit_type: a.audit_type,
-                    active_order: a.audit_order, audit_status: auditConst.auditConst.settle.status.uncheck,
+                    active_order: a.audit_order, audit_status: auditConst.settle.status.uncheck,
                     name: a.name, company: a.company, role: a.role, mobile: a.mobile,
                 });
             }
@@ -363,6 +362,7 @@ module.exports = app => {
                 await transaction.update(this.ctx.service.settle.tableName, {
                     id: settle.id, audit_status: auditConst.settle.status.checking,
                     ...settleSum,
+                    final_sid: settle.latestStage.id, final_sorder: settle.latestStage.order,
                 });
                 // 多人协同,取消下一审批人存在的锁定
                 // await this.ctx.service.settleAuditAss.cancelLock(settle, audits.map(x => { return x.audit_id; }), transaction);
@@ -677,6 +677,327 @@ module.exports = app => {
                     throw '无效审批操作';
             }
         }
+        async checkAgain(settle, force = false) {
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const time = new Date();
+            // 整理当前流程审核人状态更新
+            const finalAudits = settle.auditorGroups[settle.auditorGroups.length - 1];
+            if (!finalAudits || finalAudits.length === 0 || finalAudits[0].audit_order < 1) throw '审核数据错误';
+            const selfAudit = finalAudits.find(x => { return x.audit_id === accountId; });
+            if (!selfAudit && !force) throw '当前标段您无权审批';
+            const finalAudit = selfAudit || finalAudits[0];
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 当前审批人2次添加至流程中
+                const checkAgainAuditors = [], checkingAuditors = [];
+                finalAudits.forEach(x => {
+                    checkAgainAuditors.push({
+                        tid: x.tid, settle_id: x.settle_id, audit_id: x.audit_id,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        audit_times: x.audit_times, active_order: x.active_order + 1,
+                        audit_status: x.audit_id === finalAudit.audit_id ? auditConst.settle.status.checkAgain : auditConst.settle.status.checkSkip,
+                        audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                finalAudits.forEach(x => {
+                    checkingAuditors.push({
+                        tid: x.tid, settle_id: x.settle_id, audit_id: x.audit_id,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        audit_times: x.audit_times, active_order: x.active_order + 2,
+                        audit_status: auditConst.settle.status.checking, audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                await transaction.insert(this.tableName, [...checkAgainAuditors, ...checkingAuditors]);
+
+                // 同步 期信息
+                await transaction.update(this.ctx.service.settle.tableName, {
+                    id: settle.id, audit_status: auditConst.settle.status.checking,
+                });
+
+                // todo 添加短信通知-需要审批提醒功能
+                const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + settle.tid + '/settle/' + settle.settle_order);
+                const users = this._.map(settle.auditAssists.filter(x => { return settle.finalAuditorIds.indexOf(x.user_id) >= 0; }), 'ass_user_id');
+                users.push(...settle.finalAuditorIds);
+                // await this.ctx.helper.sendAliSms(users, smsTypeConst.const.JS, smsTypeConst.judge.approval.toString(), SmsAliConst.template.settle_check, {
+                //     qi: settle.settle_order, code: shenpiUrl,
+                // });
+                // 微信模板通知
+                const wechatData = {
+                    wap_url: shenpiUrl,
+                    qi: settle.settle_order,
+                    status: wxConst.status.check,
+                    tips: wxConst.tips.check,
+                    code: this.ctx.session.sessionProject.code,
+                };
+                await this.ctx.helper.sendWechat(users, smsTypeConst.const.JS, smsTypeConst.judge.approval.toString(), wxConst.template.settle, wechatData);
+
+                // todo 上报/审批 - 检查三方特殊推送
+                // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        /**
+         * 原报撤回,直接改动审批人状态
+         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
+         *
+         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
+         * ...
+         *
+         * @param settle
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _userCheckCancel(settle) {
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                // 审批人变成待审批状态
+                const updateData = settle.curAuditors.map(x => {
+                    return {
+                        id: x.id, audit_status: auditConst.settle.status.uncheck,
+                        audit_time: null, opinion: null,
+                    }
+                });
+                await transaction.updateRows(this.tableName, updateData);
+                await transaction.update(this.ctx.service.settle.tableName, {
+                    id: settle.id, audit_times: settle.audit_times,
+                    audit_status: settle.audit_times === 1 ? auditConst.settle.status.uncheck : auditConst.settle.status.checkNo,
+                });
+
+                // todo 上报/审批 - 检查三方特殊推送
+                // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+        }
+        /**
+         * 审批人撤回审批通过,插入两条数据
+         *
+         * @param settle
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancel(settle) {
+            if (settle.curAuditors.length === 0 || settle.curAuditors[0].active_order <= 1) {
+                throw '撤回用户数据错误';
+            }
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const selfAuditor = settle.preAuditors.find(x => { return x.audit_id === accountId; });
+            if (!selfAuditor) throw '撤回用户数据错误';
+
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `active_order` = `active_order` + 2 WHERE settle_id = ? AND audit_times = ? AND `active_order` > ?';
+                await transaction.query(sql, [settle.id, selfAuditor.audit_times, settle.curAuditors[0].active_order]);
+                // 当前审批人2次添加至流程中
+                const checkCancelAuditors = [], checkingAuditors = [];
+                settle.preAuditors.forEach(x => {
+                    checkCancelAuditors.push({
+                        tid: settle.tid, settle_id: settle.id, audit_id: x.audit_id,
+                        audit_times: x.audit_times, active_order: x.active_order + 1,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        status: x.audit_id === selfAuditor.audit_id ? auditConst.settle.status.checkCancel : auditConst.settle.status.checkSkip,
+                        audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                settle.preAuditors.forEach(x => {
+                    checkingAuditors.push({
+                        tid: settle.tid, settle_id: settle.id, audit_id: x.audit_id,
+                        audit_times: x.audit_times, active_order: x.active_order + 2,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        status: auditConst.settle.status.checking,
+                        audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                await transaction.insert(this.tableName, [...checkCancelAuditors, ...checkingAuditors]);
+                // 当前审批人变成待审批
+                await transaction.updateRows(this.tableName, settle.curAuditors.map(x => { return {
+                    id: x.id, audit_time: null, status: auditConst.settle.status.uncheck, active_order: x.active_order + 2
+                }}));
+                // 同步 期信息
+                await transaction.update(this.ctx.service.settle.tableName, {
+                    id: settle.id, audit_times: settle.audit_times,
+                });
+
+                // todo 上报/审批 - 检查三方特殊推送
+                // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        /**
+         * 审批人撤回审批退回上一人,插入两条数据
+         *
+         * @param settle
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNoPre(settle) {
+            if (settle.curAuditors.length === 0 || settle.curAuditors[0].active_order <= 1) {
+                throw '撤回用户数据错误';
+            }
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const selfAuditor = settle.preAuditors.find(x => { return x.audit_id === accountId; });
+            if (!selfAuditor) throw '撤回用户数据错误';
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                // 删除当前审批人
+                await transaction.delete(this.tableName, { id: settle.curAuditors.map(x => { return x.id; }) });
+                // 添加撤回人到审批流程中
+                const newAuditors = [];
+                settle.preAuditors.forEach(x => {
+                    newAuditors.push({
+                        tid: settle.tid, settle_id: settle.id, audit_id: x.audit_id,
+                        audit_times: x.audit_times, active_order: x.active_order + 1,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        audit_status: x.audit_id === selfAuditor.audit_id ? auditConst.settle.status.checkCancel : auditConst.settle.status.checkSkip,
+                        audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 更新上一个人,最新审批状态为审批中
+                await transaction.update(this.tableName,  { audit_time: null, opinion: '', audit_status: auditConst.settle.status.checking }, {
+                    where: { settle_id: settle.id, audit_times: selfAuditor.audit_times, active_order: selfAuditor.active_order + 2 }
+                });
+
+                // 同步 期信息
+                await transaction.update(this.ctx.service.settle.tableName, {
+                    id: settle.id, audit_times: settle.audit_times, status: auditConst.settle.status.checking,
+                });
+                // todo 上报/审批 - 检查三方特殊推送
+                // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        /**
+         * 审批人撤回审批退回原报
+         *
+         * @param settle
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNo(settle) {
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const selfAuditor = settle.preAuditors.find(x => { return x.audit_id === accountId && x.audit_status === auditConst.settle.status.checkNo; });
+            if (!selfAuditor) throw '该标段由他人审批退回,您不可撤回';
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理上一个流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `active_order` = `active_order` + 2 WHERE settle_id = ? AND audit_times = ? AND `active_order` > ?';
+                await transaction.query(sql, [settle.id, selfAuditor.audit_times, selfAuditor.active_order]);
+                // 当前审批人2次添加至流程中
+                const checkCancelAuditors = [], checkingAuditors = [];
+                settle.preAuditors.forEach(x => {
+                    checkCancelAuditors.push({
+                        tid: settle.tid, settle_id: settle.id, audit_id: x.audit_id,
+                        audit_times: x.audit_times, active_order: x.active_order + 1,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        audit_status: x.audit_id === selfAuditor.audit_id ? auditConst.settle.status.checkCancel : auditConst.settle.status.checkSkip,
+                        audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                settle.preAuditors.forEach(x => {
+                    checkingAuditors.push({
+                        tid: settle.tid, settle_id: settle.id, audit_id: x.audit_id,
+                        audit_times: x.audit_times, active_order: x.active_order + 2,
+                        audit_type: x.audit_type, audit_order: x.audit_order,
+                        audit_status: auditConst.settle.status.checking,
+                        audit_time: time, opinion: '',
+                        name: x.name, company: x.company, role: x.role, mobile: x.mobile,
+                    });
+                });
+                await transaction.insert(this.tableName, [...checkCancelAuditors, ...checkingAuditors]);
+                // 删除当前次审批流
+                await transaction.delete(this.tableName, { settle_id: settle.id, audit_times: settle.audit_times });
+
+                // 计算并合同支付最终数据
+                await transaction.update(this.ctx.service.settle.tableName, {
+                    id: settle.id, audit_times: settle.audit_times - 1, status: auditConst.settle.status.checking,
+                });
+                // todo 上报/审批 - 检查三方特殊推送
+                // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        /**
+         * 会签未全部审批通过时,撤回仅修改本人状态
+         *
+         * @param settle
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelAnd(settle) {
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const selfAuditor = settle.flowAuditors.find(x => { return x.audit_id === accountId; });
+            if (!selfAuditor || selfAuditor.audit_status !== auditConst.settle.status.checked) throw '不可撤回';
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, {
+                    id: selfAuditor.id, status: auditConst.settle.status.checking, opinion: '', audit_time: null,
+                });
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        /**
+         * 审批撤回
+         * @param {Number} settle - 结算期
+         * @return {Promise<void>}
+         */
+        async checkCancel(settle) {
+            // 分5种情况,根据settle.cancancel值判断:
+            // 1.原报发起撤回,当前流程删除,并回到待上报
+            // 2.审批人撤回审批通过,增加流程,并回到它审批中
+            // 3.审批人撤回审批退回上一人,并删除退回人,增加流程,并回到它审批中,并更新计量期状态为审批中
+            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
+            // 5.会签审批人撤回审批通过(还有其他审批人未审批通过),仅修改本人流程状态
+            if (settle.cancancel === 5) {
+                await this._auditCheckCancelAnd(settle);
+            } else {
+                switch (settle.cancancel) {
+                    case 1: await this._userCheckCancel(settle); break;
+                    case 2: await this._auditCheckCancel(settle); break;
+                    case 3: await this._auditCheckCancelNoPre(settle); break;
+                    case 4: await this._auditCheckCancelNo(settle); break;
+                    default: throw '不可撤回,请刷新页面重试';
+                }
+            }
+        }
         // ***** 审批操作
     }
 

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

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

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

@@ -252,4 +252,5 @@
         }
     });
     const nodeType = JSON.parse('<%- JSON.stringify(nodeType) %>');
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
 </script>

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

@@ -65,4 +65,5 @@
 <script src="/public/js/moment/moment.min.js"></script>
 <script>
     const readOnly = <%- ctx.revise.readOnly %>;
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
 </script>

+ 1 - 1
app/view/settle/audit_btn.ejs

@@ -22,7 +22,7 @@
 
     <% if (ctx.settle.audit_status === auditConst.status.checked) { %>
         <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm btn-block sp-list-btn">审批完成</a>
-        <% if (ctx.settle.finalAuditorIds && ctx.settle.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && ctx.settle.order === ctx.settle.highOrder) { %>
+        <% if (ctx.settle.finalAuditorIds && ctx.settle.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && ctx.settle.settle_order === ctx.settle.highOrder) { %>
             <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
         <% } %>
     <% } %>

+ 62 - 60
app/view/settle/audit_modal.ejs

@@ -511,6 +511,60 @@
     </div>
 </div>
 <% } %>
+<% if (ctx.settle && ctx.settle.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && ctx.settle.audit_status === auditConst.status.checked && ctx.settle.settle_order === ctx.settle.highOrder) { %>
+<% 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">
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                <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 class="modal-content" method="post" action="audit/checkAgain" name="settle-checkAgain">
+            <div class="modal-header">
+                <h5 class="modal-title">重新审批</h5>
+            </div>
+            <div class="modal-body">
+                <h5>确认由「终审-<%= ctx.session.sessionUser.name %>」重新审批「第<%= ctx.settle.settle_order %>期」?
+                </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" type="button" id="get-code">获取验证码</button>
+                        </div>
+                    </div>
+                </div>
+                <% } %>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %> id="re-shenpi-btn" class="btn btn-warning btn-sm" type="submit">确定重审</button>
+            </div>
+        </form>
+    </div>
+</div>
+<% } %>
+<% } %>
 <% if (ctx.settle && ctx.settle.user_id === ctx.session.sessionUser.accountId && ctx.settle.settle_order === ctx.settle.highOrder && (ctx.settle.audit_status === auditConst.status.checkNo || ctx.settle.audit_status === auditConst.status.uncheck)) { %>
 <div class="modal fade" id="del-qi" data-backdrop="static">
     <div class="modal-dialog" role="document">
@@ -542,10 +596,11 @@
             <div class="modal-body">
                 <h5>确定撤回?</h5>
             </div>
-            <div class="modal-footer">
+            <form class="modal-footer" action="audit/checkCancel" method="post">
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
-                <button type="submit" class="btn btn-danger btn-sm" id="cancel-shenpi-btn">确定撤回</button>
-            </div>
+                <button type="submit" class="btn btn-danger btn-sm" type="submit">确定撤回</button>
+            </form>
         </div>
     </div>
 </div>
@@ -755,17 +810,6 @@
             return false;
         }
     });
-    $('#audit-check').submit(function (e) {
-        const checkType = parseInt($('[name=checkType]').val());
-        const data = {
-            opinion: $(`${'#sp-done'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
-            checkType,
-        };
-        $('#sp-done').modal('hide');
-        checkType && dataChecker.checkAndPost(this.action, data);
-        $('#hide-all').hide();
-        return false;
-    });
     $('#audit-check-no').submit(function (e) {
         const checkType = parseInt($('[name=checkType]:checked').val());
         if ($('#warning-text').length) $('#warning-text').remove();
@@ -774,10 +818,6 @@
             return false;
         }
         $('#hide-all').hide();
-        // const data = {
-        //     opinion: $(`${'#sp-back'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
-        //     checkType,
-        // };
     });
 
     $('.sp-location-list').on('shown.bs.modal', function () {
@@ -814,54 +854,16 @@
         }
     });
 
-    // 重新审批按钮
-    $("#re-shenpi-btn").click(function () {
-        const data = {
-        };
+    // 重新审批
+    $('[name=settle-checkAgain]').submit(function (e) {
         <% if (ctx.session.sessionUser.loginStatus === 0) { %>
         const code = $("#sp-down-back input[name='code']").val();
-        if ($(this).hasClass('disabled')) {
-            return false;
-        }
+        if ($(this).hasClass('disabled')) return false;
         if (code.length < 6) {
-            // alert('请填写正确的验证码');
             toast('请填写正确的验证码', 'error');
             return false;
         }
-        data.code = code;
         <% } %>
-        $.ajax({
-            url: '<%- preUrl %>/audit/check/again',
-            type: 'get',
-            data: data,
-            dataTye: 'json',
-            success: function (response) {
-                if (response.err === 0) {
-                    window.location.href = response.url;
-                } else {
-                    toast(response.msg, 'error');
-                }
-            }
-        });
-    });
-
-    <% if (ctx.settle && ctx.settle.cancancel) { %>
-    $("#cancel-shenpi-btn").click(function () {
-        const data = {
-        };
-        $.ajax({
-            url: '<%- preUrl %>/audit/check/cancel',
-            type: 'get',
-            data: data,
-            dataTye: 'json',
-            success: function (response) {
-                if (response.err === 0) {
-                    window.location.href = response.url;
-                } else {
-                    toast(response.msg, 'error');
-                }
-            }
-        });
+        $('#hide-all').hide();
     });
-    <% } %>
 </script>

+ 29 - 3
app/view/settle/index.ejs

@@ -109,9 +109,12 @@
     const tenderInfo = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.tender.info)) %>'));
     const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const thirdParty = JSON.parse('<%- JSON.stringify(thirdParty) %>');
+    const settleStatusHint = JSON.parse('<%- JSON.stringify(settleStatusHint )%>');
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus )%>');
+    const settleStatusColor = JSON.parse('<%- JSON.stringify(settleStatusColor )%>');
     const billsSpreadSetting = {
         cols: [
-            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true, getValue: function(data) { return data.b_code ? '' : settleStatusHint[data.settle_status] || '' }},
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@', readOnly: true},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
@@ -126,6 +129,13 @@
             {title: '本期完成结算|数量', colSpan: '3|1', rowSpan: '1|1', field: 'cur_gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'cur_gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'cur_final_1_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'},
+            {title: '截止本期合同结算|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '截止本期完成结算|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: true},
             <% if (ctx.session.sessionProject.gxby) { %>
@@ -144,17 +154,26 @@
         frozenColCount: 5,
         frozenLineColor: '#93b5e4',
         readOnly,
+        getForeColor: function(sheet, data, row, col, foreColor) {
+            if (col.field === 'settle_status') {
+                return data ? settleStatusColor[data.settle_status] || foreColor : foreColor;
+            } else {
+                return foreColor;
+            }
+        }
     };
     const posSpreadSetting = {
         cols: [
-            {title: '选择', colSpan: '1', rowSpan: '2', field: 'selected', hAlign: 0, width: 145, formatter: '@'},
-            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true, getValue: function(data) { return settleStatusHint[data.settle_status] || '' }},
             {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
             {title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@', readOnly: true},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, formatter: '@', readOnly: true},
             {title: '本期结算|合同', colSpan: '3|1', rowSpan: '1|1', field: 'cur_contract_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'cur_qc_qty', hAlign: 2, width: 80, type: 'Number', readOnly: true},
             {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'cur_gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '截止本期结算|合同', colSpan: '3|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 80, type: 'Number', readOnly: true},
+            {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
             <% if (ctx.session.sessionProject.gxby) { %>
             {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
@@ -171,5 +190,12 @@
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
         readOnly,
+        getForeColor: function(sheet, data, row, col, foreColor) {
+            if (col.field === 'settle_status') {
+                return data ? settleStatusColor[data.settle_status] || foreColor : foreColor;
+            } else {
+                return foreColor;
+            }
+        }
     };
 </script>

+ 12 - 12
app/view/settle/list.ejs

@@ -7,7 +7,7 @@
                 结算期列表
             </h2>
             <div class="ml-auto">
-                <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && (settles.length === 0 || settles[0].status === auditConst.status.checked) && checkedStageCount > 0) { %>
+                <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && (settles.length === 0 || settles[0].audit_status === auditConst.status.checked) && checkedStageCount > 0) { %>
                 <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm">开始新一期</a>
                 <% } %>
             </div>
@@ -59,7 +59,7 @@
                         <td class="text-right"><%- (s.pre_tp ? s.pre_tp : '')%></td>
                         <td class="text-right"><%- (s.end_tp ? s.end_tp : '')%></td>
                         <% } %>
-                        <td class="<%- auditConst.auditProgressClass[s.status] %>">
+                        <td class="<%- auditConst.auditProgressClass[s.audit_status] %>">
                             <% if (s.curAuditors && s.curAuditors.length > 0) { %>
                             <% if (s.curAuditors[0].audit_type === auditType.key.common) { %>
                             <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.settle_order %>"><%- s.curAuditors[0].name %><%if (s.curAuditors[0].role !== '' && s.curAuditors[0].role !== null) { %>-<%- s.curAuditors[0].role %><% } %></a>
@@ -67,19 +67,19 @@
                             <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.settle_order %>"><%- ctx.helper.transFormToChinese(s.curAuditors[0].audit_order) + '审' %></a>
                             <% } %>
                             <% } %>
-                            <%- auditConst.auditProgress[s.status] %>
+                            <%- auditConst.auditProgress[s.audit_status] %>
                         </td>
                         <td class="text-center">
-                            <% if (s.status === auditConst.status.uncheck && s.user_id === ctx.session.sessionUser.accountId) { %>
-                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
-                            <% } else if (s.status === auditConst.status.checkNo && s.curAuditors && s.user_id === ctx.session.sessionUser.accountId) { %>
-                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
-                            <% } else if (s.status === auditConst.status.checking && s.curAuditors && s.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
-                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
-                            <% } else if (s.status === auditConst.status.checkNoPre && s.curAuditorsPre && s.curAuditorsPre.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
-                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
+                            <% if (s.audit_status === auditConst.status.uncheck && s.user_id === ctx.session.sessionUser.accountId) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.audit_status] %> btn-sm"><%- auditConst.statusButton[s.audit_status] %></a>
+                            <% } else if (s.audit_status === auditConst.status.checkNo && s.curAuditors && s.user_id === ctx.session.sessionUser.accountId) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.audit_status] %> btn-sm"><%- auditConst.statusButton[s.audit_status] %></a>
+                            <% } else if (s.audit_status === auditConst.status.checking && s.curAuditors && s.curAuditors.findIndex(x => { return x.audit_id === ctx.session.sessionUser.accountId; }) >= 0) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.audit_status] %> btn-sm"><%- auditConst.statusButton[s.audit_status] %></a>
+                            <% } else if (s.audit_status === auditConst.status.checkNoPre && s.curAuditorsPre && s.curAuditorsPre.findIndex(x => { return x.audit_id === ctx.session.sessionUser.accountId; }) >= 0) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/settle/' + s.settle_order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.audit_status] %> btn-sm"><%- auditConst.statusButton[s.audit_status] %></a>
                             <% } else { %>
-                            <span class="<%- auditConst.auditStringClass[s.status] %>"><%- auditConst.auditString[s.status] %></span>
+                            <span class="<%- auditConst.auditStringClass[s.audit_status] %>"><%- auditConst.auditString[s.audit_status] %></span>
                             <% } %>
                         </td>
                     </tr>

+ 9 - 9
app/view/settle/list_modal.ejs

@@ -1,5 +1,5 @@
 <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && ctx.tender.data.ledger_status === auditConst.status.checked &&
-        (settles.length === 0 || settles[settles.length- 1].status === auditConst.status.checked)) { %>
+        (settles.length === 0 || settles[settles.length- 1].audit_status === auditConst.status.checked)) { %>
 <!--弹出添加期-->
 <div class="modal fade" id="add-qi" data-backdrop="static">
     <div class="modal-dialog" role="document">
@@ -212,18 +212,18 @@
             historyHTML.push(`<div class="text-center text-muted">${idx+1}#</div>`);
             historyHTML.push(`<ul class="timeline-list list-unstyled mt-2 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
             his.forEach((group) => {
-                historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
+                historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.audit_status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
                 if (group.auditYear) {
                     historyHTML.push(`<div class="timeline-item-date">${group.auditYear}<span>${group.auditDate}</span><span>${group.auditTime}</span></div>`);
                 }
                 if (group.audit_order < his.length - 1) {
                     historyHTML.push('<div class="timeline-item-tail"></div>');
                 }
-                if (group.status === auditConst.status.checked) {
+                if (group.audit_status === auditConst.status.checked) {
                     historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
-                } else if (group.status === auditConst.status.checkNo || group.status === auditConst.status.checkNoPre || group.status === auditConst.status.checkCancel) {
+                } else if (group.audit_status === auditConst.status.checkNo || group.audit_status === auditConst.status.checkNoPre || group.status === auditConst.status.checkCancel) {
                     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) {
+                } else if (group.audit_status === auditConst.status.checking) {
                     historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
                 } else {
                     historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
@@ -231,8 +231,8 @@
 
                 historyHTML.push('<div class="timeline-item-content">');
                 if (group.audit_order > 0) {
-                    const statuStr = group.status !== auditConst.status.uncheck ?
-                        `<span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>` : '';
+                    const statuStr = group.audit_status !== auditConst.status.uncheck ?
+                        `<span class="pull-right ${auditConst.statusClass[group.audit_status]}">${auditConst.statusString[group.audit_status]}</span>` : '';
                     historyHTML.push(`<div class="py-1">
                         <span class="text-black-50">
                         ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
@@ -250,9 +250,9 @@
                     historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
                     historyHTML.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
                     historyHTML.push('<div class="col">');
-                    if (auditor.status === auditConst.status.checked) {
+                    if (auditor.audit_status === auditConst.status.checked) {
                         historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
-                    } if (auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre || auditor.status === auditConst.status.checkCancel) {
+                    } if (auditor.audit_status === auditConst.status.checkNo || auditor.audit_status === auditConst.status.checkNoPre || auditor.audit_status === auditConst.status.checkCancel) {
                         historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
                     }
                     historyHTML.push('</div>');

+ 19 - 2
app/view/settle/select.ejs

@@ -94,10 +94,13 @@
     const tenderInfo = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.tender.info)) %>'));
     const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const thirdParty = JSON.parse('<%- JSON.stringify(thirdParty) %>');
+    const settleStatusHint = JSON.parse('<%- JSON.stringify(settleStatusHint )%>');
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus )%>');
+    const settleStatusColor = JSON.parse('<%- JSON.stringify(settleStatusColor )%>');
     const billsSpreadSetting = {
         cols: [
             {title: '选择', colSpan: '1', rowSpan: '2', field: 'selected', hAlign: 1, width: 30, formatter: '@', cellType: 'checkbox'},
-            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true, getValue: function(data) { return data.b_code ? '' : (settleStatusHint[data.settle_status] || '') }},
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@', readOnly: true},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
@@ -130,11 +133,18 @@
         frozenColCount: 5,
         frozenLineColor: '#93b5e4',
         readOnly,
+        getForeColor: function(sheet, data, row, col, foreColor) {
+            if (col.field === 'settle_status') {
+                return data ? settleStatusColor[data.settle_status] || foreColor : foreColor;
+            } else {
+                return foreColor;
+            }
+        }
     };
     const posSpreadSetting = {
         cols: [
             {title: '选择', colSpan: '1', rowSpan: '2', field: 'selected', hAlign: 0, width: 30, formatter: '@', cellType: 'checkbox'},
-            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true, getValue: function(data) { return settleStatusHint[data.settle_status] || '' }},
             {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
             {title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@', readOnly: true},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, formatter: '@', readOnly: true},
@@ -157,5 +167,12 @@
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
         readOnly,
+        getForeColor: function(sheet, data, row, col, foreColor) {
+            if (col.field === 'settle_status') {
+                return data ? settleStatusColor[data.settle_status] || foreColor : foreColor;
+            } else {
+                return foreColor;
+            }
+        }
     };
 </script>

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

@@ -555,6 +555,7 @@
     const hintOver = <%- hintOver %>;
     const hintMinusCb = <%- hintMinusCb %>;
     let currPageFileData = [];
+    const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
 </script>
 <style>
 

+ 12 - 1
db_script/test_query.js

@@ -38,9 +38,20 @@ const loadReportArchiveData = async function() {
     }
 };
 
+const findRole = async function() {
+    const role = await querySql('SELECT * FROM zh_role_rpt_rel');
+    for (const r of role) {
+        const roleContent = r.rel_content ? JSON.parse(r.rel_content) : [];
+        for (const rc of roleContent) {
+            if (rc.sign_date) console.log(rc.sign_date, rc.sign_date_format);
+        }
+    }
+};
+
 const doCompleteTest = async function() {
     try {
-        await loadReportArchiveData();
+        // await loadReportArchiveData();
+        // await findRole();
     } catch (err) {
         console.log(err);
     }