|
@@ -0,0 +1,317 @@
|
|
|
+'use strict';
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Mai
|
|
|
+ * @date
|
|
|
+ * @version
|
|
|
+ */
|
|
|
+
|
|
|
+const auditConst = require('../const/audit');
|
|
|
+const projectLogConst = require('../const/project_log');
|
|
|
+
|
|
|
+module.exports = app => {
|
|
|
+ class Settle extends app.BaseService {
|
|
|
+ /**
|
|
|
+ * 构造函数
|
|
|
+ *
|
|
|
+ * @param {Object} ctx - egg全局变量
|
|
|
+ * @return {void}
|
|
|
+ */
|
|
|
+ constructor(ctx) {
|
|
|
+ super(ctx);
|
|
|
+ this.tableName = 'settle';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取标段下的全部结算期,按倒序
|
|
|
+ * @param tenderId
|
|
|
+ * @return {Promise<void>}
|
|
|
+ */
|
|
|
+ async getValidSettles(tenderId) {
|
|
|
+ const settles = await this.db.select(this.tableName, {
|
|
|
+ where: { tid: tenderId },
|
|
|
+ orders: [['settle_order', 'desc']],
|
|
|
+ });
|
|
|
+ if (settles.length !== 0 && !this.ctx.session.sessionUser.is_admin) {
|
|
|
+ const last = settles[0];
|
|
|
+ if (last.status === auditConst.settle.status.uncheck && !this.ctx.tender.isTourist) {
|
|
|
+ if (last.user_id !== this.ctx.session.sessionUser.accountId) {
|
|
|
+ settles.splice(0, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return settles;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增结算期
|
|
|
+ * @param tenderId - 标段id
|
|
|
+ * @param date - 结算年月
|
|
|
+ * @param period - 结算周期
|
|
|
+ * @return {Promise<void>}
|
|
|
+ */
|
|
|
+ async addSettle(tenderId, date, period) {
|
|
|
+ const settles = await this.getAllDataByCondition({
|
|
|
+ where: { tid: tenderId },
|
|
|
+ order: ['order'],
|
|
|
+ });
|
|
|
+ const pre = settles[settles.length - 1];
|
|
|
+ if (settles.length > 0 && pre.status !== auditConst.settle.status.checked) {
|
|
|
+ throw '上一期未审批通过,请等待上一期审批通过后,再新增数据';
|
|
|
+ }
|
|
|
+ const checkedStage = await this.ctx.service.stage.getLastestCompleteStage(tenderId);
|
|
|
+ if (!checkedStage) throw '不存在审批通过的计量期,请先进行期计量,再结算';
|
|
|
+
|
|
|
+ const newSettle = {
|
|
|
+ tid: tenderId,
|
|
|
+ add_sid: checkedStage.order, 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,
|
|
|
+ };
|
|
|
+ if (pre) {
|
|
|
+ newSettle.pre_contract_tp = this.ctx.helper.add(pre.pre_contract_tp, pre.contract_tp);
|
|
|
+ newSettle.pre_positive_qc_tp = this.ctx.helper.add(pre.pre_positive_qc_tp, pre.positive_qc_tp);
|
|
|
+ newSettle.pre_negative_qc_tp = this.ctx.helper.add(pre.pre_negative_qc_tp, pre.negative_qc_tp);
|
|
|
+ newSettle.pre_qc_tp = this.ctx.helper.add(pre.pre_qc_tp, pre.qc_tp);
|
|
|
+ newSettle.pre_tp = this.ctx.helper.add(pre.tp, pre.pre_tp);
|
|
|
+ }
|
|
|
+ const transaction = await this.db.beginTransaction();
|
|
|
+ try {
|
|
|
+ // 新增期记录
|
|
|
+ const result = await transaction.insert(this.tableName, newSettle);
|
|
|
+ if (result.affectedRows === 1) {
|
|
|
+ newSettle.id = result.insertId;
|
|
|
+ } else {
|
|
|
+ throw '新增期数据失败';
|
|
|
+ }
|
|
|
+ await this.ctx.service.settleAudit.copyPreAuditors(transaction, pre, newSettle);
|
|
|
+ // 存在上一期时
|
|
|
+ if (pre) {
|
|
|
+ // todo 复制上一期其他数据
|
|
|
+ }
|
|
|
+ await transaction.commit();
|
|
|
+ } catch (err) {
|
|
|
+ await transaction.rollback();
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return newSettle;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 编辑结算期
|
|
|
+ *
|
|
|
+ * @param {Number} tenderId - 标段Id
|
|
|
+ * @param {Number} order - 第N期
|
|
|
+ * @param {String} date - 结算年月
|
|
|
+ * @param {String} period - 结算周期
|
|
|
+ * @return {Promise<void>}
|
|
|
+ */
|
|
|
+ async saveSettle(tenderId, order, date, period) {
|
|
|
+ await this.db.update(this.tableName, {
|
|
|
+ settle_time: date,
|
|
|
+ settle_period: period,
|
|
|
+ }, { where: { tid: tenderId, settle_order: order } });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除结算期
|
|
|
+ * @param id
|
|
|
+ * @returns {Promise<boolean>}
|
|
|
+ */
|
|
|
+ async deleteSettle(id) {
|
|
|
+ const transaction = await this.db.beginTransaction();
|
|
|
+ try {
|
|
|
+ const settleInfo = await this.getDataById(id);
|
|
|
+ await transaction.delete(this.tableName, { id });
|
|
|
+ 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 transaction.commit();
|
|
|
+ return true;
|
|
|
+ } catch (err) {
|
|
|
+ await transaction.rollback();
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadRelaUser(settle) {
|
|
|
+ const status = auditConst.settle.status;
|
|
|
+ const accountId = this.ctx.session.sessionUser.accountId;
|
|
|
+
|
|
|
+ settle.user = await this.ctx.service.projectAccount.getAccountInfoById(settle.user_id);
|
|
|
+ settle.auditors = await this.ctx.service.settleAudit.getAuditors(settle.id, settle.audit_times); // 全部参与的审批人
|
|
|
+ settle.auditorIds = this._.map(settle.auditors, 'aid');
|
|
|
+ settle.curAuditors = settle.auditors.filter(x => { return x.status === status.checking; }); // 当前流程中审批中的审批人
|
|
|
+ settle.curAuditorIds = this._.map(settle.curAuditors, 'aid');
|
|
|
+ settle.flowAuditors = settle.curAuditors.length > 0 ? settle.auditors.filter(x => { return x.order === settle.curAuditors[0].order; }) : []; // 当前流程中参与的审批人(包含会签时,审批通过的人)
|
|
|
+ settle.flowAuditorIds = this._.map(settle.flowAuditors, 'aid');
|
|
|
+ settle.auditorGroups = this.ctx.helper.groupAuditors(settle.auditors);
|
|
|
+ settle.userGroups = this.ctx.helper.groupAuditorsUniq(settle.auditorGroups);
|
|
|
+ settle.finalAuditorIds = settle.userGroups[settle.userGroups.length - 1].map(x => { return x.aid; });
|
|
|
+
|
|
|
+ // 协作相关
|
|
|
+ settle.assists = await this.service.settleAuditAss.getData(settle); // 全部协同人
|
|
|
+ settle.assists = settle.assists.filter(x => {
|
|
|
+ return x.user_id === settle.user_id || settle.auditorIds.indexOf(x.user_id) >= 0;
|
|
|
+ }); // 过滤无效协同人
|
|
|
+ settle.userAssists = settle.assists.filter(x => { return x.user_id === settle.user_id; }); // 原报协同人
|
|
|
+ settle.userAssistIds = this._.map(settle.userAssists, 'ass_user_id');
|
|
|
+ settle.auditAssists = settle.assists.filter(x => { return x.user_id !== settle.user_id; }); // 审批协同人
|
|
|
+ settle.auditAssistIds = this._.map(settle.auditAssists, 'ass_user_id');
|
|
|
+ settle.curAssists = settle.assists.filter(x => { return settle.curAuditorIds.indexOf(x.user_id) >= 0; }); // 当前审批人的协同人
|
|
|
+ settle.curAssistsIds = this._.map(settle.curAssists, 'ass_user_id');
|
|
|
+ settle.relaAssists = settle.assists.filter(x => { return x.user_id === accountId }); // 登录人的协同人
|
|
|
+ // 当前参与人Id
|
|
|
+ settle.userIds = settle.status === settle.uncheck // 当前流程下全部参与人id
|
|
|
+ ? [settle.user_id, ...settle.userAssistIds]
|
|
|
+ : [settle.user_id, ...settle.userAssistIds, ...settle.auditorIds, ...settle.auditAssistIds];
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadAuditViewData(settle) {
|
|
|
+ const times = settle.audit_status === auditConst.settle.status.checkNo ? settle.audit_times - 1 : settle.audit_times;
|
|
|
+
|
|
|
+ if (!settle.user) settle.user = await this.ctx.service.projectAccount.getAccountInfoById(settle.user_id);
|
|
|
+ settle.auditHistory = await this.ctx.service.settleAudit.getAuditorHistory(settle.id, times);
|
|
|
+ // 获取审批流程中左边列表
|
|
|
+ if (settle.status === auditConst.settle.status.checkNo && settle.user_id !== this.ctx.session.sessionUser.accountId) {
|
|
|
+ const auditors = await this.ctx.service.settleAudit.getAuditors(settle.id, settle.audit_times - 1); // 全部参与的审批人
|
|
|
+ const auditorGroups = this.ctx.helper.groupAuditors(auditors);
|
|
|
+ settle.hisUserGroup = this.ctx.helper.groupAuditorsUniq(auditorGroups);
|
|
|
+ } else {
|
|
|
+ settle.hisUserGroup = settle.userGroups;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * cancancel = 0 不可撤回
|
|
|
+ * cancancel = 1 原报撤回
|
|
|
+ * cancancel = 2 审批人撤回 审批通过
|
|
|
+ * cancancel = 3 审批人撤回 审批退回上一人
|
|
|
+ * cancancel = 4 审批人撤回 退回原报
|
|
|
+ * cancancel = 5 会签未全部审批通过时,审批人撤回 审批通过
|
|
|
+ *
|
|
|
+ * @param settle
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ */
|
|
|
+ async _doCheckSettleCanCancel(settle) {
|
|
|
+ // 默认不可撤回
|
|
|
+ settle.cancancel = 0;
|
|
|
+ // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
|
|
|
+ const status = auditConst.settle.status;
|
|
|
+ if (settle.status === status.checked || settle.status === status.uncheck) return;
|
|
|
+
|
|
|
+ const accountId = this.ctx.session.sessionUser.accountId;
|
|
|
+ if (settle.status !== status.checkNo) {
|
|
|
+ // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
|
|
|
+ if (settle.flowAuditors.find(x => { return x.status !== status.checking}) && settle.flowAuditorIds.indexOf(accountId) < 0) return; // 当前流程存在审批人审批通过时,不可撤回
|
|
|
+ const flowAssists = settle.auditAssists.filter(x => { return settle.flowAuditorIds.indexOf(x.user_id) >= 0; });
|
|
|
+ if (flowAssists.find(x => { return x.confirm; })) return; //当前流程存在协审人确认时,不可撤回
|
|
|
+ if (settle.curAuditorIds.indexOf(accountId) < 0 && settle.flowAuditorIds.indexOf(accountId) >= 0) {
|
|
|
+ settle.cancancel = 5; // 会签未全部审批通过时,审批人撤回审批通过
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const preAuditors = settle.curAuditors[0].order !== 1 ? settle.auditors.filter(x => { return x.order === settle.curAuditors[0].order - 1; }) : [];
|
|
|
+ const preAuditorCheckAgain = preAuditors.find(pa => { return pa.status === status.checkAgain; });
|
|
|
+ const preAuditorCheckCancel = preAuditors.find(pa => { return pa.status === status.checkCancel; });
|
|
|
+ const preAuditorHasOld = preAuditors.find(pa => { return pa.is_old === 1; });
|
|
|
+ const preAuditorIds = (preAuditorCheckAgain ? [] : preAuditors.map(x => { return x.aid })); // 重审不可撤回
|
|
|
+ if ((this._.isEqual(settle.flowAuditorIds, preAuditorIds) && preAuditorCheckCancel) || preAuditorHasOld) {
|
|
|
+ return; // 不可以多次撤回
|
|
|
+ }
|
|
|
+
|
|
|
+ const preAuditChecked = preAuditors.find(pa => { return pa.status === status.checked && pa.aid === accountId; });
|
|
|
+ const preAuditCheckNoPre = preAuditors.find(pa => { return pa.status === status.checkNoPre && pa.aid === accountId; });
|
|
|
+ if (preAuditorIds.indexOf(accountId) >= 0) {
|
|
|
+ if (preAuditChecked) {
|
|
|
+ settle.cancancel = 2;// 审批人撤回审批通过
|
|
|
+ } else if (preAuditCheckNoPre) {
|
|
|
+ settle.cancancel = 3;// 审批人撤回审批退回上一人
|
|
|
+ }
|
|
|
+ settle.preAuditors = preAuditors;
|
|
|
+ } else if (preAuditors.length === 0 && accountId === settle.user_id) {
|
|
|
+ settle.cancancel = 1;// 原报撤回
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const lastAuditors = await this.service.settleAudit.getAuditors(settle.id, settle.times - 1);
|
|
|
+ const onAuditor = _.findLast(lastAuditors, { status: status.checkNo });
|
|
|
+ if (onAuditor.aid === accountId) {
|
|
|
+ settle.cancancel = 4;// 审批人撤回退回原报
|
|
|
+ settle.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ async _doCheckSettleReadOnly(settle) {
|
|
|
+ const status = auditConst.settle.status;
|
|
|
+ // 校验权限(参与人、分享、游客)
|
|
|
+ const accountId = this.session.sessionUser.accountId;
|
|
|
+ const shareIds = [];
|
|
|
+ // 是否只读
|
|
|
+ if (settle.status === status.uncheck || settle.status === status.checkNo) {
|
|
|
+ settle.readOnly = accountId !== settle.user_id && settle.userAssistIds.indexOf(accountId) < 0;
|
|
|
+ } else {
|
|
|
+ settle.readOnly = true;
|
|
|
+ }
|
|
|
+ // 读取数据相关
|
|
|
+ settle.curTimes = settle.status === status.checkNo && settle.readOnly ? settle.times - 1 : settle.times;
|
|
|
+ // 协作人相关
|
|
|
+
|
|
|
+ if (settle.status === status.uncheck) {
|
|
|
+ if (!settle.readOnly) {
|
|
|
+ settle.assist = settle.userAssists.find(x => { return x.ass_user_id === accountId; });
|
|
|
+ }
|
|
|
+ settle.curTimes = settle.times;
|
|
|
+ } else if (settle.status === status.checkNo) {
|
|
|
+ if (!settle.readOnly) {
|
|
|
+ settle.assist = settle.userAssists.find(x => { return x.ass_user_id === accountId; });
|
|
|
+ settle.curTimes = settle.times;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const ass = settle.auditAssists.find(x => { return settle.flowAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
|
|
|
+ if (!settle.readOnly) {
|
|
|
+ settle.assist = ass;
|
|
|
+ }
|
|
|
+ if (!settle.readOnly) {
|
|
|
+ settle.readOnly = !_.isEqual(settle.flowAuditorIds, settle.curAuditorIds);
|
|
|
+ settle.canCheck = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (settle.readOnly) {
|
|
|
+ settle.assist = accountId === settle.user_id || settle.auditorIds.indexOf(accountId) >= 0 ? null : settle.auditAssists.find(x => { return x.ass_user_id === accountId});
|
|
|
+ }
|
|
|
+
|
|
|
+ // 上传文件权限
|
|
|
+ const permission = this.session.sessionUser.permission;
|
|
|
+ if (settle.userIds.indexOf(accountId) >= 0 || this.session.sessionUser.is_admin) {
|
|
|
+ settle.filePermission = true;
|
|
|
+ } else {
|
|
|
+ if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) {// 分享人
|
|
|
+ if (settle.status === status.uncheck) throw '您无权查看该数据';
|
|
|
+ settle.filePermission = false;
|
|
|
+ } else if (this.tender.isTourist || this.session.sessionUser.is_admin) {
|
|
|
+ settle.filePermission = this.tender.touristPermission.file || settle.auditorIds.indexOf(accountId) !== -1;
|
|
|
+ } else {
|
|
|
+ throw '您无权查看该数据';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ async doCheckSettle(settle) {
|
|
|
+ // 读取原报、审核人等参与人数据
|
|
|
+ await this.loadRelaUser(settle);
|
|
|
+ // 是否台账修订中
|
|
|
+ const lastRevise = await this.service.ledgerRevise.getLastestRevise(this.tender.id);
|
|
|
+ settle.revising = (lastRevise && lastRevise.status !== auditConst.revise.status.checked) || false;
|
|
|
+ // 是否只读等权限
|
|
|
+ await this._doCheckSettleReadOnly(settle);
|
|
|
+ // 可否撤回,是哪一种撤回
|
|
|
+ await this._doCheckSettleCanCancel(settle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Settle;
|
|
|
+};
|