feat: 出入库记录模块完善

This commit is contained in:
louis 2024-03-06 17:13:14 +08:00
parent cf7c91eab2
commit 466c3ca384
5 changed files with 126 additions and 31 deletions

View File

@ -18,3 +18,8 @@ export enum MaterialsInOrOutEnum {
In, In,
Out Out
} }
// 系统参数key
export enum ParamConfigEnum {
MaterialsInOutPrefix = 'materials_in_out_prefix'
}

View File

@ -2,6 +2,7 @@ import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger';
import { Transform } from 'class-transformer'; import { Transform } from 'class-transformer';
import { import {
IsArray, IsArray,
IsBoolean,
IsDate, IsDate,
IsDateString, IsDateString,
IsEnum, IsEnum,
@ -11,7 +12,8 @@ import {
IsOptional, IsOptional,
IsString, IsString,
Matches, Matches,
MinLength MinLength,
ValidateIf
} from 'class-validator'; } from 'class-validator';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { PagerDto } from '~/common/dto/pager.dto'; import { PagerDto } from '~/common/dto/pager.dto';
@ -21,9 +23,15 @@ import { formatToDate } from '~/utils';
export class MaterialsInOutDto { export class MaterialsInOutDto {
@ApiProperty({ description: '产品' }) @ApiProperty({ description: '产品' })
@ValidateIf(o => !o.inventoryNumber)
@IsNumber() @IsNumber()
productId: number; productId: number;
@ApiProperty({ description: '原材料库存编号' })
@IsOptional()
@IsString()
inventoryNumber: string;
@ApiProperty({ description: '单位(字典)' }) @ApiProperty({ description: '单位(字典)' })
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ -68,10 +76,6 @@ export class MaterialsInOutDto {
@IsString() @IsString()
@ApiProperty({ description: '项目' }) @ApiProperty({ description: '项目' })
project: string; project: string;
// @ApiProperty({ description: '附件' })
// @IsOptional()
// files: Storage[];
} }
export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) {
@ -80,7 +84,7 @@ export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) {
@IsArray() @IsArray()
fileIds: number[]; fileIds: number[];
} }
export class MaterialsInOutQueryDto extends PagerDto<MaterialsInOutDto> { export class MaterialsInOutQueryDto extends PagerDto<MaterialsInOutQueryDto> {
@ApiProperty({ description: '出入库时间YYYY-MM-DD' }) @ApiProperty({ description: '出入库时间YYYY-MM-DD' })
@IsOptional() @IsOptional()
// @IsString() // @IsString()
@ -97,10 +101,41 @@ export class MaterialsInOutQueryDto extends PagerDto<MaterialsInOutDto> {
@ApiProperty({ description: '入库或出库 0:入库 1:出库' }) @ApiProperty({ description: '入库或出库 0:入库 1:出库' })
@IsOptional() @IsOptional()
@IsEnum(MaterialsInOrOutEnum) @IsEnum(MaterialsInOrOutEnum)
inOrOut: MaterialsInOrOutEnum; inOrOut?: MaterialsInOrOutEnum;
@ApiProperty({ description: '产品名称' }) @ApiProperty({ description: '产品名称' })
@IsOptional() @IsOptional()
@IsString() @IsString()
product: string; product?: string;
@ApiProperty({ description: '经办人' })
@IsOptional()
@IsString()
agent?: string;
@ApiProperty({ description: '领料单号' })
@IsOptional()
@IsString()
issuanceNumber?: string;
@ApiProperty({ description: '原材料库存编号' })
@IsOptional()
@IsString()
inventoryNumber?: string;
@IsOptional()
@IsString()
@ApiProperty({ description: '备注' })
remark?: string;
@IsOptional()
@IsString()
@ApiProperty({ description: '项目' })
project?: string;
@IsOptional()
@IsBoolean()
@ApiProperty({ description: '是否是用于创建出库记录' })
isCreateOut?: boolean;
} }

View File

@ -1,23 +1,34 @@
import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; import { ApiHideProperty, ApiProperty } from '@nestjs/swagger';
import { InjectRepository } from '@nestjs/typeorm';
import { Expose } from 'class-transformer';
import pinyin from 'pinyin';
import { import {
BeforeInsert,
Column, Column,
Entity, Entity,
JoinColumn, JoinColumn,
JoinTable, JoinTable,
ManyToMany, ManyToMany,
ManyToOne, ManyToOne,
OneToMany,
Relation, Relation,
VirtualColumn Repository
} from 'typeorm'; } from 'typeorm';
import { CommonEntity } from '~/common/entity/common.entity'; import { CommonEntity } from '~/common/entity/common.entity';
import { MaterialsInOrOutEnum } from '~/constants/enum'; import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum';
import { ProductEntity } from '~/modules/product/product.entity'; import { ProductEntity } from '~/modules/product/product.entity';
import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity'; import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity';
import { Storage } from '~/modules/tools/storage/storage.entity'; import { Storage } from '~/modules/tools/storage/storage.entity';
@Entity({ name: 'materials_in_out' }) @Entity({ name: 'materials_in_out' })
export class MaterialsInOutEntity extends CommonEntity { export class MaterialsInOutEntity extends CommonEntity {
@Column({
name: 'inventory_number',
type: 'varchar',
length: 50,
comment: '原材料库存编号'
})
@ApiProperty({ description: '原材料库存编号' })
inventoryNumber: string;
@Column({ @Column({
name: 'product_id', name: 'product_id',
type: 'int', type: 'int',
@ -55,9 +66,9 @@ export class MaterialsInOutEntity extends CommonEntity {
@Column({ @Column({
name: 'unit_price', name: 'unit_price',
type: 'decimal', type: 'decimal',
precision: 10, precision: 15,
default: 0, default: 0,
scale: 2, scale: 10,
comment: '单价' comment: '单价'
}) })
@ApiProperty({ description: '单价' }) @ApiProperty({ description: '单价' })
@ -66,9 +77,9 @@ export class MaterialsInOutEntity extends CommonEntity {
@Column({ @Column({
name: 'amount', name: 'amount',
type: 'decimal', type: 'decimal',
precision: 10, precision: 15,
default: 0, default: 0,
scale: 2, scale: 10,
comment: '金额' comment: '金额'
}) })
@ApiProperty({ description: '金额' }) @ApiProperty({ description: '金额' })
@ -104,9 +115,6 @@ export class MaterialsInOutEntity extends CommonEntity {
@JoinColumn({ name: 'product_id' }) @JoinColumn({ name: 'product_id' })
product: ProductEntity; product: ProductEntity;
@ApiHideProperty()
unit: DictItemEntity;
@ManyToMany(() => Storage, storage => storage.materialsInOut) @ManyToMany(() => Storage, storage => storage.materialsInOut)
@JoinTable({ @JoinTable({
name: 'materials_in_out_storage', name: 'materials_in_out_storage',

View File

@ -14,6 +14,8 @@ import {
} 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';
import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity';
import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum';
@Injectable() @Injectable()
export class MaterialsInOutService { export class MaterialsInOutService {
@ -22,7 +24,9 @@ export class MaterialsInOutService {
@InjectRepository(MaterialsInOutEntity) @InjectRepository(MaterialsInOutEntity)
private materialsInOutRepository: Repository<MaterialsInOutEntity>, private materialsInOutRepository: Repository<MaterialsInOutEntity>,
@InjectRepository(Storage) @InjectRepository(Storage)
private storageRepository: Repository<Storage> private storageRepository: Repository<Storage>,
@InjectRepository(ParamConfigEntity)
private paramConfigRepository: Repository<ParamConfigEntity>
) {} ) {}
/** /**
* *
@ -31,6 +35,7 @@ export class MaterialsInOutService {
page, page,
pageSize, pageSize,
product: productName, product: productName,
isCreateOut,
...ext ...ext
}: MaterialsInOutQueryDto): Promise<Pagination<MaterialsInOutEntity>> { }: MaterialsInOutQueryDto): Promise<Pagination<MaterialsInOutEntity>> {
const sqb = this.materialsInOutRepository const sqb = this.materialsInOutRepository
@ -38,23 +43,21 @@ export class MaterialsInOutService {
.leftJoin('materialsInOut.files', 'files') .leftJoin('materialsInOut.files', 'files')
.leftJoin('materialsInOut.product', 'product') .leftJoin('materialsInOut.product', 'product')
.leftJoin('product.unit', 'unit') .leftJoin('product.unit', 'unit')
.addSelect(['files.id', 'files.path', 'product.id', 'product.name', 'unit.id', 'unit.label']) .leftJoin('product.company', 'company')
.addSelect(['files.path', 'product.name', 'unit.label', 'company.name'])
.where(fieldSearch(ext)) .where(fieldSearch(ext))
.andWhere('materialsInOut.isDelete = 0') .andWhere('materialsInOut.isDelete = 0')
.addOrderBy('materialsInOut.time', 'DESC'); .addOrderBy('materialsInOut.createdAt', 'DESC');
if (productName) { if (productName) {
sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); sqb.andWhere('product.name like :productName', { productName: `%${productName}%` });
} }
let pageData = await paginate<MaterialsInOutEntity>(sqb, { if (isCreateOut) {
sqb.andWhere('materialsInOut.inOrOut = 0');
}
const pageData = await paginate<MaterialsInOutEntity>(sqb, {
page, page,
pageSize pageSize
}); });
// 产品表中的单位字段需要单独处理
pageData.items = pageData.items.map(materialsInOut => {
materialsInOut.unit = materialsInOut.product.unit;
delete materialsInOut.product.unit;
return materialsInOut;
});
return pageData; return pageData;
} }
@ -62,7 +65,22 @@ export class MaterialsInOutService {
* *
*/ */
async create(dto: MaterialsInOutDto): Promise<void> { async create(dto: MaterialsInOutDto): Promise<void> {
await this.materialsInOutRepository.insert(dto); let { inOrOut, inventoryNumber } = dto;
if (inOrOut === MaterialsInOrOutEnum.In) {
inventoryNumber = await this.generateInventoryNumber();
} else {
const inRecord = await this.materialsInOutRepository.findOne({
where: {
inventoryNumber
}
});
const { productId } = inRecord;
dto.productId = productId;
}
await this.materialsInOutRepository.insert({
...this.materialsInOutRepository.create(dto),
inventoryNumber
});
} }
/** /**
@ -141,4 +159,31 @@ export class MaterialsInOutService {
.addAndRemove(linkedFiles, materialsInOut.files); .addAndRemove(linkedFiles, materialsInOut.files);
}); });
} }
/**
*
* @returns
*/
async generateInventoryNumber() {
const prefix =
(
await this.paramConfigRepository.findOne({
where: {
key: ParamConfigEnum.MaterialsInOutPrefix
}
})
)?.value || '';
const lastMaterial = await this.materialsInOutRepository
.createQueryBuilder('materialsInOut')
.select(
`MAX(CAST(REPLACE(materialsInOut.inventoryNumber, '${prefix}', '') AS UNSIGNED))`,
'maxInventoryNumber'
)
.getRawOne();
const lastNumber = lastMaterial.maxInventoryNumber
? parseInt(lastMaterial.maxInventoryNumber.replace(prefix, ''))
: 0;
const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1;
return `${prefix}${newNumber}`;
}
} }

View File

@ -7,10 +7,12 @@ import { StorageModule } from '../tools/storage/storage.module';
import { MaterialsInOutController } from './in_out/materials_in_out.controller'; 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';
@Module({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]), TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]),
ParamConfigModule,
StorageModule StorageModule
], ],
controllers: [MaterialsInventoryController, MaterialsInOutController], controllers: [MaterialsInventoryController, MaterialsInOutController],