feat: add generated code
This commit is contained in:
78
backend/prisma/schema.prisma
Normal file
78
backend/prisma/schema.prisma
Normal file
@@ -0,0 +1,78 @@
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
enum MovementKind {
|
||||
Receipt
|
||||
Issue
|
||||
Transfer
|
||||
Adjustment
|
||||
}
|
||||
|
||||
model ProductType {
|
||||
id String @id @default(uuid())
|
||||
name String @unique
|
||||
products Product[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("product_types")
|
||||
}
|
||||
|
||||
model Product {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
productType ProductType @relation(fields: [productTypeId], references: [id])
|
||||
productTypeId String
|
||||
parent Product? @relation("ProductHierarchy", fields: [parentId], references: [id])
|
||||
parentId String?
|
||||
children Product[] @relation("ProductHierarchy")
|
||||
stockStatus StockStatus?
|
||||
lines StockMovementLine[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("products")
|
||||
}
|
||||
|
||||
model StockStatus {
|
||||
product Product @relation(fields: [productId], references: [id])
|
||||
productId String @id @unique
|
||||
quantity Decimal @default(0)
|
||||
inStock Boolean @default(false)
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
|
||||
@@map("stock_statuses")
|
||||
}
|
||||
|
||||
model StockMovement {
|
||||
id String @id @default(uuid())
|
||||
documentDate DateTime @db.Date
|
||||
kind MovementKind
|
||||
lines StockMovementLine[]
|
||||
comment String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("stock_movements")
|
||||
}
|
||||
|
||||
model StockMovementLine {
|
||||
id String @id @default(uuid())
|
||||
product Product @relation(fields: [productId], references: [id])
|
||||
productId String
|
||||
quantity Decimal
|
||||
inStock Boolean
|
||||
movement StockMovement @relation(fields: [movementId], references: [id])
|
||||
movementId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([movementId, productId])
|
||||
@@map("stock_movement_lines")
|
||||
}
|
||||
19
backend/src/app.module.ts
Normal file
19
backend/src/app.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { PrismaService } from './prisma/prisma.service';
|
||||
import { ProductTypeModule } from './product-type/product-type.module';
|
||||
import { ProductModule } from './product/product.module';
|
||||
import { StockStatusModule } from './stock-status/stock-status.module';
|
||||
import { StockMovementModule } from './stock-movement/stock-movement.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AuthModule,
|
||||
ProductTypeModule,
|
||||
ProductModule,
|
||||
StockStatusModule,
|
||||
StockMovementModule,
|
||||
],
|
||||
providers: [PrismaService],
|
||||
})
|
||||
export class AppModule {}
|
||||
9
backend/src/product-type/dto/create-product-type.dto.ts
Normal file
9
backend/src/product-type/dto/create-product-type.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { IsString, IsNotEmpty } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateProductTypeDto {
|
||||
@ApiProperty({ description: 'Наименование типа товара' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
}
|
||||
4
backend/src/product-type/dto/update-product-type.dto.ts
Normal file
4
backend/src/product-type/dto/update-product-type.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateProductTypeDto } from './create-product-type.dto';
|
||||
|
||||
export class UpdateProductTypeDto extends PartialType(CreateProductTypeDto) {}
|
||||
54
backend/src/product-type/product-type.controller.ts
Normal file
54
backend/src/product-type/product-type.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { ProductTypeService } from './product-type.service';
|
||||
import { CreateProductTypeDto } from './dto/create-product-type.dto';
|
||||
import { UpdateProductTypeDto } from './dto/update-product-type.dto';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
|
||||
const parseJson = <T>(value?: string): T | undefined => {
|
||||
if (!value) return undefined;
|
||||
try { return JSON.parse(value) as T; } catch { return undefined; }
|
||||
};
|
||||
|
||||
@ApiTags('product-type')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('product-types')
|
||||
export class ProductTypeController {
|
||||
constructor(private readonly productTypeService: ProductTypeService) {}
|
||||
|
||||
@Get()
|
||||
findAll(
|
||||
@Query('skip') skip?: string,
|
||||
@Query('take') take?: string,
|
||||
@Query('orderBy') orderBy?: string,
|
||||
@Query('where') where?: string,
|
||||
) {
|
||||
return this.productTypeService.findAll({
|
||||
skip: skip ? Number(skip) : 0,
|
||||
take: take ? Number(take) : 25,
|
||||
orderBy: parseJson(orderBy),
|
||||
where: parseJson(where),
|
||||
});
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.productTypeService.findOne({ id });
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateProductTypeDto) {
|
||||
return this.productTypeService.create(dto);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateProductTypeDto) {
|
||||
return this.productTypeService.update({ where: { id }, data: dto });
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.productTypeService.remove({ id });
|
||||
}
|
||||
}
|
||||
10
backend/src/product-type/product-type.module.ts
Normal file
10
backend/src/product-type/product-type.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ProductTypeService } from './product-type.service';
|
||||
import { ProductTypeController } from './product-type.controller';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ProductTypeController],
|
||||
providers: [ProductTypeService, PrismaService],
|
||||
})
|
||||
export class ProductTypeModule {}
|
||||
41
backend/src/product-type/product-type.service.ts
Normal file
41
backend/src/product-type/product-type.service.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { ProductType, Prisma } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class ProductTypeService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
where?: Prisma.ProductTypeWhereInput;
|
||||
orderBy?: Prisma.ProductTypeOrderByWithRelationInput;
|
||||
}): Promise<{ data: ProductType[]; total: number }> {
|
||||
const { skip, take, where, orderBy } = params;
|
||||
const [data, total] = await this.prisma.$transaction([
|
||||
this.prisma.productType.findMany({ skip, take, where, orderBy }),
|
||||
this.prisma.productType.count({ where }),
|
||||
]);
|
||||
return { data, total };
|
||||
}
|
||||
|
||||
async findOne(where: Prisma.ProductTypeWhereUniqueInput): Promise<ProductType | null> {
|
||||
return this.prisma.productType.findUnique({ where });
|
||||
}
|
||||
|
||||
async create(data: Prisma.ProductTypeCreateInput): Promise<ProductType> {
|
||||
return this.prisma.productType.create({ data });
|
||||
}
|
||||
|
||||
async update(params: {
|
||||
where: Prisma.ProductTypeWhereUniqueInput;
|
||||
data: Prisma.ProductTypeUpdateInput;
|
||||
}): Promise<ProductType> {
|
||||
return this.prisma.productType.update(params);
|
||||
}
|
||||
|
||||
async remove(where: Prisma.ProductTypeWhereUniqueInput): Promise<ProductType> {
|
||||
return this.prisma.productType.delete({ where });
|
||||
}
|
||||
}
|
||||
19
backend/src/product/dto/create-product.dto.ts
Normal file
19
backend/src/product/dto/create-product.dto.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateProductDto {
|
||||
@ApiProperty({ description: 'Наименование товара' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: 'ID типа товара' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
productTypeId: string;
|
||||
|
||||
@ApiProperty({ description: 'ID родительского товара (для иерархии)', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
parentId?: string;
|
||||
}
|
||||
4
backend/src/product/dto/update-product.dto.ts
Normal file
4
backend/src/product/dto/update-product.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateProductDto } from './create-product.dto';
|
||||
|
||||
export class UpdateProductDto extends PartialType(CreateProductDto) {}
|
||||
54
backend/src/product/product.controller.ts
Normal file
54
backend/src/product/product.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { ProductService } from './product.service';
|
||||
import { CreateProductDto } from './dto/create-product.dto';
|
||||
import { UpdateProductDto } from './dto/update-product.dto';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
|
||||
const parseJson = <T>(value?: string): T | undefined => {
|
||||
if (!value) return undefined;
|
||||
try { return JSON.parse(value) as T; } catch { return undefined; }
|
||||
};
|
||||
|
||||
@ApiTags('product')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('products')
|
||||
export class ProductController {
|
||||
constructor(private readonly productService: ProductService) {}
|
||||
|
||||
@Get()
|
||||
findAll(
|
||||
@Query('skip') skip?: string,
|
||||
@Query('take') take?: string,
|
||||
@Query('orderBy') orderBy?: string,
|
||||
@Query('where') where?: string,
|
||||
) {
|
||||
return this.productService.findAll({
|
||||
skip: skip ? Number(skip) : 0,
|
||||
take: take ? Number(take) : 25,
|
||||
orderBy: parseJson(orderBy),
|
||||
where: parseJson(where),
|
||||
});
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.productService.findOne({ id });
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateProductDto) {
|
||||
return this.productService.create(dto);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateProductDto) {
|
||||
return this.productService.update({ where: { id }, data: dto });
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.productService.remove({ id });
|
||||
}
|
||||
}
|
||||
10
backend/src/product/product.module.ts
Normal file
10
backend/src/product/product.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ProductService } from './product.service';
|
||||
import { ProductController } from './product.controller';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ProductController],
|
||||
providers: [ProductService, PrismaService],
|
||||
})
|
||||
export class ProductModule {}
|
||||
74
backend/src/product/product.service.ts
Normal file
74
backend/src/product/product.service.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { Product, Prisma } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class ProductService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
where?: Prisma.ProductWhereInput;
|
||||
orderBy?: Prisma.ProductOrderByWithRelationInput;
|
||||
}): Promise<{ data: Product[]; total: number }> {
|
||||
const { skip, take, where, orderBy } = params;
|
||||
const [data, total] = await this.prisma.$transaction([
|
||||
this.prisma.product.findMany({
|
||||
skip,
|
||||
take,
|
||||
where,
|
||||
orderBy,
|
||||
include: {
|
||||
productType: true,
|
||||
parent: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.product.count({ where }),
|
||||
]);
|
||||
return { data, total };
|
||||
}
|
||||
|
||||
async findOne(where: Prisma.ProductWhereUniqueInput): Promise<Product | null> {
|
||||
return this.prisma.product.findUnique({
|
||||
where,
|
||||
include: {
|
||||
productType: true,
|
||||
parent: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async create(data: Prisma.ProductCreateInput): Promise<Product> {
|
||||
return this.prisma.product.create({
|
||||
data,
|
||||
include: {
|
||||
productType: true,
|
||||
parent: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(params: {
|
||||
where: Prisma.ProductWhereUniqueInput;
|
||||
data: Prisma.ProductUpdateInput;
|
||||
}): Promise<Product> {
|
||||
return this.prisma.product.update({
|
||||
...params,
|
||||
include: {
|
||||
productType: true,
|
||||
parent: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async remove(where: Prisma.ProductWhereUniqueInput): Promise<Product> {
|
||||
return this.prisma.product.delete({
|
||||
where,
|
||||
include: {
|
||||
productType: true,
|
||||
parent: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { IsString, IsNotEmpty, IsNumber, IsBoolean } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateStockMovementLineDto {
|
||||
@ApiProperty({ description: 'ID товара' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
productId: string;
|
||||
|
||||
@ApiProperty({ description: 'Количество' })
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
quantity: number;
|
||||
|
||||
@ApiProperty({ description: 'Новый признак наличия после проведения' })
|
||||
@IsBoolean()
|
||||
inStock: boolean;
|
||||
}
|
||||
25
backend/src/stock-movement/dto/create-stock-movement.dto.ts
Normal file
25
backend/src/stock-movement/dto/create-stock-movement.dto.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { IsString, IsNotEmpty, IsEnum, IsArray, IsOptional } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { MovementKind } from '@prisma/client';
|
||||
import { CreateStockMovementLineDto } from './create-stock-movement-line.dto';
|
||||
|
||||
export class CreateStockMovementDto {
|
||||
@ApiProperty({ description: 'Дата документа' })
|
||||
@Type(() => Date)
|
||||
documentDate: Date;
|
||||
|
||||
@ApiProperty({ enum: MovementKind, enumName: 'MovementKind', description: 'Вид движения' })
|
||||
@IsEnum(MovementKind)
|
||||
kind: MovementKind;
|
||||
|
||||
@ApiProperty({ type: [CreateStockMovementLineDto], description: 'Строки документа' })
|
||||
@IsArray()
|
||||
@Type(() => CreateStockMovementLineDto)
|
||||
lines: CreateStockMovementLineDto[];
|
||||
|
||||
@ApiProperty({ description: 'Комментарий к документу', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
comment?: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateStockMovementLineDto } from './create-stock-movement-line.dto';
|
||||
|
||||
export class UpdateStockMovementLineDto extends PartialType(CreateStockMovementLineDto) {}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateStockMovementDto } from './create-stock-movement.dto';
|
||||
|
||||
export class UpdateStockMovementDto extends PartialType(CreateStockMovementDto) {}
|
||||
54
backend/src/stock-movement/stock-movement.controller.ts
Normal file
54
backend/src/stock-movement/stock-movement.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { StockMovementService } from './stock-movement.service';
|
||||
import { CreateStockMovementDto } from './dto/create-stock-movement.dto';
|
||||
import { UpdateStockMovementDto } from './dto/update-stock-movement.dto';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
|
||||
const parseJson = <T>(value?: string): T | undefined => {
|
||||
if (!value) return undefined;
|
||||
try { return JSON.parse(value) as T; } catch { return undefined; }
|
||||
};
|
||||
|
||||
@ApiTags('stock-movement')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('stock-movements')
|
||||
export class StockMovementController {
|
||||
constructor(private readonly stockMovementService: StockMovementService) {}
|
||||
|
||||
@Get()
|
||||
findAll(
|
||||
@Query('skip') skip?: string,
|
||||
@Query('take') take?: string,
|
||||
@Query('orderBy') orderBy?: string,
|
||||
@Query('where') where?: string,
|
||||
) {
|
||||
return this.stockMovementService.findAll({
|
||||
skip: skip ? Number(skip) : 0,
|
||||
take: take ? Number(take) : 25,
|
||||
orderBy: parseJson(orderBy),
|
||||
where: parseJson(where),
|
||||
});
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.stockMovementService.findOne({ id });
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateStockMovementDto) {
|
||||
return this.stockMovementService.create(dto);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateStockMovementDto) {
|
||||
return this.stockMovementService.update({ where: { id }, data: dto });
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.stockMovementService.remove({ id });
|
||||
}
|
||||
}
|
||||
10
backend/src/stock-movement/stock-movement.module.ts
Normal file
10
backend/src/stock-movement/stock-movement.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { StockMovementService } from './stock-movement.service';
|
||||
import { StockMovementController } from './stock-movement.controller';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Module({
|
||||
controllers: [StockMovementController],
|
||||
providers: [StockMovementService, PrismaService],
|
||||
})
|
||||
export class StockMovementModule {}
|
||||
89
backend/src/stock-movement/stock-movement.service.ts
Normal file
89
backend/src/stock-movement/stock-movement.service.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { StockMovement, Prisma } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class StockMovementService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
where?: Prisma.StockMovementWhereInput;
|
||||
orderBy?: Prisma.StockMovementOrderByWithRelationInput;
|
||||
}): Promise<{ data: StockMovement[]; total: number }> {
|
||||
const { skip, take, where, orderBy } = params;
|
||||
const [data, total] = await this.prisma.$transaction([
|
||||
this.prisma.stockMovement.findMany({
|
||||
skip,
|
||||
take,
|
||||
where,
|
||||
orderBy,
|
||||
include: {
|
||||
lines: {
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
this.prisma.stockMovement.count({ where }),
|
||||
]);
|
||||
return { data, total };
|
||||
}
|
||||
|
||||
async findOne(where: Prisma.StockMovementWhereUniqueInput): Promise<StockMovement | null> {
|
||||
return this.prisma.stockMovement.findUnique({
|
||||
where,
|
||||
include: {
|
||||
lines: {
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async create(data: Prisma.StockMovementCreateInput): Promise<StockMovement> {
|
||||
return this.prisma.stockMovement.create({
|
||||
data,
|
||||
include: {
|
||||
lines: {
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(params: {
|
||||
where: Prisma.StockMovementWhereUniqueInput;
|
||||
data: Prisma.StockMovementUpdateInput;
|
||||
}): Promise<StockMovement> {
|
||||
return this.prisma.stockMovement.update({
|
||||
...params,
|
||||
include: {
|
||||
lines: {
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async remove(where: Prisma.StockMovementWhereUniqueInput): Promise<StockMovement> {
|
||||
return this.prisma.stockMovement.delete({
|
||||
where,
|
||||
include: {
|
||||
lines: {
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
25
backend/src/stock-status/dto/create-stock-status.dto.ts
Normal file
25
backend/src/stock-status/dto/create-stock-status.dto.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { IsString, IsNotEmpty, IsNumber, IsBoolean, IsOptional } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateStockStatusDto {
|
||||
@ApiProperty({ description: 'ID товара' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
productId: string;
|
||||
|
||||
@ApiProperty({ description: 'Текущее количество на складе', required: false })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
quantity?: number;
|
||||
|
||||
@ApiProperty({ description: 'Признак наличия товара', required: false })
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
inStock?: boolean;
|
||||
|
||||
@ApiProperty({ description: 'Дата последнего обновления', required: false })
|
||||
@IsOptional()
|
||||
updatedAt?: Date;
|
||||
}
|
||||
4
backend/src/stock-status/dto/update-stock-status.dto.ts
Normal file
4
backend/src/stock-status/dto/update-stock-status.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateStockStatusDto } from './create-stock-status.dto';
|
||||
|
||||
export class UpdateStockStatusDto extends PartialType(CreateStockStatusDto) {}
|
||||
54
backend/src/stock-status/stock-status.controller.ts
Normal file
54
backend/src/stock-status/stock-status.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { StockStatusService } from './stock-status.service';
|
||||
import { CreateStockStatusDto } from './dto/create-stock-status.dto';
|
||||
import { UpdateStockStatusDto } from './dto/update-stock-status.dto';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
|
||||
const parseJson = <T>(value?: string): T | undefined => {
|
||||
if (!value) return undefined;
|
||||
try { return JSON.parse(value) as T; } catch { return undefined; }
|
||||
};
|
||||
|
||||
@ApiTags('stock-status')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('stock-statuses')
|
||||
export class StockStatusController {
|
||||
constructor(private readonly stockStatusService: StockStatusService) {}
|
||||
|
||||
@Get()
|
||||
findAll(
|
||||
@Query('skip') skip?: string,
|
||||
@Query('take') take?: string,
|
||||
@Query('orderBy') orderBy?: string,
|
||||
@Query('where') where?: string,
|
||||
) {
|
||||
return this.stockStatusService.findAll({
|
||||
skip: skip ? Number(skip) : 0,
|
||||
take: take ? Number(take) : 25,
|
||||
orderBy: parseJson(orderBy),
|
||||
where: parseJson(where),
|
||||
});
|
||||
}
|
||||
|
||||
@Get(':productId')
|
||||
findOne(@Param('productId') productId: string) {
|
||||
return this.stockStatusService.findOne({ productId });
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateStockStatusDto) {
|
||||
return this.stockStatusService.create(dto);
|
||||
}
|
||||
|
||||
@Patch(':productId')
|
||||
update(@Param('productId') productId: string, @Body() dto: UpdateStockStatusDto) {
|
||||
return this.stockStatusService.update({ where: { productId }, data: dto });
|
||||
}
|
||||
|
||||
@Delete(':productId')
|
||||
remove(@Param('productId') productId: string) {
|
||||
return this.stockStatusService.remove({ productId });
|
||||
}
|
||||
}
|
||||
10
backend/src/stock-status/stock-status.module.ts
Normal file
10
backend/src/stock-status/stock-status.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { StockStatusService } from './stock-status.service';
|
||||
import { StockStatusController } from './stock-status.controller';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Module({
|
||||
controllers: [StockStatusController],
|
||||
providers: [StockStatusService, PrismaService],
|
||||
})
|
||||
export class StockStatusModule {}
|
||||
69
backend/src/stock-status/stock-status.service.ts
Normal file
69
backend/src/stock-status/stock-status.service.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { StockStatus, Prisma } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class StockStatusService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
where?: Prisma.StockStatusWhereInput;
|
||||
orderBy?: Prisma.StockStatusOrderByWithRelationInput;
|
||||
}): Promise<{ data: StockStatus[]; total: number }> {
|
||||
const { skip, take, where, orderBy } = params;
|
||||
const [data, total] = await this.prisma.$transaction([
|
||||
this.prisma.stockStatus.findMany({
|
||||
skip,
|
||||
take,
|
||||
where,
|
||||
orderBy,
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.stockStatus.count({ where }),
|
||||
]);
|
||||
return { data, total };
|
||||
}
|
||||
|
||||
async findOne(where: Prisma.StockStatusWhereUniqueInput): Promise<StockStatus | null> {
|
||||
return this.prisma.stockStatus.findUnique({
|
||||
where,
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async create(data: Prisma.StockStatusCreateInput): Promise<StockStatus> {
|
||||
return this.prisma.stockStatus.create({
|
||||
data,
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(params: {
|
||||
where: Prisma.StockStatusWhereUniqueInput;
|
||||
data: Prisma.StockStatusUpdateInput;
|
||||
}): Promise<StockStatus> {
|
||||
return this.prisma.stockStatus.update({
|
||||
...params,
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async remove(where: Prisma.StockStatusWhereUniqueInput): Promise<StockStatus> {
|
||||
return this.prisma.stockStatus.delete({
|
||||
where,
|
||||
include: {
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user