feat: inventory
This commit is contained in:
parent
cab022af0a
commit
ce54da72c9
|
@ -26,7 +26,7 @@ export class BusinessException extends HttpException {
|
|||
code,
|
||||
message
|
||||
}),
|
||||
HttpStatus.OK
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
|
||||
this.errorCode = Number(code);
|
||||
|
|
|
@ -21,8 +21,9 @@ export enum MaterialsInOrOutEnum {
|
|||
|
||||
// 系统参数key
|
||||
export enum ParamConfigEnum {
|
||||
InventoryNumberPrefixIn = 'inventory_number_prefix_in',
|
||||
InventoryNumberPrefixOut = 'inventory_number_prefix_out',
|
||||
InventoryNumberPrefix = 'inventory_number_prefix',
|
||||
InventoryInOutNumberPrefixIn = 'inventory_inout_number_prefix_in',
|
||||
InventoryInOutNumberPrefixOut = 'inventory_inout_number_prefix_out',
|
||||
ProductNumberPrefix = 'product_number_prefix'
|
||||
}
|
||||
|
||||
|
@ -32,3 +33,10 @@ export enum ContractStatusEnum {
|
|||
Approved = 1, // 已通过
|
||||
Rejected = 2 // 已拒绝
|
||||
}
|
||||
|
||||
// 库存查询剩余咋黄台
|
||||
export enum HasInventoryStatusEnum {
|
||||
All = 0, // 全部
|
||||
Yes = 1, // 有库存
|
||||
No = 2 // 无库存
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { ExecutionContext, HttpException, HttpStatus, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
|
@ -72,7 +72,7 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) {
|
|||
const pv = await this.authService.getPasswordVersionByUid(request.user.uid);
|
||||
if (pv !== `${request.user.pv}`) {
|
||||
// 密码版本不一致,登录期间已更改过密码
|
||||
throw new BusinessException(ErrorEnum.INVALID_LOGIN);
|
||||
throw new HttpException(ErrorEnum.INVALID_LOGIN,HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// 不允许多端登录
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||
|
||||
@Injectable()
|
||||
export class BaseService {
|
||||
generateInventoryNumber(): string {
|
||||
generateInventoryInOutNumber(): string {
|
||||
// Generate a random inventory number
|
||||
return Math.floor(Math.random() * 1000000).toString();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ import { MaterialsInOutService } from './materials_in_out.service';
|
|||
import { MaterialsInOutEntity } from './materials_in_out.entity';
|
||||
import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator';
|
||||
import { definePermission, Perm } from '~/modules/auth/decorators/permission.decorator';
|
||||
import { MaterialsInOutQueryDto, MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto';
|
||||
import {
|
||||
MaterialsInOutQueryDto,
|
||||
MaterialsInOutDto,
|
||||
MaterialsInOutUpdateDto
|
||||
} from './materials_in_out.dto';
|
||||
|
||||
export const permissions = definePermission('materials_inventory:history_in_out', {
|
||||
LIST: 'list',
|
||||
|
@ -40,8 +44,8 @@ export class MaterialsInOutController {
|
|||
@Post()
|
||||
@ApiOperation({ summary: '新增原材料出入库记录' })
|
||||
@Perm(permissions.CREATE)
|
||||
async create(@Body() dto: MaterialsInOutDto): Promise<void> {
|
||||
await this.materialsInOutService.create(dto);
|
||||
async create(@Body() dto: MaterialsInOutDto): Promise<number> {
|
||||
return this.materialsInOutService.create(dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
|
|
|
@ -29,14 +29,14 @@ export class MaterialsInOutDto {
|
|||
projectId?: number;
|
||||
|
||||
@ApiProperty({ description: '产品Id' })
|
||||
@ValidateIf(o => !o.inventoryNumber)
|
||||
@ValidateIf(o => !o.inventoryInOutNumber)
|
||||
@IsNumber()
|
||||
productId: number;
|
||||
|
||||
@ApiProperty({ description: '原材料库存编号' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
inventoryNumber: string;
|
||||
inventoryInOutNumber: string;
|
||||
|
||||
@ApiProperty({ description: '库存id(产品和单价双主键决定一条库存)' })
|
||||
@IsOptional()
|
||||
|
@ -80,6 +80,11 @@ export class MaterialsInOutDto {
|
|||
@IsString()
|
||||
issuanceNumber: string;
|
||||
|
||||
@ApiProperty({ description: '库存位置' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
position: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ description: '备注' })
|
||||
|
@ -98,11 +103,12 @@ export class MaterialsInOutQueryDto extends PagerDto<MaterialsInOutQueryDto> {
|
|||
// @IsString()
|
||||
@Transform(params => {
|
||||
// 开始和结束时间用的是一天的开始和一天的结束的时分秒
|
||||
const date = params.value;
|
||||
return [
|
||||
date ? `${formatToDate(dayjs(date).startOf('month'))} 00:00:00` : null,
|
||||
date ? `${formatToDate(dayjs(date).endOf('month'))} 23:59:59` : null
|
||||
];
|
||||
return params.value
|
||||
? [
|
||||
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
|
||||
]
|
||||
: [];
|
||||
})
|
||||
time?: string[];
|
||||
|
||||
|
@ -129,7 +135,7 @@ export class MaterialsInOutQueryDto extends PagerDto<MaterialsInOutQueryDto> {
|
|||
@ApiProperty({ description: '原材料库存编号' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
inventoryNumber?: string;
|
||||
inventoryInOutNumber?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
|
|
|
@ -19,16 +19,17 @@ import { ProductEntity } from '~/modules/product/product.entity';
|
|||
import { ProjectEntity } from '~/modules/project/project.entity';
|
||||
import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity';
|
||||
import { Storage } from '~/modules/tools/storage/storage.entity';
|
||||
import { MaterialsInventoryEntity } from '../materials_inventory.entity';
|
||||
@Entity({ name: 'materials_in_out' })
|
||||
export class MaterialsInOutEntity extends CommonEntity {
|
||||
@Column({
|
||||
name: 'inventory_number',
|
||||
name: 'inventory_inout_number',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
comment: '原材料库存编号'
|
||||
comment: '原材料出入库编号'
|
||||
})
|
||||
@ApiProperty({ description: '原材料库存编号' })
|
||||
inventoryNumber: string;
|
||||
@ApiProperty({ description: '原材料出入库编号' })
|
||||
inventoryInOutNumber: string;
|
||||
|
||||
@Column({
|
||||
name: 'product_id',
|
||||
|
@ -38,6 +39,14 @@ export class MaterialsInOutEntity extends CommonEntity {
|
|||
@ApiProperty({ description: '产品' })
|
||||
productId: number;
|
||||
|
||||
@Column({
|
||||
name: 'inventory_id',
|
||||
type: 'int',
|
||||
comment: '库存'
|
||||
})
|
||||
@ApiProperty({ description: '库存' })
|
||||
inventoryId: number;
|
||||
|
||||
@Column({
|
||||
name: 'in_or_out',
|
||||
type: 'tinyint',
|
||||
|
@ -127,4 +136,8 @@ export class MaterialsInOutEntity extends CommonEntity {
|
|||
inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' }
|
||||
})
|
||||
files: Relation<Storage[]>;
|
||||
|
||||
@ManyToOne(() => MaterialsInventoryEntity)
|
||||
@JoinColumn({ name: 'inventory_id' })
|
||||
inventory: MaterialsInventoryEntity;
|
||||
}
|
||||
|
|
|
@ -43,26 +43,7 @@ export class MaterialsInOutService {
|
|||
isCreateOut,
|
||||
...ext
|
||||
}: MaterialsInOutQueryDto): Promise<Pagination<MaterialsInOutEntity>> {
|
||||
const sqb = this.materialsInOutRepository
|
||||
.createQueryBuilder('materialsInOut')
|
||||
.leftJoin('materialsInOut.files', 'files')
|
||||
.leftJoin('materialsInOut.project', 'project')
|
||||
.leftJoin('materialsInOut.product', 'product')
|
||||
.leftJoin('product.unit', 'unit')
|
||||
.leftJoin('product.files', 'productFiles')
|
||||
.leftJoin('product.company', 'company')
|
||||
.addSelect([
|
||||
'files.id',
|
||||
'files.path',
|
||||
'project.name',
|
||||
'product.name',
|
||||
'product.productSpecification',
|
||||
'product.productNumber',
|
||||
'productFiles.id',
|
||||
'productFiles.path',
|
||||
'unit.label',
|
||||
'company.name'
|
||||
])
|
||||
const sqb = this.buildSearchQuery()
|
||||
.where(fieldSearch(ext))
|
||||
.andWhere('materialsInOut.isDelete = 0')
|
||||
.addOrderBy('materialsInOut.createdAt', 'DESC');
|
||||
|
@ -85,25 +66,63 @@ export class MaterialsInOutService {
|
|||
return pageData;
|
||||
}
|
||||
|
||||
buildSearchQuery() {
|
||||
return this.materialsInOutRepository
|
||||
.createQueryBuilder('materialsInOut')
|
||||
.leftJoin('materialsInOut.files', 'files')
|
||||
.leftJoin('materialsInOut.project', 'project')
|
||||
.leftJoin('materialsInOut.product', 'product')
|
||||
.leftJoin('materialsInOut.inventory', 'inventory')
|
||||
.leftJoin('product.unit', 'unit')
|
||||
.leftJoin('product.files', 'productFiles')
|
||||
.leftJoin('product.company', 'company')
|
||||
.addSelect([
|
||||
'inventory.id',
|
||||
'inventory.position',
|
||||
'files.id',
|
||||
'files.path',
|
||||
'project.name',
|
||||
'product.name',
|
||||
'product.productSpecification',
|
||||
'product.productNumber',
|
||||
'productFiles.id',
|
||||
'productFiles.path',
|
||||
'unit.label',
|
||||
'company.name'
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
async create(dto: MaterialsInOutDto): Promise<void> {
|
||||
let { inOrOut, inventoryNumber, projectId, inventoryId } = dto;
|
||||
inventoryNumber = await this.generateInventoryNumber(inOrOut);
|
||||
async create(dto: MaterialsInOutDto): Promise<number> {
|
||||
let {
|
||||
inOrOut,
|
||||
inventoryInOutNumber,
|
||||
projectId,
|
||||
inventoryId,
|
||||
position,
|
||||
unitPrice,
|
||||
quantity,
|
||||
productId
|
||||
} = dto;
|
||||
inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut);
|
||||
let newRecordId;
|
||||
await this.entityManager.transaction(async manager => {
|
||||
// 1.生成出入库记录
|
||||
const { productId, quantity, unitPrice } = await manager.save(MaterialsInOutEntity, {
|
||||
...this.materialsInOutRepository.create(dto),
|
||||
inventoryNumber
|
||||
});
|
||||
// 2.更新增减库存
|
||||
await (
|
||||
delete dto.position;
|
||||
// 1.更新增减库存
|
||||
const inventoryEntity = await (
|
||||
Object.is(inOrOut, MaterialsInOrOutEnum.In)
|
||||
? this.materialsInventoryService.inInventory
|
||||
: this.materialsInventoryService.outInventory
|
||||
)({ productId, quantity, unitPrice, projectId, inventoryId }, manager);
|
||||
? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService)
|
||||
: this.materialsInventoryService.outInventory.bind(this.materialsInventoryService)
|
||||
)({ productId, quantity, unitPrice, projectId, inventoryId, position }, manager);
|
||||
// 2.生成出入库记录
|
||||
const { id } = await manager.save(MaterialsInOutEntity, {
|
||||
...this.materialsInOutRepository.create({ ...dto, inventoryId: inventoryEntity?.id }),
|
||||
inventoryInOutNumber
|
||||
});
|
||||
newRecordId = id;
|
||||
});
|
||||
return newRecordId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,8 +157,8 @@ export class MaterialsInOutService {
|
|||
}
|
||||
await (
|
||||
Object.is(data.inOrOut, MaterialsInOrOutEnum.In)
|
||||
? this.materialsInventoryService.inInventory
|
||||
: this.materialsInventoryService.outInventory
|
||||
? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService)
|
||||
: this.materialsInventoryService.outInventory.bind(this.materialsInventoryService)
|
||||
)(
|
||||
{
|
||||
productId: entity.productId,
|
||||
|
@ -189,7 +208,6 @@ export class MaterialsInOutService {
|
|||
manager
|
||||
);
|
||||
}
|
||||
|
||||
// 完成所有业务逻辑后,更新出入库记录
|
||||
await manager.update(MaterialsInOutEntity, id, {
|
||||
...data
|
||||
|
@ -232,8 +250,8 @@ export class MaterialsInOutService {
|
|||
// 更新库存
|
||||
await (
|
||||
Object.is(entity.inOrOut, MaterialsInOrOutEnum.In)
|
||||
? this.materialsInventoryService.outInventory
|
||||
: this.materialsInventoryService.inInventory
|
||||
? this.materialsInventoryService.outInventory.bind(this.materialsInventoryService)
|
||||
: this.materialsInventoryService.inInventory.bind(this.materialsInventoryService)
|
||||
)(
|
||||
{
|
||||
productId: entity.productId,
|
||||
|
@ -253,8 +271,7 @@ export class MaterialsInOutService {
|
|||
* 获取单个出入库信息
|
||||
*/
|
||||
async info(id: number) {
|
||||
const info = await this.materialsInOutRepository
|
||||
.createQueryBuilder('materialsInOut')
|
||||
const info = await this.buildSearchQuery()
|
||||
.where({
|
||||
id
|
||||
})
|
||||
|
@ -288,30 +305,30 @@ export class MaterialsInOutService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 生成库存单号
|
||||
* @returns 库存单号
|
||||
* 生成库存出入库单号
|
||||
* @returns 库存出入库单号
|
||||
*/
|
||||
async generateInventoryNumber(inOrOut: MaterialsInOrOutEnum = MaterialsInOrOutEnum.In) {
|
||||
async generateInventoryInOutNumber(inOrOut: MaterialsInOrOutEnum = MaterialsInOrOutEnum.In) {
|
||||
const prefix =
|
||||
(
|
||||
await this.paramConfigRepository.findOne({
|
||||
where: {
|
||||
key: inOrOut
|
||||
? ParamConfigEnum.InventoryNumberPrefixOut
|
||||
: ParamConfigEnum.InventoryNumberPrefixIn
|
||||
? ParamConfigEnum.InventoryInOutNumberPrefixOut
|
||||
: ParamConfigEnum.InventoryInOutNumberPrefixIn
|
||||
}
|
||||
})
|
||||
)?.value || '';
|
||||
const lastMaterial = await this.materialsInOutRepository
|
||||
.createQueryBuilder('materialsInOut')
|
||||
.select(
|
||||
`MAX(CAST(REPLACE(materialsInOut.inventoryNumber, '${prefix}', '') AS UNSIGNED))`,
|
||||
'maxInventoryNumber'
|
||||
`MAX(CAST(REPLACE(materialsInOut.inventoryInOutNumber, '${prefix}', '') AS UNSIGNED))`,
|
||||
'maxInventoryInOutNumber'
|
||||
)
|
||||
.where('materialsInOut.inOrOut = :inOrOut', { inOrOut })
|
||||
.getRawOne();
|
||||
const lastNumber = lastMaterial.maxInventoryNumber
|
||||
? parseInt(lastMaterial.maxInventoryNumber.replace(prefix, ''))
|
||||
const lastNumber = lastMaterial.maxInventoryInOutNumber
|
||||
? parseInt(lastMaterial.maxInventoryInOutNumber.replace(prefix, ''))
|
||||
: 0;
|
||||
const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1;
|
||||
return `${prefix}${newNumber}`;
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
IsArray,
|
||||
IsDate,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
|
@ -16,6 +17,7 @@ import { Storage } from '../tools/storage/storage.entity';
|
|||
import { Transform } from 'class-transformer';
|
||||
import dayjs from 'dayjs';
|
||||
import { formatToDate } from '~/utils';
|
||||
import { HasInventoryStatusEnum } from '~/constants/enum';
|
||||
|
||||
export class MaterialsInventoryDto {}
|
||||
|
||||
|
@ -23,7 +25,22 @@ export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryD
|
|||
export class MaterialsInventoryQueryDto extends IntersectionType(
|
||||
PagerDto<MaterialsInventoryDto>,
|
||||
PartialType(MaterialsInventoryDto)
|
||||
) {}
|
||||
) {
|
||||
@ApiProperty({ description: '产品名' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
product: string;
|
||||
|
||||
@ApiProperty({ description: '关键字' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
keyword: string;
|
||||
|
||||
@ApiProperty({ description: '产品名' })
|
||||
@IsOptional()
|
||||
@IsEnum(HasInventoryStatusEnum)
|
||||
isHasInventory: HasInventoryStatusEnum;
|
||||
}
|
||||
export class MaterialsInventoryExportDto {
|
||||
@ApiProperty({ description: '项目' })
|
||||
@IsOptional()
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm';
|
||||
import { ApiHideProperty, ApiProperty } from '@nestjs/swagger';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
Relation
|
||||
} from 'typeorm';
|
||||
import { CommonEntity } from '~/common/entity/common.entity';
|
||||
import { ProductEntity } from '../product/product.entity';
|
||||
import { ProjectEntity } from '../project/project.entity';
|
||||
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
||||
|
||||
@Entity({ name: 'materials_inventory' })
|
||||
export class MaterialsInventoryEntity extends CommonEntity {
|
||||
|
@ -22,6 +32,16 @@ export class MaterialsInventoryEntity extends CommonEntity {
|
|||
@ApiProperty({ description: '产品' })
|
||||
productId: number;
|
||||
|
||||
@Column({
|
||||
name: 'position',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
comment: '库存位置'
|
||||
})
|
||||
@ApiProperty({ description: '库存位置' })
|
||||
position: string;
|
||||
|
||||
@Column({
|
||||
name: 'quantity',
|
||||
type: 'int',
|
||||
|
@ -57,4 +77,17 @@ export class MaterialsInventoryEntity extends CommonEntity {
|
|||
@ManyToOne(() => ProductEntity)
|
||||
@JoinColumn({ name: 'product_id' })
|
||||
product: ProductEntity;
|
||||
|
||||
@Column({
|
||||
name: 'inventory_number',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
comment: '库存编号'
|
||||
})
|
||||
@ApiProperty({ description: '库存编号' })
|
||||
inventoryNumber: string;
|
||||
|
||||
@ApiHideProperty()
|
||||
@OneToMany(() => MaterialsInOutEntity, inout => inout.inventory)
|
||||
materialsInOuts: Relation<MaterialsInOutEntity[]>;
|
||||
}
|
||||
|
|
|
@ -16,14 +16,15 @@ import dayjs from 'dayjs';
|
|||
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
||||
import { fieldSearch } from '~/shared/database/field-search';
|
||||
import { groupBy, sum, uniqBy } from 'lodash';
|
||||
import { MaterialsInOrOutEnum } from '~/constants/enum';
|
||||
import { HasInventoryStatusEnum, MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum';
|
||||
import { ProjectEntity } from '../project/project.entity';
|
||||
import { calcNumber } from '~/utils';
|
||||
import { BusinessException } from '~/common/exceptions/biz.exception';
|
||||
import { ErrorEnum } from '~/constants/error-code.constant';
|
||||
import { ParamConfigEntity } from '../system/param-config/param-config.entity';
|
||||
import { isDefined } from 'class-validator';
|
||||
@Injectable()
|
||||
export class MaterialsInventoryService {
|
||||
const;
|
||||
constructor(
|
||||
@InjectEntityManager() private entityManager: EntityManager,
|
||||
@InjectRepository(MaterialsInventoryEntity)
|
||||
|
@ -31,7 +32,9 @@ export class MaterialsInventoryService {
|
|||
@InjectRepository(MaterialsInOutEntity)
|
||||
private materialsInOutRepository: Repository<MaterialsInOutEntity>,
|
||||
@InjectRepository(ProjectEntity)
|
||||
private projectRepository: Repository<ProjectEntity>
|
||||
private projectRepository: Repository<ProjectEntity>,
|
||||
@InjectRepository(ParamConfigEntity)
|
||||
private paramConfigRepository: Repository<ParamConfigEntity>
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
@ -236,7 +239,7 @@ export class MaterialsInventoryService {
|
|||
);
|
||||
number++;
|
||||
sheet.addRow([
|
||||
`${inRecord.inventoryNumber || ''}`,
|
||||
`${inRecord.inventoryInOutNumber || ''}`,
|
||||
inRecord.product.company.name || '',
|
||||
inRecord.product.name || '',
|
||||
inRecord.product.unit.label || '',
|
||||
|
@ -312,7 +315,10 @@ export class MaterialsInventoryService {
|
|||
*/
|
||||
async findAll({
|
||||
page,
|
||||
pageSize
|
||||
pageSize,
|
||||
product,
|
||||
keyword,
|
||||
isHasInventory
|
||||
}: MaterialsInventoryQueryDto): Promise<Pagination<MaterialsInventoryEntity>> {
|
||||
const queryBuilder = this.materialsInventoryRepository
|
||||
.createQueryBuilder('materialsInventory')
|
||||
|
@ -322,13 +328,34 @@ export class MaterialsInventoryService {
|
|||
.leftJoin('product.company', 'company')
|
||||
.addSelect([
|
||||
'project.name',
|
||||
'project.id',
|
||||
'unit.id',
|
||||
'unit.label',
|
||||
'company.id',
|
||||
'company.name',
|
||||
'product.id',
|
||||
'product.name',
|
||||
'product.productSpecification',
|
||||
'product.productNumber'
|
||||
])
|
||||
.where('materialsInventory.isDelete = 0');
|
||||
if (product) {
|
||||
queryBuilder.andWhere('product.name like :product', { product: `%${product}%` });
|
||||
}
|
||||
if (keyword) {
|
||||
queryBuilder.andWhere(
|
||||
'(materialsInventory.inventoryNumber like :keyword or product.name like :keyword or product.productNumber like :keyword or product.productSpecification like :keyword)',
|
||||
{
|
||||
keyword: `%${keyword}%`
|
||||
}
|
||||
);
|
||||
}
|
||||
if (isHasInventory == HasInventoryStatusEnum.Yes) {
|
||||
queryBuilder.andWhere('materialsInventory.quantity > 0');
|
||||
}
|
||||
if (isHasInventory == HasInventoryStatusEnum.No) {
|
||||
queryBuilder.andWhere('materialsInventory.quantity = 0');
|
||||
}
|
||||
return paginate<MaterialsInventoryEntity>(queryBuilder, {
|
||||
page,
|
||||
pageSize
|
||||
|
@ -361,6 +388,7 @@ export class MaterialsInventoryService {
|
|||
*/
|
||||
async inInventory(
|
||||
data: {
|
||||
position?: string;
|
||||
projectId: number;
|
||||
productId: number;
|
||||
quantity: number;
|
||||
|
@ -368,8 +396,15 @@ export class MaterialsInventoryService {
|
|||
changedUnitPrice?: number;
|
||||
},
|
||||
manager: EntityManager
|
||||
): Promise<void> {
|
||||
const { projectId, productId, quantity: inQuantity, unitPrice, changedUnitPrice } = data;
|
||||
): Promise<MaterialsInventoryEntity> {
|
||||
const {
|
||||
projectId,
|
||||
productId,
|
||||
quantity: inQuantity,
|
||||
unitPrice,
|
||||
changedUnitPrice,
|
||||
position
|
||||
} = data;
|
||||
|
||||
const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, {
|
||||
where: { projectId, productId, unitPrice }, // 根据项目,产品,价格查出之前的实时库存情况
|
||||
|
@ -378,13 +413,16 @@ export class MaterialsInventoryService {
|
|||
|
||||
// 若不存在库存,直接新增库存
|
||||
if (!exsitedInventory) {
|
||||
await manager.insert(MaterialsInventoryEntity, {
|
||||
const inventoryNumber = await this.generateInventoryNumber();
|
||||
const { raw } = await manager.insert(MaterialsInventoryEntity, {
|
||||
projectId,
|
||||
productId,
|
||||
unitPrice,
|
||||
inventoryNumber,
|
||||
position,
|
||||
quantity: inQuantity
|
||||
});
|
||||
return;
|
||||
return manager.findOne(MaterialsInventoryEntity, { where: { id: raw.insertId } });
|
||||
}
|
||||
// 若该项目存在库存,则该项目该产品的库存增加
|
||||
let { quantity, id } = exsitedInventory;
|
||||
|
@ -396,6 +434,8 @@ export class MaterialsInventoryService {
|
|||
quantity: newQuantity,
|
||||
unitPrice: changedUnitPrice || undefined
|
||||
});
|
||||
|
||||
return manager.findOne(MaterialsInventoryEntity, { where: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -407,14 +447,22 @@ export class MaterialsInventoryService {
|
|||
data: {
|
||||
quantity: number;
|
||||
inventoryId?: number;
|
||||
productId: number;
|
||||
unitPrice?: number;
|
||||
},
|
||||
manager: EntityManager
|
||||
): Promise<void> {
|
||||
const { quantity: outQuantity, inventoryId } = data;
|
||||
|
||||
): Promise<MaterialsInventoryEntity> {
|
||||
const { quantity: outQuantity, inventoryId, productId, unitPrice } = data;
|
||||
let searchPayload: any = {};
|
||||
if (inventoryId) {
|
||||
searchPayload.id = inventoryId;
|
||||
} else {
|
||||
// 删除出入库记录时,需要根据产品ID和价格查找库存
|
||||
searchPayload = { productId, unitPrice };
|
||||
}
|
||||
// 开启悲观行锁,防止脏读和修改
|
||||
const inventory = await manager.findOne(MaterialsInventoryEntity, {
|
||||
where: { id: inventoryId },
|
||||
where: searchPayload,
|
||||
lock: { mode: 'pessimistic_write' }
|
||||
});
|
||||
// 检查库存剩余
|
||||
|
@ -430,6 +478,8 @@ export class MaterialsInventoryService {
|
|||
await manager.update(MaterialsInventoryEntity, id, {
|
||||
quantity: newQuantity
|
||||
});
|
||||
|
||||
return manager.findOne(MaterialsInventoryEntity, { where: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -467,4 +517,31 @@ export class MaterialsInventoryService {
|
|||
.getOne();
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成库存编号
|
||||
* @returns 库存编号
|
||||
*/
|
||||
async generateInventoryNumber() {
|
||||
const prefix =
|
||||
(
|
||||
await this.paramConfigRepository.findOne({
|
||||
where: {
|
||||
key: ParamConfigEnum.InventoryNumberPrefix
|
||||
}
|
||||
})
|
||||
)?.value || '';
|
||||
const lastInventory = await this.materialsInventoryRepository
|
||||
.createQueryBuilder('materials_inventory')
|
||||
.select(
|
||||
`MAX(CAST(REPLACE(materials_inventory.inventoryNumber, '${prefix}', '') AS UNSIGNED))`,
|
||||
'maxInventoryNumber'
|
||||
)
|
||||
.getRawOne();
|
||||
const lastNumber = lastInventory.maxInventoryNumber
|
||||
? parseInt(lastInventory.maxInventoryNumber.replace(prefix, ''))
|
||||
: 0;
|
||||
const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1;
|
||||
return `${prefix}${newNumber}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import pinyin from 'pinyin';
|
|||
import { DictItemEntity } from '../system/dict-item/dict-item.entity';
|
||||
@Entity({ name: 'product' })
|
||||
export class ProductEntity extends CommonEntity {
|
||||
|
||||
@Column({
|
||||
name: 'product_number',
|
||||
type: 'varchar',
|
||||
|
@ -35,7 +34,7 @@ export class ProductEntity extends CommonEntity {
|
|||
})
|
||||
@ApiProperty({ description: '产品名称' })
|
||||
name: string;
|
||||
|
||||
|
||||
@Column({
|
||||
name: 'product_specification',
|
||||
type: 'varchar',
|
||||
|
@ -45,6 +44,7 @@ export class ProductEntity extends CommonEntity {
|
|||
})
|
||||
@ApiProperty({ description: '产品规格', nullable: true })
|
||||
productSpecification?: string;
|
||||
|
||||
@Column({
|
||||
name: 'remark',
|
||||
type: 'varchar',
|
||||
|
|
Loading…
Reference in New Issue