inventario
This commit is contained in:
32
apps/api/src/features/inventory/dto/create-product.dto.ts
Normal file
32
apps/api/src/features/inventory/dto/create-product.dto.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEmail, IsInt, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateProductDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
description: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString({
|
||||
message: 'price must be a string',
|
||||
})
|
||||
@IsOptional()
|
||||
price: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString({
|
||||
message: 'stock must be a number',
|
||||
})
|
||||
@IsOptional()
|
||||
stock: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString({
|
||||
message: 'urlImg must be a string',
|
||||
})
|
||||
urlImg: string;
|
||||
}
|
||||
22
apps/api/src/features/inventory/dto/update-product.dto.ts
Normal file
22
apps/api/src/features/inventory/dto/update-product.dto.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger';
|
||||
import { CreateProductDto } from './create-product.dto';
|
||||
|
||||
// import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateProductDto extends PartialType(CreateProductDto) {
|
||||
@IsOptional()
|
||||
title: string;
|
||||
|
||||
@IsOptional()
|
||||
description: string;
|
||||
|
||||
@IsOptional()
|
||||
price: string;
|
||||
|
||||
@IsOptional()
|
||||
stock: number;
|
||||
|
||||
@IsOptional()
|
||||
urlImg: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export class Product {
|
||||
id?: number;
|
||||
title: string;
|
||||
description: string;
|
||||
price: string;
|
||||
stock: number;
|
||||
urlImg: string;
|
||||
}
|
||||
70
apps/api/src/features/inventory/inventory.controller.ts
Normal file
70
apps/api/src/features/inventory/inventory.controller.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
|
||||
import { InventoryService } from './inventory.service';
|
||||
import { CreateProductDto } from './dto/create-product.dto';
|
||||
import { UpdateProductDto } from './dto/update-product.dto';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { Roles } from '../../common/decorators/roles.decorator';
|
||||
import { PaginationDto } from '../../common/dto/pagination.dto';
|
||||
|
||||
@ApiTags('inventory')
|
||||
@Controller('inventory')
|
||||
export class UsersController {
|
||||
constructor(private readonly inventoryService: InventoryService) {}
|
||||
|
||||
@Get()
|
||||
// @Roles('admin')
|
||||
@ApiOperation({ summary: 'Get all products with pagination and filters' })
|
||||
@ApiResponse({ status: 200, description: 'Return paginated products.' })
|
||||
async findAll(@Query() paginationDto: PaginationDto) {
|
||||
const result = await this.inventoryService.findAll(paginationDto);
|
||||
return {
|
||||
message: 'products fetched successfully',
|
||||
data: result.data,
|
||||
meta: result.meta
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
// @Roles('admin')
|
||||
@ApiOperation({ summary: 'Get a product by ID' })
|
||||
@ApiResponse({ status: 200, description: 'Return the product.' })
|
||||
@ApiResponse({ status: 404, description: 'product not found.' })
|
||||
async findOne(@Param('id') id: string) {
|
||||
const data = await this.inventoryService.findOne(id);
|
||||
return { message: 'product fetched successfully', data };
|
||||
}
|
||||
|
||||
@Post()
|
||||
// @Roles('admin')
|
||||
@ApiOperation({ summary: 'Create a new product' })
|
||||
@ApiResponse({ status: 201, description: 'Product created successfully.' })
|
||||
async create(
|
||||
@Body() createUserDto: CreateProductDto,
|
||||
@Query('roleId') roleId?: string,
|
||||
) {
|
||||
const data = await this.inventoryService.create(
|
||||
createUserDto,
|
||||
roleId ? parseInt(roleId) : undefined,
|
||||
);
|
||||
return { message: 'User created successfully', data };
|
||||
}
|
||||
|
||||
// @Patch(':id')
|
||||
// @Roles('admin')
|
||||
// @ApiOperation({ summary: 'Update a user' })
|
||||
// @ApiResponse({ status: 200, description: 'User updated successfully.' })
|
||||
// @ApiResponse({ status: 404, description: 'User not found.' })
|
||||
// async update(@Param('id') id: string, @Body() UpdateProductDto: UpdateProductDto) {
|
||||
// const data = await this.inventoryService.update(id, UpdateProductDto);
|
||||
// return { message: 'User updated successfully', data };
|
||||
// }
|
||||
|
||||
// @Delete(':id')
|
||||
// @Roles('admin')
|
||||
// @ApiOperation({ summary: 'Delete a user' })
|
||||
// @ApiResponse({ status: 200, description: 'User deleted successfully.' })
|
||||
// @ApiResponse({ status: 404, description: 'User not found.' })
|
||||
// async remove(@Param('id') id: string) {
|
||||
// return await this.inventoryService.remove(id);
|
||||
// }
|
||||
}
|
||||
11
apps/api/src/features/inventory/inventory.module.ts
Normal file
11
apps/api/src/features/inventory/inventory.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersController } from './inventory.controller';
|
||||
import { InventoryService } from './inventory.service';
|
||||
import { DrizzleModule } from '@/database/drizzle.module';
|
||||
|
||||
@Module({
|
||||
imports: [DrizzleModule],
|
||||
controllers: [UsersController],
|
||||
providers: [InventoryService],
|
||||
})
|
||||
export class InventoryModule {}
|
||||
209
apps/api/src/features/inventory/inventory.service.ts
Normal file
209
apps/api/src/features/inventory/inventory.service.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider';
|
||||
import { Env, validateString } from '@/common/utils';
|
||||
import { Inject, Injectable, HttpException, HttpStatus, NotFoundException, UnauthorizedException } from '@nestjs/common';
|
||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import * as schema from 'src/database/index';
|
||||
import { products } from 'src/database/index';
|
||||
import { eq, like, or, SQL, sql, and, not } from 'drizzle-orm';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { CreateProductDto } from './dto/create-product.dto';
|
||||
import { UpdateUserDto } from './dto/update-product.dto';
|
||||
import { Product } from './entities/inventory.entity';
|
||||
import { PaginationDto } from '../../common/dto/pagination.dto';
|
||||
|
||||
@Injectable()
|
||||
export class InventoryService {
|
||||
constructor(
|
||||
@Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase<typeof schema>,
|
||||
) { }
|
||||
|
||||
async findAll(paginationDto?: PaginationDto): Promise<{ data: Product[], meta: any }> {
|
||||
const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {};
|
||||
|
||||
// Calculate offset
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// Build search condition
|
||||
let searchCondition: SQL<unknown> | undefined;
|
||||
if (search) {
|
||||
searchCondition = or(
|
||||
like(products.title, `%${search}%`),
|
||||
like(products.description, `%${search}%`),
|
||||
// like(users.fullname, `%${search}%`)
|
||||
);
|
||||
}
|
||||
|
||||
// Build sort condition
|
||||
const orderBy = sortOrder === 'asc'
|
||||
? sql`${products[sortBy as keyof typeof products]} asc`
|
||||
: sql`${products[sortBy as keyof typeof products]} desc`;
|
||||
|
||||
// Get total count for pagination
|
||||
const totalCountResult = await this.drizzle
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(products)
|
||||
.where(searchCondition);
|
||||
|
||||
const totalCount = Number(totalCountResult[0].count);
|
||||
const totalPages = Math.ceil(totalCount / limit);
|
||||
|
||||
// Get paginated data
|
||||
const data = await this.drizzle
|
||||
.select({
|
||||
id: products.id,
|
||||
title: products.title,
|
||||
description: products.description,
|
||||
valuePerUnit: products.valuePerUnit,
|
||||
urlImg: products.urlImg,
|
||||
// price: products.price,
|
||||
// quantity: products.quantity,
|
||||
// isActive: products.isActive
|
||||
})
|
||||
.from(products)
|
||||
// .leftJoin(usersRole, eq(usersRole.userId, users.id))
|
||||
// .leftJoin(roles, eq(roles.id, usersRole.roleId))
|
||||
.where(searchCondition)
|
||||
.orderBy(orderBy)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
// Build pagination metadata
|
||||
const meta = {
|
||||
page,
|
||||
limit,
|
||||
totalCount,
|
||||
totalPages,
|
||||
hasNextPage: page < totalPages,
|
||||
hasPreviousPage: page > 1,
|
||||
nextPage: page < totalPages ? page + 1 : null,
|
||||
previousPage: page > 1 ? page - 1 : null,
|
||||
};
|
||||
|
||||
// console.log(data);
|
||||
|
||||
return { data, meta };
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<Product> {
|
||||
const find = await this.drizzle
|
||||
.select({
|
||||
id: products.id,
|
||||
title: products.title,
|
||||
description: products.description,
|
||||
valuePerUnit: products.valuePerUnit,
|
||||
urlImg: products.urlImg,
|
||||
})
|
||||
.from(products)
|
||||
// .leftJoin(usersRole, eq(usersRole.userId, users.id))
|
||||
// .leftJoin(roles, eq(roles.id, usersRole.roleId))
|
||||
// .leftJoin(schema.states, eq(schema.states.id, users.state))
|
||||
// .leftJoin(schema.municipalities, eq(schema.municipalities.id, users.municipality))
|
||||
// .leftJoin(schema.parishes, eq(schema.parishes.id, users.parish))
|
||||
|
||||
.where(eq(products.id, parseInt(id)));
|
||||
|
||||
if (find.length === 0) {
|
||||
throw new HttpException('User does not exist', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
return find[0];
|
||||
}
|
||||
|
||||
// Rest of the service remains the same
|
||||
async create(
|
||||
createProductDto: CreateProductDto,
|
||||
roleId: number = 2,
|
||||
): Promise<Product> {
|
||||
|
||||
|
||||
// Start a transaction
|
||||
return await this.drizzle.transaction(async (tx) => {
|
||||
// Create the user
|
||||
const [newProduct] = await tx
|
||||
.insert(products)
|
||||
.values({
|
||||
title: createProductDto.title,
|
||||
description: createProductDto.description,
|
||||
valuePerUnit: createProductDto.valuePerUnit,
|
||||
urlImg: createProductDto.urlImg,
|
||||
})
|
||||
.returning();
|
||||
|
||||
// Assign role to user
|
||||
// await tx.insert(usersRole).values({
|
||||
// userId: newProduct.id,
|
||||
// roleId: roleId,
|
||||
// });
|
||||
|
||||
// Return the created user with role
|
||||
// const [userWithRole] = await tx
|
||||
// .select({
|
||||
// id: users.id,
|
||||
// username: users.username,
|
||||
// email: users.email,
|
||||
// fullname: users.fullname,
|
||||
// phone: users.phone,
|
||||
// isActive: users.isActive,
|
||||
// role: roles.name,
|
||||
// })
|
||||
// .from(users)
|
||||
// .leftJoin(usersRole, eq(usersRole.userId, users.id))
|
||||
// .leftJoin(roles, eq(roles.id, usersRole.roleId))
|
||||
// .where(eq(users.id, newProduct.id));
|
||||
|
||||
|
||||
|
||||
return this.findOne(String(newProduct.id));
|
||||
});
|
||||
}
|
||||
|
||||
// async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
|
||||
// const userId = parseInt(id);
|
||||
|
||||
// // Check if user exists
|
||||
// await this.findOne(id);
|
||||
|
||||
// // Prepare update data
|
||||
// const updateData: any = {};
|
||||
// if (updateUserDto.username) updateData.username = updateUserDto.username;
|
||||
// if (updateUserDto.email) updateData.email = updateUserDto.email;
|
||||
// if (updateUserDto.fullname) updateData.fullname = updateUserDto.fullname;
|
||||
// if (updateUserDto.password) {
|
||||
// updateData.password = await bcrypt.hash(updateUserDto.password, 10);
|
||||
// }
|
||||
// if (updateUserDto.phone) updateData.phone = updateUserDto.phone;
|
||||
// if (updateUserDto.isActive) updateData.isActive = updateUserDto.isActive;
|
||||
|
||||
// const updateDataRole: any = {};
|
||||
// if (updateUserDto.role) updateDataRole.roleId = updateUserDto.role;
|
||||
// // Update user
|
||||
// await this.drizzle
|
||||
// .update(users)
|
||||
// .set(updateData)
|
||||
// .where(eq(users.id, userId));
|
||||
|
||||
// await this.drizzle
|
||||
// .update(usersRole)
|
||||
// .set(updateDataRole)
|
||||
// .where(eq(usersRole.userId, userId));
|
||||
|
||||
|
||||
// // Return updated user
|
||||
// return this.findOne(id);
|
||||
// }
|
||||
|
||||
|
||||
// async remove(id: string): Promise<{ message: string, data: User }> {
|
||||
// const userId = parseInt(id);
|
||||
|
||||
// // Check if user exists
|
||||
// const user = await this.findOne(id);
|
||||
|
||||
// // Delete user (this will cascade delete related records due to foreign key constraints)
|
||||
// // await this.drizzle.delete(users).where(eq(users.id, userId));
|
||||
// await this.drizzle.update(users).set({ isActive: false }).where(eq(users.id, userId));
|
||||
|
||||
// return { message: 'User deleted successfully', data: user };
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user