feat: develop domain
This commit is contained in:
parent
cd32cc1ac0
commit
f958ab7af9
|
@ -17,7 +17,6 @@ import { DomainEntity } from './domain.entity';
|
||||||
import { DomainDto, DomainQueryDto, DomainUpdateDto } from './domain.dto';
|
import { DomainDto, DomainQueryDto, DomainUpdateDto } from './domain.dto';
|
||||||
import { IdParam } from '~/common/decorators/id-param.decorator';
|
import { IdParam } from '~/common/decorators/id-param.decorator';
|
||||||
export const permissions = definePermission('app:domain', {
|
export const permissions = definePermission('app:domain', {
|
||||||
LIST: 'list',
|
|
||||||
CREATE: 'create',
|
CREATE: 'create',
|
||||||
READ: 'read',
|
READ: 'read',
|
||||||
UPDATE: 'update',
|
UPDATE: 'update',
|
||||||
|
@ -33,7 +32,6 @@ export class DomainController {
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: '获取域列表' })
|
@ApiOperation({ summary: '获取域列表' })
|
||||||
@ApiResult({ type: [DomainEntity], isPage: true })
|
@ApiResult({ type: [DomainEntity], isPage: true })
|
||||||
@Perm(permissions.LIST)
|
|
||||||
async list(@Query() dto: DomainQueryDto) {
|
async list(@Query() dto: DomainQueryDto) {
|
||||||
return this.domainService.findAll(dto);
|
return this.domainService.findAll(dto);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Post, Put, Query, Res } from '@nestjs/common';
|
||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { ApiResult } from '~/common/decorators/api-result.decorator';
|
import { ApiResult } from '~/common/decorators/api-result.decorator';
|
||||||
import { IdParam } from '~/common/decorators/id-param.decorator';
|
import { IdParam } from '~/common/decorators/id-param.decorator';
|
||||||
|
@ -9,23 +9,38 @@ import { definePermission, Perm } from '~/modules/auth/decorators/permission.dec
|
||||||
import {
|
import {
|
||||||
MaterialsInOutQueryDto,
|
MaterialsInOutQueryDto,
|
||||||
MaterialsInOutDto,
|
MaterialsInOutDto,
|
||||||
MaterialsInOutUpdateDto
|
MaterialsInOutUpdateDto,
|
||||||
|
MaterialsInOutExportDto
|
||||||
} from './materials_in_out.dto';
|
} from './materials_in_out.dto';
|
||||||
import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator';
|
import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator';
|
||||||
|
import { FastifyReply } from 'fastify';
|
||||||
export const permissions = definePermission('materials_inventory:history_in_out', {
|
export const permissions = definePermission('materials_inventory:history_in_out', {
|
||||||
LIST: 'list',
|
LIST: 'list',
|
||||||
CREATE: 'create',
|
CREATE: 'create',
|
||||||
READ: 'read',
|
READ: 'read',
|
||||||
UPDATE: 'update',
|
UPDATE: 'update',
|
||||||
DELETE: 'delete'
|
DELETE: 'delete',
|
||||||
|
EXPORT: 'export'
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
@ApiTags('Materials In Out History - 原材料出入库记录')
|
@ApiTags('Materials In Out History - 原材料出入库记录')
|
||||||
@ApiSecurityAuth()
|
@ApiSecurityAuth()
|
||||||
@Controller('materials-in-out')
|
@Controller('materials-in-out')
|
||||||
export class MaterialsInOutController {
|
export class MaterialsInOutController {
|
||||||
constructor(private materialsInOutService: MaterialsInOutService) {}
|
constructor(private materialsInOutService: MaterialsInOutService) { }
|
||||||
|
|
||||||
|
@Get('export')
|
||||||
|
@ApiOperation({ summary: '导出原材料盘点表' })
|
||||||
|
@Perm(permissions.EXPORT)
|
||||||
|
async exportMaterialsInventoryCheck(
|
||||||
|
@Domain() domain: SkDomain,
|
||||||
|
@Query() dto: MaterialsInOutExportDto,
|
||||||
|
@Res() res: FastifyReply
|
||||||
|
): Promise<void> {
|
||||||
|
await this.materialsInOutService.exportMaterialsInventoryCheck({ ...dto, domain }, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: '获取原材料出入库记录列表' })
|
@ApiOperation({ summary: '获取原材料出入库记录列表' })
|
||||||
@ApiResult({ type: [MaterialsInOutEntity], isPage: true })
|
@ApiResult({ type: [MaterialsInOutEntity], isPage: true })
|
||||||
|
|
|
@ -112,9 +112,9 @@ export class MaterialsInOutQueryDto extends IntersectionType(
|
||||||
// 开始和结束时间用的是一天的开始和一天的结束的时分秒
|
// 开始和结束时间用的是一天的开始和一天的结束的时分秒
|
||||||
return params.value
|
return params.value
|
||||||
? [
|
? [
|
||||||
params.value[0] ? `${formatToDate(params.value[0], 'YYYY-MM-DD')} 00:00:00` : null,
|
params.value[0] ? `${formatToDate(params.value[0], 'YYYY-MM-DD')} 00:00:00` : null,
|
||||||
params.value[1] ? `${formatToDate(params.value[1], 'YYYY-MM-DD')} 23:59:59` : null
|
params.value[1] ? `${formatToDate(params.value[1], 'YYYY-MM-DD')} 23:59:59` : null
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
})
|
})
|
||||||
time?: string[];
|
time?: string[];
|
||||||
|
@ -159,3 +159,41 @@ export class MaterialsInOutQueryDto extends IntersectionType(
|
||||||
@ApiProperty({ description: '是否是用于创建出库记录' })
|
@ApiProperty({ description: '是否是用于创建出库记录' })
|
||||||
isCreateOut?: boolean;
|
isCreateOut?: boolean;
|
||||||
}
|
}
|
||||||
|
export class MaterialsInOutExportDto extends IntersectionType(
|
||||||
|
|
||||||
|
DomainType
|
||||||
|
) {
|
||||||
|
|
||||||
|
@ApiProperty({ description: '导出时间YYYY-MM-DD' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@Transform(params => {
|
||||||
|
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
|
||||||
|
const date = params.value;
|
||||||
|
return [
|
||||||
|
date ? `${date[0]} 00:00:00` : null,
|
||||||
|
date ? `${date[1]} 23:59:59` : null
|
||||||
|
];
|
||||||
|
})
|
||||||
|
time?: string[];
|
||||||
|
|
||||||
|
@ApiProperty({ description: '导出文件名' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
filename?: string
|
||||||
|
|
||||||
|
@ApiProperty({ description: '入库或出库 0:入库 1:出库' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(MaterialsInOrOutEnum)
|
||||||
|
inOrOut?: MaterialsInOrOutEnum;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '产品名称' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
product?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '经办人' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
agent?: string;
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { EntityManager, In, Repository } from 'typeorm';
|
import { Between, EntityManager, In, Repository } from 'typeorm';
|
||||||
import { Pagination } from '~/helper/paginate/pagination';
|
import { Pagination } from '~/helper/paginate/pagination';
|
||||||
import { BusinessException } from '~/common/exceptions/biz.exception';
|
import { BusinessException } from '~/common/exceptions/biz.exception';
|
||||||
import { ErrorEnum } from '~/constants/error-code.constant';
|
import { ErrorEnum } from '~/constants/error-code.constant';
|
||||||
|
@ -10,7 +10,8 @@ import { Storage } from '~/modules/tools/storage/storage.entity';
|
||||||
import {
|
import {
|
||||||
MaterialsInOutQueryDto,
|
MaterialsInOutQueryDto,
|
||||||
MaterialsInOutDto,
|
MaterialsInOutDto,
|
||||||
MaterialsInOutUpdateDto
|
MaterialsInOutUpdateDto,
|
||||||
|
MaterialsInOutExportDto
|
||||||
} from './materials_in_out.dto';
|
} from './materials_in_out.dto';
|
||||||
import { MaterialsInOutEntity } from './materials_in_out.entity';
|
import { MaterialsInOutEntity } from './materials_in_out.entity';
|
||||||
import { fieldSearch } from '~/shared/database/field-search';
|
import { fieldSearch } from '~/shared/database/field-search';
|
||||||
|
@ -19,7 +20,9 @@ import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum';
|
||||||
import { MaterialsInventoryEntity } from '../materials_inventory.entity';
|
import { MaterialsInventoryEntity } from '../materials_inventory.entity';
|
||||||
import { MaterialsInventoryService } from '../materials_inventory.service';
|
import { MaterialsInventoryService } from '../materials_inventory.service';
|
||||||
import { isDefined } from 'class-validator';
|
import { isDefined } from 'class-validator';
|
||||||
|
import { FastifyReply } from 'fastify';
|
||||||
|
import * as ExcelJS from 'exceljs';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MaterialsInOutService {
|
export class MaterialsInOutService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -31,7 +34,119 @@ export class MaterialsInOutService {
|
||||||
@InjectRepository(ParamConfigEntity)
|
@InjectRepository(ParamConfigEntity)
|
||||||
private paramConfigRepository: Repository<ParamConfigEntity>,
|
private paramConfigRepository: Repository<ParamConfigEntity>,
|
||||||
private materialsInventoryService: MaterialsInventoryService
|
private materialsInventoryService: MaterialsInventoryService
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出出入库记录表
|
||||||
|
*/
|
||||||
|
async exportMaterialsInventoryCheck(
|
||||||
|
{ time, domain, filename, ...ext }: MaterialsInOutExportDto,
|
||||||
|
res: FastifyReply
|
||||||
|
): Promise<void> {
|
||||||
|
const ROW_HEIGHT = 20;
|
||||||
|
const HEADER_FONT_SIZE = 18;
|
||||||
|
|
||||||
|
// 生成数据
|
||||||
|
const sqb = this.buildSearchQuery()
|
||||||
|
.where(fieldSearch(ext))
|
||||||
|
.andWhere({
|
||||||
|
time: Between(time[0], time[1])
|
||||||
|
})
|
||||||
|
.andWhere('materialsInOut.isDelete = 0');
|
||||||
|
const data = await sqb.addOrderBy('materialsInOut.time', 'DESC').getMany();
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
const sheet = workbook.addWorksheet('出入库记录');
|
||||||
|
sheet.mergeCells('A1:T1');
|
||||||
|
// 设置标题
|
||||||
|
sheet.getCell('A1').value = '山东矿机华信智能科技有限公司出入库记录表';
|
||||||
|
// 设置日期
|
||||||
|
sheet.mergeCells('A2:C2');
|
||||||
|
sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月D日')}-${dayjs(time[1]).format('YYYY年M月D日')}`;
|
||||||
|
// 设置表头
|
||||||
|
const headers = [
|
||||||
|
'出入库单号',
|
||||||
|
'出入库',
|
||||||
|
'项目',
|
||||||
|
'公司名称',
|
||||||
|
'产品名称',
|
||||||
|
'规格型号',
|
||||||
|
'时间',
|
||||||
|
'单位',
|
||||||
|
'数量',
|
||||||
|
'单价',
|
||||||
|
'金额',
|
||||||
|
'经办人',
|
||||||
|
'领料单号',
|
||||||
|
'备注'
|
||||||
|
];
|
||||||
|
sheet.addRow(headers);
|
||||||
|
for (let index = 0; index < data.length; index++) {
|
||||||
|
const record = data[index];
|
||||||
|
sheet.addRow([
|
||||||
|
`${record.inventoryInOutNumber}`,
|
||||||
|
record.project?.name || '',
|
||||||
|
record.inOrOut === MaterialsInOrOutEnum.In ? '入库' : "出库",
|
||||||
|
record.product?.company?.name || '',
|
||||||
|
record.product?.name || '',
|
||||||
|
record.product?.productSpecification || '',
|
||||||
|
`${dayjs(record.time).format('YYYY-MM-DD HH:mm')}`,
|
||||||
|
record.product.unit.label || '',
|
||||||
|
record.quantity,
|
||||||
|
parseFloat(`${record.unitPrice || 0}`),
|
||||||
|
parseFloat(`${record.amount || 0}`),
|
||||||
|
`${record?.agent || ''}`,
|
||||||
|
record?.issuanceNumber || '',
|
||||||
|
record?.remark || ''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// 固定信息样式设定
|
||||||
|
sheet.eachRow((row, index) => {
|
||||||
|
if (index >= 3) {
|
||||||
|
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', 'S', '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="${filename}.xls"`
|
||||||
|
)
|
||||||
|
.send(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询所有出入库记录
|
* 查询所有出入库记录
|
||||||
*/
|
*/
|
||||||
|
@ -104,7 +219,8 @@ export class MaterialsInOutService {
|
||||||
position,
|
position,
|
||||||
unitPrice,
|
unitPrice,
|
||||||
quantity,
|
quantity,
|
||||||
productId
|
productId,
|
||||||
|
domain
|
||||||
} = dto;
|
} = dto;
|
||||||
inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut);
|
inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut);
|
||||||
let newRecordId;
|
let newRecordId;
|
||||||
|
@ -115,7 +231,7 @@ export class MaterialsInOutService {
|
||||||
Object.is(inOrOut, MaterialsInOrOutEnum.In)
|
Object.is(inOrOut, MaterialsInOrOutEnum.In)
|
||||||
? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService)
|
? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService)
|
||||||
: this.materialsInventoryService.outInventory.bind(this.materialsInventoryService)
|
: this.materialsInventoryService.outInventory.bind(this.materialsInventoryService)
|
||||||
)({ productId, quantity, unitPrice, projectId, inventoryId, position }, manager);
|
)({ productId, quantity, unitPrice, projectId, inventoryId, position }, manager, domain);
|
||||||
// 2.生成出入库记录
|
// 2.生成出入库记录
|
||||||
const { id } = await manager.save(MaterialsInOutEntity, {
|
const { id } = await manager.save(MaterialsInOutEntity, {
|
||||||
...this.materialsInOutRepository.create({ ...dto, inventoryId: inventoryEntity?.id }),
|
...this.materialsInOutRepository.create({ ...dto, inventoryId: inventoryEntity?.id }),
|
||||||
|
|
|
@ -45,8 +45,8 @@ export class MaterialsInventoryController {
|
||||||
@ApiOperation({ summary: '获取原材料库存列表' })
|
@ApiOperation({ summary: '获取原材料库存列表' })
|
||||||
@ApiResult({ type: [MaterialsInventoryEntity], isPage: true })
|
@ApiResult({ type: [MaterialsInventoryEntity], isPage: true })
|
||||||
@Perm(permissions.LIST)
|
@Perm(permissions.LIST)
|
||||||
async list(@Query() dto: MaterialsInventoryQueryDto) {
|
async list(@Domain() domain: SkDomain, @Query() dto: MaterialsInventoryQueryDto) {
|
||||||
return this.miService.findAll(dto);
|
return this.miService.findAll({ ...dto, domain });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|
|
@ -61,9 +61,14 @@ export class MaterialsInventoryExportDto extends DomainType {
|
||||||
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
|
// 开始和结束时间用的是一月的开始和一月的结束的时分秒
|
||||||
const date = params.value;
|
const date = params.value;
|
||||||
return [
|
return [
|
||||||
date ? `${formatToDate(dayjs(date).startOf('month'))} 00:00:00` : null,
|
date ? `${date[0]} 00:00:00` : null,
|
||||||
date ? `${formatToDate(dayjs(date).endOf('month'))} 23:59:59` : null
|
date ? `${date[1]} 23:59:59` : null
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
time?: string[];
|
time?: string[];
|
||||||
|
|
||||||
|
@ApiProperty({ description: '文件名' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
filename: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { BusinessException } from '~/common/exceptions/biz.exception';
|
||||||
import { ErrorEnum } from '~/constants/error-code.constant';
|
import { ErrorEnum } from '~/constants/error-code.constant';
|
||||||
import { ParamConfigEntity } from '../system/param-config/param-config.entity';
|
import { ParamConfigEntity } from '../system/param-config/param-config.entity';
|
||||||
import { isDefined } from 'class-validator';
|
import { isDefined } from 'class-validator';
|
||||||
|
import { DomainType } from '~/common/decorators/domain.decorator';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MaterialsInventoryService {
|
export class MaterialsInventoryService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -35,7 +36,7 @@ export class MaterialsInventoryService {
|
||||||
private projectRepository: Repository<ProjectEntity>,
|
private projectRepository: Repository<ProjectEntity>,
|
||||||
@InjectRepository(ParamConfigEntity)
|
@InjectRepository(ParamConfigEntity)
|
||||||
private paramConfigRepository: Repository<ParamConfigEntity>
|
private paramConfigRepository: Repository<ParamConfigEntity>
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出原材料盘点表
|
* 导出原材料盘点表
|
||||||
|
@ -55,7 +56,8 @@ export class MaterialsInventoryService {
|
||||||
const inventoriesInProjects = await this.materialsInventoryRepository.find({
|
const inventoriesInProjects = await this.materialsInventoryRepository.find({
|
||||||
where: {
|
where: {
|
||||||
...(projects?.length ? { projectId: In(projects.map(item => item.id)) } : null)
|
...(projects?.length ? { projectId: In(projects.map(item => item.id)) } : null)
|
||||||
}
|
},
|
||||||
|
relations: ['product', 'product.company', 'product.unit']
|
||||||
});
|
});
|
||||||
|
|
||||||
// 生成数据
|
// 生成数据
|
||||||
|
@ -86,7 +88,7 @@ export class MaterialsInventoryService {
|
||||||
const data = await sqb.addOrderBy('mio.time', 'DESC').getMany();
|
const data = await sqb.addOrderBy('mio.time', 'DESC').getMany();
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
projects = uniqBy(
|
projects = uniqBy(
|
||||||
data.map(item => item.project),
|
data.filter(item => item.inOrOut === MaterialsInOrOutEnum.Out).map(item => item.project),
|
||||||
'id'
|
'id'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,9 @@ export class MaterialsInventoryService {
|
||||||
const currentProjectInventories = inventoriesInProjects.filter(({ projectId }) =>
|
const currentProjectInventories = inventoriesInProjects.filter(({ projectId }) =>
|
||||||
Object.is(projectId, project.id)
|
Object.is(projectId, project.id)
|
||||||
);
|
);
|
||||||
const currentProjectData = data.filter(item => item.projectId === project.id);
|
const currentProjectData = data.filter(
|
||||||
|
item => item.projectId === project.id || item.inOrOut === MaterialsInOrOutEnum.Out
|
||||||
|
);
|
||||||
const currentMonthProjectData = currentProjectData.filter(item => {
|
const currentMonthProjectData = currentProjectData.filter(item => {
|
||||||
return (
|
return (
|
||||||
dayjs(item.time).isAfter(dayjs(time[0])) && dayjs(item.time).isBefore(dayjs(time[1]))
|
dayjs(item.time).isAfter(dayjs(time[0])) && dayjs(item.time).isBefore(dayjs(time[1]))
|
||||||
|
@ -110,7 +114,7 @@ export class MaterialsInventoryService {
|
||||||
sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`;
|
sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`;
|
||||||
// 设置表头
|
// 设置表头
|
||||||
const headers = [
|
const headers = [
|
||||||
'出入库单号',
|
'序号',
|
||||||
'公司名称',
|
'公司名称',
|
||||||
'产品名称',
|
'产品名称',
|
||||||
'单位',
|
'单位',
|
||||||
|
@ -128,8 +132,6 @@ export class MaterialsInventoryService {
|
||||||
'结存数量',
|
'结存数量',
|
||||||
'单价',
|
'单价',
|
||||||
'金额',
|
'金额',
|
||||||
'经办人',
|
|
||||||
'领料单号',
|
|
||||||
'备注'
|
'备注'
|
||||||
];
|
];
|
||||||
sheet.addRow(headers);
|
sheet.addRow(headers);
|
||||||
|
@ -152,8 +154,6 @@ export class MaterialsInventoryService {
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'',
|
|
||||||
'',
|
|
||||||
''
|
''
|
||||||
]);
|
]);
|
||||||
for (let i = 1; i <= 7; i++) {
|
for (let i = 1; i <= 7; i++) {
|
||||||
|
@ -175,7 +175,7 @@ export class MaterialsInventoryService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 16; i <= 21; i++) {
|
for (let i = 16; i <= 19; i++) {
|
||||||
sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`);
|
sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,24 +196,40 @@ export class MaterialsInventoryService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 根据库存Id分组
|
||||||
const groupedData = groupBy<MaterialsInOutEntity>(
|
const groupedData = groupBy<MaterialsInOutEntity>(
|
||||||
currentMonthProjectData,
|
currentMonthProjectData,
|
||||||
record => `${record.productId}-${record.unitPrice}`
|
record => record.inventoryId
|
||||||
);
|
);
|
||||||
let number = 0;
|
let number = 0;
|
||||||
const groupedInventories = groupBy(
|
const groupedInventories = groupBy(currentProjectInventories, item => item.id);
|
||||||
currentProjectInventories,
|
let orderNo = 0;
|
||||||
item => `${item.projectId}_${item.productId}`
|
|
||||||
);
|
for (const key in groupedInventories) {
|
||||||
for (const key in groupedData) {
|
orderNo++;
|
||||||
// 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。
|
// 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。---废弃
|
||||||
const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In);
|
// 2024.04.16 改成
|
||||||
const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out);
|
const inventory = groupedInventories[key][0];
|
||||||
const currInventories =
|
const outRecords = groupedData[key].filter(
|
||||||
groupedInventories[`${inRecord.projectId}_${inRecord.productId}`]?.shift();
|
item => item.inOrOut === MaterialsInOrOutEnum.Out
|
||||||
const allDataFromMonth = data.filter(
|
|
||||||
res => res.projectId === inRecord.projectId && res.productId === inRecord.productId
|
|
||||||
);
|
);
|
||||||
|
const inRecords = groupedData[key].filter(item => item.inOrOut === MaterialsInOrOutEnum.In);
|
||||||
|
const outRecordQuantity = outRecords
|
||||||
|
.map(item => item.quantity)
|
||||||
|
.reduce((acc, cur) => {
|
||||||
|
return calcNumber(acc, cur, 'add');
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const inRecordQuantity = inRecords
|
||||||
|
.map(item => item.quantity)
|
||||||
|
.reduce((acc, cur) => {
|
||||||
|
return calcNumber(acc, cur, 'add');
|
||||||
|
}, 0);
|
||||||
|
// 这里的单价默认入库价格和出库价格一致,所以直接用总数量*入库单价
|
||||||
|
const outRecordAmount = calcNumber(outRecordQuantity, inventory.unitPrice || 0, 'multiply');
|
||||||
|
const inRecordAmount = calcNumber(inRecordQuantity, inventory.unitPrice || 0, 'multiply');
|
||||||
|
const currInventories = groupedInventories[key]?.shift();
|
||||||
|
const allDataFromMonth = data.filter(res => res.inventoryId == Number(key));
|
||||||
let currentQuantity = 0;
|
let currentQuantity = 0;
|
||||||
let balanceQuantity = 0;
|
let balanceQuantity = 0;
|
||||||
// 月初库存数量
|
// 月初库存数量
|
||||||
|
@ -234,32 +250,33 @@ export class MaterialsInventoryService {
|
||||||
// 结存库存数量
|
// 结存库存数量
|
||||||
balanceQuantity = calcNumber(
|
balanceQuantity = calcNumber(
|
||||||
currentQuantity,
|
currentQuantity,
|
||||||
calcNumber(inRecord.quantity, outRecord?.quantity || 0, 'subtract'),
|
calcNumber(inRecordQuantity, outRecordQuantity, 'subtract'),
|
||||||
'add'
|
'add'
|
||||||
);
|
);
|
||||||
number++;
|
number++;
|
||||||
sheet.addRow([
|
sheet.addRow([
|
||||||
`${inRecord.inventoryInOutNumber || ''}`,
|
`${orderNo}`,
|
||||||
inRecord.product.company.name || '',
|
inventory.product?.company?.name || '',
|
||||||
inRecord.product.name || '',
|
inventory.product?.name || '',
|
||||||
inRecord.product.unit.label || '',
|
inventory.product.unit.label || '',
|
||||||
currentQuantity,
|
currentQuantity,
|
||||||
parseFloat(`${inRecord.unitPrice || 0}`),
|
parseFloat(`${inventory.unitPrice || 0}`),
|
||||||
calcNumber(currentQuantity, inRecord.unitPrice || 0, 'multiply'),
|
calcNumber(currentQuantity, inventory.unitPrice || 0, 'multiply'),
|
||||||
inRecord.time,
|
// inRecord.time,
|
||||||
inRecord.quantity || 0,
|
'',
|
||||||
parseFloat(`${inRecord.unitPrice || 0}`),
|
inRecordQuantity,
|
||||||
parseFloat(`${inRecord.amount || 0}`),
|
parseFloat(`${inventory.unitPrice || 0}`),
|
||||||
outRecord?.time || '',
|
parseFloat(`${inRecordAmount}`),
|
||||||
outRecord?.quantity || 0,
|
// outRecord?.time || '',
|
||||||
parseFloat(`${outRecord?.unitPrice || 0}`),
|
'',
|
||||||
parseFloat(`${outRecord?.amount || 0}`),
|
outRecordQuantity,
|
||||||
|
parseFloat(`${inventory?.unitPrice || 0}`),
|
||||||
|
parseFloat(`${outRecordAmount}`),
|
||||||
balanceQuantity,
|
balanceQuantity,
|
||||||
parseFloat(`${inRecord?.unitPrice || 0}`),
|
parseFloat(`${inventory?.unitPrice || 0}`),
|
||||||
calcNumber(balanceQuantity, inRecord?.unitPrice || 0, 'multiply'),
|
calcNumber(balanceQuantity, inventory?.unitPrice || 0, 'multiply'),
|
||||||
`${inRecord?.agent || ''}/${outRecord?.agent || ''}`,
|
// `${inRecord?.agent || ''}/${outRecord?.agent || ''}`,
|
||||||
outRecord?.issuanceNumber || '',
|
''
|
||||||
`${inRecord?.remark || ''}/${outRecord?.remark || ''}`
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
sheet.getCell('A1').font = { size: HEADER_FONT_SIZE };
|
sheet.getCell('A1').font = { size: HEADER_FONT_SIZE };
|
||||||
|
@ -319,7 +336,8 @@ export class MaterialsInventoryService {
|
||||||
product,
|
product,
|
||||||
keyword,
|
keyword,
|
||||||
projectId,
|
projectId,
|
||||||
isHasInventory
|
isHasInventory,
|
||||||
|
domain
|
||||||
}: MaterialsInventoryQueryDto): Promise<Pagination<MaterialsInventoryEntity>> {
|
}: MaterialsInventoryQueryDto): Promise<Pagination<MaterialsInventoryEntity>> {
|
||||||
const queryBuilder = this.materialsInventoryRepository
|
const queryBuilder = this.materialsInventoryRepository
|
||||||
.createQueryBuilder('materialsInventory')
|
.createQueryBuilder('materialsInventory')
|
||||||
|
@ -339,7 +357,8 @@ export class MaterialsInventoryService {
|
||||||
'product.productSpecification',
|
'product.productSpecification',
|
||||||
'product.productNumber'
|
'product.productNumber'
|
||||||
])
|
])
|
||||||
.where('materialsInventory.isDelete = 0');
|
.where(fieldSearch({ domain }))
|
||||||
|
.andWhere('materialsInventory.isDelete = 0');
|
||||||
if (product) {
|
if (product) {
|
||||||
queryBuilder.andWhere('product.name like :product', { product: `%${product}%` });
|
queryBuilder.andWhere('product.name like :product', { product: `%${product}%` });
|
||||||
}
|
}
|
||||||
|
@ -402,7 +421,8 @@ export class MaterialsInventoryService {
|
||||||
unitPrice?: number;
|
unitPrice?: number;
|
||||||
changedUnitPrice?: number;
|
changedUnitPrice?: number;
|
||||||
},
|
},
|
||||||
manager: EntityManager
|
manager: EntityManager,
|
||||||
|
domain?: DomainType
|
||||||
): Promise<MaterialsInventoryEntity> {
|
): Promise<MaterialsInventoryEntity> {
|
||||||
const {
|
const {
|
||||||
projectId,
|
projectId,
|
||||||
|
@ -415,9 +435,9 @@ export class MaterialsInventoryService {
|
||||||
} = data;
|
} = data;
|
||||||
let searchPayload: any = {};
|
let searchPayload: any = {};
|
||||||
if (isDefined(inventoryId)) {
|
if (isDefined(inventoryId)) {
|
||||||
searchPayload = { id: inventoryId };
|
searchPayload = { id: inventoryId, domain };
|
||||||
} else {
|
} else {
|
||||||
searchPayload = { projectId, productId, unitPrice };
|
searchPayload = { projectId, productId, unitPrice, domain };
|
||||||
}
|
}
|
||||||
const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, {
|
const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, {
|
||||||
where: searchPayload, // 根据项目,产品,价格查出之前的实时库存情况
|
where: searchPayload, // 根据项目,产品,价格查出之前的实时库存情况
|
||||||
|
@ -461,7 +481,7 @@ export class MaterialsInventoryService {
|
||||||
quantity: number;
|
quantity: number;
|
||||||
inventoryId?: number;
|
inventoryId?: number;
|
||||||
},
|
},
|
||||||
manager: EntityManager
|
manager: EntityManager,
|
||||||
): Promise<MaterialsInventoryEntity> {
|
): Promise<MaterialsInventoryEntity> {
|
||||||
const { quantity: outQuantity, inventoryId } = data;
|
const { quantity: outQuantity, inventoryId } = data;
|
||||||
// 开启悲观行锁,防止脏读和修改
|
// 开启悲观行锁,防止脏读和修改
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { ApiResult } from '~/common/decorators/api-result.decorator';
|
||||||
import { ProjectEntity } from './project.entity';
|
import { ProjectEntity } from './project.entity';
|
||||||
import { ProjectDto, ProjectQueryDto, ProjectUpdateDto } from './project.dto';
|
import { ProjectDto, ProjectQueryDto, ProjectUpdateDto } from './project.dto';
|
||||||
import { IdParam } from '~/common/decorators/id-param.decorator';
|
import { IdParam } from '~/common/decorators/id-param.decorator';
|
||||||
|
import { Domain, SkDomain } from '~/common/decorators/domain.decorator';
|
||||||
export const permissions = definePermission('app:project', {
|
export const permissions = definePermission('app:project', {
|
||||||
LIST: 'list',
|
LIST: 'list',
|
||||||
CREATE: 'create',
|
CREATE: 'create',
|
||||||
|
@ -34,8 +35,8 @@ export class ProjectController {
|
||||||
@ApiOperation({ summary: '分页获取项目列表' })
|
@ApiOperation({ summary: '分页获取项目列表' })
|
||||||
@ApiResult({ type: [ProjectEntity], isPage: true })
|
@ApiResult({ type: [ProjectEntity], isPage: true })
|
||||||
@Perm(permissions.LIST)
|
@Perm(permissions.LIST)
|
||||||
async list(@Query() dto: ProjectQueryDto) {
|
async list(@Domain() domain: SkDomain, @Query() dto: ProjectQueryDto) {
|
||||||
return this.projectService.findAll(dto);
|
return this.projectService.findAll({ ...dto, domain });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@ -49,8 +50,8 @@ export class ProjectController {
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: '新增项目' })
|
@ApiOperation({ summary: '新增项目' })
|
||||||
@Perm(permissions.CREATE)
|
@Perm(permissions.CREATE)
|
||||||
async create(@Body() dto: ProjectDto): Promise<void> {
|
async create(@Domain() domain: SkDomain, @Body() dto: ProjectDto): Promise<void> {
|
||||||
await this.projectService.create(dto);
|
await this.projectService.create({ ...dto, domain });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { PagerDto } from '~/common/dto/pager.dto';
|
||||||
import { Storage } from '../tools/storage/storage.entity';
|
import { Storage } from '../tools/storage/storage.entity';
|
||||||
import { IsUnique } from '~/shared/database/constraints/unique.constraint';
|
import { IsUnique } from '~/shared/database/constraints/unique.constraint';
|
||||||
import { ProjectEntity } from './project.entity';
|
import { ProjectEntity } from './project.entity';
|
||||||
|
import { DomainType } from '~/common/decorators/domain.decorator';
|
||||||
|
|
||||||
export class ProjectDto {
|
export class ProjectDto extends DomainType {
|
||||||
@ApiProperty({ description: '项目名称' })
|
@ApiProperty({ description: '项目名称' })
|
||||||
@IsUnique(ProjectEntity, { message: '已存在同名项目' })
|
@IsUnique(ProjectEntity, { message: '已存在同名项目' })
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ -31,7 +32,8 @@ export class ComapnyCreateDto extends PartialType(ProjectDto) {
|
||||||
|
|
||||||
export class ProjectQueryDto extends IntersectionType(
|
export class ProjectQueryDto extends IntersectionType(
|
||||||
PagerDto<ProjectDto>,
|
PagerDto<ProjectDto>,
|
||||||
PartialType(ProjectDto)
|
PartialType(ProjectDto),
|
||||||
|
DomainType
|
||||||
) {
|
) {
|
||||||
@ApiProperty({ description: '项目名称' })
|
@ApiProperty({ description: '项目名称' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { CommonEntity } from '~/common/entity/common.entity';
|
||||||
import { Storage } from '../tools/storage/storage.entity';
|
import { Storage } from '../tools/storage/storage.entity';
|
||||||
import { ProductEntity } from '../product/product.entity';
|
import { ProductEntity } from '../product/product.entity';
|
||||||
import { MaterialsInOutEntity } from '../materials_inventory/in_out/materials_in_out.entity';
|
import { MaterialsInOutEntity } from '../materials_inventory/in_out/materials_in_out.entity';
|
||||||
|
import { SkDomain } from '~/common/decorators/domain.decorator';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目实体类
|
* 项目实体类
|
||||||
|
@ -24,6 +25,10 @@ export class ProjectEntity extends CommonEntity {
|
||||||
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
||||||
isDelete: number;
|
isDelete: number;
|
||||||
|
|
||||||
|
@Column({ type: 'int', default: 1, comment: '所属域' })
|
||||||
|
@ApiProperty({ description: '所属域' })
|
||||||
|
domain: SkDomain;
|
||||||
|
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
@OneToMany(() => MaterialsInOutEntity, product => product.project)
|
@OneToMany(() => MaterialsInOutEntity, product => product.project)
|
||||||
materialsInOuts: Relation<MaterialsInOutEntity[]>;
|
materialsInOuts: Relation<MaterialsInOutEntity[]>;
|
||||||
|
|
|
@ -15,10 +15,11 @@ import {
|
||||||
ValidateIf
|
ValidateIf
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { DomainType } from '~/common/decorators/domain.decorator';
|
||||||
|
|
||||||
import { PagerDto } from '~/common/dto/pager.dto';
|
import { PagerDto } from '~/common/dto/pager.dto';
|
||||||
|
|
||||||
export class UserDto {
|
export class UserDto extends DomainType {
|
||||||
@ApiProperty({ description: '头像' })
|
@ApiProperty({ description: '头像' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ -83,9 +84,9 @@ export class UserDto {
|
||||||
status: number;
|
status: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserUpdateDto extends PartialType(UserDto) {}
|
export class UserUpdateDto extends PartialType(UserDto) { }
|
||||||
|
|
||||||
export class UserQueryDto extends IntersectionType(PagerDto<UserDto>, PartialType(UserDto)) {
|
export class UserQueryDto extends IntersectionType(PagerDto<UserDto>, PartialType(UserDto), DomainType) {
|
||||||
@ApiProperty({ description: '归属大区', example: 1, required: false })
|
@ApiProperty({ description: '归属大区', example: 1, required: false })
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { UserPasswordDto } from './dto/password.dto';
|
||||||
import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto';
|
import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto';
|
||||||
import { UserEntity } from './user.entity';
|
import { UserEntity } from './user.entity';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import { Domain, SkDomain } from '~/common/decorators/domain.decorator';
|
import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator';
|
||||||
|
|
||||||
export const permissions = definePermission('system:user', {
|
export const permissions = definePermission('system:user', {
|
||||||
LIST: 'list',
|
LIST: 'list',
|
||||||
|
@ -42,7 +42,7 @@ export class UserController {
|
||||||
constructor(
|
constructor(
|
||||||
private userService: UserService,
|
private userService: UserService,
|
||||||
private menuService: MenuService
|
private menuService: MenuService
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: '获取用户列表' })
|
@ApiOperation({ summary: '获取用户列表' })
|
||||||
|
@ -62,8 +62,8 @@ export class UserController {
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: '新增用户' })
|
@ApiOperation({ summary: '新增用户' })
|
||||||
@Perm(permissions.CREATE)
|
@Perm(permissions.CREATE)
|
||||||
async create(@Body() dto: UserDto): Promise<void> {
|
async create(@Domain() domain: SkDomain, @Body() dto: UserDto): Promise<void> {
|
||||||
await this.userService.create(dto);
|
await this.userService.create({ ...dto, domain });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
|
|
@ -4,7 +4,9 @@ import { SkDomain } from '~/common/decorators/domain.decorator';
|
||||||
export const fieldSearch = <T>(entity: Partial<T>): ObjectLiteral => {
|
export const fieldSearch = <T>(entity: Partial<T>): ObjectLiteral => {
|
||||||
let result = {};
|
let result = {};
|
||||||
for (let key in entity) {
|
for (let key in entity) {
|
||||||
if (entity.hasOwnProperty(key)) {
|
if (key == 'domain') {
|
||||||
|
result = { ...result, domain: entity['domain'] || -1 };
|
||||||
|
} else if (entity.hasOwnProperty(key)) {
|
||||||
switch (typeof entity[key]) {
|
switch (typeof entity[key]) {
|
||||||
case 'number':
|
case 'number':
|
||||||
result = { ...result, ...(isNumber(entity[key]) ? { [key]: entity[key] } : null) };
|
result = { ...result, ...(isNumber(entity[key]) ? { [key]: entity[key] } : null) };
|
||||||
|
|
Loading…
Reference in New Issue