| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- 'use strict';
- const contractConst = require('../const/contract');
- const { buildCatalog } = require('../lib/weapp_contract_catalog');
- const moment = require('moment');
- async function resolveContractScope(ctx) {
- const spid = typeof ctx.query.spid === 'string' ? ctx.query.spid.trim() : '';
- const tid = ctx.query.tid === undefined ? null : Number(ctx.query.tid);
- if (!spid) throw '缺少项目ID';
- if (tid !== null && (!Number.isSafeInteger(tid) || tid <= 0)) throw '标段ID错误';
- const subProject = await ctx.service.subProject.getDataById(spid);
- if (!subProject || subProject.is_delete || subProject.is_folder ||
- String(subProject.project_id) !== String(ctx.session.sessionProject.id)) {
- throw '项目不存在或无权查看';
- }
- subProject.page_show = ctx.service.subProject.getPageShow(subProject.page_show);
- if (tid ? !subProject.page_show.openTenderContract : !subProject.page_show.openContract) {
- throw tid ? '该项目未开启标段合同管理' : '该项目未开启合同管理';
- }
- const user = ctx.session.sessionUser;
- if (user.is_admin) {
- subProject.permission = ctx.service.subProjPermission.adminPermission;
- } else {
- const permission = await ctx.service.subProjPermission.getSubProjectUserPermission(spid, user.accountId);
- if (!permission || (!tid && ![3, 4, 5].some(value => permission.contract_permission.includes(value)))) {
- throw '当前账号权限不足,请联系管理员添加权限';
- }
- subProject.permission = permission;
- }
- ctx.subProject = subProject;
- if (!tid) return { scope: { spid }, info: subProject, user };
- const tender = await ctx.service.tender.getDataById(tid);
- if (!tender || String(tender.spid) !== spid ||
- String(tender.project_id) !== String(ctx.session.sessionProject.id)) {
- throw '标段不存在或无权查看';
- }
- if (!user.is_admin) {
- const audit = await ctx.service.contractAudit.getDataByCondition({ tid, uid: user.accountId });
- if (!audit) throw '当前账号没有该标段的合同权限';
- }
- return { scope: { tid }, info: tender, user };
- }
- async function resolveVisibleContract(ctx) {
- const type = ctx.query.type;
- const cid = typeof ctx.query.cid === 'string' ? ctx.query.cid.trim() : '';
- if (!['expenses', 'income'].includes(type) || !cid) throw '合同参数错误';
- const { scope, user } = await resolveContractScope(ctx);
- const contractType = contractConst.type[type];
- const contracts = await ctx.service.contract.getListByUsers({ ...scope, contract_type: contractType }, user);
- const contract = contracts.find(item => String(item.id) === cid);
- if (!contract) throw '合同不存在或无权查看';
- return { scope, contractType, contract };
- }
- const fileMimeTypes = {
- pdf: 'application/pdf',
- doc: 'application/msword',
- docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- xls: 'application/vnd.ms-excel',
- xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- ppt: 'application/vnd.ms-powerpoint',
- pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
- jpg: 'image/jpeg',
- jpeg: 'image/jpeg',
- png: 'image/png',
- gif: 'image/gif',
- };
- module.exports = app => {
- class WeappContractController extends app.BaseController {
- async tenderList(ctx) {
- try {
- const spid = typeof ctx.query.spid === 'string' ? ctx.query.spid.trim() : '';
- if (!spid) throw '缺少项目ID';
- const subProject = await ctx.service.subProject.getDataById(spid);
- if (!subProject || subProject.is_delete || subProject.is_folder ||
- String(subProject.project_id) !== String(ctx.session.sessionProject.id)) {
- throw '项目不存在或无权查看';
- }
- subProject.page_show = ctx.service.subProject.getPageShow(subProject.page_show);
- if (!subProject.page_show.openTenderContract) throw '该项目未开启标段合同管理';
- const user = ctx.session.sessionUser;
- if (!user.is_admin) {
- const permission = await ctx.service.subProjPermission.getSubProjectUserPermission(spid, user.accountId);
- if (!permission) throw '您无权查看该项目';
- subProject.permission = permission;
- }
- ctx.subProject = subProject;
- const accountInfo = await ctx.service.projectAccount.getDataById(user.accountId);
- const tenderList = await ctx.service.tender.getContractList('', null, user.is_admin ? 1 : 0);
- const categoryData = await ctx.service.category.getAllCategory(subProject);
- ctx.body = {
- code: 0, msg: '', data: {
- tenderList,
- categoryData,
- selfCategoryLevel: accountInfo ? accountInfo.self_category_level : '',
- },
- };
- } catch (error) {
- this.log(error);
- ctx.body = { code: -1, msg: error.toString(), data: null };
- }
- }
- async catalog(ctx) {
- try {
- const type = ctx.query.type;
- if (!['expenses', 'income'].includes(type)) throw '合同目录参数错误';
- const contractType = contractConst.type[type];
- const { scope, info, user } = await resolveContractScope(ctx);
- const options = { ...scope, contract_type: contractType };
- // Web 端首次打开详情时也会按项目模板初始化目录。
- await ctx.service.contractTree.insertTree(scope, info);
- const folders = await ctx.service.contractTree.getAllDataByCondition({ where: options });
- const contracts = await ctx.service.contract.getListByUsers(options, user);
- const contractIds = [...new Set(contracts.map(contract => contract.id))];
- const fileCounts = await ctx.service.contractAtt.getCounts(contractIds);
- for (const contract of contracts) {
- contract.fileCount = fileCounts.get(String(contract.id)) || 0;
- }
- ctx.body = { code: 0, msg: '', data: { nodes: buildCatalog(folders, contracts) } };
- } catch (error) {
- this.log(error);
- ctx.body = { code: -1, msg: error.toString(), data: null };
- }
- }
- async summary(ctx) {
- try {
- const { scope, user } = await resolveContractScope(ctx);
- const now = moment().startOf('month');
- const months = Array.from({ length: 6 }, (_, index) => now.clone().subtract(5 - index, 'months').format('YYYY-MM'));
- const start = now.clone().subtract(5, 'months').format('YYYY-MM-DD HH:mm:ss');
- const end = now.clone().add(1, 'month').format('YYYY-MM-DD HH:mm:ss');
- const summarize = async contractType => {
- const visibleContracts = await ctx.service.contract.getListByUsers({ ...scope, contract_type: contractType }, user);
- const contracts = [...new Map(visibleContracts.map(item => [item.id, item])).values()];
- const total = contracts.reduce((sum, item) => ctx.helper.add(sum, item.total_price || 0), 0);
- const settled = contracts.reduce((sum, item) => ctx.helper.add(sum, item.yf_price || 0), 0);
- const paid = contracts.reduce((sum, item) => ctx.helper.add(sum, item.sf_price || 0), 0);
- const unpaid = ctx.helper.sub(total, paid);
- const percent = total ? ctx.helper.round(ctx.helper.div(settled, total) * 100, 2) : 0;
- const ids = [...new Set(contracts.map(item => item.id))];
- const monthly = await ctx.service.contractPay.getMonthlyYfPrice(ids, start, end);
- const byMonth = new Map(monthly.map(item => [item.month, item.amount]));
- return { total, paid, unpaid, percent, trend: months.map(month => byMonth.get(month) || 0) };
- };
- const [expenses, income] = await Promise.all([
- summarize(contractConst.type.expenses),
- summarize(contractConst.type.income),
- ]);
- ctx.body = { code: 0, msg: '', data: { months, expenses, income } };
- } catch (error) {
- this.log(error);
- ctx.body = { code: -1, msg: error.toString(), data: null };
- }
- }
- async detail(ctx) {
- try {
- const { scope, contractType, contract } = await resolveVisibleContract(ctx);
- const where = { ...scope, contract_type: contractType, cid: contract.id };
- const [pays, files] = await Promise.all([
- ctx.service.contractPay.getAllDataByCondition({ where, orders: [['create_time', 'desc']] }),
- ctx.service.contractAtt.getAllDataByCondition({ where, orders: [['upload_time', 'desc']] }),
- ]);
- ctx.body = {
- code: 0, msg: '', data: {
- contract: {
- id: contract.id,
- c_code: contract.c_code,
- name: contract.name,
- create_time: contract.create_time,
- total_price: contract.total_price,
- pay_price: contract.pay_price,
- debit_price: contract.debit_price,
- yf_price: contract.yf_price,
- sf_price: contract.sf_price,
- party_a: contract.party_a,
- party_a_user: contract.party_a_user,
- party_b: contract.party_b,
- party_b_user: contract.party_b_user,
- sign_date: contract.sign_date,
- address: contract.address,
- entity: contract.entity,
- bank: contract.bank,
- bank_account: contract.bank_account,
- settle_code: contract.settle_code,
- remark: contract.remark,
- },
- pays: pays.map(pay => ({
- id: pay.id,
- pay_type: pay.pay_type,
- pay_time: pay.pay_time,
- sf_price: pay.sf_price,
- })),
- files: files.map(file => ({
- id: file.id,
- filename: file.filename,
- filesize: file.filesize,
- fileext: file.fileext,
- })),
- },
- };
- } catch (error) {
- this.log(error);
- ctx.body = { code: -1, msg: error.toString(), data: null };
- }
- }
- async downloadFile(ctx) {
- try {
- const { scope, contractType, contract } = await resolveVisibleContract(ctx);
- const file = await ctx.service.contractAtt.getDataById(ctx.params.fid);
- if (!file || String(file.cid) !== String(contract.id) ||
- Number(file.contract_type) !== contractType ||
- (scope.tid && Number(file.tid) !== scope.tid) ||
- (scope.spid && String(file.spid) !== scope.spid)) {
- throw '文件不存在或无权查看';
- }
- const ext = String(file.fileext || '').replace(/^\./, '').toLowerCase();
- ctx.response.set({
- 'Content-Type': fileMimeTypes[ext] || 'application/octet-stream',
- 'Content-Disposition': `inline; filename*=UTF-8''${encodeURIComponent(file.filename || 'file')}`,
- });
- ctx.body = await ctx.helper.ossFileGet(file.filepath);
- } catch (error) {
- this.log(error);
- ctx.status = 404;
- ctx.body = { code: -1, msg: error.toString(), data: null };
- }
- }
- }
- return WeappContractController;
- };
|