feat: 导出盘点表初步完成

This commit is contained in:
louis 2024-03-07 16:28:03 +08:00
parent 384b11bd4c
commit 96135fffd1
6 changed files with 222 additions and 31 deletions

View File

@ -33,3 +33,4 @@ SMTP_PORT = 465
SMTP_USER = nest_admin@163.com
SMTP_PASS = VIPLLOIPMETTROYU

View File

@ -33,9 +33,9 @@ export class MaterialsInventoryController {
@ApiOperation({ summary: '导出原材料盘点表' })
@Perm(permissions.EXPORT)
async exportMaterialsInventoryCheck(
@Param() dto: MaterialsInventoryExportDto,
@Query() dto: MaterialsInventoryExportDto,
@Res() res: FastifyReply
): Promise<any> {
): Promise<void> {
await this.miService.exportMaterialsInventoryCheck(dto, res);
}

View File

@ -32,6 +32,7 @@ export class MaterialsInventoryExportDto {
@ApiProperty({ description: '导出时间YYYY-MM-DD' })
@IsOptional()
@IsArray()
@Transform(params => {
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
const date = params.value;

View File

@ -8,12 +8,15 @@ import { MaterialsInOutController } from './in_out/materials_in_out.controller';
import { MaterialsInOutService } from './in_out/materials_in_out.service';
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
import { ParamConfigModule } from '../system/param-config/param-config.module';
import { ProjectModule } from '../project/project.module';
import { ProjectEntity } from '../project/project.entity';
@Module({
imports: [
TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]),
TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity,ProjectEntity]),
ParamConfigModule,
StorageModule
StorageModule,
ProjectModule
],
controllers: [MaterialsInventoryController, MaterialsInOutController],
providers: [MaterialsInventoryService, MaterialsInOutService]

View File

@ -12,48 +12,234 @@ import { Pagination } from '~/helper/paginate/pagination';
import { FastifyReply } from 'fastify';
import { paginate } from '~/helper/paginate';
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()
export class MaterialsInventoryService {
const;
constructor(
@InjectEntityManager() private entityManager: EntityManager,
@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 sheet = workbook.addWorksheet('CDKEY');
// 设置表头
sheet.columns = [
{ header: '名称', key: 'name', width: 32 },
{ header: '创建人', key: 'create_user', width: 32 },
{ header: '创建时间', key: 'create_time', width: 32 }
];
const data = [
{
name: '名称',
create_user: 'admin',
create_time: '2023-05-18 12:00:00'
let projects: ProjectEntity[] = [];
if (projectId) {
projects = [await this.projectRepository.findOneBy({ id: projectId })];
}
// 生成数据
const sqb = this.materialsInOutRepository
.createQueryBuilder('mio')
.leftJoin('mio.project', 'project')
.leftJoin('mio.product', 'product')
.leftJoin('product.unit', 'unit')
.leftJoin('product.company', 'company')
.addSelect(['project.id','project.name', 'product.name', 'unit.label', 'company.name'])
.where(fieldSearch({ time }))
.andWhere('mio.isDelete = 0');
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 = [
'库存编号',
'公司名称',
'产品名称',
'单位',
'库存数量',
'单价',
'金额',
'',
'',
'',
'',
'',
'',
'',
'',
'结存数量',
'单价',
'金额',
'经办人',
'领料单号',
'备注'
];
data.forEach(item => {
sheet.addRow({
name: item.name,
create_user: item.create_user,
create_time: item.create_time
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();
res
.header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
.header(
'Content-Disposition',
`attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xlsx')}"`
`attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xls')}"`
)
.send(buffer);
}