'use strict'; /** * * * @author Mai * @date * @version */ const stdDataAddType = { withParent: 1, child: 2, next: 3, }; const audit = require('../const/audit'); const accountGroup = require('../const/account_group').group; const tenderMenu = require('../../config/menu').tenderMenu; const measureType = require('../const/tender').measureType; const spreadConst = require('../const/spread'); const fs = require('fs'); const LzString = require('lz-string'); module.exports = app => { class ReviseController extends app.BaseController { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @return {void} */ constructor(ctx) { super(ctx); } /** * 是否可以新增修订 * @param ctx * @returns {Promise} * @private */ async _getAddReviseValid(ctx) { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true); return (ctx.tender.data.user_id === ctx.session.sessionUser.accountId) && (ctx.tender.data.ledger_status === audit.revise.status.checked) && (!revise || !revise.valid || revise.status === audit.revise.status.checked) && (!lastStage || lastStage.status !== audit.stage.status.checking); } /** * 台账修订页面 (Get) * * @param {object} ctx - egg全局变量 * @return {void} */ async index(ctx) { try { // 分页相关 const count = await ctx.service.ledgerRevise.count({tid: ctx.tender.id}); const ledgerRevise = await ctx.service.ledgerRevise.getReviseList(ctx.tender.id); if (ledgerRevise.length > 0) { const revise = ledgerRevise[0]; if (revise.status === audit.revise.status.checked || !revise.valid) { revise.lastest = true; } else { if (ledgerRevise.length > 1) ledgerRevise[1].lastest = true; if (revise.status === audit.revise.status.checking) { revise.curAuditor = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times); } } } const addValid = await this._getAddReviseValid(ctx); const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(ctx.tender.data.valuation); const renderData = { tender: ctx.tender.data, tenderMenu: this.menu.tenderMenu, preUrl: '/tender/' + ctx.tender.id, pageInfo: { page: ctx.page, total: Math.ceil(count/app.config.pageSize), queryData: JSON.stringify(ctx.urlInfo.query), }, ledgerRevise, addValid, auditConst: audit.revise, stdBills, stdChapters, }; await this.layout('revise/index.ejs', renderData, 'revise/modal.ejs'); } catch (err) { this.log(err); ctx.redirect(ctx.request.header.referer); } } /** * 新增 修订 (Post) * @param ctx * @returns {Promise} */ async add(ctx) { try { const addValid = await this._getAddReviseValid(ctx); if (!addValid) { throw '无法新增修订'; } const revise = await ctx.service.ledgerRevise.add(ctx.tender.id, ctx.session.sessionUser.accountId); ctx.redirect('/tender/' + ctx.tender.id + '/revise/info'); } catch(err) { this.log(err); ctx.redirect(ctx.request.header.referer); } } /** * 作废 修订 (Post) * @param ctx * @returns {Promise} */ async cancel(ctx) { try { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (revise.uid !== ctx.session.sessionUser.accountId) { throw '您无权作废该修订'; } if (revise.status === audit.revise.checking) { throw '修订审批中,不可作废'; } else if (revise.status === audit.revise.checked) { throw '修订已审批通过,不可作废'; } if (revise.valid) { const result = await ctx.service.ledgerRevise.cancelRevise(revise); } ctx.redirect('/tender/' + ctx.tender.id + '/revise/'); } catch(err) { this.log(err); ctx.redirect(ctx.request.header.referer); } } /** * 保存 修订详情 (Ajax-post) * @param ctx * @returns {Promise} */ async save(ctx) { try { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (!revise.valid) { throw '该修订已作废'; } else if (revise.status === audit.revise.status.uncheck || revise.status === audit.revise.status.checkNo) { if (ctx.session.sessionUser.accountId !== revise.uid) { throw '您无权修改'; } } else if (revise.status === audit.revise.status.checking) { throw '修订在审批中,不可修改修订数据'; } else if (revise.staut === audit.revise.status.checked) { throw '修订已经审批通过,不可修改修订数据,请新增下一修订'; } const data = JSON.parse(ctx.request.body.data); if (data.content === undefined) { throw '保存数据有误' } const result = await ctx.service.ledgerRevise.defaultUpdate({id: revise.id, content: data.content}); if (result.affectedRows !== 1) { throw '保存数据失败,请重试'; } ctx.body = {err: 0, msg: '', data: null}; } catch(err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '保存数据失败,请重试'); } } /** * 获取SpreadSetting * @private */ _getSpreadSetting(revise) { const _ = this.app._; function removeFieldCols(setting, cols) { _.remove(setting.cols, function(c) { return cols.indexOf(c.field) > -1; }); } const tender = this.ctx.tender; const setting = tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl; const ledger = JSON.parse(JSON.stringify(setting.ledger)); const pos = JSON.parse(JSON.stringify(setting.pos)); if (revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked) { ledger.readOnly = true; pos.readOnly = true; } if (tender.data.measure_type === measureType.tz.value) { removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols); } if (!tender.info.display.ledger.dgnQty) { removeFieldCols(ledger, spreadConst.filterCols.dgnCols); } return [ledger, pos]; } async _getDefaultReviseInfoData(ctx, revise) { const reviseBills = revise.bills_file ? JSON.parse(await fs.readFileSync(this.ctx.app.config.filePath + revise.bills_file, 'utf8')) : await ctx.service.reviseBills.getData(ctx.tender.id); const revisePos = revise.pos_file ? JSON.parse(await fs.readFileSync(this.ctx.app.config.filePath + revise.pos_file, 'utf8')) : await ctx.service.revisePos.getData(ctx.tender.id); const [ledgerSpread, posSpread] = this._getSpreadSetting(revise); const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(ctx.tender.data.valuation); const curAuditor = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times); const auditors = await ctx.service.reviseAudit.getAuditors(revise.id, revise.times); const user = await ctx.service.projectAccount.getAccountInfoById(revise.uid); const auditHistory = []; if (revise.times > 1) { for (let i = 1; i < revise.times; i++) { auditHistory.push(await ctx.service.reviseAudit.getAuditors(revise.id, i)); } } return { revise: revise, tender: ctx.tender.data, reviseBills, revisePos, ledgerSpread, posSpread, tenderMenu, measureType, preUrl: '/tender/' + ctx.tender.id, audit: audit.revise, jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.info), stdBills, stdChapters, curAuditor, auditors, user, auditHistory, }; } /** * 历史修订页面(修订完成后,用于查看) * @param ctx * @param revise * @returns {Promise} * @private */ async _getHistoryReviseInfo(ctx, revise) { const renderData = await this._getDefaultReviseInfoData(ctx, revise); renderData.ledgerSpread.readOnly = true; renderData.posSpread.readOnly = true; renderData.readOnly = true; renderData.history = true; renderData.historyRevise = await ctx.service.ledgerRevise.getReviseList(ctx.tender.id); await this.layout('revise/info.ejs', renderData, 'revise/info_modal.ejs'); } /** * 修订页面(草稿,审批中) * @param ctx * @param revise * @returns {Promise} * @private */ async _getAuditReviseInfo(ctx, revise) { const renderData = await this._getDefaultReviseInfoData(ctx, revise); renderData.readOnly = true; renderData.ledgerSpread.readOnly = true; renderData.posSpread.readOnly = true; renderData.history = false; renderData.historyRevise = []; renderData.curAuditor = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times); await this.layout('revise/info.ejs', renderData, 'revise/info_modal.ejs'); } async _getDraftReviseInfo(ctx, revise) { const renderData = await this._getDefaultReviseInfoData(ctx, revise); // 台账只读、使用数据 renderData.readOnly = false; const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true); if (lastStage) { if (lastStage.status === audit.stage.status.checked) { const usedPreBills = await ctx.service.stageBillsFinal.getUsedBills(ctx.tender.id, lastStage.order); for (const b of renderData.reviseBills) { b.used = usedPreBills.indexOf(b.id) >= 0; } const usedPrePos = await ctx.service.stagePosFinal.getUsedPos(ctx.tender.id, lastStage.order); for (const p of renderData.revisePos) { p.used = usedPrePos.indexOf(p.id) >= 0; } } else { const usedPreBills = lastStage.order > 1 ? await ctx.service.stageBillsFinal.getUsedBills(ctx.tender.id, lastStage.order - 1) : []; const usedCurBills = await ctx.service.stageBills.getStageUsedBills(ctx.tender.id, lastStage.id); for (const b of renderData.reviseBills) { b.used = usedPreBills.indexOf(b.id) >= 0 || usedCurBills.indexOf(b.id) >= 0; } const usedPrePos = lastStage.order > 1 ? await ctx.service.stagePosFinal.getUsedPos(ctx.tender.id, lastStage.order - 1) : []; const usedCurPos = await ctx.service.stagePos.getStageUsedPos(ctx.tender.id, lastStage.id); for (const p of renderData.revisePos) { p.used = usedPrePos.indexOf(p.id) >= 0 || usedCurPos.indexOf(p.id) >= 0; } } } // 修订历史 renderData.history = false; renderData.historyRevise = []; // 添加审批人相关 renderData.accountGroup = accountGroup; renderData.accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: ctx.session.sessionProject.id, enable: 1 }, columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group'], }); renderData.auditorList = await ctx.service.reviseAudit.getAuditors(revise.id, revise.times); renderData.curAuditor = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times); await this.layout('revise/info.ejs', renderData, 'revise/info_modal.ejs'); } /** * 修订 详细页面(Get) * @param ctx * @returns {Promise} */ async info(ctx) { try { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (!revise) throw '台账修订数据有误'; if (revise.uid === ctx.session.sessionUser.accountId && (revise.status === audit.revise.status.uncheck || revise.status === audit.revise.status.checkNo)) { await this._getDraftReviseInfo(ctx, revise); } else if (revise.status === audit.revise.status.checked) { await this._getHistoryReviseInfo(ctx, revise); } else { await this._getAuditReviseInfo(ctx, revise); } } catch (err) { this.log(err); ctx.redirect(ctx.request.header.referer); } } async history(ctx) { try { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id, false); if (!revise) throw '台账修订数据有误'; const reviseBills = await ctx.service.ledger.getData(ctx.tender.id); const revisePos = await ctx.service.pos.getPosData({tid: ctx.tender.id}); const [ledgerSpread, posSpread] = this._getSpreadSetting(revise); ledgerSpread.readOnly = true; posSpread.readOnly = true; const historyRevise = await ctx.service.ledgerRevise.getReviseList(ctx.tender.id); const renderData = { measureType, audit, revise, ledgerSpread, posSpread, reviseBills, revisePos, readOnly: true, historyRevise, jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.history), }; await this.layout('revise/history.ejs', renderData); } catch (err) { this.log(err); ctx.redirect(ctx.request.header.referer); } } async historyInfo(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data || !data.rid || data.rid === '') throw '查询的台账修订有误'; const reviseInfo = await ctx.service.ledgerRevise.getRevise(ctx.tender.id, data.rid); reviseInfo.end_time_str = reviseInfo.end_time ? reviseInfo.end_time.toLocaleString() : ''; ctx.body = { err: 0, msg: '', data: reviseInfo }; } catch(err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '获取台账修订历史数据错误'); } } async checkRevise(ctx) { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (revise.uid !== ctx.session.sessionUser.accountId) { throw '修订数据错误'; } else if (revise.status === audit.revise.status.checking) { throw '修订审批中,不可修改'; } else if (revise.status === audit.revise.status.checked) { throw '修订已完成,如需修订台账,请新增修订'; } return revise; } async _billsBase(revise, type, data) { if (isNaN(data.id) || data.id <= 0) throw '数据错误'; if (type !== 'add') { if (isNaN(data.count) || data.count <= 0) data.count = 1; } switch (type) { case 'add': return await this.ctx.service.reviseBills.addReviseNode(revise.tid, revise.id, data.id, data.count); case 'delete': return await this.ctx.service.reviseBills.delete(revise.tid, data.id, data.count); case 'up-move': return await this.ctx.service.reviseBills.upMoveNode(revise.tid, data.id, data.count); case 'down-move': return await this.ctx.service.reviseBills.downMoveNode(revise.tid, data.id, data.count); case 'up-level': return await this.ctx.service.reviseBills.upLevelNode(revise.tid, data.id, data.count); case 'down-level': return await this.ctx.service.reviseBills.downLevelNode(revise.tid, data.id, data.count); } } async _batchInsert(revise, data) { if ((isNaN(data.id) || data.id <= 0) || !data.batchType) { throw '参数错误'; } switch (data.batchType) { case 'child': return await this.ctx.service.reviseBills.batchInsertChild(revise.tid, revise.id, data.id, data.batchData); case 'next': return await this.ctx.service.reviseBills.batchInsertNext(revise.tid, revise.id, data.id, data.batchData); default: throw '参数错误'; } } async _pasteBlock(revise, data) { if ((isNaN(data.id) || data.id <= 0) || (!data.tid && data.tid <= 0) || (!data.block || data.block.length <= 0)) throw '参数错误'; return await this.ctx.service.revise.pasteBlock(data.tid, data.id, data.block); } async _addStd(revise, data) { if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误'; // todo 校验项目是否使用该库的权限 let stdLib, addType; switch (data.stdType) { case 'xmj': stdLib = this.ctx.service.stdXmj; addType = stdDataAddType.withParent; break; case 'gcl': stdLib = this.ctx.service.stdGcl; const selectNode = await this.ctx.service.reviseBills.getDataByKid(revise.tid, data.id); if (selectNode.b_code) { addType = stdDataAddType.next; } else { addType = stdDataAddType.child; } break; default: throw '未知标准库'; } const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode); switch (addType) { case stdDataAddType.child: return await this.ctx.service.reviseBills.addStdNodeAsChild(revise.tid, data.id, stdData, revise.id); break; case stdDataAddType.next: return await this.ctx.service.reviseBills.addStdNode(revise.tid, data.id, stdData, revise.id); case stdDataAddType.withParent: return await this.ctx.service.reviseBills.addStdNodeWithParent(revise.tid, stdData, stdLib, revise.id); default: throw '未知添加方式'; } } async _addDeal(revise, data) { if (!data.type || !data.dealBills) throw '数据错误'; data.dealBills.unit_price = this.ctx.helper.round(data.dealBills.unit_price, this.ctx.tender.info.decimal.up); if (data.type === 'child') { return await this.ctx.service.reviseBills.addChild(revise.tid, data.id, data.dealBills, revise.id); } else if (data.type === 'next') { return await this.ctx.service.reviseBills.addBillsNode(revise.tid, data.id, data.dealBills, revise.id); } else { throw '数据错误'; } } async _addBg(revise, data) { if (!data.type || !data.bgBills) throw '数据错误'; data.bgBills.unit_price = this.ctx.helper.round(data.bgBills.unit_price, this.ctx.tender.info.decimal.up); if (data.type === 'child') { return await this.ctx.service.reviseBills.addChild(revise.tid, data.id, data.bgBills, revise.id); } else if (data.type === 'next') { return await this.ctx.service.reviseBills.addBillsNode(revise.tid, data.id, data.bgBills, revise.id); } else { throw '数据错误'; } } async _updatePos(revise, data) { await this.checkMeasureType(measureType.tz.value); if (!data.posPostType) throw '数据错误'; switch (data.posPostType) { case 'add': return await this.ctx.service.revisePos.addPos(revise.tid, revise.id, data.postData); case 'update': if (data.postData instanceof Array) { return await this.ctx.service.revisePos.updatePosArr(revise.tid, data.postData); } else { return await this.ctx.service.revisePos.updatePos(revise.tid, data.postData); } case 'delete': return await this.ctx.service.revisePos.deletePos(revise.tid, data.postData); case 'paste': return await this.ctx.service.revisePos.pastePosData(revise.tid, revise.id, data.postData); default: throw '未知操作'; } } async update(ctx) { try { if (!ctx.tender.data) throw '标段数据错误'; const data = JSON.parse(ctx.request.body.data); if (!data.postType || !data.postData) throw '数据错误'; const responseData = {err: 0, msg: '', data: {}}; const revise = await this.checkRevise(ctx); switch (data.postType) { case 'add': case 'delete': case 'up-move': case 'down-move': case 'up-level': case 'down-level': responseData.data = await this._billsBase(revise, data.postType, data.postData); break; case 'update': responseData.data = await this.ctx.service.reviseBills.updateCalc(revise.tid, data.postData); break; case 'batch-insert': responseData.data = await this._batchInsert(revise, data.postData); break; case 'add-deal': responseData.data = await this._addDeal(revise, data.postData); break; case 'add-bg': responseData.data = await this._addDeal(revise, data.postData); break; case 'add-std': responseData.data = await this._addStd(revise, data.postData); break; case 'paste-block': responseData.data = await this._pasteBlock(revise, data.postData); break; case 'pos': responseData.data = await this._updatePos(revise, data); break; default: throw '未知操作'; } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '数据错误'); } } /** * 上传 清单Excel 并导入 * @param ctx * @return {Promise} */ async uploadExcel(ctx) { try { const revise = await this.checkRevise(ctx); const ueType = ctx.params.ueType; const compressData = ctx.request.body.data; const data = JSON.parse(LzString.decompressFromUTF16(compressData)); const responseData = { err: 0, msg: '', data: {}, }; switch (ueType) { case 'gcl2xmj': responseData.data = await ctx.service.reviseBills.importGclExcel(data.id, data.sheet, {crid: revise.id}); break; } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 新增审批人(Ajax) * * @param ctx * @return {Promise} */ async addAuditor(ctx) { try { const data = JSON.parse(ctx.request.body.data); const id = data.auditorId; if (isNaN(id) || id <= 0) { throw '参数错误'; } const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (!revise) throw '台账修订数据有误'; // 检查权限等 if (revise.uid !== ctx.session.sessionUser.accountId) throw '您无权添加审核人'; if (revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked) { throw '当前不允许添加审核人'; } // 检查审核人是否已存在 const exist = await ctx.service.reviseAudit.getAuditor(revise.id, id, revise.times); if (exist) throw '该审核人已存在,请勿重复添加'; const result = await ctx.service.reviseAudit.addAuditor(revise, id); if (!result) throw '添加审核人失败'; const resultData = await ctx.service.reviseAudit.getAuditor(revise.id, id, revise.times); ctx.body = {err: 0, msg: '', data: resultData}; } catch (err) { this.log(err, '数据错误'); ctx.body = this.ajaxErrorBody(err); } } /** * 移除审批人(Ajax) * * @param ctx * @return {Promise} */ async removeAuditor(ctx) { try { const data = JSON.parse(ctx.request.body.data); const id = data.auditorId instanceof Number ? data.auditorId : this.app._.toNumber(data.auditorId); if (isNaN(id) || id <= 0) { throw '参数错误'; } const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (!revise) throw '台账修订数据有误'; const result = await ctx.service.reviseAudit.deleteAuditor(revise, id); if (!result) { throw '移除审核人失败'; } const resultData = await ctx.service.reviseAudit.getAuditors(revise.id, revise.times); ctx.body = {err: 0, msg: '', data: resultData}; } catch (err) { this.log(err, '数据错误'); ctx.body = this.ajaxErrorBody(err); } } /** * 上报(post) * * @param ctx * @return {Promise} */ async start(ctx) { try { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (!revise || revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked) { throw '台账修订数据有误'; } if (revise.content === '') { throw '未填写修订内容,请先填写并保存修订内容'; } if (revise.uid !== ctx.session.sessionUser.accountId) throw '上报失败'; await ctx.service.reviseAudit.start(revise, revise.times); ctx.redirect('/tender/' + ctx.tender.id + '/revise/info'); } catch (err) { this.log(err); this.postError(err, '上报失败'); ctx.redirect('/tender/' + ctx.tender.id + '/revise/info'); } } /** * 审批(post) * * @param ctx * @return {Promise} */ async check(ctx) { try { const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id); if (!revise || revise.status !== audit.revise.status.checking) throw '台账修订数据有误'; const curAudit = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times); if (curAudit.audit_id !== ctx.session.sessionUser.accountId) throw '审批失败'; const checkType = parseInt(ctx.request.body.checkType); if (!checkType || isNaN(checkType)) throw '提交数据错误'; await ctx.service.reviseAudit.check(revise, checkType, ctx.request.body.opinion, revise.times); ctx.redirect('/tender/' + ctx.tender.id + '/revise/info'); } catch (err) { this.log(err); this.postError(err, '审批失败'); ctx.redirect('/tender/' + ctx.tender.id + '/revise/info'); } } } return ReviseController; };