feat: 导出盘点表初步完成
This commit is contained in:
parent
384b11bd4c
commit
96135fffd1
|
@ -33,3 +33,4 @@ SMTP_PORT = 465
|
|||
SMTP_USER = nest_admin@163.com
|
||||
SMTP_PASS = VIPLLOIPMETTROYU
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ export class MaterialsInventoryExportDto {
|
|||
|
||||
@ApiProperty({ description: '导出时间YYYY-MM-DD' })
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@Transform(params => {
|
||||
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
|
||||
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 { 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]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue