change source of account id in prepared token to domain account id, create consumer salesInvoiceModule
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
dist
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
.vscode
|
||||
.idea
|
||||
.DS_Store
|
||||
*.log
|
||||
coverage
|
||||
.nyc_output
|
||||
.jest-cache
|
||||
prisma/dev.db
|
||||
prisma/dev.db-journal
|
||||
.turbo
|
||||
.next
|
||||
out
|
||||
build
|
||||
dist
|
||||
@@ -0,0 +1,24 @@
|
||||
# Server Configuration
|
||||
NODE_ENV=development
|
||||
PORT=5002
|
||||
|
||||
# Database Configuration
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=consumer_db
|
||||
DB_USER=consumer_user
|
||||
DB_PASSWORD=consumer_password
|
||||
DB_ROOT_PASSWORD=root_password
|
||||
DATABASE_URL=mysql://consumer_user:consumer_password@localhost:3306/consumer_db
|
||||
SHADOW_DATABASE_URL=mysql://consumer_user:consumer_password@localhost:3306/consumer_db_shadow
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
|
||||
JWT_EXPIRATION=604800
|
||||
|
||||
# CORS Configuration
|
||||
CORS_ORIGINS=http://localhost:5000,http://127.0.0.1:5000
|
||||
|
||||
# File Upload Configuration
|
||||
UPLOAD_PATH=./uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
# Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copy dependency files
|
||||
COPY pnpm-lock.yaml package.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Generate Prisma client and build the application
|
||||
RUN pnpm exec prisma generate
|
||||
RUN pnpm run build
|
||||
|
||||
# Production stage
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Install dumb-init for proper signal handling
|
||||
RUN apk add --no-cache dumb-init
|
||||
|
||||
# Copy package files
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# Install only production dependencies
|
||||
RUN pnpm install --frozen-lockfile --prod
|
||||
|
||||
# Copy built application from builder
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
||||
COPY prisma ./prisma
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nodejs -u 1001
|
||||
|
||||
USER nodejs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5002
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:5002/api/v1/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
|
||||
|
||||
# Use dumb-init to handle signals properly
|
||||
ENTRYPOINT ["dumb-init", "--"]
|
||||
|
||||
# Start application
|
||||
CMD ["node", "dist/main.js"]
|
||||
@@ -0,0 +1,73 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
database:
|
||||
image: mariadb:11.4-jammy
|
||||
container_name: consumer_api_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_password}
|
||||
MARIADB_DATABASE: ${DB_NAME:-consumer_db}
|
||||
MARIADB_USER: ${DB_USER:-consumer_user}
|
||||
MARIADB_PASSWORD: ${DB_PASSWORD:-consumer_password}
|
||||
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: "false"
|
||||
ports:
|
||||
- "${DB_PORT:-3306}:3306"
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
- ./prisma/migrations:/docker-entrypoint-initdb.d
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- consumer_network
|
||||
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: consumer_api
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
PORT: ${PORT:-5002}
|
||||
DATABASE_URL: mysql://${DB_USER:-consumer_user}:${DB_PASSWORD:-consumer_password}@database:3306/${DB_NAME:-consumer_db}
|
||||
SHADOW_DATABASE_URL: mysql://${DB_USER:-consumer_user}:${DB_PASSWORD:-consumer_password}@database:3306/${DB_NAME:-consumer_db}_shadow
|
||||
JWT_SECRET: ${JWT_SECRET:-your_jwt_secret_key_change_in_production}
|
||||
JWT_EXPIRATION: ${JWT_EXPIRATION:-604800}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:5000,http://127.0.0.1:5000}
|
||||
ports:
|
||||
- "${PORT:-5002}:5002"
|
||||
volumes:
|
||||
- ./src:/app/src
|
||||
- /app/dist
|
||||
- /app/node_modules
|
||||
networks:
|
||||
- consumer_network
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--quiet",
|
||||
"--tries=1",
|
||||
"--spider",
|
||||
"http://localhost:5002/api/v1/health",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
consumer_network:
|
||||
driver: bridge
|
||||
@@ -60,7 +60,7 @@ export class PosGuard implements CanActivate {
|
||||
user: {
|
||||
accounts: {
|
||||
some: {
|
||||
account_id: account.account_id,
|
||||
id: account.account_id,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -81,20 +81,16 @@ export class PosGuard implements CanActivate {
|
||||
throw new ForbiddenException('شما دسترسی لازم را ندارید.')
|
||||
}
|
||||
|
||||
const foundedAccount = await this.prisma.account.findUnique({
|
||||
const foundedAccount = await this.prisma.consumerAccount.findUnique({
|
||||
where: {
|
||||
id: account.account_id,
|
||||
},
|
||||
select: {
|
||||
consumer_account: {
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (foundedAccount?.consumer_account?.role === 'OWNER') {
|
||||
if (foundedAccount?.role === 'OWNER') {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ export class AuthUtils {
|
||||
id: accountId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
admin_account: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -58,10 +57,16 @@ export class AuthUtils {
|
||||
},
|
||||
})
|
||||
|
||||
if (!adminAccount) throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
if (!adminAccount || !adminAccount.admin_account)
|
||||
throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
|
||||
const { user, id } = adminAccount.admin_account
|
||||
return {
|
||||
...adminAccount.admin_account?.user,
|
||||
fullname: `${adminAccount.admin_account?.user.first_name} ${adminAccount.admin_account?.user.last_name}`,
|
||||
account_id: id,
|
||||
user: {
|
||||
...user,
|
||||
fullname: `${user.first_name} ${user.last_name}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +78,7 @@ export class AuthUtils {
|
||||
select: {
|
||||
consumer_account: {
|
||||
select: {
|
||||
id: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -86,8 +92,17 @@ export class AuthUtils {
|
||||
},
|
||||
})
|
||||
|
||||
if (!consumerAccount) throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
return consumerAccount.consumer_account?.user
|
||||
if (!consumerAccount || !consumerAccount.consumer_account)
|
||||
throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
|
||||
const { user, id } = consumerAccount.consumer_account
|
||||
return {
|
||||
account_id: id,
|
||||
user: {
|
||||
...user,
|
||||
fullname: `${user.first_name} ${user.last_name}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async getProviderAccountInfo(accountId: string) {
|
||||
@@ -98,6 +113,7 @@ export class AuthUtils {
|
||||
select: {
|
||||
provider_account: {
|
||||
select: {
|
||||
id: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -111,8 +127,17 @@ export class AuthUtils {
|
||||
},
|
||||
})
|
||||
|
||||
if (!providerAccount) throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
return providerAccount.provider_account?.user
|
||||
if (!providerAccount || !providerAccount.provider_account)
|
||||
throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
|
||||
const { user, id } = providerAccount.provider_account
|
||||
return {
|
||||
account_id: id,
|
||||
user: {
|
||||
...user,
|
||||
fullname: user.name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async getPartnerAccountInfo(accountId: string) {
|
||||
@@ -123,6 +148,7 @@ export class AuthUtils {
|
||||
select: {
|
||||
partner_account: {
|
||||
select: {
|
||||
id: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -136,33 +162,50 @@ export class AuthUtils {
|
||||
},
|
||||
})
|
||||
|
||||
if (!partnerAccount) throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
return partnerAccount.partner_account?.user
|
||||
if (!partnerAccount || !partnerAccount.partner_account)
|
||||
throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
|
||||
const { user, id } = partnerAccount.partner_account
|
||||
return {
|
||||
account_id: id,
|
||||
user: {
|
||||
...user,
|
||||
fullname: user.name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async generateLoginResponse(account: Account) {
|
||||
let preparedAccount = {
|
||||
type: account.type,
|
||||
username: account.username,
|
||||
account_id: account.id,
|
||||
account_id: '',
|
||||
user: {},
|
||||
} as AccessTokenPayload
|
||||
|
||||
let accountInfo = {
|
||||
user: {},
|
||||
account_id: '',
|
||||
}
|
||||
|
||||
switch (account.type) {
|
||||
case AccountType.ADMIN:
|
||||
preparedAccount.user = await this.getAdminAccountInfo(account.id)
|
||||
accountInfo = await this.getAdminAccountInfo(account.id)
|
||||
break
|
||||
case AccountType.CONSUMER:
|
||||
preparedAccount.user = await this.getConsumerAccountInfo(account.id)
|
||||
accountInfo = await this.getConsumerAccountInfo(account.id)
|
||||
break
|
||||
case AccountType.PARTNER:
|
||||
preparedAccount.user = await this.getPartnerAccountInfo(account.id)
|
||||
accountInfo = await this.getPartnerAccountInfo(account.id)
|
||||
break
|
||||
case AccountType.PROVIDER:
|
||||
preparedAccount.user = await this.getProviderAccountInfo(account.id)
|
||||
accountInfo = await this.getProviderAccountInfo(account.id)
|
||||
break
|
||||
}
|
||||
|
||||
preparedAccount.account_id = accountInfo.account_id
|
||||
preparedAccount.user = accountInfo.user
|
||||
|
||||
if (!preparedAccount.user) {
|
||||
throw new NotFoundException('متاسفانه کاربر شما یافت نشد')
|
||||
} else {
|
||||
|
||||
@@ -2,9 +2,10 @@ import { PrismaModule } from '@/prisma/prisma.module'
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ComplexPosesController } from './poses.controller'
|
||||
import { ComplexPosesService } from './poses.service'
|
||||
import { ConsumerPosSalesInvoicesModule } from './sales-invoices/sales-invoices.module'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, ConsumerPosSalesInvoicesModule],
|
||||
controllers: [ComplexPosesController],
|
||||
providers: [ComplexPosesService],
|
||||
exports: [ComplexPosesService],
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import {
|
||||
ArrayMinSize,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator'
|
||||
import type { CustomerIndividual, CustomerLegal } from 'generated/prisma/client'
|
||||
import { CustomerType } from 'generated/prisma/enums'
|
||||
import { CreateSalesInvoiceItemDto } from '../sales-invoice-items/dto/create-sales-invoice-item.dto'
|
||||
|
||||
export class CreateSalesInvoiceDto {
|
||||
// @TODO: totalAmount must calculated instead of get from api
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true, default: 0 })
|
||||
total_amount: number
|
||||
|
||||
@ApiProperty({ required: true })
|
||||
@IsDateString(
|
||||
{ strict: true },
|
||||
{ message: 'invoice_date must be a valid ISO-8601 string' },
|
||||
)
|
||||
invoice_date: Date
|
||||
|
||||
@ApiProperty({ required: true })
|
||||
@IsObject()
|
||||
payments: {
|
||||
terminal: number
|
||||
cash: number
|
||||
set_off: number
|
||||
}
|
||||
|
||||
@ApiProperty()
|
||||
@ArrayMinSize(1)
|
||||
items: CreateSalesInvoiceItemDto[]
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
notes?: string
|
||||
|
||||
@ApiProperty({ required: true, default: CustomerType.UNKNOWN })
|
||||
@IsEnum(CustomerType)
|
||||
customer_type: CustomerType
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
customer_id?: string
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
customer?:
|
||||
| {
|
||||
first_name: string
|
||||
last_name: string
|
||||
}
|
||||
| CustomerIndividual
|
||||
| CustomerLegal
|
||||
}
|
||||
|
||||
export class UpdateSalesInvoiceDto extends PartialType(CreateSalesInvoiceDto) {}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
||||
import { IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'
|
||||
import type { SaleInvoiceType } from 'common/interfaces/sale-invoice-payload'
|
||||
import { UnitType } from 'generated/prisma/enums'
|
||||
|
||||
export class CreateSalesInvoiceItemDto {
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true })
|
||||
unit_price: number
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true, default: 1 })
|
||||
quantity: number
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true })
|
||||
total_amount: number
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true })
|
||||
discount_amount: number
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ required: true })
|
||||
invoice_id: string
|
||||
|
||||
@IsEnum(UnitType)
|
||||
@ApiProperty({ enum: Object.values(UnitType) })
|
||||
unit_type: UnitType
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
good_id?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
service_id?: string
|
||||
|
||||
// @IsEnum(SalesInvoiceItemPricingModel)
|
||||
// @ApiProperty({ enum: Object.values(SalesInvoiceItemPricingModel) })
|
||||
// @IsOptional()
|
||||
// pricingModel: SalesInvoiceItemPricingModel
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
@ApiProperty({ required: false, default: {} })
|
||||
payload: SaleInvoiceType
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false, default: '' })
|
||||
notes: string
|
||||
}
|
||||
|
||||
export class UpdateSalesInvoiceItemDto extends PartialType(CreateSalesInvoiceItemDto) {}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
||||
import { SalesInvoiceItemsService } from './sales-invoice-items.service'
|
||||
|
||||
@Controller('sales_invoice_items')
|
||||
export class SalesInvoiceItemsController {
|
||||
constructor(private readonly salesInvoiceItemsService: SalesInvoiceItemsService) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.salesInvoiceItemsService.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.salesInvoiceItemsService.findOne(+id)
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: any) {
|
||||
return this.salesInvoiceItemsService.create(data)
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { SalesInvoiceItemsController } from './sales-invoice-items.controller'
|
||||
import { SalesInvoiceItemsService } from './sales-invoice-items.service'
|
||||
|
||||
@Module({
|
||||
controllers: [SalesInvoiceItemsController],
|
||||
providers: [SalesInvoiceItemsService],
|
||||
})
|
||||
export class SalesInvoiceItemsModule {}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
|
||||
@Injectable()
|
||||
export class SalesInvoiceItemsService {
|
||||
findAll() {
|
||||
// TODO: Implement fetching all sales invoice items
|
||||
return []
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
// TODO: Implement fetching a single sales invoice item
|
||||
return {}
|
||||
}
|
||||
|
||||
create(data: any) {
|
||||
// TODO: Implement sales invoice item creation
|
||||
return data
|
||||
}
|
||||
}
|
||||
+32
@@ -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,
|
||||
) {}
|
||||
+24
@@ -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)
|
||||
}
|
||||
}
|
||||
+9
@@ -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 {}
|
||||
+19
@@ -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
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { Controller, Get, Param } from '@nestjs/common'
|
||||
|
||||
import { TokenAccount } from '@/common/decorators/tokenInfo.decorator'
|
||||
import { ApiTags } from '@nestjs/swagger'
|
||||
import { SalesInvoicesService } from './sales-invoices.service'
|
||||
|
||||
@ApiTags('ConsumerComplexPoses')
|
||||
@Controller('consumer/complexes/:complexId/poses/:posId/sales_invoices')
|
||||
export class SalesInvoicesController {
|
||||
constructor(private readonly salesInvoicesService: SalesInvoicesService) {}
|
||||
|
||||
@Get()
|
||||
findAll(
|
||||
@TokenAccount('userId') userId: string,
|
||||
@Param('complexId') complexId: string,
|
||||
@Param('posId') posId: string,
|
||||
) {
|
||||
return this.salesInvoicesService.findAll(userId, complexId, posId)
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(
|
||||
@Param('complexId') complexId: string,
|
||||
@Param('posId') posId: string,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return this.salesInvoicesService.findOne(complexId, posId, id)
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { SalesInvoicesController } from './sales-invoices.controller'
|
||||
import { SalesInvoicesService } from './sales-invoices.service'
|
||||
|
||||
@Module({
|
||||
controllers: [SalesInvoicesController],
|
||||
providers: [SalesInvoicesService],
|
||||
})
|
||||
export class ConsumerPosSalesInvoicesModule {}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
import { ResponseMapper } from '@/common/response/response-mapper'
|
||||
import { SalesInvoiceWhereInput } from '@/generated/prisma/models'
|
||||
import { PrismaService } from '@/prisma/prisma.service'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
|
||||
@Injectable()
|
||||
export class SalesInvoicesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(user_id: string, complex_id: string, pos_id: string) {
|
||||
const defaultWhere: SalesInvoiceWhereInput = {
|
||||
pos_id,
|
||||
pos: {
|
||||
complex: {
|
||||
id: complex_id,
|
||||
business_activity: {
|
||||
user_id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const pageSize = 10
|
||||
const page = 1
|
||||
|
||||
const [items, count] = await this.prisma.$transaction([
|
||||
this.prisma.salesInvoice.findMany({
|
||||
where: defaultWhere,
|
||||
select: {
|
||||
id: true,
|
||||
code: true,
|
||||
invoice_date: true,
|
||||
notes: true,
|
||||
total_amount: true,
|
||||
|
||||
items: {
|
||||
select: {
|
||||
unit_type: true,
|
||||
discount: true,
|
||||
notes: true,
|
||||
quantity: true,
|
||||
total_amount: true,
|
||||
unit_price: true,
|
||||
payload: true,
|
||||
good: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
sku: true,
|
||||
barcode: true,
|
||||
local_sku: true,
|
||||
pricing_model: true,
|
||||
unit_type: true,
|
||||
category: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
payments: {
|
||||
select: {
|
||||
amount: true,
|
||||
paid_at: true,
|
||||
payment_method: true,
|
||||
},
|
||||
},
|
||||
customer: {
|
||||
select: {
|
||||
type: true,
|
||||
customer_individuals: {
|
||||
select: {
|
||||
economic_code: true,
|
||||
first_name: true,
|
||||
last_name: true,
|
||||
postal_code: true,
|
||||
national_id: true,
|
||||
},
|
||||
},
|
||||
customer_legals: {
|
||||
select: {
|
||||
economic_code: true,
|
||||
postal_code: true,
|
||||
registration_number: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
unknown_customer: true,
|
||||
},
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.salesInvoice.count({
|
||||
where: defaultWhere,
|
||||
}),
|
||||
])
|
||||
return ResponseMapper.paginate(items, { count, page, pageSize })
|
||||
}
|
||||
|
||||
findOne(complex_id: string, pos_id: string, id: string) {
|
||||
// TODO: Implement fetching a single sales invoice
|
||||
return {}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export class PosMiddleware implements NestMiddleware {
|
||||
|
||||
const consumerAccount = await this.prisma.consumerAccount.findUnique({
|
||||
where: {
|
||||
account_id: tokenAccount.account_id,
|
||||
id: tokenAccount.account_id,
|
||||
},
|
||||
})
|
||||
if (!consumerAccount) {
|
||||
|
||||
@@ -54,13 +54,14 @@ export class CreateSalesInvoiceDto {
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
customer?:
|
||||
| {
|
||||
customer?: {
|
||||
customer_individual?: Omit<CustomerIndividual, 'complex_id' | 'customer_id'>
|
||||
customer_legal?: Omit<CustomerLegal, 'complex_id' | 'customer_id'>
|
||||
customer_unknown?: {
|
||||
first_name: string
|
||||
last_name: string
|
||||
}
|
||||
| CustomerIndividual
|
||||
| CustomerLegal
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateSalesInvoiceDto extends PartialType(CreateSalesInvoiceDto) {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IPosPayload } from '@/common/models/posPayload.model'
|
||||
import { SalesInvoiceCreateInput } from '@/generated/prisma/models'
|
||||
import { PrismaService } from '@/prisma/prisma.service'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { BadRequestException, Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from 'common/response/response-mapper'
|
||||
import type { CustomerIndividual, CustomerLegal } from 'generated/prisma/client'
|
||||
import { CustomerType, PaymentMethodType } from 'generated/prisma/enums'
|
||||
@@ -65,99 +66,76 @@ export class SalesInvoicesService {
|
||||
if (customer_id) {
|
||||
} else if (
|
||||
customer_type === CustomerType.INDIVIDUAL &&
|
||||
isCustomerIndividual(customer)
|
||||
customer?.customer_individual
|
||||
) {
|
||||
let customerIndividual = await $tx.customerIndividual.findUnique({
|
||||
const customer_individual = customer.customer_individual
|
||||
const customerIndividual = await $tx.customerIndividual.upsert({
|
||||
where: {
|
||||
complex_id_national_id: {
|
||||
complex_id,
|
||||
national_id: customer.national_id,
|
||||
national_id: customer_individual.national_id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (customerIndividual) {
|
||||
await $tx.customerIndividual.update({
|
||||
where: {
|
||||
complex_id_national_id: {
|
||||
complex_id,
|
||||
national_id: customer.national_id,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
...customer,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
const customerIndividual = await $tx.customerIndividual.create({
|
||||
data: {
|
||||
...customer,
|
||||
},
|
||||
})
|
||||
const createdCustomer = await $tx.customer.create({
|
||||
data: {
|
||||
create: {
|
||||
...customer_individual,
|
||||
customer: {
|
||||
create: {
|
||||
type: CustomerType.INDIVIDUAL,
|
||||
customer_individuals: {
|
||||
},
|
||||
},
|
||||
complex: {
|
||||
connect: {
|
||||
customer_id: customerIndividual.customer_id,
|
||||
id: complex_id,
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
select: {
|
||||
customer_id: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (createdCustomer && createdCustomer.id) {
|
||||
newCustomerId = createdCustomer.id
|
||||
if (!customerIndividual) {
|
||||
throw new BadRequestException('متاسفانه مشکلی در اطلاعات مشتری وجود دارد.')
|
||||
}
|
||||
}
|
||||
} else if (data.customer_type === CustomerType.LEGAL && isCustomerLegal(customer)) {
|
||||
let customerLegal = await $tx.customerLegal.findUnique({
|
||||
where: {
|
||||
complex_id_registration_number: {
|
||||
complex_id,
|
||||
registration_number: customer.registration_number,
|
||||
},
|
||||
},
|
||||
})
|
||||
newCustomerId = customerIndividual.customer_id
|
||||
} else if (data.customer_type === CustomerType.LEGAL && customer?.customer_legal) {
|
||||
const customer_legal = customer.customer_legal
|
||||
|
||||
if (customerLegal) {
|
||||
await $tx.customerLegal.update({
|
||||
const customerLegal = await $tx.customerLegal.upsert({
|
||||
where: {
|
||||
complex_id_registration_number: {
|
||||
complex_id,
|
||||
registration_number: customer.registration_number,
|
||||
registration_number: customer_legal.registration_number,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
...customer,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
const customerLegal = await $tx.customerLegal.create({
|
||||
data: {
|
||||
...customer,
|
||||
},
|
||||
})
|
||||
const createdCustomer = await $tx.customer.create({
|
||||
data: {
|
||||
create: {
|
||||
...customer_legal,
|
||||
customer: {
|
||||
create: {
|
||||
type: CustomerType.LEGAL,
|
||||
|
||||
customer_individuals: {
|
||||
},
|
||||
},
|
||||
complex: {
|
||||
connect: {
|
||||
customer_id: customerLegal.customer_id,
|
||||
id: complex_id,
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
select: {
|
||||
customer_id: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (createdCustomer && createdCustomer.id) {
|
||||
newCustomerId = createdCustomer.id
|
||||
}
|
||||
if (!customerLegal) {
|
||||
throw new BadRequestException('متاسفانه مشکلی در اطلاعات مشتری وجود دارد.')
|
||||
}
|
||||
newCustomerId = customerLegal.customer_id
|
||||
} else if (data.customer_type === CustomerType.UNKNOWN) {
|
||||
}
|
||||
|
||||
const salesInvoice = await $tx.salesInvoice.create({
|
||||
data: {
|
||||
const salesInvoiceData: SalesInvoiceCreateInput = {
|
||||
...invoiceData,
|
||||
total_amount: data.total_amount,
|
||||
code: 'INV-' + Date.now(),
|
||||
@@ -169,7 +147,9 @@ export class SalesInvoicesService {
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price,
|
||||
total_amount: item.total_amount,
|
||||
payload: JSON.parse(JSON.stringify(item.payload)),
|
||||
payload: item.payload
|
||||
? JSON.parse(JSON.stringify(item.payload))
|
||||
: undefined,
|
||||
unit_type: item.unit_type,
|
||||
// pricing_model: item.pricingModel,
|
||||
})),
|
||||
@@ -183,7 +163,7 @@ export class SalesInvoicesService {
|
||||
},
|
||||
// customer: {
|
||||
// connect: {
|
||||
// id: newCustomerId,
|
||||
// id: newCustomerId || undefined,
|
||||
// },
|
||||
// },
|
||||
account: {
|
||||
@@ -196,7 +176,18 @@ export class SalesInvoicesService {
|
||||
id: pos_id,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (newCustomerId) {
|
||||
salesInvoiceData.customer = {
|
||||
connect: {
|
||||
id: newCustomerId || undefined,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const salesInvoice = await $tx.salesInvoice.create({
|
||||
data: salesInvoiceData,
|
||||
})
|
||||
|
||||
return salesInvoice
|
||||
|
||||
Reference in New Issue
Block a user