feat: add config module with controller and service
- Implemented ConfigController for handling config-related requests. - Created ConfigService for business logic related to configurations. - Added CreateConfigDto for validating configuration data. feat: add good categories module with controller and service - Implemented GoodCategoriesController for managing good categories. - Created GoodCategoriesService for business logic related to good categories. - Added CreateGoodCategoryDto for validating good category data. feat: add goods module with controller and service - Implemented GoodsController for managing goods. - Created GoodsService for business logic related to goods. - Added CreateGoodDto for validating good data. feat: add profile module with controller and service - Implemented ProfileController for managing user profiles. - Created ProfileService for business logic related to profiles. - Added CreateProfileDto for profile data structure. feat: add sales invoice payments module with controller and service - Implemented SalesInvoicePaymentsController for managing invoice payments. - Created SalesInvoicePaymentsService for business logic related to payments. - Added CreateSalesInvoicePaymentDto for validating payment data. feat: add service categories module with controller and service - Implemented ServiceCategoriesController for managing service categories. - Created ServiceCategoriesService for business logic related to service categories. - Added CreateServiceCategoryDto for validating service category data. feat: add services module with controller and service - Implemented ServicesController for managing services. - Created ServicesService for business logic related to services. - Added CreateServiceDto for validating service data. feat: add trigger logs module with controller and service - Implemented TriggerLogsController for managing trigger logs. - Created TriggerLogsService for business logic related to trigger logs. - Added CreateTriggerLogDto for validating trigger log data. feat: add uploaders module for handling file uploads - Implemented UploadersController for managing file uploads. - Created ImageUploaderService for image upload logic. - Created UploaderService for file handling and storage.
This commit is contained in:
@@ -1,41 +1,49 @@
|
||||
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'
|
||||
import { Controller, Get } from '@nestjs/common'
|
||||
import { ApiTags } from '@nestjs/swagger'
|
||||
import { ResponseMapper } from '../common/response/response-mapper'
|
||||
import { AuthService } from './auth.service'
|
||||
import { CurrentUser } from './current-user.decorator'
|
||||
import { RefreshTokenDto } from './dto/refresh-token.dto'
|
||||
import { SendCodeDto } from './dto/send-code.dto'
|
||||
import { VerifyCodeDto } from './dto/verify-code.dto'
|
||||
import { JwtAuthGuard } from './jwt-auth.guard'
|
||||
import { reqTokenPayload } from './current-user.decorator'
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly auth: AuthService) {}
|
||||
|
||||
@Post('send-code')
|
||||
async sendCode(@Body() dto: SendCodeDto) {
|
||||
return this.auth.sendCode(dto)
|
||||
}
|
||||
// @Public()
|
||||
// @Post('send_code')
|
||||
// async sendCode(@Body() dto: SendCodeDto) {
|
||||
// return this.auth.sendCode(dto)
|
||||
// }
|
||||
|
||||
@Post('login')
|
||||
async login(@Body() dto: VerifyCodeDto) {
|
||||
return this.auth.loginWithCode(dto)
|
||||
}
|
||||
// @Public()
|
||||
// @Post('login')
|
||||
// async login(@Body() dto: VerifyCodeDto, @Res({ passthrough: true }) res) {
|
||||
// const result = await this.auth.loginWithCode(dto)
|
||||
// // Set accessToken as httpOnly cookie
|
||||
// // @ts-ignore
|
||||
// if (result?.data?.accessToken) {
|
||||
// // @ts-ignore
|
||||
// res.cookie('accessToken', result.data.accessToken, {
|
||||
// httpOnly: true,
|
||||
// secure: process.env.NODE_ENV === 'production',
|
||||
// sameSite: 'lax',
|
||||
// maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
|
||||
// })
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
|
||||
@Post('refresh')
|
||||
async refresh(@Body() dto: RefreshTokenDto) {
|
||||
return this.auth.refreshToken(dto)
|
||||
}
|
||||
// @Post('refresh')
|
||||
// async refresh(@Body() dto: RefreshTokenDto) {
|
||||
// return this.auth.refreshToken(dto)
|
||||
// }
|
||||
|
||||
@Post('logout')
|
||||
async logout(@Body() dto: RefreshTokenDto) {
|
||||
return this.auth.logout(dto.refreshToken)
|
||||
}
|
||||
// @Post('logout')
|
||||
// async logout(@Body() dto: RefreshTokenDto) {
|
||||
// return this.auth.logout(dto.refreshToken)
|
||||
// }
|
||||
|
||||
@Get('me')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async me(@CurrentUser() user: any) {
|
||||
return ResponseMapper.single(user)
|
||||
async me(@reqTokenPayload() user: any) {
|
||||
return this.auth.me(user.username)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ import { Module } from '@nestjs/common'
|
||||
import { JwtModule } from '@nestjs/jwt'
|
||||
import 'dotenv/config'
|
||||
import { env } from 'prisma/config'
|
||||
import { PrismaModule } from '../prisma/prisma.module'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { AuthController } from './auth.controller'
|
||||
import { AuthService } from './auth.service'
|
||||
import { JwtAuthGuard } from './jwt-auth.guard'
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -15,8 +14,8 @@ import { JwtAuthGuard } from './jwt-auth.guard'
|
||||
signOptions: { expiresIn: Number(env('JWT_EXPIRES_IN')) || '15m' },
|
||||
}),
|
||||
],
|
||||
providers: [AuthService, JwtAuthGuard],
|
||||
providers: [AuthService],
|
||||
controllers: [AuthController],
|
||||
exports: [AuthService, JwtAuthGuard],
|
||||
exports: [AuthService, JwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
+151
-110
@@ -1,126 +1,167 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { JwtService } from '@nestjs/jwt'
|
||||
import { createHash, randomBytes } from 'crypto'
|
||||
import 'dotenv/config'
|
||||
import { env } from 'prisma/config'
|
||||
import { ResponseMapper } from '../common/response/response-mapper'
|
||||
import { PrismaService } from '../prisma/prisma.service'
|
||||
import { RefreshTokenDto } from './dto/refresh-token.dto'
|
||||
import { SendCodeDto } from './dto/send-code.dto'
|
||||
import { VerifyCodeDto } from './dto/verify-code.dto'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly OTP_TTL_MINUTES = 2
|
||||
private readonly ACCESS_TOKEN_EXP = Number(env('JWT_EXPIRES_IN')) || '15m'
|
||||
private readonly REFRESH_TOKEN_EXP_DAYS = 30
|
||||
// private readonly OTP_TTL_MINUTES = 2
|
||||
// private readonly ACCESS_TOKEN_EXP = Number(env('JWT_EXPIRES_IN')) || '150m'
|
||||
// private readonly REFRESH_TOKEN_EXP_DAYS = 30
|
||||
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private jwt: JwtService,
|
||||
) {}
|
||||
|
||||
private generateCode() {
|
||||
// For development you may want fixed code via env, otherwise random 5-digit
|
||||
const staticCode = process.env.OTP_STATIC_CODE
|
||||
if (staticCode) return staticCode
|
||||
return Math.floor(10000 + Math.random() * 90000).toString()
|
||||
}
|
||||
// private generateCode() {
|
||||
// // For development you may want fixed code via env, otherwise random 5-digit
|
||||
// const staticCode = process.env.OTP_STATIC_CODE
|
||||
// if (staticCode) return staticCode
|
||||
// return Math.floor(10000 + Math.random() * 90000).toString()
|
||||
// }
|
||||
|
||||
async sendCode(dto: SendCodeDto) {
|
||||
const { mobileNumber } = dto
|
||||
const user = await this.prisma.user.findFirst({ where: { mobileNumber } })
|
||||
if (!user) {
|
||||
throw new NotFoundException('کاربری با این شماره تماس یافت نشد')
|
||||
}
|
||||
const code = this.generateCode()
|
||||
const expiresAt = new Date(Date.now() + this.OTP_TTL_MINUTES * 60 * 1000)
|
||||
// // @TODO: we must detect context of request to verify user need to logged in as which role (admin, user, etc) and generate token with that role.
|
||||
// async sendCode(dto: SendCodeDto) {
|
||||
// const { mobile_number } = dto
|
||||
// const account = await this.prisma.account.findFirst({
|
||||
// where: {
|
||||
// user: {
|
||||
// mobile_number,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// console.log(mobile_number)
|
||||
|
||||
await this.prisma.otpCode.create({
|
||||
data: {
|
||||
mobileNumber,
|
||||
code,
|
||||
expiresAt,
|
||||
},
|
||||
// if (!account) {
|
||||
// throw new NotFoundException('کاربری با این شماره تماس یافت نشد')
|
||||
// }
|
||||
|
||||
// const existingCode = await this.prisma.verificationCode.findFirst({
|
||||
// where: { account_id: account.id, expires_at: { gt: new Date() } },
|
||||
// })
|
||||
// if (existingCode) {
|
||||
// throw new BadRequestException('کد برای شما ارسال شده است')
|
||||
// }
|
||||
|
||||
// const code = this.generateCode()
|
||||
// const expires_at = new Date(Date.now() + this.OTP_TTL_MINUTES * 60 * 1000)
|
||||
|
||||
// await this.prisma.verificationCode.create({
|
||||
// data: {
|
||||
// account_id: account.id,
|
||||
// code,
|
||||
// expires_at,
|
||||
// },
|
||||
// })
|
||||
|
||||
// // TODO: integrate SMS provider. For now return the code for dev.
|
||||
// return {
|
||||
// ok: true,
|
||||
// }
|
||||
// }
|
||||
|
||||
// async loginWithCode(dto: VerifyCodeDto) {
|
||||
// const { mobile_number, code } = dto
|
||||
// const account = await this.prisma.account.findFirst({
|
||||
// where: { user: { mobile_number } },
|
||||
// include: {
|
||||
// user: true,
|
||||
// },
|
||||
// })
|
||||
// if (!account) {
|
||||
// throw new NotFoundException('کاربری با این شماره تماس یافت نشد')
|
||||
// }
|
||||
// const otp = await this.prisma.verificationCode.findFirst({
|
||||
// where: { account_id: account.id },
|
||||
// })
|
||||
// if (!otp) throw new BadRequestException('اطلاعات ورودی درست نیست')
|
||||
// // if (otp.expires_at) throw new BadRequestException('کد قبلن استفاده شده است')
|
||||
// if (otp.expires_at.getTime() < Date.now())
|
||||
// throw new BadRequestException('کد وارد شده منقضی شده است')
|
||||
// if (otp.code !== code) throw new BadRequestException('کد وارد شده اشتباه است')
|
||||
// // mark used
|
||||
|
||||
// const signTokenData = {
|
||||
// sub: account.user.id,
|
||||
// mobile_number: account.user.mobile_number,
|
||||
// type: account.type,
|
||||
// username: account.username,
|
||||
// }
|
||||
|
||||
// if (account.pos_id) {
|
||||
// const pos = await this.prisma.pos.findUnique({
|
||||
// where: { id: account.pos_id },
|
||||
// include: {
|
||||
// complex: true,
|
||||
// licenses: true,
|
||||
// },
|
||||
// })
|
||||
// if (!pos) {
|
||||
// throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
// }
|
||||
// Object.assign(signTokenData, {
|
||||
// pos_id: pos.id,
|
||||
// pos_name: pos.serial,
|
||||
// complex: pos.complex,
|
||||
// license: pos.licenses[0],
|
||||
// })
|
||||
// }
|
||||
// console.log(signTokenData)
|
||||
|
||||
// const accessToken = this.jwt.sign(signTokenData, { expiresIn: this.ACCESS_TOKEN_EXP })
|
||||
// const refreshTokenPlain = randomBytes(48).toString('hex')
|
||||
// const tokenHash = createHash('sha256').update(refreshTokenPlain).digest('hex')
|
||||
// const expires_at = new Date(
|
||||
// Date.now() + this.REFRESH_TOKEN_EXP_DAYS * 24 * 60 * 60 * 1000,
|
||||
// )
|
||||
// await this.prisma.token.upsert({
|
||||
// where: { type_account_id: { type: TokenType.ACCESS, account_id: account.id } },
|
||||
// update: { token: tokenHash, expires_at },
|
||||
// create: {
|
||||
// token: tokenHash,
|
||||
// account_id: account.id,
|
||||
// expires_at,
|
||||
// type: TokenType.ACCESS,
|
||||
// },
|
||||
// })
|
||||
// await this.prisma.verificationCode.delete({ where: { id: otp.id } })
|
||||
|
||||
// return ResponseMapper.single({
|
||||
// accessToken,
|
||||
// refreshToken: refreshTokenPlain,
|
||||
// account,
|
||||
// })
|
||||
// }
|
||||
|
||||
// // async refreshToken(dto: RefreshTokenDto) {
|
||||
// // const { refreshToken } = dto
|
||||
// // const tokenHash = createHash('sha256').update(refreshToken).digest('hex')
|
||||
// // const record = await this.prisma.refreshToken.findFirst({ where: { tokenHash } })
|
||||
// // if (!record || record.revoked || record.expiresAt.getTime() < Date.now()) {
|
||||
// // throw new UnauthorizedException('Invalid refresh token')
|
||||
// // }
|
||||
// // const user = await this.prisma.user.findUnique({ where: { id: record.userId } })
|
||||
// // if (!user) throw new UnauthorizedException('User not found')
|
||||
// // const accessToken = this.jwt.sign(
|
||||
// // { sub: user.id, mobileNumber: user.mobileNumber },
|
||||
// // { expiresIn: this.ACCESS_TOKEN_EXP },
|
||||
// // )
|
||||
// // return { accessToken }
|
||||
// // }
|
||||
|
||||
// // async logout() {
|
||||
// // await this.prisma.token.deleteMany({
|
||||
// // where: { tokenHash },
|
||||
// // data: { revoked: true },
|
||||
// // })
|
||||
// // return { ok: true }
|
||||
// // }
|
||||
|
||||
async me(username: string) {
|
||||
return ResponseMapper.single({
|
||||
username,
|
||||
})
|
||||
|
||||
// TODO: integrate SMS provider. For now return the code for dev.
|
||||
return {
|
||||
ok: true,
|
||||
}
|
||||
}
|
||||
|
||||
async loginWithCode(dto: VerifyCodeDto) {
|
||||
const { mobileNumber, code } = dto
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: { mobileNumber },
|
||||
include: {
|
||||
role: true,
|
||||
},
|
||||
})
|
||||
if (!user) {
|
||||
throw new NotFoundException('کاربری با این شماره تماس یافت نشد')
|
||||
}
|
||||
|
||||
const otp = await this.prisma.otpCode.findFirst({
|
||||
where: { mobileNumber, used: false },
|
||||
})
|
||||
|
||||
if (!otp) throw new BadRequestException('اطلاعات ورودی درست نیست')
|
||||
if (otp.used) throw new BadRequestException('کد قبلن استفاده شده است')
|
||||
if (otp.expiresAt.getTime() < Date.now())
|
||||
throw new BadRequestException('کد وارد شده منقضی شده است')
|
||||
if (otp.code !== code) throw new BadRequestException('کد وارد شده اشتباه است')
|
||||
|
||||
// mark used
|
||||
await this.prisma.otpCode.update({ where: { id: otp.id }, data: { used: true } })
|
||||
|
||||
const accessToken = this.jwt.sign(
|
||||
{ sub: user.id, mobileNumber },
|
||||
{ expiresIn: this.ACCESS_TOKEN_EXP },
|
||||
)
|
||||
const refreshTokenPlain = randomBytes(48).toString('hex')
|
||||
const tokenHash = createHash('sha256').update(refreshTokenPlain).digest('hex')
|
||||
const expiresAt = new Date(
|
||||
Date.now() + this.REFRESH_TOKEN_EXP_DAYS * 24 * 60 * 60 * 1000,
|
||||
)
|
||||
|
||||
await this.prisma.refreshToken.create({
|
||||
data: { tokenHash, userId: user.id, expiresAt },
|
||||
})
|
||||
|
||||
return ResponseMapper.single({ accessToken, refreshToken: refreshTokenPlain, user })
|
||||
}
|
||||
|
||||
async refreshToken(dto: RefreshTokenDto) {
|
||||
const { refreshToken } = dto
|
||||
const tokenHash = createHash('sha256').update(refreshToken).digest('hex')
|
||||
const record = await this.prisma.refreshToken.findFirst({ where: { tokenHash } })
|
||||
if (!record || record.revoked || record.expiresAt.getTime() < Date.now()) {
|
||||
throw new UnauthorizedException('Invalid refresh token')
|
||||
}
|
||||
|
||||
const user = await this.prisma.user.findUnique({ where: { id: record.userId } })
|
||||
if (!user) throw new UnauthorizedException('User not found')
|
||||
|
||||
const accessToken = this.jwt.sign(
|
||||
{ sub: user.id, mobileNumber: user.mobileNumber },
|
||||
{ expiresIn: this.ACCESS_TOKEN_EXP },
|
||||
)
|
||||
return { accessToken }
|
||||
}
|
||||
|
||||
async logout(refreshToken: string) {
|
||||
const tokenHash = createHash('sha256').update(refreshToken).digest('hex')
|
||||
await this.prisma.refreshToken.updateMany({
|
||||
where: { tokenHash },
|
||||
data: { revoked: true },
|
||||
})
|
||||
return { ok: true }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
|
||||
import { AccessTokenPayload } from '../../common/models/token-model'
|
||||
|
||||
export const CurrentUser = createParamDecorator(
|
||||
export const reqTokenPayload = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
const req = ctx.switchToHttp().getRequest()
|
||||
return req.user
|
||||
return req.dataPayload as AccessTokenPayload
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2,11 +2,11 @@ import { ApiProperty } from '@nestjs/swagger'
|
||||
import { IsString } from 'class-validator'
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty({ description: 'Mobile number used as username', example: '09123456789' })
|
||||
@ApiProperty({ description: 'Mobile number used as username', example: '09120258156' })
|
||||
@IsString()
|
||||
mobileNumber: string
|
||||
|
||||
@ApiProperty({ description: 'OTP password (one-time password)', example: '12345' })
|
||||
@ApiProperty({ description: 'OTP password (one-time password)', example: '11111' })
|
||||
@IsString()
|
||||
password: string
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'
|
||||
import { IsString } from 'class-validator'
|
||||
|
||||
export class SendCodeDto {
|
||||
@ApiProperty({ example: '09123456789' })
|
||||
@ApiProperty({ example: '09120258156' })
|
||||
@IsString()
|
||||
mobileNumber: string
|
||||
mobile_number: string
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ import { ApiProperty } from '@nestjs/swagger'
|
||||
import { IsString } from 'class-validator'
|
||||
|
||||
export class VerifyCodeDto {
|
||||
@ApiProperty({ example: '09123456789' })
|
||||
@ApiProperty({ example: '09120258156' })
|
||||
@IsString()
|
||||
mobileNumber: string
|
||||
mobile_number: string
|
||||
|
||||
@ApiProperty({ example: '12345' })
|
||||
@ApiProperty({ example: '11111' })
|
||||
@IsString()
|
||||
code: string
|
||||
}
|
||||
|
||||
@@ -4,31 +4,58 @@ import {
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common'
|
||||
import { Reflector } from '@nestjs/core'
|
||||
import { JwtService } from '@nestjs/jwt'
|
||||
import { Request } from 'express'
|
||||
import { IS_PUBLIC_KEY } from '../../common/decorators/public.decorator'
|
||||
import { IWithJWTPayloadRequest } from '../../common/models/token-model'
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard implements CanActivate {
|
||||
constructor(private jwt: JwtService) {}
|
||||
constructor(
|
||||
private jwt: JwtService,
|
||||
private reflector: Reflector,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest<Request>()
|
||||
const auth = req.headers.authorization
|
||||
if (!auth) throw new UnauthorizedException('Missing Authorization header')
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
])
|
||||
if (isPublic) return true
|
||||
|
||||
const parts = auth.split(' ')
|
||||
if (parts.length !== 2 || parts[0] !== 'Bearer') {
|
||||
throw new UnauthorizedException('Invalid Authorization header format')
|
||||
const req = context.switchToHttp().getRequest<IWithJWTPayloadRequest>()
|
||||
|
||||
// Log headers to inspect whether Authorization is present
|
||||
|
||||
// Try token from cookie first, then from Authorization header (Bearer)
|
||||
let token: string | undefined = req.cookies?.accessToken
|
||||
if (!token) {
|
||||
const authHeader = (req.headers.authorization || req.headers.Authorization) as
|
||||
| string
|
||||
| undefined
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
token = authHeader.slice(7)
|
||||
}
|
||||
}
|
||||
|
||||
const token = parts[1]
|
||||
if (!token) throw new UnauthorizedException('Missing access token')
|
||||
|
||||
try {
|
||||
const payload = this.jwt.verify(token, {
|
||||
secret: process.env.JWT_SECRET || 'secret',
|
||||
})
|
||||
;(req as any).user = payload
|
||||
|
||||
console.log(payload)
|
||||
|
||||
if (payload.type !== 'POS')
|
||||
throw new UnauthorizedException('Invalid or expired token')
|
||||
|
||||
// Set the typed dataPayload to the request
|
||||
req.dataPayload = payload
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw new UnauthorizedException('Invalid or expired token')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { ConfigService } from './config.service'
|
||||
import { CreateConfigDto } from './dto/create-config.dto'
|
||||
|
||||
@Controller('config')
|
||||
export class ConfigController {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') uuid: string) {
|
||||
return this.configService.findOne(uuid)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: CreateConfigDto) {
|
||||
return this.configService.create(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ConfigController } from './config.controller'
|
||||
import { ConfigService } from './config.service'
|
||||
|
||||
@Module({
|
||||
controllers: [ConfigController],
|
||||
providers: [ConfigService],
|
||||
})
|
||||
export class ConfigModule {}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Public } from '../../common/decorators/public.decorator'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { CreateConfigDto } from './dto/create-config.dto'
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
@Public()
|
||||
async findOne(uuid: string) {
|
||||
const config = await this.prisma.device.findUnique({
|
||||
where: {
|
||||
uuid,
|
||||
},
|
||||
})
|
||||
|
||||
return ResponseMapper.single(config)
|
||||
}
|
||||
|
||||
@Public()
|
||||
async create(data: CreateConfigDto) {
|
||||
const config = await this.prisma.device.upsert({
|
||||
where: {
|
||||
uuid: data.uuid,
|
||||
},
|
||||
update: {
|
||||
...data,
|
||||
},
|
||||
create: {
|
||||
...data,
|
||||
},
|
||||
})
|
||||
|
||||
return ResponseMapper.create(config)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateConfigDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
app_version: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
build_number: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
uuid: string
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
fcm_token?: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
platform: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
brand: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
model: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
device: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
os_version: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
sdk_version: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
release_number: string
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
browser_name: string
|
||||
}
|
||||
|
||||
export class UpdateConfigDto extends PartialType(CreateConfigDto) {}
|
||||
@@ -1,34 +1,22 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { CustomersService } from './customers.service'
|
||||
import { CreateCustomerDto } from './dto/create-customer.dto'
|
||||
import { UpdateCustomerDto } from './dto/update-customer.dto'
|
||||
|
||||
@Controller('customers')
|
||||
export class CustomersController {
|
||||
constructor(private readonly customersService: CustomersService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateCustomerDto) {
|
||||
return this.customersService.create(dto)
|
||||
}
|
||||
constructor(private readonly customerService: CustomersService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.customersService.findAll()
|
||||
return this.customerService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.customersService.findOne(Number(id))
|
||||
return this.customerService.findOne(+id)
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateCustomerDto) {
|
||||
return this.customersService.update(Number(id), dto)
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.customersService.remove(Number(id))
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.customerService.create(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { CustomersController } from './customers.controller'
|
||||
import { CustomersService } from './customers.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [CustomersController],
|
||||
providers: [CustomersService],
|
||||
})
|
||||
|
||||
@@ -1,33 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class CustomersService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
async create(data: any) {
|
||||
const item = await this.prisma.customer.create({ data })
|
||||
return ResponseMapper.create(item)
|
||||
findAll() {
|
||||
// TODO: Implement fetching all customers
|
||||
return []
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
const items = await this.prisma.customer.findMany()
|
||||
return ResponseMapper.list(items)
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single customer
|
||||
return {}
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const item = await this.prisma.customer.findUnique({ where: { id } })
|
||||
if (!item) return null
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
|
||||
async update(id: number, data: any) {
|
||||
const item = await this.prisma.customer.update({ where: { id }, data })
|
||||
return ResponseMapper.update(item)
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.customer.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
create(data: any) {
|
||||
// TODO: Implement customer creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { IsEmail, IsOptional, IsString } from 'class-validator'
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsBoolean, IsEmail, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateCustomerDto {
|
||||
@IsString()
|
||||
firstName: string
|
||||
@ApiProperty()
|
||||
first_name: string
|
||||
|
||||
@IsString()
|
||||
lastName: string
|
||||
@ApiProperty()
|
||||
last_name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
@ApiProperty({ required: false })
|
||||
email?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
mobileNumber?: string
|
||||
@ApiProperty()
|
||||
mobile_number: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
address?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
city?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
state?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
country?: string
|
||||
@IsBoolean()
|
||||
@ApiProperty({ required: false })
|
||||
is_active?: boolean
|
||||
}
|
||||
|
||||
export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsBoolean, IsEmail, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class UpdateCustomerDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
firstName?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
lastName?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
email?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
mobileNumber?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
city?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
state?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
country?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
isActive?: boolean
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateGoodCategoryDto {
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
image_url?: string
|
||||
}
|
||||
|
||||
export class UpdateGoodCategoryDto extends PartialType(CreateGoodCategoryDto) {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { reqTokenPayload } from '../auth/current-user.decorator'
|
||||
import { CreateGoodCategoryDto } from './dto/create-good-category.dto'
|
||||
import { GoodCategoriesService } from './good-categories.service'
|
||||
|
||||
@Controller('good-categories')
|
||||
export class GoodCategoriesController {
|
||||
constructor(private readonly goodCategoriesService: GoodCategoriesService) {}
|
||||
|
||||
@Get()
|
||||
findAll(@reqTokenPayload() account) {
|
||||
return this.goodCategoriesService.findAll(account.complex_id)
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string, @reqTokenPayload() account) {
|
||||
return this.goodCategoriesService.findOne(id, account.complex_id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: CreateGoodCategoryDto, @reqTokenPayload() account) {
|
||||
return this.goodCategoriesService.create(data, account.complex_id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { GoodCategoriesController } from './good-categories.controller'
|
||||
import { GoodCategoriesService } from './good-categories.service'
|
||||
|
||||
@Module({
|
||||
controllers: [GoodCategoriesController],
|
||||
providers: [GoodCategoriesService],
|
||||
})
|
||||
export class GoodCategoriesModule {}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { CreateGoodCategoryDto } from './dto/create-good-category.dto'
|
||||
|
||||
@Injectable()
|
||||
export class GoodCategoriesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
async findAll(complex_id: string) {
|
||||
// console.log(account)
|
||||
|
||||
const categories = await this.prisma.goodCategory.findMany({
|
||||
where: {
|
||||
complex_id,
|
||||
},
|
||||
include: {
|
||||
goods: {
|
||||
include: {
|
||||
_count: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return ResponseMapper.list(categories)
|
||||
}
|
||||
|
||||
findOne(categoryId: string, complex_id: string) {
|
||||
return {}
|
||||
}
|
||||
|
||||
create(data: CreateGoodCategoryDto, complex_id: string) {
|
||||
const category = this.prisma.goodCategory.create({
|
||||
data: {
|
||||
...data,
|
||||
complex_id,
|
||||
account_id: '',
|
||||
},
|
||||
})
|
||||
return ResponseMapper.create(category)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateGoodDto {
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
description?: string
|
||||
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
sku: string
|
||||
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
local_sku: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
barcode?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
category_id?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: false })
|
||||
base_sale_price?: number
|
||||
}
|
||||
|
||||
export class UpdateGoodDto extends PartialType(CreateGoodDto) {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { reqTokenPayload } from '../auth/current-user.decorator'
|
||||
import { CreateGoodDto } from './dto/create-good.dto'
|
||||
import { GoodsService } from './goods.service'
|
||||
|
||||
@Controller('goods')
|
||||
export class GoodsController {
|
||||
constructor(private readonly goodsService: GoodsService) {}
|
||||
|
||||
@Get()
|
||||
findAll(@reqTokenPayload() account) {
|
||||
return this.goodsService.findAll(account.complex_id)
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') goodId: string, @reqTokenPayload() account) {
|
||||
return this.goodsService.findOne(goodId, account.complex_id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: CreateGoodDto, @reqTokenPayload() account) {
|
||||
return this.goodsService.create(data, account.complex_id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { GoodsController } from './goods.controller'
|
||||
import { GoodsService } from './goods.service'
|
||||
|
||||
@Module({
|
||||
controllers: [GoodsController],
|
||||
providers: [GoodsService],
|
||||
})
|
||||
export class GoodsModule {}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { CreateGoodDto } from './dto/create-good.dto'
|
||||
|
||||
@Injectable()
|
||||
export class GoodsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(complex_id: string) {
|
||||
const goods = await this.prisma.good.findMany({
|
||||
where: {
|
||||
complex_id,
|
||||
},
|
||||
omit: {
|
||||
account_id: true,
|
||||
complex_id: true,
|
||||
deleted_at: true,
|
||||
},
|
||||
})
|
||||
|
||||
return ResponseMapper.list(goods)
|
||||
}
|
||||
|
||||
async findOne(good_id: string, complex_id: string) {
|
||||
const good = await this.prisma.good.findUnique({
|
||||
where: {
|
||||
complex_id,
|
||||
id: good_id,
|
||||
},
|
||||
|
||||
omit: {
|
||||
account_id: true,
|
||||
complex_id: true,
|
||||
deleted_at: true,
|
||||
},
|
||||
})
|
||||
|
||||
return ResponseMapper.single(good)
|
||||
}
|
||||
|
||||
async create(data: CreateGoodDto, complex_id: string) {
|
||||
const { category_id, ...rest } = data
|
||||
|
||||
const dataToCreate = {
|
||||
...rest,
|
||||
complex_id,
|
||||
account_id: '',
|
||||
category: {
|
||||
connect: {
|
||||
id: category_id,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const good = await this.prisma.good.create({
|
||||
data: dataToCreate,
|
||||
omit: {
|
||||
account_id: true,
|
||||
complex_id: true,
|
||||
deleted_at: true,
|
||||
},
|
||||
})
|
||||
return ResponseMapper.create(good)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateProductCategoryDto {
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
imageUrl?: string
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class UpdateProductCategoryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
imageUrl?: string
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'
|
||||
import { CreateProductCategoryDto } from './dto/create-product-category.dto'
|
||||
import { UpdateProductCategoryDto } from './dto/update-product-category.dto'
|
||||
import { ProductCategoriesService } from './product-categories.service'
|
||||
|
||||
@Controller('product-categories')
|
||||
export class ProductCategoriesController {
|
||||
constructor(private readonly productCategoriesService: ProductCategoriesService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateProductCategoryDto) {
|
||||
return this.productCategoriesService.create(dto)
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.productCategoriesService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.productCategoriesService.findOne(Number(id))
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateProductCategoryDto) {
|
||||
return this.productCategoriesService.update(Number(id), dto)
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.productCategoriesService.remove(Number(id))
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { ProductCategoriesController } from './product-categories.controller'
|
||||
import { ProductCategoriesService } from './product-categories.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [ProductCategoriesController],
|
||||
providers: [ProductCategoriesService],
|
||||
})
|
||||
export class ProductCategoriesModule {}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class ProductCategoriesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
async create(data: any) {
|
||||
const item = await this.prisma.goodCategory.create({ data })
|
||||
return ResponseMapper.create(item)
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
const items = await this.prisma.goodCategory.findMany()
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const item = await this.prisma.goodCategory.findUnique({ where: { id } })
|
||||
if (!item) return null
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
|
||||
async update(id: number, data: any) {
|
||||
const item = await this.prisma.goodCategory.update({ where: { id }, data })
|
||||
return ResponseMapper.update(item)
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.goodCategory.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateProductDto {
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sku?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
barcode?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
brandId?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
categoryId?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
minimumStockAlertLevel?: number
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class UpdateProductDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sku?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
barcode?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
brandId?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
categoryId?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
minimumStockAlertLevel?: number
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common'
|
||||
import { ApiQuery } from '@nestjs/swagger'
|
||||
import { CreateProductDto } from './dto/create-product.dto'
|
||||
import { UpdateProductDto } from './dto/update-product.dto'
|
||||
import { ProductsService } from './products.service'
|
||||
|
||||
@Controller('products')
|
||||
export class ProductsController {
|
||||
constructor(private readonly productsService: ProductsService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateProductDto) {
|
||||
return this.productsService.create(dto)
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiQuery({ name: 'page', required: false, type: Number, default: 1 })
|
||||
@ApiQuery({ name: 'pageSize', required: false, type: Number, default: 30 })
|
||||
findAll(@Query('page') page: number = 1, @Query('pageSize') pageSize: number = 30) {
|
||||
return this.productsService.findAll(page, pageSize)
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.productsService.findOne(Number(id))
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateProductDto) {
|
||||
return this.productsService.update(Number(id), dto)
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.productsService.remove(Number(id))
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { ProductsController } from './products.controller'
|
||||
import { ProductsService } from './products.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [ProductsController],
|
||||
providers: [ProductsService],
|
||||
})
|
||||
export class ProductsModule {}
|
||||
@@ -1,131 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ShortEntity } from '../../common/interfaces/response-models'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { Prisma } from '../../generated/prisma/client'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { CreateProductDto } from './dto/create-product.dto'
|
||||
import { UpdateProductDto } from './dto/update-product.dto'
|
||||
|
||||
@Injectable()
|
||||
export class ProductsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async create(data: CreateProductDto) {
|
||||
const { brandId, categoryId, ...rest } = data
|
||||
|
||||
const dataToCreate = {
|
||||
...rest,
|
||||
brand: brandId ? { connect: { id: Number(brandId) } } : undefined,
|
||||
category: categoryId ? { connect: { id: Number(categoryId) } } : undefined,
|
||||
}
|
||||
|
||||
const item = await this.prisma.product.create({ data: dataToCreate })
|
||||
return ResponseMapper.create(item)
|
||||
}
|
||||
|
||||
async findAll(page = 1, pageSize = 20) {
|
||||
const query: Prisma.ProductFindManyArgs = {}
|
||||
|
||||
const [items, count] = await this.prisma.$transaction([
|
||||
this.prisma.product.findMany({
|
||||
include: { brand: true, category: true, stockBalances: true },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.product.count(),
|
||||
])
|
||||
|
||||
const mapped = items.map(p => {
|
||||
const { brandId, categoryId, ...rest } = p as any
|
||||
const brand: ShortEntity | null = p.brand
|
||||
? { id: p.brand.id, name: p.brand.name }
|
||||
: null
|
||||
const category: ShortEntity | null = p.category
|
||||
? { id: p.category.id, name: p.category.name }
|
||||
: null
|
||||
const stock =
|
||||
p.stockBalances && p.stockBalances.length > 0
|
||||
? p.stockBalances.reduce((acc, sb) => acc + Number(sb.quantity), 0)
|
||||
: 0
|
||||
|
||||
return {
|
||||
...rest,
|
||||
salePrice: Number(p.salePrice),
|
||||
minimumStockAlertLevel: Number(p.minimumStockAlertLevel),
|
||||
brand,
|
||||
category,
|
||||
stock,
|
||||
}
|
||||
})
|
||||
return ResponseMapper.paginate(mapped, count, page, pageSize)
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const product = await this.prisma.product.findUniqueOrThrow({
|
||||
where: { id },
|
||||
include: {
|
||||
brand: {
|
||||
select: { id: true, name: true },
|
||||
},
|
||||
category: {
|
||||
select: { id: true, name: true },
|
||||
},
|
||||
stockBalances: {
|
||||
select: {
|
||||
quantity: true,
|
||||
avgCost: true,
|
||||
inventory: { select: { id: true, name: true } },
|
||||
},
|
||||
},
|
||||
salesInvoiceItems: {
|
||||
select: { id: true },
|
||||
},
|
||||
},
|
||||
omit: {
|
||||
brandId: true,
|
||||
categoryId: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { salesInvoiceItems, ...rest } = product
|
||||
|
||||
return ResponseMapper.single({
|
||||
...rest,
|
||||
salePrice: Number(product.salePrice),
|
||||
minimumStockAlertLevel: Number(product.minimumStockAlertLevel),
|
||||
stock: product.stockBalances?.reduce((acc, sb) => acc + Number(sb.quantity), 0),
|
||||
avgCost:
|
||||
product.stockBalances?.reduce((acc, sb) => acc + Number(sb.avgCost), 0) /
|
||||
product.stockBalances.length,
|
||||
salesCount: product.salesInvoiceItems.length,
|
||||
})
|
||||
}
|
||||
|
||||
async update(id: number, data: UpdateProductDto) {
|
||||
const { brandId, categoryId, ...rest } = data
|
||||
|
||||
const dataToUpdate = {
|
||||
...rest,
|
||||
brand:
|
||||
brandId === null
|
||||
? { disconnect: true }
|
||||
: brandId
|
||||
? { connect: { id: Number(brandId) } }
|
||||
: undefined,
|
||||
category:
|
||||
categoryId === null
|
||||
? { disconnect: true }
|
||||
: categoryId
|
||||
? { connect: { id: Number(categoryId) } }
|
||||
: undefined,
|
||||
}
|
||||
|
||||
const item = await this.prisma.product.update({ where: { id }, data: dataToUpdate })
|
||||
return ResponseMapper.update(item)
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.product.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { PartialType } from '@nestjs/swagger'
|
||||
|
||||
export class CreateProfileDto {}
|
||||
|
||||
export class UpdateProfileDto extends PartialType(CreateProfileDto) {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Controller, Get } from '@nestjs/common'
|
||||
import { reqTokenPayload } from '../auth/current-user.decorator'
|
||||
import { ProfileService } from './profile.service'
|
||||
|
||||
@Controller('profile')
|
||||
export class ProfileController {
|
||||
constructor(private readonly profileService: ProfileService) {}
|
||||
|
||||
@Get('me')
|
||||
findOne(@reqTokenPayload() account) {
|
||||
return this.profileService.findOne(account)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ProfileController } from './profile.controller'
|
||||
import { ProfileService } from './profile.service'
|
||||
|
||||
@Module({
|
||||
controllers: [ProfileController],
|
||||
providers: [ProfileService],
|
||||
})
|
||||
export class ProfileModule {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Public } from '../../common/decorators/public.decorator'
|
||||
import type { AccessTokenPayload } from '../../common/models/token-model'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class ProfileService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
@Public()
|
||||
async findOne(account: AccessTokenPayload) {
|
||||
return ResponseMapper.single(account)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,32 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsNumber } from 'class-validator'
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateSalesInvoiceItemDto {
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@ApiProperty()
|
||||
count: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
unitPrice: number
|
||||
@ApiProperty()
|
||||
unit_price: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
totalAmount: number
|
||||
@ApiProperty()
|
||||
total_amount: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
invoiceId: number
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
invoice_id: string
|
||||
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
productId: number
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
good_id?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
service_id?: string
|
||||
}
|
||||
|
||||
export class UpdateSalesInvoiceItemDto extends PartialType(CreateSalesInvoiceItemDto) {}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsNumber, IsOptional } from 'class-validator'
|
||||
|
||||
export class UpdateSalesInvoiceItemDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
count?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
unitPrice?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
totalAmount?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
invoiceId?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
productId?: number
|
||||
}
|
||||
@@ -1,38 +1,22 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'
|
||||
import { CreateSalesInvoiceItemDto } from './dto/create-sales-invoice-item.dto'
|
||||
import { UpdateSalesInvoiceItemDto } from './dto/update-sales-invoice-item.dto'
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { SalesInvoiceItemsService } from './sales-invoice-items.service'
|
||||
|
||||
@Controller('sales-invoices/:invoiceId/items')
|
||||
@Controller('sales-invoice-items')
|
||||
export class SalesInvoiceItemsController {
|
||||
constructor(private readonly service: SalesInvoiceItemsService) {}
|
||||
|
||||
@Post()
|
||||
create(@Param('invoiceId') invoiceId: string, @Body() dto: CreateSalesInvoiceItemDto) {
|
||||
return this.service.createForInvoice(Number(invoiceId), dto)
|
||||
}
|
||||
constructor(private readonly salesInvoiceItemsService: SalesInvoiceItemsService) {}
|
||||
|
||||
@Get()
|
||||
findAll(@Param('invoiceId') invoiceId: string) {
|
||||
return this.service.findAllForInvoice(Number(invoiceId))
|
||||
findAll() {
|
||||
return this.salesInvoiceItemsService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('invoiceId') invoiceId: string, @Param('id') id: string) {
|
||||
return this.service.findOneForInvoice(Number(invoiceId), Number(id))
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.salesInvoiceItemsService.findOne(+id)
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(
|
||||
@Param('invoiceId') invoiceId: string,
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdateSalesInvoiceItemDto,
|
||||
) {
|
||||
return this.service.updateForInvoice(Number(invoiceId), Number(id), dto)
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('invoiceId') invoiceId: string, @Param('id') id: string) {
|
||||
return this.service.removeForInvoice(Number(invoiceId), Number(id))
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.salesInvoiceItemsService.create(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { SalesInvoiceItemsController } from './sales-invoice-items.controller'
|
||||
import { SalesInvoiceItemsService } from './sales-invoice-items.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [SalesInvoiceItemsController],
|
||||
providers: [SalesInvoiceItemsService],
|
||||
})
|
||||
|
||||
@@ -1,98 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { CreateSalesInvoiceItemDto } from './dto/create-sales-invoice-item.dto'
|
||||
|
||||
@Injectable()
|
||||
export class SalesInvoiceItemsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async create(dto: CreateSalesInvoiceItemDto) {
|
||||
const payload: any = { ...dto }
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'invoiceId')) {
|
||||
payload.invoice = { connect: { id: Number(payload.invoiceId) } }
|
||||
delete payload.invoiceId
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'productId')) {
|
||||
payload.product = { connect: { id: Number(payload.productId) } }
|
||||
delete payload.productId
|
||||
}
|
||||
|
||||
const item = await this.prisma.salesInvoiceItem.create({ data: payload })
|
||||
return ResponseMapper.create(item)
|
||||
}
|
||||
async findAll(invoiceId?: number) {
|
||||
const where = invoiceId ? { invoiceId: Number(invoiceId) } : undefined
|
||||
const items = await this.prisma.salesInvoiceItem.findMany({
|
||||
where,
|
||||
include: { product: true },
|
||||
})
|
||||
return ResponseMapper.list(items)
|
||||
findAll() {
|
||||
// TODO: Implement fetching all sales invoice items
|
||||
return []
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const item = await this.prisma.salesInvoiceItem.findUnique({
|
||||
where: { id },
|
||||
include: { product: true, invoice: true },
|
||||
})
|
||||
if (!item) return null
|
||||
return ResponseMapper.single(item)
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single sales invoice item
|
||||
return {}
|
||||
}
|
||||
|
||||
async createForInvoice(invoiceId: number, dto: CreateSalesInvoiceItemDto) {
|
||||
const payload: any = { ...dto, invoiceId }
|
||||
return this.create(payload)
|
||||
}
|
||||
|
||||
async findAllForInvoice(invoiceId: number) {
|
||||
return this.findAll(invoiceId)
|
||||
}
|
||||
|
||||
async findOneForInvoice(invoiceId: number, id: number) {
|
||||
const item = await this.prisma.salesInvoiceItem.findFirst({
|
||||
where: { id, invoiceId },
|
||||
include: { product: true, invoice: true },
|
||||
})
|
||||
if (!item) return null
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
|
||||
async update(id: number, data: any) {
|
||||
const payload: any = { ...data }
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'productId')) {
|
||||
payload.product = { connect: { id: Number(payload.productId) } }
|
||||
delete payload.productId
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'invoiceId')) {
|
||||
payload.invoice = { connect: { id: Number(payload.invoiceId) } }
|
||||
delete payload.invoiceId
|
||||
}
|
||||
|
||||
const item = await this.prisma.salesInvoiceItem.update({
|
||||
where: { id },
|
||||
data: payload,
|
||||
})
|
||||
return ResponseMapper.update(item)
|
||||
}
|
||||
|
||||
async updateForInvoice(invoiceId: number, id: number, data: any) {
|
||||
const existing = await this.prisma.salesInvoiceItem.findFirst({
|
||||
where: { id, invoiceId },
|
||||
})
|
||||
if (!existing) return null
|
||||
return this.update(id, data)
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.salesInvoiceItem.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
|
||||
async removeForInvoice(invoiceId: number, id: number) {
|
||||
const existing = await this.prisma.salesInvoiceItem.findFirst({
|
||||
where: { id, invoiceId },
|
||||
})
|
||||
if (!existing) return null
|
||||
return this.remove(id)
|
||||
create(data: any) {
|
||||
// TODO: Implement sales invoice item creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsDateString, IsEnum, IsNumber, IsString } from 'class-validator'
|
||||
|
||||
enum PaymentMethodType {
|
||||
CASH = 'CASH',
|
||||
CARD = 'CARD',
|
||||
BANK = 'BANK',
|
||||
CHECK = 'CHECK',
|
||||
OTHER = 'OTHER',
|
||||
}
|
||||
|
||||
export class CreateSalesInvoicePaymentDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
invoice_id: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
amount: number
|
||||
|
||||
@ApiProperty({ enum: PaymentMethodType })
|
||||
@IsEnum(PaymentMethodType)
|
||||
payment_method: PaymentMethodType
|
||||
|
||||
@ApiProperty()
|
||||
@IsDateString()
|
||||
paid_at: string
|
||||
}
|
||||
|
||||
export class UpdateSalesInvoicePaymentDto extends PartialType(
|
||||
CreateSalesInvoicePaymentDto,
|
||||
) {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { SalesInvoicePaymentsService } from './sales-invoice-payments.service'
|
||||
|
||||
@Controller('sales-invoice-payments')
|
||||
export class SalesInvoicePaymentsController {
|
||||
constructor(
|
||||
private readonly salesInvoicePaymentsService: SalesInvoicePaymentsService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.salesInvoicePaymentsService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.salesInvoicePaymentsService.findOne(+id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.salesInvoicePaymentsService.create(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { SalesInvoicePaymentsController } from './sales-invoice-payments.controller'
|
||||
import { SalesInvoicePaymentsService } from './sales-invoice-payments.service'
|
||||
|
||||
@Module({
|
||||
controllers: [SalesInvoicePaymentsController],
|
||||
providers: [SalesInvoicePaymentsService],
|
||||
})
|
||||
export class SalesInvoicePaymentsModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
|
||||
@Injectable()
|
||||
export class SalesInvoicePaymentsService {
|
||||
findAll() {
|
||||
// TODO: Implement fetching all sales invoice payments
|
||||
return []
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single sales invoice payment
|
||||
return {}
|
||||
}
|
||||
|
||||
create(data: any) {
|
||||
// TODO: Implement sales invoice payment creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,24 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateSalesInvoiceDto {
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
code: string
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
totalAmount: number
|
||||
@ApiProperty()
|
||||
total_amount: number
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
customerId?: number
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
customer_id?: string
|
||||
}
|
||||
|
||||
export class UpdateSalesInvoiceDto extends PartialType(CreateSalesInvoiceDto) {}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class UpdateSalesInvoiceDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
code?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
totalAmount?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
customerId?: number | null
|
||||
}
|
||||
@@ -1,32 +1,22 @@
|
||||
import { Controller, Delete, Get, Param } from '@nestjs/common'
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { SalesInvoicesService } from './sales-invoices.service'
|
||||
|
||||
@Controller('sales-invoices')
|
||||
export class SalesInvoicesController {
|
||||
constructor(private readonly service: SalesInvoicesService) {}
|
||||
|
||||
// @Post()
|
||||
// create(@Body() dto: CreateSalesInvoiceDto) {
|
||||
// return this.service.create(dto)
|
||||
// }
|
||||
constructor(private readonly salesInvoicesService: SalesInvoicesService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.service.findAll()
|
||||
return this.salesInvoicesService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.service.findOne(Number(id))
|
||||
return this.salesInvoicesService.findOne(+id)
|
||||
}
|
||||
|
||||
// @Patch(':id')
|
||||
// update(@Param('id') id: string, @Body() dto: UpdateSalesInvoiceDto) {
|
||||
// return this.service.update(Number(id), dto)
|
||||
// }
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.service.remove(Number(id))
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.salesInvoicesService.create(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { SalesInvoicesController } from './sales-invoices.controller'
|
||||
import { SalesInvoicesService } from './sales-invoices.service'
|
||||
import { SalesInvoicesWorkflow } from './sales-invoices.workflow'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [SalesInvoicesController],
|
||||
providers: [SalesInvoicesService, SalesInvoicesWorkflow],
|
||||
exports: [SalesInvoicesWorkflow],
|
||||
providers: [SalesInvoicesService],
|
||||
})
|
||||
export class SalesInvoicesModule {}
|
||||
|
||||
@@ -1,49 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class SalesInvoicesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
// async create(dto: CreateSalesInvoiceDto) {
|
||||
// const payload: any = { ...dto }
|
||||
// if (Object.prototype.hasOwnProperty.call(payload, 'customerId')) {
|
||||
// payload.customer = { connect: { id: Number(payload.customerId) } }
|
||||
// delete payload.customerId
|
||||
// }
|
||||
|
||||
// const item = await this.prisma.salesInvoice.create({ data: payload })
|
||||
// return ResponseMapper.create(item)
|
||||
// }
|
||||
|
||||
async findAll() {
|
||||
const items = await this.prisma.salesInvoice.findMany({ include: { customer: true } })
|
||||
return ResponseMapper.list(items)
|
||||
findAll() {
|
||||
// TODO: Implement fetching all sales invoices
|
||||
return []
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const item = await this.prisma.salesInvoice.findUnique({
|
||||
where: { id },
|
||||
include: { items: true, customer: true },
|
||||
})
|
||||
if (!item) return null
|
||||
return ResponseMapper.single(item)
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single sales invoice
|
||||
return {}
|
||||
}
|
||||
|
||||
// async update(id: number, data: any) {
|
||||
// const payload: any = { ...data }
|
||||
// if (Object.prototype.hasOwnProperty.call(payload, 'customerId')) {
|
||||
// if (payload.customerId === null) payload.customer = { disconnect: true }
|
||||
// else payload.customer = { connect: { id: Number(payload.customerId) } }
|
||||
// delete payload.customerId
|
||||
// }
|
||||
// const item = await this.prisma.salesInvoice.update({ where: { id }, data: payload })
|
||||
// return ResponseMapper.update(item)
|
||||
// }
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.salesInvoice.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
create(data: any) {
|
||||
// TODO: Implement sales invoice creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
MovementReferenceType,
|
||||
MovementType,
|
||||
Prisma,
|
||||
} from '../../generated/prisma/client'
|
||||
|
||||
export class SalesInvoicesWorkflow {
|
||||
constructor() {}
|
||||
|
||||
async onCreateByOrder(tx: Prisma.TransactionClient, orderId: number) {
|
||||
const { posAccount } = await tx.order.findUniqueOrThrow({
|
||||
where: { id: orderId },
|
||||
select: { posAccount: { select: { inventoryId: true, id: true } } },
|
||||
})
|
||||
|
||||
const posInventoryId = posAccount.inventoryId
|
||||
const orderItems = await tx.orderItem.findMany({
|
||||
where: {
|
||||
orderId,
|
||||
},
|
||||
})
|
||||
|
||||
await tx.stockMovement.createMany({
|
||||
data: orderItems.map(item => ({
|
||||
productId: item.productId,
|
||||
orderId,
|
||||
type: MovementType.OUT,
|
||||
referenceType: MovementReferenceType.SALES,
|
||||
referenceId: String(orderId),
|
||||
quantity: item.quantity,
|
||||
inventoryId: posInventoryId,
|
||||
unitPrice: item.unitPrice,
|
||||
totalCost: item.totalAmount,
|
||||
avgCost: item.unitPrice,
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateServiceCategoryDto {
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
imageUrl?: string
|
||||
}
|
||||
|
||||
export class UpdateServiceCategoryDto extends PartialType(CreateServiceCategoryDto) {}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { ServiceCategoriesService } from './service-categories.service'
|
||||
|
||||
@Controller('service-categories')
|
||||
export class ServiceCategoriesController {
|
||||
constructor(private readonly serviceCategoriesService: ServiceCategoriesService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.serviceCategoriesService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.serviceCategoriesService.findOne(+id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.serviceCategoriesService.create(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ServiceCategoriesController } from './service-categories.controller'
|
||||
import { ServiceCategoriesService } from './service-categories.service'
|
||||
|
||||
@Module({
|
||||
controllers: [ServiceCategoriesController],
|
||||
providers: [ServiceCategoriesService],
|
||||
})
|
||||
export class ServiceCategoriesModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
|
||||
@Injectable()
|
||||
export class ServiceCategoriesService {
|
||||
findAll() {
|
||||
// TODO: Implement fetching all service categories
|
||||
return []
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single service category
|
||||
return {}
|
||||
}
|
||||
|
||||
create(data: any) {
|
||||
// TODO: Implement service category creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class CreateServiceDto {
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
description?: string
|
||||
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
sku: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
barcode?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
categoryId?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: false })
|
||||
baseSalePrice?: number
|
||||
}
|
||||
|
||||
export class UpdateServiceDto extends PartialType(CreateServiceDto) {}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { ServicesService } from './services.service'
|
||||
|
||||
@Controller('services')
|
||||
export class ServicesController {
|
||||
constructor(private readonly servicesService: ServicesService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.servicesService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.servicesService.findOne(+id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.servicesService.create(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ServicesController } from './services.controller'
|
||||
import { ServicesService } from './services.service'
|
||||
|
||||
@Module({
|
||||
controllers: [ServicesController],
|
||||
providers: [ServicesService],
|
||||
})
|
||||
export class ServicesModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
|
||||
@Injectable()
|
||||
export class ServicesService {
|
||||
findAll() {
|
||||
// TODO: Implement fetching all services
|
||||
return []
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single service
|
||||
return {}
|
||||
}
|
||||
|
||||
create(data: any) {
|
||||
// TODO: Implement service creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
SELECT p.id
|
||||
FROM Products p
|
||||
WHERE (
|
||||
SELECT COALESCE(SUM(sb.quantity), 0)
|
||||
FROM Stock_Balance sb
|
||||
WHERE
|
||||
sb.productId = p.id
|
||||
) < p.minimumStockAlertLevel
|
||||
ORDER BY (
|
||||
p.minimumStockAlertLevel - (
|
||||
SELECT COALESCE(SUM(sb.quantity), 0)
|
||||
FROM Stock_Balance sb
|
||||
WHERE
|
||||
sb.productId = p.id
|
||||
)
|
||||
) DESC
|
||||
LIMIT 10
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common'
|
||||
import { StatisticsService } from './statistics.service'
|
||||
|
||||
@Controller('statistics')
|
||||
export class StatisticsController {
|
||||
constructor(private readonly statisticsService: StatisticsService) {}
|
||||
|
||||
@Get('top-last-sales')
|
||||
getTopLastSales() {
|
||||
return this.statisticsService.getTopLastSales()
|
||||
}
|
||||
|
||||
@Get('top-alert-stocks')
|
||||
getTopAlertStocks() {
|
||||
return this.statisticsService.getTopAlertStocks()
|
||||
}
|
||||
|
||||
@Get('top-supplier-debts')
|
||||
getTopSupplierDebts() {
|
||||
return this.statisticsService.getTopSupplierDebts()
|
||||
}
|
||||
|
||||
@Get('top-sales')
|
||||
getTopSellProducts() {
|
||||
return this.statisticsService.getTopSellProducts()
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { StatisticsController } from './statistics.controller'
|
||||
import { StatisticsService } from './statistics.service'
|
||||
|
||||
@Module({
|
||||
controllers: [StatisticsController],
|
||||
providers: [StatisticsService, PrismaService],
|
||||
})
|
||||
export class StatisticsModule {}
|
||||
@@ -1,184 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class StatisticsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private readSqlFile(filename: string): string {
|
||||
const filePath = path.join(__dirname, 'queries', filename)
|
||||
return fs.readFileSync(filePath, 'utf8')
|
||||
}
|
||||
|
||||
async getTopLastSales() {
|
||||
const sales = await this.prisma.salesInvoice.findMany({
|
||||
take: 10,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
include: {
|
||||
customer: { select: { id: true, firstName: true, lastName: true } },
|
||||
posAccount: { select: { id: true, name: true } },
|
||||
items: {
|
||||
include: {
|
||||
product: { select: { id: true, name: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const mapped = sales.map(sale => ({
|
||||
...sale,
|
||||
totalAmount: Number(sale.totalAmount),
|
||||
itemsCount: sale.items.length,
|
||||
}))
|
||||
|
||||
return ResponseMapper.list(mapped)
|
||||
}
|
||||
|
||||
async getTopAlertStocks() {
|
||||
const productIds = await this.prisma.$queryRaw<{ id: number }[]>`
|
||||
SELECT p.id
|
||||
FROM Products p
|
||||
WHERE (SELECT COALESCE(SUM(sb.quantity), 0) FROM Stock_Balance sb WHERE sb.productId = p.id) < p.minimumStockAlertLevel
|
||||
-- ORDER BY (p.minimumStockAlertLevel - (SELECT COALESCE(SUM(sb.quantity), 0) FROM stock_balance sb WHERE sb.productId = p.id)) DESC
|
||||
LIMIT 10
|
||||
`
|
||||
|
||||
const ids = productIds.map(p => p.id)
|
||||
|
||||
if (ids.length === 0) {
|
||||
return ResponseMapper.list([])
|
||||
}
|
||||
|
||||
const products = await this.prisma.product.findMany({
|
||||
where: { id: { in: ids } },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
sku: true,
|
||||
minimumStockAlertLevel: true,
|
||||
stockBalances: {
|
||||
select: {
|
||||
quantity: true,
|
||||
inventory: { select: { id: true, name: true } },
|
||||
},
|
||||
},
|
||||
category: { select: { id: true, name: true } },
|
||||
brand: { select: { id: true, name: true } },
|
||||
},
|
||||
})
|
||||
|
||||
const mappedData = products.map(product => ({
|
||||
...product,
|
||||
stock: product.stockBalances.reduce((acc, sb) => acc + Number(sb.quantity), 0),
|
||||
deficit:
|
||||
Number(product.minimumStockAlertLevel) -
|
||||
product.stockBalances.reduce((acc, sb) => acc + Number(sb.quantity), 0),
|
||||
}))
|
||||
|
||||
return ResponseMapper.list(mappedData)
|
||||
}
|
||||
|
||||
async getTopSupplierDebts() {
|
||||
// Get unpaid receipts
|
||||
const receipts = await this.prisma.purchaseReceipt.findMany({
|
||||
where: {
|
||||
status: { not: 'PAID' },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
totalAmount: true,
|
||||
paidAmount: true,
|
||||
supplierId: true,
|
||||
},
|
||||
})
|
||||
|
||||
const debtMap = new Map<
|
||||
number,
|
||||
{ supplierId: number; totalDebt: number; unpaidReceipts: number }
|
||||
>()
|
||||
|
||||
for (const receipt of receipts) {
|
||||
const debt = Number(receipt.totalAmount) - Number(receipt.paidAmount)
|
||||
if (debt > 0) {
|
||||
const existing = debtMap.get(receipt.supplierId)
|
||||
if (existing) {
|
||||
existing.totalDebt += debt
|
||||
existing.unpaidReceipts += 1
|
||||
} else {
|
||||
debtMap.set(receipt.supplierId, {
|
||||
supplierId: receipt.supplierId,
|
||||
totalDebt: debt,
|
||||
unpaidReceipts: 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const supplierIds = Array.from(debtMap.keys())
|
||||
const suppliers = await this.prisma.supplier.findMany({
|
||||
where: { id: { in: supplierIds } },
|
||||
select: { id: true, firstName: true, lastName: true },
|
||||
})
|
||||
|
||||
const supplierMap = new Map(
|
||||
suppliers.map(s => [s.id, `${s.firstName} ${s.lastName}`.trim()]),
|
||||
)
|
||||
|
||||
const mapped = Array.from(debtMap.values())
|
||||
.map(item => ({
|
||||
id: item.supplierId,
|
||||
name: supplierMap.get(item.supplierId) || 'Unknown',
|
||||
totalDebt: item.totalDebt,
|
||||
unpaidReceipts: item.unpaidReceipts,
|
||||
}))
|
||||
.sort((a, b) => b.totalDebt - a.totalDebt)
|
||||
.slice(0, 10)
|
||||
|
||||
return ResponseMapper.list(mapped)
|
||||
}
|
||||
|
||||
async getTopSellProducts() {
|
||||
const productData = await this.prisma.$queryRaw<any[]>`
|
||||
SELECT p.id, p.name, p.sku, COALESCE(SUM(sii.count), 0) as totalSold
|
||||
FROM Products p
|
||||
LEFT JOIN Sales_Invoice_Items sii ON p.id = sii.productId
|
||||
GROUP BY p.id
|
||||
HAVING totalSold > 0
|
||||
ORDER BY totalSold DESC
|
||||
LIMIT 10
|
||||
`
|
||||
|
||||
const ids = productData.map(p => p.id)
|
||||
|
||||
if (ids.length === 0) {
|
||||
return ResponseMapper.list([])
|
||||
}
|
||||
|
||||
const products = await this.prisma.product.findMany({
|
||||
where: { id: { in: ids } },
|
||||
select: {
|
||||
id: true,
|
||||
category: { select: { id: true, name: true } },
|
||||
brand: { select: { id: true, name: true } },
|
||||
},
|
||||
})
|
||||
|
||||
const productMap = new Map(
|
||||
products.map(p => [p.id, { category: p.category, brand: p.brand }]),
|
||||
)
|
||||
|
||||
const mapped = productData.map(product => ({
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
sku: product.sku,
|
||||
totalSold: Number(product.totalSold),
|
||||
category: productMap.get(product.id)?.category,
|
||||
brand: productMap.get(product.id)?.brand,
|
||||
}))
|
||||
|
||||
return ResponseMapper.list(mapped)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common'
|
||||
import { StockBalanceService } from './stock-balance.service'
|
||||
|
||||
@Controller('stock-balance')
|
||||
export class StockBalanceController {
|
||||
constructor(private readonly service: StockBalanceService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.service.findAll()
|
||||
}
|
||||
|
||||
// @Get(':itemId')
|
||||
// findOne(@Param('itemId') itemId: string) {
|
||||
// return this.service.findOne(Number(itemId))
|
||||
// }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { StockBalanceController } from './stock-balance.controller'
|
||||
import { StockBalanceService } from './stock-balance.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [StockBalanceController],
|
||||
providers: [StockBalanceService],
|
||||
})
|
||||
export class StockBalanceModule {}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class StockBalanceService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll() {
|
||||
const items = await this.prisma.stockBalance.findMany()
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
|
||||
// async findOne(productId: number) {
|
||||
// const item = await this.prisma.stockBalance.findUnique({ where: { productId: productId } })
|
||||
// if (!item) return null
|
||||
// return ResponseMapper.single(item)
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsString } from 'class-validator'
|
||||
|
||||
export class CreateTriggerLogDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
message: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
name: string
|
||||
}
|
||||
|
||||
export class UpdateTriggerLogDto extends PartialType(CreateTriggerLogDto) {}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { TriggerLogsService } from './trigger-logs.service'
|
||||
|
||||
@Controller('trigger-logs')
|
||||
export class TriggerLogsController {
|
||||
constructor(private readonly triggerLogsService: TriggerLogsService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.triggerLogsService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.triggerLogsService.findOne(+id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.triggerLogsService.create(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { TriggerLogsController } from './trigger-logs.controller'
|
||||
import { TriggerLogsService } from './trigger-logs.service'
|
||||
|
||||
@Module({
|
||||
controllers: [TriggerLogsController],
|
||||
providers: [TriggerLogsService],
|
||||
})
|
||||
export class TriggerLogsModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
|
||||
@Injectable()
|
||||
export class TriggerLogsService {
|
||||
findAll() {
|
||||
// TODO: Implement fetching all trigger logs
|
||||
return []
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single trigger log
|
||||
return {}
|
||||
}
|
||||
|
||||
create(data: any) {
|
||||
// TODO: Implement trigger log creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { File } from 'multer'
|
||||
import { UploaderService } from './uploader.service'
|
||||
|
||||
@Injectable()
|
||||
@@ -7,14 +6,14 @@ export class ImageUploaderService {
|
||||
constructor(private readonly uploaderService: UploaderService) {}
|
||||
|
||||
async uploadImage(
|
||||
file: File,
|
||||
accountId: string,
|
||||
file: Express.Multer.File,
|
||||
account_id: string,
|
||||
type: string,
|
||||
): Promise<string> {
|
||||
return this.uploaderService.uploadFile(
|
||||
file.buffer,
|
||||
file.originalname,
|
||||
accountId,
|
||||
account_id,
|
||||
type,
|
||||
)
|
||||
}
|
||||
+2
-2
@@ -2,8 +2,8 @@ import { Body, Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/c
|
||||
import { FileInterceptor } from '@nestjs/platform-express'
|
||||
import { ImageUploaderService } from './image-uploader.service'
|
||||
|
||||
@Controller('uploads')
|
||||
export class UploadsController {
|
||||
@Controller('uploaders')
|
||||
export class UploadersController {
|
||||
constructor(private readonly imageUploaderService: ImageUploaderService) {}
|
||||
|
||||
@Post('image')
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ImageUploaderService } from './image-uploader.service'
|
||||
import { UploaderService } from './uploader.service'
|
||||
import { UploadsController } from './uploads.controller'
|
||||
import { UploadersController } from './uploaders.controller'
|
||||
|
||||
@Module({
|
||||
providers: [UploaderService, ImageUploaderService],
|
||||
controllers: [UploadsController],
|
||||
controllers: [UploadersController],
|
||||
exports: [UploaderService, ImageUploaderService],
|
||||
})
|
||||
export class UploadsModule {}
|
||||
export class UploadersModule {}
|
||||
Reference in New Issue
Block a user