feat: 产品根据项目进行库存数量维护完成
This commit is contained in:
parent
5d9ce4cef2
commit
b07434e536
|
@ -57,6 +57,7 @@ export enum ErrorEnum {
|
||||||
// Contract
|
// Contract
|
||||||
CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号',
|
CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号',
|
||||||
|
|
||||||
// Inventory 库存不足
|
// Inventory
|
||||||
INVENTORY_INSUFFICIENT = '1408:库存不足'
|
INVENTORY_INSUFFICIENT = '1408:库存不足',
|
||||||
|
MATERIALS_IN_OUT_NOT_FOUND = '1409:出入库信息不存在',
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { ParamConfigEntity } from '~/modules/system/param-config/param-config.en
|
||||||
import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum';
|
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';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MaterialsInOutService {
|
export class MaterialsInOutService {
|
||||||
|
@ -83,8 +84,8 @@ export class MaterialsInOutService {
|
||||||
* 新增
|
* 新增
|
||||||
*/
|
*/
|
||||||
async create(dto: MaterialsInOutDto): Promise<void> {
|
async create(dto: MaterialsInOutDto): Promise<void> {
|
||||||
let { inOrOut, inventoryNumber } = dto;
|
let { inOrOut, inventoryNumber, projectId } = dto;
|
||||||
if (inOrOut === MaterialsInOrOutEnum.In) {
|
if (Object.is(inOrOut, MaterialsInOrOutEnum.In)) {
|
||||||
// 入库
|
// 入库
|
||||||
inventoryNumber = await this.generateInventoryNumber();
|
inventoryNumber = await this.generateInventoryNumber();
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,12 +101,16 @@ export class MaterialsInOutService {
|
||||||
|
|
||||||
await this.entityManager.transaction(async manager => {
|
await this.entityManager.transaction(async manager => {
|
||||||
// 1.生成出入库记录
|
// 1.生成出入库记录
|
||||||
const { productId, quantity } = await manager.create(MaterialsInOutEntity, {
|
const { productId, quantity, unitPrice } = await manager.save(MaterialsInOutEntity, {
|
||||||
...this.materialsInOutRepository.create(dto),
|
...this.materialsInOutRepository.create(dto),
|
||||||
inventoryNumber
|
inventoryNumber
|
||||||
});
|
});
|
||||||
// 2.更新库存
|
// 2.更新增减库存
|
||||||
await this.materialsInventoryService.inInventory({ productId, inQuantity: quantity });
|
await (
|
||||||
|
Object.is(inOrOut, MaterialsInOrOutEnum.In)
|
||||||
|
? this.materialsInventoryService.inInventory
|
||||||
|
: this.materialsInventoryService.outInventory
|
||||||
|
)({ productId, quantity, unitPrice, projectId }, manager);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,9 +119,52 @@ export class MaterialsInOutService {
|
||||||
*/
|
*/
|
||||||
async update(id: number, { fileIds, ...data }: Partial<MaterialsInOutUpdateDto>): Promise<void> {
|
async update(id: number, { fileIds, ...data }: Partial<MaterialsInOutUpdateDto>): Promise<void> {
|
||||||
await this.entityManager.transaction(async manager => {
|
await this.entityManager.transaction(async manager => {
|
||||||
|
const entity = await manager.findOne(MaterialsInOutEntity, {
|
||||||
|
where: {
|
||||||
|
id
|
||||||
|
},
|
||||||
|
lock: { mode: 'pessimistic_write' }
|
||||||
|
});
|
||||||
await manager.update(MaterialsInOutEntity, id, {
|
await manager.update(MaterialsInOutEntity, id, {
|
||||||
...data
|
...data
|
||||||
});
|
});
|
||||||
|
let changedQuantity = 0;
|
||||||
|
if (isDefined(data.quantity) && entity.quantity !== data.quantity) {
|
||||||
|
if (entity.inOrOut === MaterialsInOrOutEnum.In) {
|
||||||
|
// 入库减少等于出库
|
||||||
|
if (data.quantity - entity.quantity < 0) {
|
||||||
|
data.inOrOut = MaterialsInOrOutEnum.Out;
|
||||||
|
} else {
|
||||||
|
// 入库增多等于入库
|
||||||
|
data.inOrOut = MaterialsInOrOutEnum.In;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 出库减少等于入库
|
||||||
|
if (data.quantity - entity.quantity < 0) {
|
||||||
|
data.inOrOut = MaterialsInOrOutEnum.In;
|
||||||
|
} else {
|
||||||
|
// 出库增多等于出库
|
||||||
|
data.inOrOut = MaterialsInOrOutEnum.Out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changedQuantity = Math.abs(data.quantity - entity.quantity);
|
||||||
|
}
|
||||||
|
// 2.更新增减库存
|
||||||
|
if (changedQuantity !== 0) {
|
||||||
|
await (
|
||||||
|
Object.is(data.inOrOut, MaterialsInOrOutEnum.In)
|
||||||
|
? this.materialsInventoryService.inInventory
|
||||||
|
: this.materialsInventoryService.outInventory
|
||||||
|
)(
|
||||||
|
{
|
||||||
|
productId: entity.productId,
|
||||||
|
quantity: Math.abs(changedQuantity),
|
||||||
|
unitPrice: undefined,
|
||||||
|
projectId: entity.projectId
|
||||||
|
},
|
||||||
|
manager
|
||||||
|
);
|
||||||
|
}
|
||||||
if (fileIds?.length) {
|
if (fileIds?.length) {
|
||||||
const count = await this.storageRepository
|
const count = await this.storageRepository
|
||||||
.createQueryBuilder('storage')
|
.createQueryBuilder('storage')
|
||||||
|
@ -139,6 +187,34 @@ export class MaterialsInOutService {
|
||||||
* 删除
|
* 删除
|
||||||
*/
|
*/
|
||||||
async delete(id: number): Promise<void> {
|
async delete(id: number): Promise<void> {
|
||||||
|
await this.entityManager.transaction(async manager => {
|
||||||
|
const entity = await manager.findOne(MaterialsInOutEntity, {
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
isDelete: 0
|
||||||
|
},
|
||||||
|
lock: { mode: 'pessimistic_write' }
|
||||||
|
});
|
||||||
|
if (!entity) {
|
||||||
|
throw new BusinessException(ErrorEnum.MATERIALS_IN_OUT_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新库存
|
||||||
|
await (
|
||||||
|
Object.is(entity.inOrOut, MaterialsInOrOutEnum.In)
|
||||||
|
? this.materialsInventoryService.outInventory
|
||||||
|
: this.materialsInventoryService.inInventory
|
||||||
|
)(
|
||||||
|
{
|
||||||
|
productId: entity.productId,
|
||||||
|
quantity: entity.quantity,
|
||||||
|
unitPrice: undefined,
|
||||||
|
projectId: entity.projectId
|
||||||
|
},
|
||||||
|
manager
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// 出入库比较重要,做逻辑删除
|
// 出入库比较重要,做逻辑删除
|
||||||
await this.materialsInOutRepository.update(id, { isDelete: 1 });
|
await this.materialsInOutRepository.update(id, { isDelete: 1 });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm';
|
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm';
|
||||||
import { CommonEntity } from '~/common/entity/common.entity';
|
import { CommonEntity } from '~/common/entity/common.entity';
|
||||||
|
import { ProductEntity } from '../product/product.entity';
|
||||||
|
import { ProjectEntity } from '../project/project.entity';
|
||||||
|
|
||||||
@Entity({ name: 'materials_inventory' })
|
@Entity({ name: 'materials_inventory' })
|
||||||
export class MaterialsInventoryEntity extends CommonEntity {
|
export class MaterialsInventoryEntity extends CommonEntity {
|
||||||
|
@Column({
|
||||||
|
name: 'project_id',
|
||||||
|
type: 'int',
|
||||||
|
comment: '项目'
|
||||||
|
})
|
||||||
|
@ApiProperty({ description: '项目' })
|
||||||
|
projectId: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
name: 'product_id',
|
name: 'product_id',
|
||||||
type: 'int',
|
type: 'int',
|
||||||
|
@ -24,9 +34,9 @@ export class MaterialsInventoryEntity 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: '库存产品单价' })
|
||||||
|
@ -39,4 +49,12 @@ export class MaterialsInventoryEntity extends CommonEntity {
|
||||||
@Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' })
|
@Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' })
|
||||||
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
||||||
isDelete: number;
|
isDelete: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => ProjectEntity)
|
||||||
|
@JoinColumn({ name: 'project_id' })
|
||||||
|
project: ProjectEntity;
|
||||||
|
|
||||||
|
@ManyToOne(() => ProductEntity)
|
||||||
|
@JoinColumn({ name: 'product_id' })
|
||||||
|
product: ProductEntity;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { MaterialsInventoryEntity } from './materials_inventory.entity';
|
import { MaterialsInventoryEntity } from './materials_inventory.entity';
|
||||||
import { EntityManager, Repository } from 'typeorm';
|
import { EntityManager, In, MoreThan, Repository } from 'typeorm';
|
||||||
import {
|
import {
|
||||||
MaterialsInventoryDto,
|
MaterialsInventoryDto,
|
||||||
MaterialsInventoryExportDto,
|
MaterialsInventoryExportDto,
|
||||||
|
@ -15,7 +15,7 @@ import * as ExcelJS from 'exceljs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
import { MaterialsInOutEntity } from './in_out/materials_in_out.entity';
|
||||||
import { fieldSearch } from '~/shared/database/field-search';
|
import { fieldSearch } from '~/shared/database/field-search';
|
||||||
import { groupBy, uniqBy } from 'lodash';
|
import { groupBy, sum, uniqBy } from 'lodash';
|
||||||
import { MaterialsInOrOutEnum } from '~/constants/enum';
|
import { MaterialsInOrOutEnum } from '~/constants/enum';
|
||||||
import { ProjectEntity } from '../project/project.entity';
|
import { ProjectEntity } from '../project/project.entity';
|
||||||
import { calcNumber } from '~/utils';
|
import { calcNumber } from '~/utils';
|
||||||
|
@ -48,6 +48,13 @@ export class MaterialsInventoryService {
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
projects = [await this.projectRepository.findOneBy({ id: projectId })];
|
projects = [await this.projectRepository.findOneBy({ id: projectId })];
|
||||||
}
|
}
|
||||||
|
// 查询出项目产品所属的当前库存
|
||||||
|
const inventoriesInProjects = await this.materialsInventoryRepository.find({
|
||||||
|
where: {
|
||||||
|
...(projects?.length ? { projectId: In(projects.map(item => item.id)) } : null)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 生成数据
|
// 生成数据
|
||||||
const sqb = this.materialsInOutRepository
|
const sqb = this.materialsInOutRepository
|
||||||
.createQueryBuilder('mio')
|
.createQueryBuilder('mio')
|
||||||
|
@ -56,12 +63,15 @@ export class MaterialsInventoryService {
|
||||||
.leftJoin('product.unit', 'unit')
|
.leftJoin('product.unit', 'unit')
|
||||||
.leftJoin('product.company', 'company')
|
.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 }))
|
.where({
|
||||||
|
time: MoreThan(time[0])
|
||||||
|
})
|
||||||
.andWhere('mio.isDelete = 0');
|
.andWhere('mio.isDelete = 0');
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
sqb.andWhere('project.id = :projectId', { projectId });
|
sqb.andWhere('project.id = :projectId', { projectId });
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
|
@ -71,12 +81,19 @@ export class MaterialsInventoryService {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const project of projects) {
|
for (const project of projects) {
|
||||||
|
const currentProjectInventories = inventoriesInProjects.filter(({ projectId }) =>
|
||||||
|
Object.is(projectId, project.id)
|
||||||
|
);
|
||||||
const currentProjectData = data.filter(item => item.projectId === project.id);
|
const currentProjectData = data.filter(item => item.projectId === project.id);
|
||||||
|
const currentMonthProjectData = currentProjectData.filter(item => {
|
||||||
|
return (
|
||||||
|
dayjs(item.time).isAfter(dayjs(time[0])) && dayjs(item.time).isBefore(dayjs(time[1]))
|
||||||
|
);
|
||||||
|
});
|
||||||
const sheet = workbook.addWorksheet(project.name);
|
const sheet = workbook.addWorksheet(project.name);
|
||||||
sheet.mergeCells('A1:T1');
|
sheet.mergeCells('A1:T1');
|
||||||
// 设置标题
|
// 设置标题
|
||||||
sheet.getCell('A1').value = '山东矿机华信智能科技有限公司原材料盘点表';
|
sheet.getCell('A1').value = '山东矿机华信智能科技有限公司原材料盘点表';
|
||||||
|
|
||||||
// 设置日期
|
// 设置日期
|
||||||
sheet.mergeCells('A2:B2');
|
sheet.mergeCells('A2:B2');
|
||||||
sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`;
|
sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`;
|
||||||
|
@ -168,21 +185,53 @@ export class MaterialsInventoryService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupedData = groupBy<MaterialsInOutEntity>(currentProjectData, 'inventoryNumber');
|
const groupedData = groupBy<MaterialsInOutEntity>(currentMonthProjectData, 'inventoryNumber');
|
||||||
let number = 0;
|
let number = 0;
|
||||||
|
const groupedInventories = groupBy(
|
||||||
|
currentProjectInventories,
|
||||||
|
item => `${item.projectId}_${item.productId}`
|
||||||
|
);
|
||||||
for (const key in groupedData) {
|
for (const key in groupedData) {
|
||||||
// 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。
|
// 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。
|
||||||
const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In);
|
const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In);
|
||||||
const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out);
|
const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out);
|
||||||
|
const currInventories =
|
||||||
|
groupedInventories[`${inRecord.projectId}_${inRecord.productId}`]?.shift();
|
||||||
|
const allDataFromMonth = data.filter(
|
||||||
|
res => res.projectId === inRecord.projectId && res.productId === inRecord.productId
|
||||||
|
);
|
||||||
|
let currentQuantity = 0;
|
||||||
|
let balanceQuantity = 0;
|
||||||
|
// 月初库存数量
|
||||||
|
if (currInventories) {
|
||||||
|
const sumIn = sum(
|
||||||
|
allDataFromMonth
|
||||||
|
.filter(res => Object.is(res.inOrOut, MaterialsInOrOutEnum.In))
|
||||||
|
.map(item => item.quantity)
|
||||||
|
);
|
||||||
|
const sumOut = sum(
|
||||||
|
allDataFromMonth
|
||||||
|
.filter(res => Object.is(res.inOrOut, MaterialsInOrOutEnum.Out))
|
||||||
|
.map(item => item.quantity)
|
||||||
|
);
|
||||||
|
const sumDistance = calcNumber(sumIn, sumOut, 'subtract');
|
||||||
|
currentQuantity = calcNumber(currInventories.quantity, sumDistance, 'subtract');
|
||||||
|
}
|
||||||
|
// 结存库存数量
|
||||||
|
balanceQuantity = calcNumber(
|
||||||
|
currentQuantity,
|
||||||
|
calcNumber(inRecord.quantity, outRecord.quantity, 'subtract'),
|
||||||
|
'add'
|
||||||
|
);
|
||||||
number++;
|
number++;
|
||||||
sheet.addRow([
|
sheet.addRow([
|
||||||
`${inRecord.inventoryNumber}`,
|
`${inRecord.inventoryNumber || ''}`,
|
||||||
inRecord.product.company.name,
|
inRecord.product.company.name || '',
|
||||||
inRecord.product.name,
|
inRecord.product.name || '',
|
||||||
inRecord.product.unit.label,
|
inRecord.product.unit.label || '',
|
||||||
'0',
|
currentQuantity,
|
||||||
'0',
|
parseFloat(`${inRecord.unitPrice || 0}`),
|
||||||
'0',
|
calcNumber(currentQuantity, inRecord.unitPrice || 0, 'multiply'),
|
||||||
inRecord.time,
|
inRecord.time,
|
||||||
inRecord.quantity,
|
inRecord.quantity,
|
||||||
parseFloat(`${inRecord.unitPrice || 0}`),
|
parseFloat(`${inRecord.unitPrice || 0}`),
|
||||||
|
@ -191,12 +240,12 @@ export class MaterialsInventoryService {
|
||||||
outRecord?.quantity || '',
|
outRecord?.quantity || '',
|
||||||
parseFloat(`${outRecord.unitPrice || 0}`),
|
parseFloat(`${outRecord.unitPrice || 0}`),
|
||||||
parseFloat(`${outRecord.amount || 0}`),
|
parseFloat(`${outRecord.amount || 0}`),
|
||||||
'0',
|
balanceQuantity,
|
||||||
'0',
|
parseFloat(`${outRecord.unitPrice || 0}`),
|
||||||
'0',
|
calcNumber(balanceQuantity, outRecord.unitPrice || 0, 'multiply'),
|
||||||
outRecord?.agent,
|
outRecord?.agent || '',
|
||||||
outRecord?.issuanceNumber,
|
outRecord?.issuanceNumber || '',
|
||||||
outRecord?.remark
|
outRecord?.remark || ''
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
sheet.getCell('A1').font = { size: HEADER_FONT_SIZE };
|
sheet.getCell('A1').font = { size: HEADER_FONT_SIZE };
|
||||||
|
@ -256,8 +305,12 @@ export class MaterialsInventoryService {
|
||||||
}: MaterialsInventoryQueryDto): Promise<Pagination<MaterialsInventoryEntity>> {
|
}: MaterialsInventoryQueryDto): Promise<Pagination<MaterialsInventoryEntity>> {
|
||||||
const queryBuilder = this.materialsInventoryRepository
|
const queryBuilder = this.materialsInventoryRepository
|
||||||
.createQueryBuilder('materialsInventory')
|
.createQueryBuilder('materialsInventory')
|
||||||
|
.leftJoin('materialsInventory.project', 'project')
|
||||||
|
.leftJoin('materialsInventory.product', 'product')
|
||||||
|
.leftJoin('product.unit', 'unit')
|
||||||
|
.leftJoin('product.company', 'company')
|
||||||
|
.addSelect(['project.name', 'product.name', 'unit.label', 'company.name'])
|
||||||
.where('materialsInventory.isDelete = 0');
|
.where('materialsInventory.isDelete = 0');
|
||||||
|
|
||||||
return paginate<MaterialsInventoryEntity>(queryBuilder, {
|
return paginate<MaterialsInventoryEntity>(queryBuilder, {
|
||||||
page,
|
page,
|
||||||
pageSize
|
pageSize
|
||||||
|
@ -283,35 +336,37 @@ export class MaterialsInventoryService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产品入库
|
* 产品入库后计算最新库存
|
||||||
|
* @param data 传入项目ID,产品ID和入库数量和单价
|
||||||
|
* @param manager 传入事务对象防止开启多重事务
|
||||||
*/
|
*/
|
||||||
async inInventory(data: {
|
async inInventory(
|
||||||
|
data: {
|
||||||
|
projectId: number;
|
||||||
productId: number;
|
productId: number;
|
||||||
inQuantity: number;
|
quantity: number;
|
||||||
unitPrice?: number;
|
unitPrice?: number;
|
||||||
}): Promise<void> {
|
},
|
||||||
const { productId, inQuantity, unitPrice } = data;
|
manager: EntityManager
|
||||||
|
): Promise<void> {
|
||||||
|
const { projectId, productId, quantity: inQuantity, unitPrice } = data;
|
||||||
|
|
||||||
await this.entityManager.transaction(async manager => {
|
const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, {
|
||||||
const exsitedInventory = await this.materialsInventoryRepository.findOne({
|
where: { projectId, productId }, // 查出某个项目的某个产品的库存情况
|
||||||
where: { productId },
|
|
||||||
lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改
|
lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改
|
||||||
});
|
});
|
||||||
|
|
||||||
// 若不存在库存,直接新增库存
|
// 若不存在库存,直接新增库存
|
||||||
if (!exsitedInventory) {
|
if (!exsitedInventory) {
|
||||||
await this.entityManager.transaction(async manager => {
|
|
||||||
if (exsitedInventory) {
|
|
||||||
await manager.insert(MaterialsInventoryEntity, {
|
await manager.insert(MaterialsInventoryEntity, {
|
||||||
|
projectId,
|
||||||
productId,
|
productId,
|
||||||
unitPrice,
|
unitPrice,
|
||||||
quantity: inQuantity
|
quantity: inQuantity
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 若存在库存,则库存增加
|
// 若该项目存在库存,则该项目该产品的库存增加
|
||||||
let { quantity, id } = exsitedInventory;
|
let { quantity, id } = exsitedInventory;
|
||||||
const newQuantity = calcNumber(quantity || 0, inQuantity || 0, 'add');
|
const newQuantity = calcNumber(quantity || 0, inQuantity || 0, 'add');
|
||||||
if (isNaN(newQuantity)) {
|
if (isNaN(newQuantity)) {
|
||||||
|
@ -320,26 +375,34 @@ export class MaterialsInventoryService {
|
||||||
await manager.update(MaterialsInventoryEntity, id, {
|
await manager.update(MaterialsInventoryEntity, id, {
|
||||||
quantity: newQuantity
|
quantity: newQuantity
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产品入库
|
* 产品出库
|
||||||
|
* @param data 传入产品id和入库数量和单价
|
||||||
|
* @param manager 传入事务对象防止开启多重事务
|
||||||
*/
|
*/
|
||||||
async outInventory(data: { productId: number; outQuantity: number }): Promise<void> {
|
async outInventory(
|
||||||
const { productId, outQuantity } = data;
|
data: {
|
||||||
|
projectId: number;
|
||||||
|
productId: number;
|
||||||
|
quantity: number;
|
||||||
|
unitPrice?: number;
|
||||||
|
},
|
||||||
|
manager: EntityManager
|
||||||
|
): Promise<void> {
|
||||||
|
const { projectId, productId, quantity: outQuantity } = data;
|
||||||
|
|
||||||
await this.entityManager.transaction(async manager => {
|
|
||||||
// 开启悲观行锁,防止脏读和修改
|
// 开启悲观行锁,防止脏读和修改
|
||||||
const inventory = await this.materialsInventoryRepository.findOne({
|
const inventory = await manager.findOne(MaterialsInventoryEntity, {
|
||||||
where: { productId },
|
where: { projectId, productId },
|
||||||
lock: { mode: 'pessimistic_write' }
|
lock: { mode: 'pessimistic_write' }
|
||||||
});
|
});
|
||||||
// 检查库存剩余
|
// 检查库存剩余
|
||||||
if (inventory.quantity < outQuantity) {
|
if (inventory.quantity < outQuantity) {
|
||||||
throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT);
|
throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT);
|
||||||
}
|
}
|
||||||
// 库存充足,可以出库
|
// 若该项目的该产品库存充足,则该项目该产品的库存减少
|
||||||
let { quantity, id } = inventory;
|
let { quantity, id } = inventory;
|
||||||
const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract');
|
const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract');
|
||||||
if (isNaN(newQuantity)) {
|
if (isNaN(newQuantity)) {
|
||||||
|
@ -348,13 +411,7 @@ export class MaterialsInventoryService {
|
||||||
await manager.update(MaterialsInventoryEntity, id, {
|
await manager.update(MaterialsInventoryEntity, id, {
|
||||||
quantity: newQuantity
|
quantity: newQuantity
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 产品出库
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除
|
* 删除
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,11 @@ export class ProductDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '产品备注' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
remark: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '单位(字典)' })
|
@ApiProperty({ description: '单位(字典)' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
|
|
@ -26,6 +26,15 @@ export class ProductEntity extends CommonEntity {
|
||||||
@ApiProperty({ description: '产品名称' })
|
@ApiProperty({ description: '产品名称' })
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'remark',
|
||||||
|
type: 'varchar',
|
||||||
|
length: 255,
|
||||||
|
comment: '备注'
|
||||||
|
})
|
||||||
|
@ApiProperty({ description: '产品备注' })
|
||||||
|
remark: string;
|
||||||
|
|
||||||
@Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' })
|
@Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' })
|
||||||
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
@ApiProperty({ description: '删除状态:0未删除,1已删除' })
|
||||||
isDelete: number;
|
isDelete: number;
|
||||||
|
|
|
@ -67,10 +67,10 @@ export function calcNumber(
|
||||||
return add(bignumber(firstNumber), bignumber(secondNumber)).toNumber();
|
return add(bignumber(firstNumber), bignumber(secondNumber)).toNumber();
|
||||||
case 'subtract':
|
case 'subtract':
|
||||||
return subtract(bignumber(firstNumber), bignumber(secondNumber)).toNumber();
|
return subtract(bignumber(firstNumber), bignumber(secondNumber)).toNumber();
|
||||||
// case 'multiply':
|
case 'multiply':
|
||||||
// return multiply(bignumber(firstNumber), bignumber(secondNumber));
|
return (multiply(bignumber(firstNumber), bignumber(secondNumber)) as BigNumber).toNumber();
|
||||||
// case 'divide':
|
case 'divide':
|
||||||
// return divide(bignumber(firstNumber), bignumber(secondNumber));
|
return (divide(bignumber(firstNumber), bignumber(secondNumber)) as BigNumber).toNumber();
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue