feat: 盘点表计算模块
This commit is contained in:
parent
3610d8fdc6
commit
5d9ce4cef2
|
@ -88,6 +88,7 @@
|
|||
"helmet": "^7.1.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mathjs": "^12.4.0",
|
||||
"minio": "^7.1.3",
|
||||
"mysql2": "^3.9.1",
|
||||
"nanoid": "^3.3.7",
|
||||
|
|
|
@ -131,6 +131,9 @@ dependencies:
|
|||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
mathjs:
|
||||
specifier: ^12.4.0
|
||||
version: 12.4.0
|
||||
minio:
|
||||
specifier: ^7.1.3
|
||||
version: 7.1.3
|
||||
|
@ -4881,6 +4884,10 @@ packages:
|
|||
dot-prop: 5.3.0
|
||||
dev: true
|
||||
|
||||
/complex.js@2.1.1:
|
||||
resolution: {integrity: sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==}
|
||||
dev: false
|
||||
|
||||
/component-emitter@1.3.1:
|
||||
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
|
||||
dev: true
|
||||
|
@ -5429,6 +5436,10 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/decimal.js@10.4.3:
|
||||
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
|
||||
dev: false
|
||||
|
||||
/decode-uri-component@0.2.2:
|
||||
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
@ -5912,6 +5923,10 @@ packages:
|
|||
/escape-html@1.0.3:
|
||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||
|
||||
/escape-latex@1.2.0:
|
||||
resolution: {integrity: sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==}
|
||||
dev: false
|
||||
|
||||
/escape-string-applescript@1.0.0:
|
||||
resolution: {integrity: sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -6662,6 +6677,10 @@ packages:
|
|||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/fraction.js@4.3.4:
|
||||
resolution: {integrity: sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==}
|
||||
dev: false
|
||||
|
||||
/fresh@0.5.2:
|
||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
@ -7671,6 +7690,10 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/javascript-natural-sort@0.7.1:
|
||||
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
|
||||
dev: false
|
||||
|
||||
/jest-changed-files@29.7.0:
|
||||
resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
@ -8709,6 +8732,22 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/mathjs@12.4.0:
|
||||
resolution: {integrity: sha512-4Moy0RNjwMSajEkGGxNUyMMC/CZAcl87WBopvNsJWB4E4EFebpTedr+0/rhqmnOSTH3Wu/3WfiWiw6mqiaHxVw==}
|
||||
engines: {node: '>= 18'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.9
|
||||
complex.js: 2.1.1
|
||||
decimal.js: 10.4.3
|
||||
escape-latex: 1.2.0
|
||||
fraction.js: 4.3.4
|
||||
javascript-natural-sort: 0.7.1
|
||||
seedrandom: 3.0.5
|
||||
tiny-emitter: 2.1.0
|
||||
typed-function: 4.1.1
|
||||
dev: false
|
||||
|
||||
/memfs@3.5.3:
|
||||
resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
@ -10861,6 +10900,10 @@ packages:
|
|||
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
|
||||
dev: false
|
||||
|
||||
/seedrandom@3.0.5:
|
||||
resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==}
|
||||
dev: false
|
||||
|
||||
/segmentit@2.0.3:
|
||||
resolution: {integrity: sha512-7mn2XL3OdTUQ+AhHz7SbgyxLTaQRzTWQNVwiK+UlTO8aePGbSwvKUzTwE4238+OUY9MoR6ksAg35zl8sfTunQQ==}
|
||||
requiresBuild: true
|
||||
|
@ -11635,6 +11678,10 @@ packages:
|
|||
/through@2.3.8:
|
||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
||||
|
||||
/tiny-emitter@2.1.0:
|
||||
resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
|
||||
dev: false
|
||||
|
||||
/tiny-inflate@1.0.3:
|
||||
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
|
||||
|
||||
|
@ -11893,6 +11940,11 @@ packages:
|
|||
resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
|
||||
dev: true
|
||||
|
||||
/typed-function@4.1.1:
|
||||
resolution: {integrity: sha512-Pq1DVubcvibmm8bYcMowjVnnMwPVMeh0DIdA8ad8NZY2sJgapANJmiigSUwlt+EgXxpfIv8MWrQXTIzkfYZLYQ==}
|
||||
engines: {node: '>= 14'}
|
||||
dev: false
|
||||
|
||||
/typedarray@0.0.6:
|
||||
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
|
||||
dev: true
|
||||
|
|
|
@ -55,5 +55,8 @@ export enum ErrorEnum {
|
|||
PRODUCT_EXIST = '1406:产品已存在',
|
||||
|
||||
// Contract
|
||||
CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号'
|
||||
CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号',
|
||||
|
||||
// Inventory 库存不足
|
||||
INVENTORY_INSUFFICIENT = '1408:库存不足'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class BaseService {
|
||||
generateInventoryNumber(): string {
|
||||
// Generate a random inventory number
|
||||
return Math.floor(Math.random() * 1000000).toString();
|
||||
}
|
||||
|
||||
// Add more common methods here
|
||||
}
|
|
@ -16,6 +16,8 @@ import { MaterialsInOutEntity } from './materials_in_out.entity';
|
|||
import { fieldSearch } from '~/shared/database/field-search';
|
||||
import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity';
|
||||
import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum';
|
||||
import { MaterialsInventoryEntity } from '../materials_inventory.entity';
|
||||
import { MaterialsInventoryService } from '../materials_inventory.service';
|
||||
|
||||
@Injectable()
|
||||
export class MaterialsInOutService {
|
||||
|
@ -26,7 +28,8 @@ export class MaterialsInOutService {
|
|||
@InjectRepository(Storage)
|
||||
private storageRepository: Repository<Storage>,
|
||||
@InjectRepository(ParamConfigEntity)
|
||||
private paramConfigRepository: Repository<ParamConfigEntity>
|
||||
private paramConfigRepository: Repository<ParamConfigEntity>,
|
||||
private materialsInventoryService: MaterialsInventoryService
|
||||
) {}
|
||||
/**
|
||||
* 查询所有出入库记录
|
||||
|
@ -57,7 +60,7 @@ export class MaterialsInOutService {
|
|||
.where(fieldSearch(ext))
|
||||
.andWhere('materialsInOut.isDelete = 0')
|
||||
.addOrderBy('materialsInOut.createdAt', 'DESC');
|
||||
|
||||
|
||||
if (productName) {
|
||||
sqb.andWhere('product.name like :productName', { productName: `%${productName}%` });
|
||||
}
|
||||
|
@ -82,8 +85,10 @@ export class MaterialsInOutService {
|
|||
async create(dto: MaterialsInOutDto): Promise<void> {
|
||||
let { inOrOut, inventoryNumber } = dto;
|
||||
if (inOrOut === MaterialsInOrOutEnum.In) {
|
||||
// 入库
|
||||
inventoryNumber = await this.generateInventoryNumber();
|
||||
} else {
|
||||
// 出库
|
||||
const inRecord = await this.materialsInOutRepository.findOne({
|
||||
where: {
|
||||
inventoryNumber
|
||||
|
@ -92,9 +97,15 @@ export class MaterialsInOutService {
|
|||
const { productId } = inRecord;
|
||||
dto.productId = productId;
|
||||
}
|
||||
await this.materialsInOutRepository.insert({
|
||||
...this.materialsInOutRepository.create(dto),
|
||||
inventoryNumber
|
||||
|
||||
await this.entityManager.transaction(async manager => {
|
||||
// 1.生成出入库记录
|
||||
const { productId, quantity } = await manager.create(MaterialsInOutEntity, {
|
||||
...this.materialsInOutRepository.create(dto),
|
||||
inventoryNumber
|
||||
});
|
||||
// 2.更新库存
|
||||
await this.materialsInventoryService.inInventory({ productId, inQuantity: quantity });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ export const permissions = definePermission('app:materials_inventory', {
|
|||
EXPORT: 'export'
|
||||
} as const);
|
||||
|
||||
@ApiTags('MaterialsI Inventory - 原材料盘点')
|
||||
@ApiTags('MaterialsI Inventory - 原材料库存')
|
||||
@ApiSecurityAuth()
|
||||
@Controller('materials-inventory')
|
||||
export class MaterialsInventoryController {
|
||||
|
@ -40,7 +40,7 @@ export class MaterialsInventoryController {
|
|||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: '获取原材料盘点列表' })
|
||||
@ApiOperation({ summary: '获取原材料库存列表' })
|
||||
@ApiResult({ type: [MaterialsInventoryEntity], isPage: true })
|
||||
@Perm(permissions.LIST)
|
||||
async list(@Query() dto: MaterialsInventoryQueryDto) {
|
||||
|
@ -48,7 +48,7 @@ export class MaterialsInventoryController {
|
|||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: '获取原材料盘点信息' })
|
||||
@ApiOperation({ summary: '获取原材料库存信息' })
|
||||
@ApiResult({ type: MaterialsInventoryDto })
|
||||
@Perm(permissions.READ)
|
||||
async info(@IdParam() id: number) {
|
||||
|
@ -56,21 +56,21 @@ export class MaterialsInventoryController {
|
|||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增原材料盘点' })
|
||||
@ApiOperation({ summary: '新增原材料库存' })
|
||||
@Perm(permissions.CREATE)
|
||||
async create(@Body() dto: MaterialsInventoryDto): Promise<void> {
|
||||
await this.miService.create(dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@ApiOperation({ summary: '更新原材料盘点' })
|
||||
@ApiOperation({ summary: '更新原材料库存' })
|
||||
@Perm(permissions.UPDATE)
|
||||
async update(@IdParam() id: number, @Body() dto: MaterialsInventoryUpdateDto): Promise<void> {
|
||||
await this.miService.update(id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: '删除原材料盘点' })
|
||||
@ApiOperation({ summary: '删除原材料库存' })
|
||||
@Perm(permissions.DELETE)
|
||||
async delete(@IdParam() id: number): Promise<void> {
|
||||
await this.miService.delete(id);
|
||||
|
|
|
@ -1,193 +1,41 @@
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm';
|
||||
import { CommonEntity } from '~/common/entity/common.entity';
|
||||
import { Storage } from '../tools/storage/storage.entity';
|
||||
|
||||
@Entity({ name: 'materials_inventory' })
|
||||
export class MaterialsInventoryEntity extends CommonEntity {
|
||||
@Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' })
|
||||
@ApiProperty({ description: '公司名称' })
|
||||
companyName: number;
|
||||
|
||||
@Column({
|
||||
name: 'product',
|
||||
name: 'product_id',
|
||||
type: 'int',
|
||||
comment: '产品名称(字典)'
|
||||
comment: '产品'
|
||||
})
|
||||
@ApiProperty({ description: '产品名称(字典)' })
|
||||
product: number;
|
||||
@ApiProperty({ description: '产品' })
|
||||
productId: number;
|
||||
|
||||
@Column({
|
||||
name: 'unit',
|
||||
type: 'int',
|
||||
comment: '单位(字典)'
|
||||
})
|
||||
@ApiProperty({ description: '单位(字典)' })
|
||||
unit: number;
|
||||
|
||||
@Column({
|
||||
name: 'previous_inventory_quantity',
|
||||
name: 'quantity',
|
||||
type: 'int',
|
||||
default: 0,
|
||||
comment: '之前的库存数量'
|
||||
comment: '库存产品数量'
|
||||
})
|
||||
@ApiProperty({ description: '之前的库存数量' })
|
||||
previousInventoryQuantity: number;
|
||||
@ApiProperty({ description: '库存产品数量' })
|
||||
quantity: number;
|
||||
|
||||
@Column({
|
||||
name: 'previous_unit_price',
|
||||
name: 'unit_price',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '之前的单价'
|
||||
comment: '库存产品单价'
|
||||
})
|
||||
@ApiProperty({ description: '之前的单价' })
|
||||
previousUnitPrice: number;
|
||||
|
||||
@Column({
|
||||
name: 'previous_amount',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
scale: 2,
|
||||
default: 0,
|
||||
comment: '之前的金额'
|
||||
})
|
||||
@ApiProperty({ description: '之前的金额' })
|
||||
previousAmount: number;
|
||||
|
||||
@Column({
|
||||
name: 'inventory_time',
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
comment: '入库时间'
|
||||
})
|
||||
@ApiProperty({ description: '入库时间' })
|
||||
inventoryTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'inventory_quantity',
|
||||
type: 'int',
|
||||
default: 0,
|
||||
comment: '入库数量'
|
||||
})
|
||||
@ApiProperty({ description: '入库数量' })
|
||||
inventoryQuantity: number;
|
||||
|
||||
@Column({
|
||||
name: 'inventory_unit_price',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '入库单价'
|
||||
})
|
||||
@ApiProperty({ description: '入库单价' })
|
||||
inventoryUnitPrice: number;
|
||||
|
||||
@Column({
|
||||
name: 'inventory_amount',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '入库金额'
|
||||
})
|
||||
@ApiProperty({ description: '入库金额' })
|
||||
inventoryAmount: number;
|
||||
|
||||
@Column({
|
||||
name: 'out_time',
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
comment: '出库时间'
|
||||
})
|
||||
@ApiProperty({ description: '出库时间' })
|
||||
outime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'out_quantity',
|
||||
type: 'int',
|
||||
default: 0,
|
||||
comment: '出库数量'
|
||||
})
|
||||
@ApiProperty({ description: '出库数量' })
|
||||
outQuantity: number;
|
||||
|
||||
@Column({
|
||||
name: 'out_unit_price',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '出库单价'
|
||||
})
|
||||
@ApiProperty({ description: '出库单价' })
|
||||
outUnitPrice: number;
|
||||
|
||||
@Column({
|
||||
name: 'out_amount',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '出库金额'
|
||||
})
|
||||
@ApiProperty({ description: '出库金额' })
|
||||
outAmount: number;
|
||||
|
||||
@Column({
|
||||
name: 'current_inventory_quantity',
|
||||
type: 'int',
|
||||
default: 0,
|
||||
comment: '现在的结存数量'
|
||||
})
|
||||
@ApiProperty({ description: '现在的结存数量' })
|
||||
currentInventoryQuantity: number;
|
||||
|
||||
@Column({
|
||||
name: 'current_unit_price',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '现在的单价'
|
||||
})
|
||||
@ApiProperty({ description: '现在的单价' })
|
||||
currentUnitPrice: number;
|
||||
|
||||
@Column({
|
||||
name: 'current_amount',
|
||||
type: 'decimal',
|
||||
precision: 10,
|
||||
default: 0,
|
||||
scale: 2,
|
||||
comment: '现在的金额'
|
||||
})
|
||||
@ApiProperty({ description: '现在的金额' })
|
||||
currentAmount: number;
|
||||
|
||||
@Column({ name: 'agent', type: 'varchar', length: 50, comment: '经办人', nullable: true })
|
||||
@ApiProperty({ description: '经办人' })
|
||||
agent: string;
|
||||
|
||||
@Column({
|
||||
name: 'issuance_number',
|
||||
type: 'varchar',
|
||||
length: 100,
|
||||
comment: '领料单号'
|
||||
})
|
||||
@ApiProperty({ description: '领料单号' })
|
||||
issuanceNumber: string;
|
||||
@ApiProperty({ description: '库存产品单价' })
|
||||
unitPrice: number;
|
||||
|
||||
@Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true })
|
||||
@ApiProperty({ description: '备注' })
|
||||
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;
|
||||
|
|
|
@ -18,6 +18,9 @@ import { fieldSearch } from '~/shared/database/field-search';
|
|||
import { groupBy, uniqBy } from 'lodash';
|
||||
import { MaterialsInOrOutEnum } 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';
|
||||
@Injectable()
|
||||
export class MaterialsInventoryService {
|
||||
const;
|
||||
|
@ -52,7 +55,7 @@ export class MaterialsInventoryService {
|
|||
.leftJoin('mio.product', 'product')
|
||||
.leftJoin('product.unit', 'unit')
|
||||
.leftJoin('product.company', 'company')
|
||||
.addSelect(['project.id','project.name', 'product.name', 'unit.label', 'company.name'])
|
||||
.addSelect(['project.id', 'project.name', 'product.name', 'unit.label', 'company.name'])
|
||||
.where(fieldSearch({ time }))
|
||||
.andWhere('mio.isDelete = 0');
|
||||
|
||||
|
@ -262,14 +265,14 @@ export class MaterialsInventoryService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* 新增库存
|
||||
*/
|
||||
async create(dto: MaterialsInventoryDto): Promise<void> {
|
||||
await this.materialsInventoryRepository.insert(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* 更新库存
|
||||
*/
|
||||
async update(id: number, data: Partial<MaterialsInventoryUpdateDto>): Promise<void> {
|
||||
await this.entityManager.transaction(async manager => {
|
||||
|
@ -279,6 +282,79 @@ export class MaterialsInventoryService {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品入库
|
||||
*/
|
||||
async inInventory(data: {
|
||||
productId: number;
|
||||
inQuantity: number;
|
||||
unitPrice?: number;
|
||||
}): Promise<void> {
|
||||
const { productId, inQuantity, unitPrice } = data;
|
||||
|
||||
await this.entityManager.transaction(async manager => {
|
||||
const exsitedInventory = await this.materialsInventoryRepository.findOne({
|
||||
where: { productId },
|
||||
lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改
|
||||
});
|
||||
|
||||
// 若不存在库存,直接新增库存
|
||||
if (!exsitedInventory) {
|
||||
await this.entityManager.transaction(async manager => {
|
||||
if (exsitedInventory) {
|
||||
await manager.insert(MaterialsInventoryEntity, {
|
||||
productId,
|
||||
unitPrice,
|
||||
quantity: inQuantity
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 若存在库存,则库存增加
|
||||
let { quantity, id } = exsitedInventory;
|
||||
const newQuantity = calcNumber(quantity || 0, inQuantity || 0, 'add');
|
||||
if (isNaN(newQuantity)) {
|
||||
throw new Error('库存数量不合法');
|
||||
}
|
||||
await manager.update(MaterialsInventoryEntity, id, {
|
||||
quantity: newQuantity
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品入库
|
||||
*/
|
||||
async outInventory(data: { productId: number; outQuantity: number }): Promise<void> {
|
||||
const { productId, outQuantity } = data;
|
||||
|
||||
await this.entityManager.transaction(async manager => {
|
||||
// 开启悲观行锁,防止脏读和修改
|
||||
const inventory = await this.materialsInventoryRepository.findOne({
|
||||
where: { productId },
|
||||
lock: { mode: 'pessimistic_write' }
|
||||
});
|
||||
// 检查库存剩余
|
||||
if (inventory.quantity < outQuantity) {
|
||||
throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT);
|
||||
}
|
||||
// 库存充足,可以出库
|
||||
let { quantity, id } = inventory;
|
||||
const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract');
|
||||
if (isNaN(newQuantity)) {
|
||||
throw new Error('库存数量不合法');
|
||||
}
|
||||
await manager.update(MaterialsInventoryEntity, id, {
|
||||
quantity: newQuantity
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品出库
|
||||
*/
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { customAlphabet, nanoid } from 'nanoid';
|
||||
|
||||
import { md5 } from './crypto.util';
|
||||
import { add, subtract, multiply, divide, bignumber, BigNumber } from 'mathjs';
|
||||
|
||||
export function getAvatar(mail: string | undefined) {
|
||||
if (!mail) return '';
|
||||
|
@ -53,3 +54,25 @@ export const hashString = function (str, seed = 0) {
|
|||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||
};
|
||||
/**
|
||||
* 使用mathjs进行四则运算,不丢失精度
|
||||
*/
|
||||
export function calcNumber(
|
||||
firstNumber: number,
|
||||
secondNumber: number,
|
||||
option: CalclateOption
|
||||
): number {
|
||||
switch (option) {
|
||||
case 'add':
|
||||
return add(bignumber(firstNumber), bignumber(secondNumber)).toNumber();
|
||||
case 'subtract':
|
||||
return subtract(bignumber(firstNumber), bignumber(secondNumber)).toNumber();
|
||||
// case 'multiply':
|
||||
// return multiply(bignumber(firstNumber), bignumber(secondNumber));
|
||||
// case 'divide':
|
||||
// return divide(bignumber(firstNumber), bignumber(secondNumber));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
type CalclateOption = 'add' | 'subtract' | 'multiply' | 'divide';
|
||||
|
|
Loading…
Reference in New Issue