feat: 导出盘点表初步完成
This commit is contained in:
parent
384b11bd4c
commit
96135fffd1
|
@ -32,4 +32,5 @@ SMTP_HOST = smtp.163.com
|
||||||
SMTP_PORT = 465
|
SMTP_PORT = 465
|
||||||
SMTP_USER = nest_admin@163.com
|
SMTP_USER = nest_admin@163.com
|
||||||
SMTP_PASS = VIPLLOIPMETTROYU
|
SMTP_PASS = VIPLLOIPMETTROYU
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ module.exports = {
|
||||||
autorestart: true,
|
autorestart: true,
|
||||||
exec_mode: 'cluster',
|
exec_mode: 'cluster',
|
||||||
watch: false,
|
watch: false,
|
||||||
instances: cpuLen,
|
instances: cpuLen,
|
||||||
max_memory_restart: '520M',
|
max_memory_restart: '520M',
|
||||||
args: '',
|
args: '',
|
||||||
env: {
|
env: {
|
||||||
|
|
|
@ -33,9 +33,9 @@ export class MaterialsInventoryController {
|
||||||
@ApiOperation({ summary: '导出原材料盘点表' })
|
@ApiOperation({ summary: '导出原材料盘点表' })
|
||||||
@Perm(permissions.EXPORT)
|
@Perm(permissions.EXPORT)
|
||||||
async exportMaterialsInventoryCheck(
|
async exportMaterialsInventoryCheck(
|
||||||
@Param() dto: MaterialsInventoryExportDto,
|
@Query() dto: MaterialsInventoryExportDto,
|
||||||
@Res() res: FastifyReply
|
@Res() res: FastifyReply
|
||||||
): Promise<any> {
|
): Promise<void> {
|
||||||
await this.miService.exportMaterialsInventoryCheck(dto, res);
|
await this.miService.exportMaterialsInventoryCheck(dto, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ export class MaterialsInventoryExportDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
projectId: number;
|
projectId: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '导出时间YYYY-MM-DD' })
|
@ApiProperty({ description: '导出时间YYYY-MM-DD' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
@Transform(params => {
|
@Transform(params => {
|
||||||
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
|
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
|
||||||
const date = params.value;
|
const date = params.value;
|
||||||
|
|
|
@ -8,12 +8,15 @@ import { MaterialsInOutController } from './in_out/materials_in_out.controller';
|
||||||
import { MaterialsInOutService } from './in_out/materials_in_out.service';
|
import { MaterialsInOutService } from './in_out/materials_in_out.service';
|
||||||
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
||||||
import { ParamConfigModule } from '../system/param-config/param-config.module';
|
import { ParamConfigModule } from '../system/param-config/param-config.module';
|
||||||
|
import { ProjectModule } from '../project/project.module';
|
||||||
|
import { ProjectEntity } from '../project/project.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]),
|
TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity,ProjectEntity]),
|
||||||
ParamConfigModule,
|
ParamConfigModule,
|
||||||
StorageModule
|
StorageModule,
|
||||||
|
ProjectModule
|
||||||
],
|
],
|
||||||
controllers: [MaterialsInventoryController, MaterialsInOutController],
|
controllers: [MaterialsInventoryController, MaterialsInOutController],
|
||||||
providers: [MaterialsInventoryService, MaterialsInOutService]
|
providers: [MaterialsInventoryService, MaterialsInOutService]
|
||||||
|
|
|
@ -12,48 +12,234 @@ import { Pagination } from '~/helper/paginate/pagination';
|
||||||
import { FastifyReply } from 'fastify';
|
import { FastifyReply } from 'fastify';
|
||||||
import { paginate } from '~/helper/paginate';
|
import { paginate } from '~/helper/paginate';
|
||||||
import * as ExcelJS from 'exceljs';
|
import * as ExcelJS from 'exceljs';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
||||||
|
import { fieldSearch } from '~/shared/database/field-search';
|
||||||
|
import { groupBy, uniqBy } from 'lodash';
|
||||||
|
import { MaterialsInOrOutEnum } from '~/constants/enum';
|
||||||
|
import { ProjectEntity } from '../project/project.entity';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MaterialsInventoryService {
|
export class MaterialsInventoryService {
|
||||||
|
const;
|
||||||
constructor(
|
constructor(
|
||||||
@InjectEntityManager() private entityManager: EntityManager,
|
@InjectEntityManager() private entityManager: EntityManager,
|
||||||
@InjectRepository(MaterialsInventoryEntity)
|
@InjectRepository(MaterialsInventoryEntity)
|
||||||
private materialsInventoryRepository: Repository<MaterialsInventoryEntity>
|
private materialsInventoryRepository: Repository<MaterialsInventoryEntity>,
|
||||||
|
@InjectRepository(MaterialsInOutEntity)
|
||||||
|
private materialsInOutRepository: Repository<MaterialsInOutEntity>,
|
||||||
|
@InjectRepository(ProjectEntity)
|
||||||
|
private projectRepository: Repository<ProjectEntity>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出原材料盘点表
|
* 导出原材料盘点表
|
||||||
*/
|
*/
|
||||||
async exportMaterialsInventoryCheck(dto: MaterialsInventoryExportDto, res: FastifyReply) {
|
async exportMaterialsInventoryCheck(
|
||||||
|
{ time, projectId }: MaterialsInventoryExportDto,
|
||||||
|
res: FastifyReply
|
||||||
|
): Promise<void> {
|
||||||
|
const ROW_HEIGHT = 20;
|
||||||
|
const HEADER_FONT_SIZE = 18;
|
||||||
const workbook = new ExcelJS.Workbook();
|
const workbook = new ExcelJS.Workbook();
|
||||||
// 创建一个工作表
|
let projects: ProjectEntity[] = [];
|
||||||
const sheet = workbook.addWorksheet('CDKEY');
|
if (projectId) {
|
||||||
// 设置表头
|
projects = [await this.projectRepository.findOneBy({ id: projectId })];
|
||||||
sheet.columns = [
|
}
|
||||||
{ header: '名称', key: 'name', width: 32 },
|
// 生成数据
|
||||||
{ header: '创建人', key: 'create_user', width: 32 },
|
const sqb = this.materialsInOutRepository
|
||||||
{ header: '创建时间', key: 'create_time', width: 32 }
|
.createQueryBuilder('mio')
|
||||||
];
|
.leftJoin('mio.project', 'project')
|
||||||
const data = [
|
.leftJoin('mio.product', 'product')
|
||||||
{
|
.leftJoin('product.unit', 'unit')
|
||||||
name: '名称',
|
.leftJoin('product.company', 'company')
|
||||||
create_user: 'admin',
|
.addSelect(['project.id','project.name', 'product.name', 'unit.label', 'company.name'])
|
||||||
create_time: '2023-05-18 12:00:00'
|
.where(fieldSearch({ time }))
|
||||||
}
|
.andWhere('mio.isDelete = 0');
|
||||||
];
|
|
||||||
data.forEach(item => {
|
|
||||||
sheet.addRow({
|
|
||||||
name: item.name,
|
|
||||||
create_user: item.create_user,
|
|
||||||
create_time: item.create_time
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (projectId) {
|
||||||
|
sqb.andWhere('project.id = :projectId', { projectId });
|
||||||
|
}
|
||||||
|
const data = await sqb.addOrderBy('mio.time', 'DESC').getMany();
|
||||||
|
if (!projectId) {
|
||||||
|
projects = uniqBy(
|
||||||
|
data.map(item => item.project),
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const project of projects) {
|
||||||
|
const currentProjectData = data.filter(item => item.projectId === project.id);
|
||||||
|
const sheet = workbook.addWorksheet(project.name);
|
||||||
|
sheet.mergeCells('A1:T1');
|
||||||
|
// 设置标题
|
||||||
|
sheet.getCell('A1').value = '山东矿机华信智能科技有限公司原材料盘点表';
|
||||||
|
|
||||||
|
// 设置日期
|
||||||
|
sheet.mergeCells('A2:B2');
|
||||||
|
sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`;
|
||||||
|
// 设置表头
|
||||||
|
const headers = [
|
||||||
|
'库存编号',
|
||||||
|
'公司名称',
|
||||||
|
'产品名称',
|
||||||
|
'单位',
|
||||||
|
'库存数量',
|
||||||
|
'单价',
|
||||||
|
'金额',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'结存数量',
|
||||||
|
'单价',
|
||||||
|
'金额',
|
||||||
|
'经办人',
|
||||||
|
'领料单号',
|
||||||
|
'备注'
|
||||||
|
];
|
||||||
|
sheet.addRow(headers);
|
||||||
|
sheet.addRow([
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'入库时间',
|
||||||
|
'数量',
|
||||||
|
'单价',
|
||||||
|
'金额',
|
||||||
|
'出库时间',
|
||||||
|
'数量',
|
||||||
|
'单价',
|
||||||
|
'金额',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
''
|
||||||
|
]);
|
||||||
|
for (let i = 1; i <= 7; i++) {
|
||||||
|
sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`);
|
||||||
|
}
|
||||||
|
// 入库
|
||||||
|
sheet.mergeCells('H3:K3');
|
||||||
|
sheet.getCell('H3').value = '入库';
|
||||||
|
|
||||||
|
// 出库
|
||||||
|
sheet.mergeCells('L3:O3');
|
||||||
|
sheet.getCell('L3').value = '出库';
|
||||||
|
|
||||||
|
for (let i = 8; i <= 15; i++) {
|
||||||
|
sheet.getCell(`${String.fromCharCode(64 + i)}4`).style.fill = {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: 'solid',
|
||||||
|
fgColor: { argb: 'FFFFC000' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 16; i <= 21; i++) {
|
||||||
|
sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 固定信息样式设定
|
||||||
|
sheet.eachRow((row, index) => {
|
||||||
|
row.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
row.font = { bold: true };
|
||||||
|
row.height = ROW_HEIGHT;
|
||||||
|
if (index >= 3) {
|
||||||
|
row.eachCell(cell => {
|
||||||
|
cell.border = {
|
||||||
|
top: { style: 'thin' },
|
||||||
|
left: { style: 'thin' },
|
||||||
|
bottom: { style: 'thin' },
|
||||||
|
right: { style: 'thin' }
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupedData = groupBy<MaterialsInOutEntity>(currentProjectData, 'inventoryNumber');
|
||||||
|
let number = 0;
|
||||||
|
for (const key in groupedData) {
|
||||||
|
// 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。
|
||||||
|
const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In);
|
||||||
|
const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out);
|
||||||
|
number++;
|
||||||
|
sheet.addRow([
|
||||||
|
`${inRecord.inventoryNumber}`,
|
||||||
|
inRecord.product.company.name,
|
||||||
|
inRecord.product.name,
|
||||||
|
inRecord.product.unit.label,
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
inRecord.time,
|
||||||
|
inRecord.quantity,
|
||||||
|
parseFloat(`${inRecord.unitPrice || 0}`),
|
||||||
|
parseFloat(`${inRecord.amount || 0}`),
|
||||||
|
outRecord?.time || '',
|
||||||
|
outRecord?.quantity || '',
|
||||||
|
parseFloat(`${outRecord.unitPrice || 0}`),
|
||||||
|
parseFloat(`${outRecord.amount || 0}`),
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
outRecord?.agent,
|
||||||
|
outRecord?.issuanceNumber,
|
||||||
|
outRecord?.remark
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
sheet.getCell('A1').font = { size: HEADER_FONT_SIZE };
|
||||||
|
|
||||||
|
// 固定信息样式设定
|
||||||
|
sheet.eachRow((row, index) => {
|
||||||
|
if (index >= 5) {
|
||||||
|
row.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
row.height = ROW_HEIGHT;
|
||||||
|
row.eachCell(cell => {
|
||||||
|
cell.border = {
|
||||||
|
top: { style: 'thin' },
|
||||||
|
left: { style: 'thin' },
|
||||||
|
bottom: { style: 'thin' },
|
||||||
|
right: { style: 'thin' }
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sheet.columns.forEach((column, index: number) => {
|
||||||
|
let maxColumnLength = 0;
|
||||||
|
const autoWidth = ['B', 'C', 'U'];
|
||||||
|
if (String.fromCharCode(65 + index) === 'B') maxColumnLength = 20;
|
||||||
|
if (autoWidth.includes(String.fromCharCode(65 + index))) {
|
||||||
|
column.eachCell({ includeEmpty: true }, (cell, rowIndex) => {
|
||||||
|
if (rowIndex >= 5) {
|
||||||
|
const columnLength = `${cell.value || ''}`.length;
|
||||||
|
if (columnLength > maxColumnLength) {
|
||||||
|
maxColumnLength = columnLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
column.width = maxColumnLength < 12 ? 12 : maxColumnLength; // Minimum width of 10
|
||||||
|
} else {
|
||||||
|
column.width = 12;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//读取buffer进行传输
|
||||||
const buffer = await workbook.xlsx.writeBuffer();
|
const buffer = await workbook.xlsx.writeBuffer();
|
||||||
res
|
res
|
||||||
.header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
.header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||||
.header(
|
.header(
|
||||||
'Content-Disposition',
|
'Content-Disposition',
|
||||||
`attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xlsx')}"`
|
`attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xls')}"`
|
||||||
)
|
)
|
||||||
.send(buffer);
|
.send(buffer);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue