feat: 原材料盘点
This commit is contained in:
parent
fde00d6ad1
commit
ba5e47b766
|
@ -0,0 +1,6 @@
|
||||||
|
export enum DictTypeStatusEnum {
|
||||||
|
/** 启用 */
|
||||||
|
ENABLE = 1,
|
||||||
|
/** 禁用 */
|
||||||
|
DISABLE = 0
|
||||||
|
}
|
|
@ -1,4 +1,70 @@
|
||||||
import { Controller } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common';
|
||||||
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ApiResult } from '~/common/decorators/api-result.decorator';
|
||||||
|
import { IdParam } from '~/common/decorators/id-param.decorator';
|
||||||
|
import { Perm, definePermission } from '../auth/decorators/permission.decorator';
|
||||||
|
import { ContractQueryDto, ContractDto, ContractUpdateDto } from '../contract/contract.dto';
|
||||||
|
import { MaterialsInventoryService } from './materials_inventory.service';
|
||||||
|
import { MaterialsInventoryEntity } from './materials_inventory.entity';
|
||||||
|
import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator';
|
||||||
|
|
||||||
|
export const permissions = definePermission('app:materials_inventory', {
|
||||||
|
LIST: 'list',
|
||||||
|
CREATE: 'create',
|
||||||
|
READ: 'read',
|
||||||
|
UPDATE: 'update',
|
||||||
|
DELETE: 'delete'
|
||||||
|
} as const);
|
||||||
|
|
||||||
|
@ApiTags('MaterialsI Inventory - 原材料盘点')
|
||||||
|
@ApiSecurityAuth()
|
||||||
@Controller('materials-inventory')
|
@Controller('materials-inventory')
|
||||||
export class MaterialsInventoryController {}
|
export class MaterialsInventoryController {
|
||||||
|
constructor(private miService: MaterialsInventoryService) {}
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: '获取原材料盘点列表' })
|
||||||
|
@ApiResult({ type: [MaterialsInventoryEntity], isPage: true })
|
||||||
|
@Perm(permissions.LIST)
|
||||||
|
async list(@Query() dto: ContractQueryDto) {
|
||||||
|
return this.miService.findAll(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: '获取原材料盘点信息' })
|
||||||
|
@ApiResult({ type: ContractDto })
|
||||||
|
@Perm(permissions.READ)
|
||||||
|
async info(@IdParam() id: number) {
|
||||||
|
return this.miService.info(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: '新增原材料盘点' })
|
||||||
|
@Perm(permissions.CREATE)
|
||||||
|
async create(@Body() dto: ContractDto): Promise<void> {
|
||||||
|
await this.miService.create(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put(':id')
|
||||||
|
@ApiOperation({ summary: '更新原材料盘点' })
|
||||||
|
@Perm(permissions.UPDATE)
|
||||||
|
async update(@IdParam() id: number, @Body() dto: ContractUpdateDto): Promise<void> {
|
||||||
|
await this.miService.update(id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: '删除原材料盘点' })
|
||||||
|
@Perm(permissions.DELETE)
|
||||||
|
async delete(@IdParam() id: number): Promise<void> {
|
||||||
|
await this.miService.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put('unlink-attachments/:id')
|
||||||
|
@ApiOperation({ summary: '附件解除关联' })
|
||||||
|
@Perm(permissions.UPDATE)
|
||||||
|
async unlinkAttachments(
|
||||||
|
@IdParam() id: number,
|
||||||
|
@Body() { fileIds }: ContractUpdateDto
|
||||||
|
): Promise<void> {
|
||||||
|
await this.miService.unlinkAttachments(id, fileIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsArray,
|
||||||
|
IsDate,
|
||||||
|
IsDateString,
|
||||||
|
IsIn,
|
||||||
|
IsInt,
|
||||||
|
IsNumber,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
Matches,
|
||||||
|
MinLength
|
||||||
|
} from 'class-validator';
|
||||||
|
import { PagerDto } from '~/common/dto/pager.dto';
|
||||||
|
import { Storage } from '../tools/storage/storage.entity';
|
||||||
|
|
||||||
|
export class MaterialsInventoryDto {
|
||||||
|
@ApiProperty({ description: '附件' })
|
||||||
|
files: Storage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryDto) {
|
||||||
|
@ApiProperty({ description: '附件' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
fileIds: number[];
|
||||||
|
}
|
||||||
|
export class MaterialsInventoryQueryDto extends IntersectionType(
|
||||||
|
PagerDto<MaterialsInventoryDto>,
|
||||||
|
PartialType(MaterialsInventoryDto)
|
||||||
|
) {}
|
|
@ -1,9 +1,10 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Column, Entity } from 'typeorm';
|
import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm';
|
||||||
import { CommonEntity } from '~/common/entity/common.entity';
|
import { CommonEntity } from '~/common/entity/common.entity';
|
||||||
|
import { Storage } from '../tools/storage/storage.entity';
|
||||||
|
|
||||||
@Entity({ name: 'meterials_inventory' })
|
@Entity({ name: 'materials_inventory' })
|
||||||
export class MeterialsInventoryEntity extends CommonEntity {
|
export class MaterialsInventoryEntity extends CommonEntity {
|
||||||
@Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' })
|
@Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' })
|
||||||
@ApiProperty({ description: '公司名称' })
|
@ApiProperty({ description: '公司名称' })
|
||||||
companyName: number;
|
companyName: number;
|
||||||
|
@ -182,4 +183,20 @@ export class MeterialsInventoryEntity extends CommonEntity {
|
||||||
@Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true })
|
@Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true })
|
||||||
@ApiProperty({ description: '备注' })
|
@ApiProperty({ description: '备注' })
|
||||||
remark: string;
|
remark: string;
|
||||||
|
|
||||||
|
@Column({ name: 'project', type: 'varchar', length: 255, comment: '项目', nullable: false })
|
||||||
|
@ApiProperty({ description: '项目' })
|
||||||
|
project: string;
|
||||||
|
|
||||||
|
@Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' })
|
||||||
|
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
||||||
|
isDelete: number;
|
||||||
|
|
||||||
|
@ManyToMany(() => Storage, storage => storage.contracts)
|
||||||
|
@JoinTable({
|
||||||
|
name: 'materials_inventory_storage',
|
||||||
|
joinColumn: { name: 'materials_inventory_id', referencedColumnName: 'id' },
|
||||||
|
inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' }
|
||||||
|
})
|
||||||
|
files: Relation<Storage[]>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';
|
||||||
import { MaterialsInventoryController } from './materials_inventory.controller';
|
import { MaterialsInventoryController } from './materials_inventory.controller';
|
||||||
import { MaterialsInventoryService } from './materials_inventory.service';
|
import { MaterialsInventoryService } from './materials_inventory.service';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { MeterialsInventoryEntity } from './materials_inventory.entity';
|
import { MaterialsInventoryEntity } from './materials_inventory.entity';
|
||||||
|
import { StorageModule } from '../tools/storage/storage.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([MeterialsInventoryEntity])],
|
imports: [TypeOrmModule.forFeature([MaterialsInventoryEntity]), StorageModule],
|
||||||
controllers: [MaterialsInventoryController],
|
controllers: [MaterialsInventoryController],
|
||||||
providers: [MaterialsInventoryService]
|
providers: [MaterialsInventoryService]
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,140 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { MaterialsInventoryEntity } from './materials_inventory.entity';
|
||||||
|
import { Storage } from '../tools/storage/storage.entity';
|
||||||
|
import { EntityManager, Repository } from 'typeorm';
|
||||||
|
import {
|
||||||
|
MaterialsInventoryDto,
|
||||||
|
MaterialsInventoryQueryDto,
|
||||||
|
MaterialsInventoryUpdateDto
|
||||||
|
} from './materials_inventory.dto';
|
||||||
|
import { Pagination } from '~/helper/paginate/pagination';
|
||||||
|
import { BusinessException } from '~/common/exceptions/biz.exception';
|
||||||
|
import { ErrorEnum } from '~/constants/error-code.constant';
|
||||||
|
import { paginate } from '~/helper/paginate';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MaterialsInventoryService {}
|
export class MaterialsInventoryService {
|
||||||
|
constructor(
|
||||||
|
@InjectEntityManager() private entityManager: EntityManager,
|
||||||
|
@InjectRepository(MaterialsInventoryEntity)
|
||||||
|
private materialsInventoryRepository: Repository<MaterialsInventoryEntity>,
|
||||||
|
@InjectRepository(Storage)
|
||||||
|
private storageRepository: Repository<Storage>
|
||||||
|
) {}
|
||||||
|
/**
|
||||||
|
* 列举所有角色:除去超级管理员
|
||||||
|
*/
|
||||||
|
async findAll({
|
||||||
|
page,
|
||||||
|
pageSize
|
||||||
|
// materialsInventoryNumber,
|
||||||
|
// title,
|
||||||
|
// type,
|
||||||
|
// status
|
||||||
|
}: MaterialsInventoryQueryDto): Promise<Pagination<MaterialsInventoryEntity>> {
|
||||||
|
const queryBuilder = this.materialsInventoryRepository
|
||||||
|
.createQueryBuilder('materialsInventory')
|
||||||
|
.leftJoin('materialsInventory.files', 'files')
|
||||||
|
.addSelect(['files.id', 'files.path'])
|
||||||
|
// .where({
|
||||||
|
// ...(materialsInventoryNumber ? { materialsInventoryNumber: Like(`%${materialsInventoryNumber}%`) } : null),
|
||||||
|
// ...(title ? { title: Like(`%${title}%`) } : null),
|
||||||
|
// ...(isNumber(type) ? { type } : null),
|
||||||
|
// ...(isNumber(status) ? { status } : null)
|
||||||
|
// })
|
||||||
|
.andWhere('materialsInventory.isDelete = 0');
|
||||||
|
|
||||||
|
return paginate<MaterialsInventoryEntity>(queryBuilder, {
|
||||||
|
page,
|
||||||
|
pageSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*/
|
||||||
|
async create(dto: MaterialsInventoryDto): Promise<void> {
|
||||||
|
await this.materialsInventoryRepository.insert(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新
|
||||||
|
*/
|
||||||
|
async update(
|
||||||
|
id: number,
|
||||||
|
{ fileIds, ...data }: Partial<MaterialsInventoryUpdateDto>
|
||||||
|
): Promise<void> {
|
||||||
|
await this.entityManager.transaction(async manager => {
|
||||||
|
await manager.update(MaterialsInventoryEntity, id, {
|
||||||
|
...data
|
||||||
|
});
|
||||||
|
const materialsInventory = await this.materialsInventoryRepository
|
||||||
|
.createQueryBuilder('materialsInventory')
|
||||||
|
.leftJoinAndSelect('materialsInventory.files', 'files')
|
||||||
|
.where('materialsInventory.id = :id', { id })
|
||||||
|
.getOne();
|
||||||
|
if (fileIds?.length) {
|
||||||
|
const count = await this.storageRepository
|
||||||
|
.createQueryBuilder('storage')
|
||||||
|
.where('storage.id in(:fileIds)', { fileIds })
|
||||||
|
.getCount();
|
||||||
|
if (count !== fileIds?.length) {
|
||||||
|
throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
// 附件要批量更新
|
||||||
|
await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.relation(MaterialsInventoryEntity, 'files')
|
||||||
|
.of(id)
|
||||||
|
.addAndRemove(fileIds, materialsInventory.files);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*/
|
||||||
|
async delete(id: number): Promise<void> {
|
||||||
|
// 合同比较重要,做逻辑删除
|
||||||
|
await this.materialsInventoryRepository.update(id, { isDelete: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单个合同信息
|
||||||
|
*/
|
||||||
|
async info(id: number) {
|
||||||
|
const info = await this.materialsInventoryRepository
|
||||||
|
.createQueryBuilder('materialsInventory')
|
||||||
|
.where({
|
||||||
|
id
|
||||||
|
})
|
||||||
|
.andWhere('materialsInventory.isDelete = 0')
|
||||||
|
.getOne();
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解除附件关联
|
||||||
|
* @param id 合同ID
|
||||||
|
* @param fileIds 附件ID
|
||||||
|
*/
|
||||||
|
async unlinkAttachments(id: number, fileIds: number[]) {
|
||||||
|
await this.entityManager.transaction(async manager => {
|
||||||
|
const materialsInventory = await this.materialsInventoryRepository
|
||||||
|
.createQueryBuilder('materialsInventory')
|
||||||
|
.leftJoinAndSelect('materialsInventory.files', 'files')
|
||||||
|
.where('materialsInventory.id = :id', { id })
|
||||||
|
.getOne();
|
||||||
|
const linkedFiles = materialsInventory.files
|
||||||
|
.map(item => item.id)
|
||||||
|
.filter(item => !fileIds.includes(item));
|
||||||
|
// 附件要批量更新
|
||||||
|
await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.relation(MaterialsInventoryEntity, 'files')
|
||||||
|
.of(id)
|
||||||
|
.addAndRemove(linkedFiles, materialsInventory.files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,12 +34,12 @@ export class DictTypeController {
|
||||||
return this.dictTypeService.page(dto);
|
return this.dictTypeService.page(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('select-options')
|
@Post('all')
|
||||||
@ApiOperation({ summary: '一次性获取所有的字典类型(不分页)' })
|
@ApiOperation({ summary: '一次性获取所有的字典类型(不分页)' })
|
||||||
@ApiResult({ type: [DictTypeEntity] })
|
@ApiResult({ type: [DictTypeEntity] })
|
||||||
@Perm(permissions.LIST)
|
@Perm(permissions.LIST)
|
||||||
async getAll(): Promise<DictTypeEntity[]> {
|
async getAll(@Body() dto: DictTypeQueryDto): Promise<DictTypeEntity[]> {
|
||||||
return this.dictTypeService.getAll();
|
return this.dictTypeService.getAll(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { ApiProperty, PartialType } from '@nestjs/swagger';
|
import { ApiProperty, PartialType } from '@nestjs/swagger';
|
||||||
import { IsInt, IsOptional, IsString, MinLength } from 'class-validator';
|
import { IsArray, IsBoolean, IsInt, IsOptional, IsString, MinLength } from 'class-validator';
|
||||||
|
|
||||||
import { PagerDto } from '~/common/dto/pager.dto';
|
import { PagerDto } from '~/common/dto/pager.dto';
|
||||||
|
|
||||||
import { DictTypeEntity } from './dict-type.entity';
|
import { DictTypeEntity } from './dict-type.entity';
|
||||||
|
import { isBoolean } from 'lodash';
|
||||||
|
|
||||||
export class DictTypeDto extends PartialType(DictTypeEntity) {
|
export class DictTypeDto extends PartialType(DictTypeEntity) {
|
||||||
@ApiProperty({ description: '字典类型名称' })
|
@ApiProperty({ description: '字典类型名称' })
|
||||||
|
@ -37,4 +38,14 @@ export class DictTypeQueryDto extends PagerDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
code: string;
|
code: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否用于前端store缓存' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
withItems: boolean;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '需要前端store缓存的code' })
|
||||||
|
@IsArray()
|
||||||
|
@IsOptional()
|
||||||
|
storeCodes: string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Column, Entity } from 'typeorm';
|
import { Column, Entity, OneToMany, Relation } from 'typeorm';
|
||||||
|
|
||||||
import { CompleteEntity } from '~/common/entity/common.entity';
|
import { CompleteEntity } from '~/common/entity/common.entity';
|
||||||
|
import { DictItemEntity } from '../dict-item/dict-item.entity';
|
||||||
|
|
||||||
@Entity({ name: 'sys_dict_type' })
|
@Entity({ name: 'sys_dict_type' })
|
||||||
export class DictTypeEntity extends CompleteEntity {
|
export class DictTypeEntity extends CompleteEntity {
|
||||||
|
@ -20,4 +21,9 @@ export class DictTypeEntity extends CompleteEntity {
|
||||||
@Column({ type: 'varchar', nullable: true })
|
@Column({ type: 'varchar', nullable: true })
|
||||||
@ApiProperty({ description: '备注' })
|
@ApiProperty({ description: '备注' })
|
||||||
remark: string;
|
remark: string;
|
||||||
|
|
||||||
|
@OneToMany(() => DictItemEntity, dictItem => dictItem.type, {
|
||||||
|
cascade: true
|
||||||
|
})
|
||||||
|
dictItems: Relation<DictItemEntity[]>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { Like, Repository } from 'typeorm';
|
import { In, Like, Repository } from 'typeorm';
|
||||||
|
|
||||||
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,6 +10,7 @@ import { Pagination } from '~/helper/paginate/pagination';
|
||||||
import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity';
|
import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity';
|
||||||
|
|
||||||
import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto';
|
import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto';
|
||||||
|
import { DictTypeStatusEnum } from '~/constants/enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DictTypeService {
|
export class DictTypeService {
|
||||||
|
@ -36,8 +37,19 @@ export class DictTypeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 一次性获取所有的字典类型 */
|
/** 一次性获取所有的字典类型 */
|
||||||
async getAll() {
|
async getAll({ withItems, storeCodes }: DictTypeQueryDto) {
|
||||||
return this.dictTypeRepository.find();
|
const sqb = this.dictTypeRepository
|
||||||
|
.createQueryBuilder('dict_type')
|
||||||
|
.addSelect(['dict_type.name', 'dict_type.code', 'dict_type.status']);
|
||||||
|
if (withItems) {
|
||||||
|
sqb
|
||||||
|
.leftJoin('dict_type.dictItems', 'dictItems')
|
||||||
|
.addSelect(['dictItems.id', 'dictItems.value', 'dictItems.label', 'dictItems.status']);
|
||||||
|
}
|
||||||
|
sqb.where('dict_type.status =:status', { status: DictTypeStatusEnum.ENABLE });
|
||||||
|
withItems && sqb.andWhere('dictItems.status =:status', { status: DictTypeStatusEnum.ENABLE });
|
||||||
|
storeCodes && sqb.andWhere({ code: In(storeCodes) });
|
||||||
|
return sqb.getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue