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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
60
frontend/src/App.tsx
Normal file
60
frontend/src/App.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Admin, Resource } from 'react-admin';
|
||||||
|
import { dataProvider } from './dataProvider';
|
||||||
|
import { authProvider } from './authProvider';
|
||||||
|
|
||||||
|
// ProductType resources
|
||||||
|
import { ProductTypeList, ProductTypeCreate, ProductTypeEdit, ProductTypeShow } from './resources/product-type';
|
||||||
|
|
||||||
|
// Product resources
|
||||||
|
import { ProductList, ProductCreate, ProductEdit, ProductShow } from './resources/product';
|
||||||
|
|
||||||
|
// StockStatus resources
|
||||||
|
import { StockStatusList, StockStatusCreate, StockStatusEdit, StockStatusShow } from './resources/stock-status';
|
||||||
|
|
||||||
|
// StockMovement resources
|
||||||
|
import { StockMovementList, StockMovementCreate, StockMovementEdit, StockMovementShow } from './resources/stock-movement';
|
||||||
|
|
||||||
|
// StockMovementLine resources
|
||||||
|
import { StockMovementLineList, StockMovementLineCreate, StockMovementLineEdit, StockMovementLineShow } from './resources/stock-movement-line';
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<Admin dataProvider={dataProvider} authProvider={authProvider}>
|
||||||
|
<Resource
|
||||||
|
name="product-types"
|
||||||
|
list={ProductTypeList}
|
||||||
|
create={ProductTypeCreate}
|
||||||
|
edit={ProductTypeEdit}
|
||||||
|
show={ProductTypeShow}
|
||||||
|
/>
|
||||||
|
<Resource
|
||||||
|
name="products"
|
||||||
|
list={ProductList}
|
||||||
|
create={ProductCreate}
|
||||||
|
edit={ProductEdit}
|
||||||
|
show={ProductShow}
|
||||||
|
/>
|
||||||
|
<Resource
|
||||||
|
name="stock-statuses"
|
||||||
|
list={StockStatusList}
|
||||||
|
create={StockStatusCreate}
|
||||||
|
edit={StockStatusEdit}
|
||||||
|
show={StockStatusShow}
|
||||||
|
/>
|
||||||
|
<Resource
|
||||||
|
name="stock-movements"
|
||||||
|
list={StockMovementList}
|
||||||
|
create={StockMovementCreate}
|
||||||
|
edit={StockMovementEdit}
|
||||||
|
show={StockMovementShow}
|
||||||
|
/>
|
||||||
|
<Resource
|
||||||
|
name="stock-movement-lines"
|
||||||
|
list={StockMovementLineList}
|
||||||
|
create={StockMovementLineCreate}
|
||||||
|
edit={StockMovementLineEdit}
|
||||||
|
show={StockMovementLineShow}
|
||||||
|
/>
|
||||||
|
</Admin>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
||||||
15
frontend/src/main.tsx
Normal file
15
frontend/src/main.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import App from './App';
|
||||||
|
import { initKeycloak } from './authProvider';
|
||||||
|
|
||||||
|
// Initialize Keycloak before rendering the app
|
||||||
|
initKeycloak().then(() => {
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Failed to initialize Keycloak:', error);
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Create, SimpleForm, TextInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductTypeCreate = () => (
|
||||||
|
<Create>
|
||||||
|
<SimpleForm>
|
||||||
|
<TextInput source="name" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Create>
|
||||||
|
);
|
||||||
10
frontend/src/resources/product-type/ProductTypeEdit.tsx
Normal file
10
frontend/src/resources/product-type/ProductTypeEdit.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Edit, SimpleForm, TextInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductTypeEdit = () => (
|
||||||
|
<Edit>
|
||||||
|
<SimpleForm>
|
||||||
|
<TextInput disabled source="id" />
|
||||||
|
<TextInput source="name" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Edit>
|
||||||
|
);
|
||||||
13
frontend/src/resources/product-type/ProductTypeList.tsx
Normal file
13
frontend/src/resources/product-type/ProductTypeList.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { List, DataTable, TextField, DateField, EditButton } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductTypeList = () => (
|
||||||
|
<List>
|
||||||
|
<DataTable>
|
||||||
|
<DataTable.Col source="id" />
|
||||||
|
<DataTable.Col source="name" />
|
||||||
|
<DataTable.Col source="createdAt" field={DateField} />
|
||||||
|
<DataTable.Col source="updatedAt" field={DateField} />
|
||||||
|
<DataTable.Col><EditButton /></DataTable.Col>
|
||||||
|
</DataTable>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
12
frontend/src/resources/product-type/ProductTypeShow.tsx
Normal file
12
frontend/src/resources/product-type/ProductTypeShow.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Show, SimpleShowLayout, TextField, DateField } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductTypeShow = () => (
|
||||||
|
<Show>
|
||||||
|
<SimpleShowLayout>
|
||||||
|
<TextField source="id" />
|
||||||
|
<TextField source="name" />
|
||||||
|
<DateField source="createdAt" />
|
||||||
|
<DateField source="updatedAt" />
|
||||||
|
</SimpleShowLayout>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
4
frontend/src/resources/product-type/index.ts
Normal file
4
frontend/src/resources/product-type/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { ProductTypeList } from './ProductTypeList';
|
||||||
|
export { ProductTypeEdit } from './ProductTypeEdit';
|
||||||
|
export { ProductTypeCreate } from './ProductTypeCreate';
|
||||||
|
export { ProductTypeShow } from './ProductTypeShow';
|
||||||
15
frontend/src/resources/product/ProductCreate.tsx
Normal file
15
frontend/src/resources/product/ProductCreate.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Create, SimpleForm, TextInput, ReferenceInput, SelectInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductCreate = () => (
|
||||||
|
<Create>
|
||||||
|
<SimpleForm>
|
||||||
|
<TextInput source="name" />
|
||||||
|
<ReferenceInput source="productTypeId" reference="product-types">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<ReferenceInput source="parentId" reference="products">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
</SimpleForm>
|
||||||
|
</Create>
|
||||||
|
);
|
||||||
16
frontend/src/resources/product/ProductEdit.tsx
Normal file
16
frontend/src/resources/product/ProductEdit.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Edit, SimpleForm, TextInput, ReferenceInput, SelectInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductEdit = () => (
|
||||||
|
<Edit>
|
||||||
|
<SimpleForm>
|
||||||
|
<TextInput disabled source="id" />
|
||||||
|
<TextInput source="name" />
|
||||||
|
<ReferenceInput source="productTypeId" reference="product-types">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<ReferenceInput source="parentId" reference="products">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
</SimpleForm>
|
||||||
|
</Edit>
|
||||||
|
);
|
||||||
19
frontend/src/resources/product/ProductList.tsx
Normal file
19
frontend/src/resources/product/ProductList.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { List, DataTable, TextField, ReferenceField, DateField, EditButton } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductList = () => (
|
||||||
|
<List>
|
||||||
|
<DataTable>
|
||||||
|
<DataTable.Col source="id" />
|
||||||
|
<DataTable.Col source="name" />
|
||||||
|
<DataTable.Col source="productTypeId" field={ReferenceField} reference="product-types">
|
||||||
|
<TextField source="name" />
|
||||||
|
</DataTable.Col>
|
||||||
|
<DataTable.Col source="parentId" field={ReferenceField} reference="products">
|
||||||
|
<TextField source="name" />
|
||||||
|
</DataTable.Col>
|
||||||
|
<DataTable.Col source="createdAt" field={DateField} />
|
||||||
|
<DataTable.Col source="updatedAt" field={DateField} />
|
||||||
|
<DataTable.Col><EditButton /></DataTable.Col>
|
||||||
|
</DataTable>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
18
frontend/src/resources/product/ProductShow.tsx
Normal file
18
frontend/src/resources/product/ProductShow.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Show, SimpleShowLayout, TextField, ReferenceField, DateField } from 'react-admin';
|
||||||
|
|
||||||
|
export const ProductShow = () => (
|
||||||
|
<Show>
|
||||||
|
<SimpleShowLayout>
|
||||||
|
<TextField source="id" />
|
||||||
|
<TextField source="name" />
|
||||||
|
<ReferenceField source="productTypeId" reference="product-types">
|
||||||
|
<TextField source="name" />
|
||||||
|
</ReferenceField>
|
||||||
|
<ReferenceField source="parentId" reference="products">
|
||||||
|
<TextField source="name" />
|
||||||
|
</ReferenceField>
|
||||||
|
<DateField source="createdAt" />
|
||||||
|
<DateField source="updatedAt" />
|
||||||
|
</SimpleShowLayout>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
4
frontend/src/resources/product/index.ts
Normal file
4
frontend/src/resources/product/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { ProductList } from './ProductList';
|
||||||
|
export { ProductEdit } from './ProductEdit';
|
||||||
|
export { ProductCreate } from './ProductCreate';
|
||||||
|
export { ProductShow } from './ProductShow';
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Create, SimpleForm, ReferenceInput, SelectInput, NumberInput, BooleanInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockMovementLineCreate = () => (
|
||||||
|
<Create>
|
||||||
|
<SimpleForm>
|
||||||
|
<ReferenceInput source="productId" reference="products">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<ReferenceInput source="movementId" reference="stock-movements">
|
||||||
|
<SelectInput optionText="id" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<NumberInput source="quantity" />
|
||||||
|
<BooleanInput source="inStock" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Create>
|
||||||
|
);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Edit, SimpleForm, TextInput, ReferenceInput, SelectInput, NumberInput, BooleanInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockMovementLineEdit = () => (
|
||||||
|
<Edit>
|
||||||
|
<SimpleForm>
|
||||||
|
<TextInput disabled source="id" />
|
||||||
|
<ReferenceInput source="productId" reference="products">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<ReferenceInput source="movementId" reference="stock-movements">
|
||||||
|
<SelectInput optionText="id" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<NumberInput source="quantity" />
|
||||||
|
<BooleanInput source="inStock" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Edit>
|
||||||
|
);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { List, DataTable, TextField, ReferenceField, NumberField, BooleanField, DateField, EditButton } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockMovementLineList = () => (
|
||||||
|
<List>
|
||||||
|
<DataTable>
|
||||||
|
<DataTable.Col source="id" />
|
||||||
|
<DataTable.Col source="productId" field={ReferenceField} reference="products">
|
||||||
|
<TextField source="name" />
|
||||||
|
</DataTable.Col>
|
||||||
|
<DataTable.Col source="movementId" field={ReferenceField} reference="stock-movements">
|
||||||
|
<TextField source="id" />
|
||||||
|
</DataTable.Col>
|
||||||
|
<DataTable.Col source="quantity" field={NumberField} />
|
||||||
|
<DataTable.Col source="inStock" field={BooleanField} />
|
||||||
|
<DataTable.Col source="createdAt" field={DateField} />
|
||||||
|
<DataTable.Col source="updatedAt" field={DateField} />
|
||||||
|
<DataTable.Col><EditButton /></DataTable.Col>
|
||||||
|
</DataTable>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Show, SimpleShowLayout, TextField, ReferenceField, NumberField, BooleanField, DateField } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockMovementLineShow = () => (
|
||||||
|
<Show>
|
||||||
|
<SimpleShowLayout>
|
||||||
|
<TextField source="id" />
|
||||||
|
<ReferenceField source="productId" reference="products">
|
||||||
|
<TextField source="name" />
|
||||||
|
</ReferenceField>
|
||||||
|
<ReferenceField source="movementId" reference="stock-movements">
|
||||||
|
<TextField source="id" />
|
||||||
|
</ReferenceField>
|
||||||
|
<NumberField source="quantity" />
|
||||||
|
<BooleanField source="inStock" />
|
||||||
|
<DateField source="createdAt" />
|
||||||
|
<DateField source="updatedAt" />
|
||||||
|
</SimpleShowLayout>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
4
frontend/src/resources/stock-movement-line/index.ts
Normal file
4
frontend/src/resources/stock-movement-line/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { StockMovementLineList } from './StockMovementLineList';
|
||||||
|
export { StockMovementLineEdit } from './StockMovementLineEdit';
|
||||||
|
export { StockMovementLineCreate } from './StockMovementLineCreate';
|
||||||
|
export { StockMovementLineShow } from './StockMovementLineShow';
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Create, SimpleForm, TextInput, DateInput, SelectInput } from 'react-admin';
|
||||||
|
|
||||||
|
const kindChoices = [
|
||||||
|
{ id: 'Receipt', name: 'Receipt' },
|
||||||
|
{ id: 'Issue', name: 'Issue' },
|
||||||
|
{ id: 'Transfer', name: 'Transfer' },
|
||||||
|
{ id: 'Adjustment', name: 'Adjustment' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const StockMovementCreate = () => (
|
||||||
|
<Create>
|
||||||
|
<SimpleForm>
|
||||||
|
<DateInput source="documentDate" />
|
||||||
|
<SelectInput source="kind" choices={kindChoices} />
|
||||||
|
<TextInput multiline source="comment" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Create>
|
||||||
|
);
|
||||||
19
frontend/src/resources/stock-movement/StockMovementEdit.tsx
Normal file
19
frontend/src/resources/stock-movement/StockMovementEdit.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Edit, SimpleForm, TextInput, DateInput, SelectInput } from 'react-admin';
|
||||||
|
|
||||||
|
const kindChoices = [
|
||||||
|
{ id: 'Receipt', name: 'Receipt' },
|
||||||
|
{ id: 'Issue', name: 'Issue' },
|
||||||
|
{ id: 'Transfer', name: 'Transfer' },
|
||||||
|
{ id: 'Adjustment', name: 'Adjustment' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const StockMovementEdit = () => (
|
||||||
|
<Edit>
|
||||||
|
<SimpleForm>
|
||||||
|
<TextInput disabled source="id" />
|
||||||
|
<DateInput source="documentDate" />
|
||||||
|
<SelectInput source="kind" choices={kindChoices} />
|
||||||
|
<TextInput multiline source="comment" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Edit>
|
||||||
|
);
|
||||||
15
frontend/src/resources/stock-movement/StockMovementList.tsx
Normal file
15
frontend/src/resources/stock-movement/StockMovementList.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { List, DataTable, TextField, DateField, EditButton } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockMovementList = () => (
|
||||||
|
<List>
|
||||||
|
<DataTable>
|
||||||
|
<DataTable.Col source="id" />
|
||||||
|
<DataTable.Col source="documentDate" field={DateField} />
|
||||||
|
<DataTable.Col source="kind" />
|
||||||
|
<DataTable.Col source="comment" />
|
||||||
|
<DataTable.Col source="createdAt" field={DateField} />
|
||||||
|
<DataTable.Col source="updatedAt" field={DateField} />
|
||||||
|
<DataTable.Col><EditButton /></DataTable.Col>
|
||||||
|
</DataTable>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
14
frontend/src/resources/stock-movement/StockMovementShow.tsx
Normal file
14
frontend/src/resources/stock-movement/StockMovementShow.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Show, SimpleShowLayout, TextField, DateField } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockMovementShow = () => (
|
||||||
|
<Show>
|
||||||
|
<SimpleShowLayout>
|
||||||
|
<TextField source="id" />
|
||||||
|
<DateField source="documentDate" />
|
||||||
|
<TextField source="kind" />
|
||||||
|
<TextField source="comment" />
|
||||||
|
<DateField source="createdAt" />
|
||||||
|
<DateField source="updatedAt" />
|
||||||
|
</SimpleShowLayout>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
4
frontend/src/resources/stock-movement/index.ts
Normal file
4
frontend/src/resources/stock-movement/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { StockMovementList } from './StockMovementList';
|
||||||
|
export { StockMovementEdit } from './StockMovementEdit';
|
||||||
|
export { StockMovementCreate } from './StockMovementCreate';
|
||||||
|
export { StockMovementShow } from './StockMovementShow';
|
||||||
13
frontend/src/resources/stock-status/StockStatusCreate.tsx
Normal file
13
frontend/src/resources/stock-status/StockStatusCreate.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Create, SimpleForm, ReferenceInput, SelectInput, NumberInput, BooleanInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockStatusCreate = () => (
|
||||||
|
<Create>
|
||||||
|
<SimpleForm>
|
||||||
|
<ReferenceInput source="productId" reference="products">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<NumberInput source="quantity" />
|
||||||
|
<BooleanInput source="inStock" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Create>
|
||||||
|
);
|
||||||
13
frontend/src/resources/stock-status/StockStatusEdit.tsx
Normal file
13
frontend/src/resources/stock-status/StockStatusEdit.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Edit, SimpleForm, ReferenceInput, SelectInput, NumberInput, BooleanInput } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockStatusEdit = () => (
|
||||||
|
<Edit>
|
||||||
|
<SimpleForm>
|
||||||
|
<ReferenceInput source="productId" reference="products">
|
||||||
|
<SelectInput optionText="name" />
|
||||||
|
</ReferenceInput>
|
||||||
|
<NumberInput source="quantity" />
|
||||||
|
<BooleanInput source="inStock" />
|
||||||
|
</SimpleForm>
|
||||||
|
</Edit>
|
||||||
|
);
|
||||||
15
frontend/src/resources/stock-status/StockStatusList.tsx
Normal file
15
frontend/src/resources/stock-status/StockStatusList.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { List, DataTable, ReferenceField, TextField, NumberField, BooleanField, DateField, EditButton } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockStatusList = () => (
|
||||||
|
<List>
|
||||||
|
<DataTable>
|
||||||
|
<DataTable.Col source="productId" field={ReferenceField} reference="products">
|
||||||
|
<TextField source="name" />
|
||||||
|
</DataTable.Col>
|
||||||
|
<DataTable.Col source="quantity" field={NumberField} />
|
||||||
|
<DataTable.Col source="inStock" field={BooleanField} />
|
||||||
|
<DataTable.Col source="updatedAt" field={DateField} />
|
||||||
|
<DataTable.Col><EditButton /></DataTable.Col>
|
||||||
|
</DataTable>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
14
frontend/src/resources/stock-status/StockStatusShow.tsx
Normal file
14
frontend/src/resources/stock-status/StockStatusShow.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Show, SimpleShowLayout, ReferenceField, TextField, NumberField, BooleanField, DateField } from 'react-admin';
|
||||||
|
|
||||||
|
export const StockStatusShow = () => (
|
||||||
|
<Show>
|
||||||
|
<SimpleShowLayout>
|
||||||
|
<ReferenceField source="productId" reference="products">
|
||||||
|
<TextField source="name" />
|
||||||
|
</ReferenceField>
|
||||||
|
<NumberField source="quantity" />
|
||||||
|
<BooleanField source="inStock" />
|
||||||
|
<DateField source="updatedAt" />
|
||||||
|
</SimpleShowLayout>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
4
frontend/src/resources/stock-status/index.ts
Normal file
4
frontend/src/resources/stock-status/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { StockStatusList } from './StockStatusList';
|
||||||
|
export { StockStatusEdit } from './StockStatusEdit';
|
||||||
|
export { StockStatusCreate } from './StockStatusCreate';
|
||||||
|
export { StockStatusShow } from './StockStatusShow';
|
||||||
Reference in New Issue
Block a user