|
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
|
|
const moment = require('moment');
|
|
|
|
//业绩报表相关
|
|
|
|
async function getReceivedDetail(ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { keywordTarget, keyword, limit, page, toExport } = ctx.query
|
|
|
|
let where = {}
|
|
|
|
if (keywordTarget == 'contract' && keyword) {
|
|
|
|
where.contractNo = { $like: `%${keyword}%` }
|
|
|
|
}
|
|
|
|
let findOption = {
|
|
|
|
where: where,
|
|
|
|
order: [['id', 'ASC']]
|
|
|
|
}
|
|
|
|
if (!toExport) {//非导出时考虑分页
|
|
|
|
if (limit) {
|
|
|
|
findOption.limit = Number(limit)
|
|
|
|
}
|
|
|
|
if (page && limit) {
|
|
|
|
findOption.offset = Number(page) * Number(limit)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let res = await models.ReceivableDetail.findAndCountAll(findOption);
|
|
|
|
if (toExport) {//数据导出相关
|
|
|
|
await exportReceivedDetail(ctx, res.rows);
|
|
|
|
} else {
|
|
|
|
ctx.status = 200
|
|
|
|
ctx.body = {
|
|
|
|
count: res.count,
|
|
|
|
rows: res.rows
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = { name: 'FindAllError', message: '获取失败' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function exportReceivedDetail(ctx, dataList) {
|
|
|
|
try {
|
|
|
|
let header = [{
|
|
|
|
title: '年度',
|
|
|
|
key: 'year',
|
|
|
|
}, {
|
|
|
|
title: '序号',
|
|
|
|
key: 'serialNo',
|
|
|
|
}, {
|
|
|
|
title: '编号',
|
|
|
|
key: 'number',
|
|
|
|
}, {
|
|
|
|
title: '部门',
|
|
|
|
key: 'department',
|
|
|
|
}, {
|
|
|
|
title: '销售人员',
|
|
|
|
key: 'sale',
|
|
|
|
}, {
|
|
|
|
title: '合同编号',
|
|
|
|
key: 'contractNo',
|
|
|
|
}, {
|
|
|
|
title: '客户名称',
|
|
|
|
key: 'customer',
|
|
|
|
}, {
|
|
|
|
title: '项目名称',
|
|
|
|
key: 'item',
|
|
|
|
}, {
|
|
|
|
title: '合同金额',
|
|
|
|
key: 'amount',
|
|
|
|
}, {
|
|
|
|
title: '变更后合同金额',
|
|
|
|
key: 'changeAmount',
|
|
|
|
}, {
|
|
|
|
title: '回款年份',
|
|
|
|
key: 'receivableYear',
|
|
|
|
}, {
|
|
|
|
title: '回款日期',
|
|
|
|
key: 'receivableDate',
|
|
|
|
}, {
|
|
|
|
title: '回款金额',
|
|
|
|
key: 'receivableAmount',
|
|
|
|
}, {
|
|
|
|
title: '开票-回款',
|
|
|
|
key: 'invoicedBack',
|
|
|
|
}, {
|
|
|
|
title: '剩余合同金额',
|
|
|
|
key: 'remainConAmount',
|
|
|
|
}, {
|
|
|
|
title: '收入确认时间',
|
|
|
|
key: 'incomeConfirmdate',
|
|
|
|
}, {
|
|
|
|
title: '第三方付款单位',
|
|
|
|
key: 'thirdPayment',
|
|
|
|
}, {
|
|
|
|
title: '备注',
|
|
|
|
key: 'remark',
|
|
|
|
}]
|
|
|
|
const { utils: { simpleExcelDown } } = ctx.app.fs;
|
|
|
|
let exportData = []
|
|
|
|
for (let { dataValues: item } of dataList) {
|
|
|
|
exportData.push(item)
|
|
|
|
}
|
|
|
|
const fileName = `回款明细表_${moment().format('YYYYMMDDHHmmss')}` + '.xlsx'
|
|
|
|
const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName, needIndexCell: false })
|
|
|
|
const fileData = fs.readFileSync(filePath);
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.set('Content-Type', 'application/x-xls');
|
|
|
|
ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
|
|
|
|
ctx.body = fileData;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async function getAchievementDetail(ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { keywordTarget, keyword, limit, page, toExport } = ctx.query
|
|
|
|
let where = {}
|
|
|
|
if (keywordTarget == 'saler' && keyword) {
|
|
|
|
where.sale = { $like: `%${keyword}%` }
|
|
|
|
}
|
|
|
|
if (keywordTarget == 'line' && keyword) {
|
|
|
|
where.serviceLine = { $like: `%${keyword}%` }
|
|
|
|
}
|
|
|
|
if (keywordTarget == 'dep' && keyword) {
|
|
|
|
where.department = { $like: `%${keyword}%` }
|
|
|
|
}
|
|
|
|
let findOption = {
|
|
|
|
where: where,
|
|
|
|
order: [['id', 'ASC']]
|
|
|
|
}
|
|
|
|
if (!toExport) {//非导出时考虑分页
|
|
|
|
if (limit) {
|
|
|
|
findOption.limit = Number(limit)
|
|
|
|
}
|
|
|
|
if (page && limit) {
|
|
|
|
findOption.offset = Number(page) * Number(limit)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let res = await models.PerformanceDetail.findAndCountAll(findOption);
|
|
|
|
if (toExport) {//数据导出相关
|
|
|
|
await exportAchievementDetail(ctx, res.rows);
|
|
|
|
} else {
|
|
|
|
ctx.status = 200
|
|
|
|
ctx.body = {
|
|
|
|
count: res.count,
|
|
|
|
rows: res.rows
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = { name: 'FindAllError', message: '获取失败' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function exportAchievementDetail(ctx, dataList) {
|
|
|
|
try {
|
|
|
|
let header = [{
|
|
|
|
title: '收到合同日期',
|
|
|
|
key: 'recConDate',
|
|
|
|
}, {
|
|
|
|
title: '月份',
|
|
|
|
key: 'month',
|
|
|
|
}, {
|
|
|
|
title: '部门',
|
|
|
|
key: 'department',
|
|
|
|
}, {
|
|
|
|
title: '销售人员',
|
|
|
|
key: 'sale',
|
|
|
|
}, {
|
|
|
|
title: '客户名称',
|
|
|
|
key: 'customer',
|
|
|
|
}, {
|
|
|
|
title: '项目名称',
|
|
|
|
key: 'item',
|
|
|
|
}, {
|
|
|
|
title: '合同金额',
|
|
|
|
key: 'amount',
|
|
|
|
}, {
|
|
|
|
title: '实际业绩',
|
|
|
|
key: 'realPerformance',
|
|
|
|
}, {
|
|
|
|
title: '考核业绩',
|
|
|
|
key: 'assessmentPerformance',
|
|
|
|
}, {
|
|
|
|
title: '价格是否特批',
|
|
|
|
key: 'isApproval',
|
|
|
|
}, {
|
|
|
|
title: '特批折算比例',
|
|
|
|
key: 'approvalProp',
|
|
|
|
}, {
|
|
|
|
title: '预支提成及委外费用',
|
|
|
|
key: 'cost',
|
|
|
|
}, {
|
|
|
|
title: '业务线',
|
|
|
|
key: 'serviceLine',
|
|
|
|
}, {
|
|
|
|
title: '客户类型',
|
|
|
|
key: 'cusType',
|
|
|
|
}, {
|
|
|
|
title: '行业',
|
|
|
|
key: 'industry',
|
|
|
|
}, {
|
|
|
|
title: '信息来源',
|
|
|
|
key: 'source',
|
|
|
|
}, {
|
|
|
|
title: '项目类型',
|
|
|
|
key: 'itemType',
|
|
|
|
}, {
|
|
|
|
title: '客户省份',
|
|
|
|
key: 'cusProvince',
|
|
|
|
}, {
|
|
|
|
title: '客户属性',
|
|
|
|
key: 'cusAttribute',
|
|
|
|
}, {
|
|
|
|
title: '复购次数',
|
|
|
|
key: 'repurchaseCount',
|
|
|
|
}, {
|
|
|
|
title: '是否可复制的业务路径',
|
|
|
|
key: 'reproducible',
|
|
|
|
}, {
|
|
|
|
title: '省外业务',
|
|
|
|
key: 'outProvince',
|
|
|
|
}, {
|
|
|
|
title: '复购业务',
|
|
|
|
key: 'repurchase',
|
|
|
|
}, {
|
|
|
|
title: '可复制的业务路径',
|
|
|
|
key: 'isreproduce',
|
|
|
|
}]
|
|
|
|
const { utils: { simpleExcelDown } } = ctx.app.fs;
|
|
|
|
let exportData = []
|
|
|
|
for (let { dataValues: item } of dataList) {
|
|
|
|
//item.isApproval = JSON.stringify(item.isApproval) === 'null' ? '-' : item.isApproval ? '是' : '否';
|
|
|
|
//item.reproducible = JSON.stringify(item.reproducible) === 'null' ? '-' : item.reproducible ? '是' : '否';
|
|
|
|
exportData.push(item)
|
|
|
|
}
|
|
|
|
const fileName = `业绩明细表_${moment().format('YYYYMMDDHHmmss')}` + '.xlsx'
|
|
|
|
const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName })
|
|
|
|
const fileData = fs.readFileSync(filePath);
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.set('Content-Type', 'application/x-xls');
|
|
|
|
ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
|
|
|
|
ctx.body = fileData;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 查询明细表已存在编号数据,回款、合同、开票通用
|
|
|
|
* @param {*} ctx ctx ctx.query:{tableModel表模型名}
|
|
|
|
*/
|
|
|
|
async function getReceivedNumbers(ctx) {
|
|
|
|
let errorMsg = { name: 'FindError', message: '查询失败' };
|
|
|
|
try {
|
|
|
|
const { tableModel } = ctx.query;
|
|
|
|
const models = ctx.fs.dc.models;
|
|
|
|
let modelName = tableModel || 'ReceivableDetail'
|
|
|
|
let list = await models[modelName].findAll({//查编号
|
|
|
|
attributes: ['number']
|
|
|
|
});
|
|
|
|
ctx.status = 200
|
|
|
|
ctx.body = list;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = errorMsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 导入明细表数据,回款、合同、开票通用
|
|
|
|
* @param {*} ctx ctx ctx.query:{tableModel表模型名}
|
|
|
|
*/
|
|
|
|
async function importBackDetails(ctx) {
|
|
|
|
let errorMsg = { message: '导入失败' };
|
|
|
|
const transaction = await ctx.fs.dc.orm.transaction();
|
|
|
|
try {
|
|
|
|
const models = ctx.fs.dc.models;
|
|
|
|
const data = ctx.request.body;
|
|
|
|
const { tableModel } = ctx.query;
|
|
|
|
let modelName = tableModel || 'ReceivableDetail'
|
|
|
|
let addArr = [];
|
|
|
|
let dataList = await models[modelName].findAll({//查编号
|
|
|
|
attributes: ['number']
|
|
|
|
});
|
|
|
|
data.map(d => {
|
|
|
|
let exist = dataList.find(m => m.dataValues.number == d.number);
|
|
|
|
if (!exist) {
|
|
|
|
addArr.push(d);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
//只处理新增的
|
|
|
|
if (addArr.length) {
|
|
|
|
await models[modelName].bulkCreate(addArr, { transaction });
|
|
|
|
}
|
|
|
|
await transaction.commit();
|
|
|
|
ctx.status = 204;
|
|
|
|
} catch (error) {
|
|
|
|
await transaction.rollback();
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = errorMsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function importAchieveDetails(ctx) {
|
|
|
|
let errorMsg = { message: '导入业绩明细失败' };
|
|
|
|
const transaction = await ctx.fs.dc.orm.transaction();
|
|
|
|
try {
|
|
|
|
const models = ctx.fs.dc.models;
|
|
|
|
const data = ctx.request.body;
|
|
|
|
//业绩明细 没有唯一标识,前端校验后,导啥存啥
|
|
|
|
if (data.length) {
|
|
|
|
await models.PerformanceDetail.bulkCreate(data);
|
|
|
|
}
|
|
|
|
await transaction.commit();
|
|
|
|
ctx.status = 204;
|
|
|
|
} catch (error) {
|
|
|
|
await transaction.rollback();
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = errorMsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 查询合同明细表数据
|
|
|
|
* @param {*} ctx ctx ctx.query:{keywordTarget-关键字项、keyword-关键字内容、limit-页宽, page-页码}
|
|
|
|
*/
|
|
|
|
async function getContractDetail(ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { keywordTarget, keyword, limit, page } = ctx.query;
|
|
|
|
const where = {};
|
|
|
|
if (keywordTarget && keyword) {
|
|
|
|
where[keywordTarget] = { $iLike: `%${keyword}%` };
|
|
|
|
}
|
|
|
|
let contractDetail = await models.ContractDetail.findAndCountAll({
|
|
|
|
attributes: { exclude: ['iscopy', 'hiredate', 'regularDate'] },
|
|
|
|
where: where,
|
|
|
|
offset: Number(page) * Number(limit),
|
|
|
|
limit: Number(limit),
|
|
|
|
order: [['id', 'ASC']]
|
|
|
|
});
|
|
|
|
ctx.status = 200
|
|
|
|
ctx.body = contractDetail;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = { name: 'FindError', message: '查询合同明细表数据失败' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 查询开票明细表数据
|
|
|
|
* @param {*} ctx ctx ctx.query:{keywordTarget-关键字项、keyword-关键字内容、limit-页宽, page-页码}
|
|
|
|
*/
|
|
|
|
async function getInvoicingDetail(ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { keywordTarget, keyword, limit, page } = ctx.query;
|
|
|
|
const where = {};
|
|
|
|
if (keywordTarget && keyword) {
|
|
|
|
where[keywordTarget] = { $iLike: `%${keyword}%` };
|
|
|
|
}
|
|
|
|
let invoiceDetail = await models.InvoiceDetail.findAndCountAll({
|
|
|
|
where: where,
|
|
|
|
offset: Number(page) * Number(limit),
|
|
|
|
limit: Number(limit),
|
|
|
|
order: [['id', 'ASC']]
|
|
|
|
});
|
|
|
|
ctx.status = 200
|
|
|
|
ctx.body = invoiceDetail;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = { name: 'FindError', message: '查询开票明细表数据失败' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 导出合同明细表数据
|
|
|
|
*/
|
|
|
|
async function exportContractDetail(ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
let exportData = await models.ContractDetail.findAll({
|
|
|
|
attributes: { exclude: ['iscopy', 'hiredate', 'regularDate'] },
|
|
|
|
order: [['id', 'ASC']]
|
|
|
|
});
|
|
|
|
const { utils: { simpleExcelDown, contractDetailsColumnKeys } } = ctx.app.fs;
|
|
|
|
let header = [];
|
|
|
|
Object.keys(contractDetailsColumnKeys).map(key => {
|
|
|
|
header.push({ title: contractDetailsColumnKeys[key], key: key });
|
|
|
|
})
|
|
|
|
const fileName = `合同明细表_${moment().format('YYYYMMDDHHmmss')}` + '.xlsx'
|
|
|
|
const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName, needIndexCell: false })
|
|
|
|
const fileData = fs.readFileSync(filePath);
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.set('Content-Type', 'application/x-xls');
|
|
|
|
ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
|
|
|
|
ctx.body = fileData;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = { name: 'ExportAllError', message: '导出合同明细表数据失败' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 导出开票明细表数据
|
|
|
|
*/
|
|
|
|
async function exportInvoicingDetail(ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
let exportData = await models.InvoiceDetail.findAll({
|
|
|
|
order: [['id', 'ASC']]
|
|
|
|
});
|
|
|
|
const { utils: { simpleExcelDown, invoicingDetailsColumnKeys } } = ctx.app.fs;
|
|
|
|
let header = [];
|
|
|
|
Object.keys(invoicingDetailsColumnKeys).map(key => {
|
|
|
|
header.push({ title: invoicingDetailsColumnKeys[key], key: key });
|
|
|
|
})
|
|
|
|
const fileName = `开票明细表_${moment().format('YYYYMMDDHHmmss')}` + '.xlsx'
|
|
|
|
const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName, needIndexCell: false })
|
|
|
|
const fileData = fs.readFileSync(filePath);
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.set('Content-Type', 'application/x-xls');
|
|
|
|
ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
|
|
|
|
ctx.body = fileData;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = { name: 'ExportAllError', message: '导出开票明细表数据失败' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = {
|
|
|
|
getReceivedDetail,//回款
|
|
|
|
getAchievementDetail,//业绩
|
|
|
|
|
|
|
|
getReceivedNumbers,//查询回款、合同、开票明细表 已有的所有编号
|
|
|
|
importBackDetails,//导入回款、合同、开票明细
|
|
|
|
importAchieveDetails,//导入业绩明细
|
|
|
|
getContractDetail,
|
|
|
|
getInvoicingDetail,
|
|
|
|
exportContractDetail,
|
|
|
|
exportInvoicingDetail
|
|
|
|
}
|