feat(stocks): implement new endpoints and methods for stock management
This commit enhances the stocks module by adding new import modules, implementing more stock related endpoints and methods, and creating a new dto for stock data validation. The stocks service now includes methods to get a product's stock, create, alter, get the current stock, rank products by stock, get all stocks, decrement and increment product stock. StocksController now holds endpoints to add a new stock, get products with more or less stock, edit a stock, and get a stock of a specific product.
This commit is contained in:
parent
3451f17123
commit
e2f7ae8b88
@ -1,12 +1,74 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
UseGuards
|
||||
} from "@nestjs/common";
|
||||
import { CreateStockDto, EditStockDto } from "src/stocks/stocks.dto";
|
||||
import { StocksService } from "src/stocks/stocks.service";
|
||||
import { error } from "winston";
|
||||
import { AdminGuard, UserGuard } from "src/auth/auth.guard";
|
||||
|
||||
@Controller('stocks')
|
||||
export class StocksController {
|
||||
//POST new stock of a product (via id) [admin]
|
||||
constructor(
|
||||
private readonly stocks: StocksService
|
||||
) {}
|
||||
|
||||
//PATCH existing stock of a product (via id) [admin]
|
||||
|
||||
//GET current stock of a product [user]
|
||||
|
||||
//GET product with more or less stock (10 each)
|
||||
@UseGuards(AdminGuard)
|
||||
@Post('add')
|
||||
async addStock(@Body() stock: CreateStockDto) {
|
||||
try {
|
||||
await this.stocks.createStock(stock.productId, stock.quantity);
|
||||
} catch (error) {
|
||||
throw new BadRequestException(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@Get('more/:limit')
|
||||
async getProductsMoreStock(@Param('limit') limit: number) {
|
||||
try {
|
||||
const products = await this.stocks.getProductsRankedByStock(limit || 10, false);
|
||||
return products
|
||||
} catch (error) {
|
||||
throw new BadRequestException(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@Get('less/:limit')
|
||||
async getProductsLessStock(@Param('limit') limit: number) {
|
||||
try {
|
||||
const products = await this.stocks.getProductsRankedByStock(limit || 10, true);
|
||||
return products
|
||||
} catch (error) {
|
||||
throw new BadRequestException(error.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@UseGuards(AdminGuard)
|
||||
@Patch(':productId')
|
||||
async editStock(@Param('productId') id: string, @Body() stock: EditStockDto) {
|
||||
try {
|
||||
await this.stocks.alterStock(id, stock.quantity);
|
||||
} catch (error) {
|
||||
throw new BadRequestException(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(UserGuard)
|
||||
@Get(':productId')
|
||||
async getStock(@Param('productId') productId: string) {
|
||||
try {
|
||||
const stock = await this.stocks.getCurrentStock(productId);
|
||||
return stock;
|
||||
} catch (error) {
|
||||
throw new NotFoundException(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
src/stocks/stocks.dto.ts
Normal file
24
src/stocks/stocks.dto.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {
|
||||
IsEmail, IsInt,
|
||||
IsNotEmpty,
|
||||
IsString,
|
||||
IsStrongPassword, IsUUID,
|
||||
MaxLength, Min,
|
||||
MinLength
|
||||
} from "class-validator";
|
||||
import { optional } from "zod";
|
||||
|
||||
export class CreateStockDto {
|
||||
@IsUUID()
|
||||
productId: string;
|
||||
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
export class EditStockDto {
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
quantity: number;
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { StocksController } from './stocks.controller';
|
||||
import { StocksService } from './stocks.service';
|
||||
import { DrizzleModule } from "src/drizzle/drizzle.module";
|
||||
import { ProductsModule } from "src/products/products.module";
|
||||
|
||||
@Module({
|
||||
imports: [DrizzleModule, ProductsModule],
|
||||
controllers: [StocksController],
|
||||
providers: [StocksService]
|
||||
})
|
||||
|
@ -1,39 +1,114 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { DrizzleService } from "src/drizzle/drizzle.service";
|
||||
import { ProductsService } from "src/products/products.service";
|
||||
import { StocksTable } from "src/schema";
|
||||
import { asc, desc, eq } from "drizzle-orm";
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class StocksService {
|
||||
//create a new stock for a product
|
||||
constructor(
|
||||
private readonly db: DrizzleService,
|
||||
private readonly products: ProductsService
|
||||
) {}
|
||||
|
||||
private async getProductStock(productId: string) {
|
||||
return this.db.use()
|
||||
.select()
|
||||
.from(StocksTable)
|
||||
.where(eq(StocksTable.productId, productId))
|
||||
.execute();
|
||||
}
|
||||
|
||||
async createStock(productId: string, quantity: number) {
|
||||
// implementation here
|
||||
const product = await this.products.findById(productId)
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
const existingStock = await this.getProductStock(productId)
|
||||
if (existingStock.length > 0) {
|
||||
throw new BadRequestException(`Stock already exists, edit the existing one. (quantity: ${existingStock[0].quantity})`);
|
||||
}
|
||||
|
||||
return this.db.use()
|
||||
.insert(StocksTable)
|
||||
.values({
|
||||
quantity: quantity,
|
||||
productId: productId,
|
||||
});
|
||||
}
|
||||
|
||||
//alter an existing stock of a product (via product id)
|
||||
async alterStock(productId: string, newQuantity: number) {
|
||||
// implementation here
|
||||
const product = await this.products.findById(productId)
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
}
|
||||
|
||||
//get the current stock of a product (via product id)
|
||||
async getCurrentStock(productId: string) {
|
||||
// implementation here
|
||||
const product = await this.products.findById(productId)
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
//get products with more or less stock (10 each)
|
||||
async getProductsRankedByStock(quantity: number, asc: boolean) {
|
||||
// implementation here
|
||||
async getProductsRankedByStock(limit: number, _asc: boolean) {
|
||||
const order = _asc ? asc(StocksTable.quantity) : desc(StocksTable.quantity);
|
||||
return this.db.use()
|
||||
.select()
|
||||
.from(StocksTable)
|
||||
.orderBy(order)
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
//get all product's stocks with pagination
|
||||
async getAllStocks(page: number, limit: number) {
|
||||
// implementation here
|
||||
async getAllStocks(page: number, limit: number, _asc: boolean) {
|
||||
const order = _asc ? asc(StocksTable.quantity) : desc(StocksTable.quantity);
|
||||
return this.db.use()
|
||||
.select()
|
||||
.from(StocksTable)
|
||||
.orderBy(order)
|
||||
.offset((page - 1) * limit)
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
//decrement stock of a product (via product id and quantity to decrement)
|
||||
async decrementStock(productId: string, quantityToDrop: number) {
|
||||
// implementation here
|
||||
const product = await this.products.findById(productId)
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
|
||||
const existingStock = await this.getProductStock(productId)
|
||||
if (existingStock.length === 0) {
|
||||
throw new BadRequestException("Stock not exists, create an existing one.");
|
||||
}
|
||||
|
||||
if (existingStock[0].quantity - quantityToDrop < 0) {
|
||||
throw new BadRequestException(`Operation not permitted, stock does not meet the quantity to do that operation. (current: ${existingStock[0].quantity}, after: ${existingStock[0].quantity - quantityToDrop})`);
|
||||
}
|
||||
|
||||
return this.db.use()
|
||||
.update(StocksTable)
|
||||
.set({
|
||||
quantity: existingStock[0].quantity - quantityToDrop,
|
||||
})
|
||||
.where(eq(StocksTable.productId, productId));
|
||||
}
|
||||
|
||||
//increment stock of a product (via product id and quantity to increment)
|
||||
async incrementStock(productId: string, quantity: number) {
|
||||
// implementation here
|
||||
const product = await this.products.findById(productId)
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
const existingStock = await this.getProductStock(productId)
|
||||
if (existingStock.length === 0) {
|
||||
throw new BadRequestException("Stock not exists, create an existing one.");
|
||||
}
|
||||
return this.db.use()
|
||||
.update(StocksTable)
|
||||
.set({
|
||||
quantity: existingStock[0].quantity + quantity,
|
||||
})
|
||||
.where(eq(StocksTable.productId, productId));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user