refactor: restructure purchase receipts module and related workflows
- Removed old DTOs for creating and updating purchase receipts. - Updated purchase receipts controller and service to use new DTOs and workflows. - Introduced transaction helper for managing database transactions. - Added new workflows for handling purchase receipt payments and supplier ledgers. - Implemented new logic for managing purchase receipt items and payments. - Enhanced error handling for payment processing in workflows. - Updated supplier ledger management to reflect changes in purchase receipts.
This commit is contained in:
@@ -2,10 +2,12 @@ import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { BankAccountsController } from './bank-accounts.controller'
|
||||
import { BankAccountsService } from './bank-accounts.service'
|
||||
import { BankAccountsWorkflow } from './bank-accounts.workflow'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [BankAccountsController],
|
||||
providers: [BankAccountsService],
|
||||
providers: [BankAccountsService, BankAccountsWorkflow],
|
||||
exports: [BankAccountsWorkflow],
|
||||
})
|
||||
export class BankAccountsModule {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { withTransaction } from '../../common/database/transaction.helper'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
|
||||
@@ -6,8 +7,16 @@ import { PrismaService } from '../../prisma/prisma.service'
|
||||
export class BankAccountsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
async create(data: any) {
|
||||
const item = await this.prisma.bankAccount.create({ data })
|
||||
return ResponseMapper.create(item)
|
||||
return withTransaction(this.prisma, async tx => {
|
||||
const item = await tx.bankAccount.create({ data })
|
||||
await tx.bankAccountBalance.create({
|
||||
data: {
|
||||
bankAccountId: item.id,
|
||||
balance: 0,
|
||||
},
|
||||
})
|
||||
return ResponseMapper.create(item)
|
||||
})
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Prisma } from '../../generated/prisma/client'
|
||||
import { AddTransactionToBankAccountDto } from './dto/add-transaction.dto'
|
||||
|
||||
@Injectable()
|
||||
export class BankAccountsWorkflow {
|
||||
async addTransaction(
|
||||
tx: Prisma.TransactionClient,
|
||||
payload: AddTransactionToBankAccountDto,
|
||||
) {
|
||||
const item = await tx.bankAccountBalance.findUnique({
|
||||
where: {
|
||||
bankAccountId: payload.bankAccountId,
|
||||
},
|
||||
})
|
||||
|
||||
const balance = item ? Number(item.balance) : 0
|
||||
|
||||
const newBalance = item
|
||||
? payload.type === 'DEPOSIT'
|
||||
? balance + payload.amount
|
||||
: balance - payload.amount
|
||||
: payload.type === 'DEPOSIT'
|
||||
? payload.amount
|
||||
: -payload.amount
|
||||
|
||||
await tx.bankAccountTransaction.create({
|
||||
data: {
|
||||
bankAccount: { connect: { id: payload.bankAccountId } },
|
||||
amount: payload.amount,
|
||||
type: payload.type,
|
||||
balanceAfter: newBalance,
|
||||
|
||||
referenceId: payload.referenceId,
|
||||
referenceType: payload.referenceType,
|
||||
},
|
||||
})
|
||||
|
||||
console.log('first')
|
||||
|
||||
const bankAccountBalanceItem = await tx.bankAccountBalance.findUnique({
|
||||
where: {
|
||||
bankAccountId: payload.bankAccountId,
|
||||
},
|
||||
})
|
||||
|
||||
if (!bankAccountBalanceItem) {
|
||||
return await tx.bankAccountBalance.create({
|
||||
data: {
|
||||
bankAccount: { connect: { id: payload.bankAccountId } },
|
||||
balance: newBalance,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return await tx.bankAccountBalance.update({
|
||||
where: {
|
||||
bankAccountId: payload.bankAccountId,
|
||||
},
|
||||
data: {
|
||||
balance: newBalance,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { IsEnum, IsInt } from 'class-validator'
|
||||
import {
|
||||
BankAccountTransactionType,
|
||||
BankTransactionRefType,
|
||||
} from '../../../generated/prisma/enums'
|
||||
|
||||
export class AddTransactionToBankAccountDto {
|
||||
@IsInt()
|
||||
bankAccountId: number
|
||||
|
||||
@IsInt()
|
||||
amount: number
|
||||
|
||||
@IsInt()
|
||||
referenceId: number
|
||||
|
||||
@IsEnum(BankTransactionRefType)
|
||||
referenceType: BankTransactionRefType
|
||||
|
||||
@IsEnum(BankAccountTransactionType)
|
||||
type: BankAccountTransactionType
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { PurchaseReceiptPaymentsController } from './purchase-receipt-payments.controller'
|
||||
import { PurchaseReceiptPaymentsService } from './purchase-receipt-payments.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [PurchaseReceiptPaymentsController],
|
||||
providers: [PurchaseReceiptPaymentsService],
|
||||
})
|
||||
export class PurchaseReceiptPaymentsModule {}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { ArrayMinSize, IsInt, IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
import { CreatePurchaseReceiptItemDto } from '../../purchase-receipt-items/dto/create-purchase-receipt-item.dto'
|
||||
|
||||
export class CreatePurchaseReceiptDto {
|
||||
@IsString()
|
||||
code: string
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
totalAmount: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
paidAmount: number
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
supplierId: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
inventoryId: number
|
||||
|
||||
@Type(() => Array)
|
||||
@ArrayMinSize(1)
|
||||
items: Array<Omit<CreatePurchaseReceiptItemDto, 'receiptId'>>
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
|
||||
export class UpdatePurchaseReceiptDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
code?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
totalAmount?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
supplierId?: number | null
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
inventoryId?: number | null
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'
|
||||
import { CreatePurchaseReceiptDto } from './dto/create-purchase-receipt.dto'
|
||||
import { UpdatePurchaseReceiptDto } from './dto/update-purchase-receipt.dto'
|
||||
import { PurchaseReceiptsService } from './purchase-receipts.service'
|
||||
|
||||
@Controller('purchase-receipts')
|
||||
export class PurchaseReceiptsController {
|
||||
constructor(private readonly service: PurchaseReceiptsService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreatePurchaseReceiptDto) {
|
||||
return this.service.create(dto)
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.service.findAll()
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.service.findOne(Number(id))
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdatePurchaseReceiptDto) {
|
||||
return this.service.update(Number(id), dto)
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.service.remove(Number(id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { withTransaction } from '../../../common/database/transaction.helper'
|
||||
import { ResponseMapper } from '../../../common/response/response-mapper'
|
||||
import { Prisma } from '../../../generated/prisma/client'
|
||||
import { PrismaService } from '../../../prisma/prisma.service'
|
||||
import { CreatePurchaseReceiptDto } from './dto/create-purchase-receipt.dto'
|
||||
import { PurchaseReceiptWorkflow } from './purchase-receipts.workflow'
|
||||
|
||||
@Injectable()
|
||||
export class PurchaseReceiptsService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private purchaseReceiptWorkflow: PurchaseReceiptWorkflow,
|
||||
) {}
|
||||
|
||||
async create(dto: CreatePurchaseReceiptDto) {
|
||||
const data: Prisma.PurchaseReceiptCreateInput = {
|
||||
code: dto.code,
|
||||
totalAmount: new Prisma.Decimal(dto.totalAmount),
|
||||
description: dto.description ?? null,
|
||||
supplier: { connect: { id: dto.supplierId } },
|
||||
inventory: { connect: { id: dto.inventoryId } },
|
||||
|
||||
items: dto.items?.length
|
||||
? {
|
||||
create: dto.items.map((item, idx) => {
|
||||
const mapped = {
|
||||
count: new Prisma.Decimal(item.count ?? 0),
|
||||
unitPrice: new Prisma.Decimal(item.unitPrice ?? 0),
|
||||
totalAmount: new Prisma.Decimal(item.totalAmount ?? 0),
|
||||
description: item.description ?? null,
|
||||
product: { connect: { id: item.productId } },
|
||||
}
|
||||
return mapped
|
||||
}),
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
|
||||
return withTransaction(this.prisma, async tx => {
|
||||
const item = await tx.purchaseReceipt.create({
|
||||
data,
|
||||
include: { items: true },
|
||||
omit: {
|
||||
supplierId: true,
|
||||
inventoryId: true,
|
||||
},
|
||||
})
|
||||
await this.purchaseReceiptWorkflow.onCreatePurchaseReceipt(
|
||||
tx,
|
||||
dto.supplierId,
|
||||
dto.totalAmount,
|
||||
item.id,
|
||||
)
|
||||
return ResponseMapper.create(item)
|
||||
})
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
const items = await this.prisma.purchaseReceipt.findMany({
|
||||
include: { supplier: true, inventory: true },
|
||||
})
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const item = await this.prisma.purchaseReceipt.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
supplier: {
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
},
|
||||
},
|
||||
inventory: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
items: 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, 'supplierId')) {
|
||||
if (payload.supplierId === null) payload.supplier = { disconnect: true }
|
||||
else payload.supplier = { connect: { id: Number(payload.supplierId) } }
|
||||
delete payload.supplierId
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'inventoryId')) {
|
||||
if (payload.inventoryId === null) payload.inventory = { disconnect: true }
|
||||
else payload.inventory = { connect: { id: Number(payload.inventoryId) } }
|
||||
delete payload.inventoryId
|
||||
}
|
||||
|
||||
const item = await this.prisma.purchaseReceipt.update({
|
||||
where: { id },
|
||||
data: payload,
|
||||
})
|
||||
return ResponseMapper.update(item)
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.purchaseReceipt.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Prisma } from '../../../generated/prisma/client'
|
||||
import { SupplierLedgerWorkflow } from '../../suppliers/ledger/supplier-ledger.workflow'
|
||||
|
||||
@Injectable()
|
||||
export class PurchaseReceiptWorkflow {
|
||||
constructor(private supplierLedgerWorkflow: SupplierLedgerWorkflow) {}
|
||||
|
||||
async onCreatePurchaseReceipt(
|
||||
tx: Prisma.TransactionClient,
|
||||
supplierId: number,
|
||||
amount: number,
|
||||
sourceId: number,
|
||||
) {
|
||||
await this.supplierLedgerWorkflow.updateLedgerBalance(
|
||||
tx,
|
||||
supplierId,
|
||||
amount,
|
||||
sourceId,
|
||||
'PURCHASE',
|
||||
)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsNotEmpty, IsNumber, Min } from 'class-validator'
|
||||
|
||||
export class CreatePurchaseReceiptItemDto {
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
count: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
unitPrice: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
totalAmount: number
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNotEmpty()
|
||||
productId: number
|
||||
|
||||
description?: string
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsInt, IsNumber, IsOptional } from 'class-validator'
|
||||
|
||||
export class UpdatePurchaseReceiptItemDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
count?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
unitPrice?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
total?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
receiptId?: number
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
productId?: number
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'
|
||||
import { CreatePurchaseReceiptItemDto } from './dto/create-purchase-receipt-item.dto'
|
||||
import { UpdatePurchaseReceiptItemDto } from './dto/update-purchase-receipt-item.dto'
|
||||
import { PurchaseReceiptItemsService } from './purchase-receipt-items.service'
|
||||
|
||||
@Controller('purchase-receipts/:receiptId/items')
|
||||
export class PurchaseReceiptItemsController {
|
||||
constructor(private readonly service: PurchaseReceiptItemsService) {}
|
||||
|
||||
@Post()
|
||||
create(
|
||||
@Param('receiptId') receiptId: string,
|
||||
@Body() dto: CreatePurchaseReceiptItemDto,
|
||||
) {
|
||||
return this.service.createForReceipt(Number(receiptId), dto)
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll(@Param('receiptId') receiptId: string) {
|
||||
return this.service.findAllForReceipt(Number(receiptId))
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('receiptId') receiptId: string, @Param('id') id: string) {
|
||||
return this.service.findOneForReceipt(Number(receiptId), Number(id))
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(
|
||||
@Param('receiptId') receiptId: string,
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdatePurchaseReceiptItemDto,
|
||||
) {
|
||||
return this.service.updateForReceipt(Number(receiptId), Number(id), dto)
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('receiptId') receiptId: string, @Param('id') id: string) {
|
||||
return this.service.removeForReceipt(Number(receiptId), Number(id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../../prisma/prisma.module'
|
||||
import { PurchaseReceiptItemsController } from './purchase-receipt-items.controller'
|
||||
import { PurchaseReceiptItemsService } from './purchase-receipt-items.service'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [PurchaseReceiptItemsController],
|
||||
providers: [PurchaseReceiptItemsService],
|
||||
})
|
||||
export class PurchaseReceiptItemsModule {}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../../prisma/prisma.service'
|
||||
import { CreatePurchaseReceiptItemDto } from './dto/create-purchase-receipt-item.dto'
|
||||
|
||||
@Injectable()
|
||||
export class PurchaseReceiptItemsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async create(dto: CreatePurchaseReceiptItemDto) {
|
||||
const payload: any = { ...dto }
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'productId')) {
|
||||
payload.product = { connect: { id: Number(payload.productId) } }
|
||||
delete payload.productId
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'receiptId')) {
|
||||
payload.receipt = { connect: { id: Number(payload.receiptId) } }
|
||||
delete payload.receiptId
|
||||
}
|
||||
payload.unitPrice = String(payload.unitPrice)
|
||||
payload.count = String(payload.count)
|
||||
payload.total = String(payload.total)
|
||||
const item = await this.prisma.purchaseReceiptItem.create({ data: payload })
|
||||
return ResponseMapper.create(item)
|
||||
}
|
||||
async findAll(receiptId: number) {
|
||||
const where = { receiptId: Number(receiptId) }
|
||||
const items = await this.prisma.purchaseReceiptItem.findMany({
|
||||
where,
|
||||
include: { product: true },
|
||||
})
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const item = await this.prisma.purchaseReceiptItem.findUnique({
|
||||
where: { id },
|
||||
include: { product: true, receipt: true },
|
||||
})
|
||||
if (!item) return null
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
|
||||
async createForReceipt(receiptId: number, dto: CreatePurchaseReceiptItemDto) {
|
||||
const payload: any = { ...dto, receiptId }
|
||||
return this.create(payload)
|
||||
}
|
||||
|
||||
async findAllForReceipt(receiptId: number) {
|
||||
return this.findAll(receiptId)
|
||||
}
|
||||
|
||||
async findOneForReceipt(receiptId: number, id: number) {
|
||||
const item = await this.prisma.purchaseReceiptItem.findFirst({
|
||||
where: { id, receiptId },
|
||||
include: { product: true, receipt: 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, 'receiptId')) {
|
||||
payload.receipt = { connect: { id: Number(payload.receiptId) } }
|
||||
delete payload.receiptId
|
||||
}
|
||||
|
||||
const item = await this.prisma.purchaseReceiptItem.update({
|
||||
where: { id },
|
||||
data: payload,
|
||||
})
|
||||
return ResponseMapper.update(item)
|
||||
}
|
||||
|
||||
async updateForReceipt(receiptId: number, id: number, data: any) {
|
||||
const existing = await this.prisma.purchaseReceiptItem.findFirst({
|
||||
where: { id, receiptId },
|
||||
})
|
||||
if (!existing) return null
|
||||
return this.update(id, data)
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const item = await this.prisma.purchaseReceiptItem.delete({ where: { id } })
|
||||
return ResponseMapper.single(item)
|
||||
}
|
||||
|
||||
async removeForReceipt(receiptId: number, id: number) {
|
||||
const existing = await this.prisma.purchaseReceiptItem.findFirst({
|
||||
where: { id, receiptId },
|
||||
})
|
||||
if (!existing) return null
|
||||
return this.remove(id)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { IsEnum, IsInt, IsNumber, IsOptional, IsString } from 'class-validator'
|
||||
import { PaymentMethodType, PaymentType } from '../../../generated/prisma/enums'
|
||||
import { PaymentMethodType, PaymentType } from '../../../../generated/prisma/enums'
|
||||
|
||||
export class CreatePurchaseReceiptPaymentDto {
|
||||
@IsNumber()
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
|
||||
import { PrismaModule } from '../../../prisma/prisma.module'
|
||||
import { BankAccountsModule } from '../../bank-accounts/bank-accounts.module'
|
||||
import { PurchaseReceiptPaymentsController } from './purchase-receipt-payments.controller'
|
||||
import { PurchaseReceiptPaymentsService } from './purchase-receipt-payments.service'
|
||||
import { PurchaseReceiptPaymentsWorkflow } from './purchase-receipt-payments.workflow'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, BankAccountsModule],
|
||||
controllers: [PurchaseReceiptPaymentsController],
|
||||
providers: [PurchaseReceiptPaymentsService, PurchaseReceiptPaymentsWorkflow],
|
||||
exports: [PurchaseReceiptPaymentsWorkflow],
|
||||
})
|
||||
export class PurchaseReceiptPaymentsModule {}
|
||||
+21
-6
@@ -1,14 +1,29 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { withTransaction } from '../../../common/database/transaction.helper'
|
||||
import { ResponseMapper } from '../../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../../prisma/prisma.service'
|
||||
import { CreatePurchaseReceiptPaymentDto } from './dto/create-purchase-receipt-payment.dto'
|
||||
import { PurchaseReceiptPaymentsWorkflow } from './purchase-receipt-payments.workflow'
|
||||
|
||||
@Injectable()
|
||||
export class PurchaseReceiptPaymentsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private purchaseReceiptPaymentsWorkflow: PurchaseReceiptPaymentsWorkflow,
|
||||
) {}
|
||||
|
||||
async create(data: any) {
|
||||
const item = await this.prisma.purchaseReceiptPayments.create({ data })
|
||||
return ResponseMapper.create(item)
|
||||
async create(data: CreatePurchaseReceiptPaymentDto) {
|
||||
return withTransaction(this.prisma, async tx => {
|
||||
const item = await tx.purchaseReceiptPayments.create({ data })
|
||||
await this.purchaseReceiptPaymentsWorkflow.onAddPayment(
|
||||
tx,
|
||||
data.bankAccountId,
|
||||
data.receiptId,
|
||||
data.amount,
|
||||
data.type,
|
||||
)
|
||||
return ResponseMapper.create(item)
|
||||
})
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Prisma } from '../../../generated/prisma/client'
|
||||
import { PaymentType } from '../../../generated/prisma/enums'
|
||||
import { BankAccountsWorkflow } from '../../bank-accounts/bank-accounts.workflow'
|
||||
|
||||
@Injectable()
|
||||
export class PurchaseReceiptPaymentsWorkflow {
|
||||
constructor(private bankAccountsWorkflow: BankAccountsWorkflow) {}
|
||||
|
||||
async onAddPayment(
|
||||
tx: Prisma.TransactionClient,
|
||||
bankAccountId: number,
|
||||
receiptId: number,
|
||||
amount: number,
|
||||
type: PaymentType,
|
||||
) {
|
||||
const { paidAmount, totalAmount } = await tx.purchaseReceipt.findUniqueOrThrow({
|
||||
where: { id: receiptId },
|
||||
})
|
||||
|
||||
if (type === 'PAYMENT' && Number(paidAmount) + Number(amount) > Number(totalAmount)) {
|
||||
throw new Error('مجموع مبلغ پرداختی بیشتر از مبلغ فاکتور است.')
|
||||
}
|
||||
|
||||
if (type === 'REFUND' && Number(paidAmount) - Number(amount) < 0) {
|
||||
throw new Error(
|
||||
'مجموع مبلغ بازپرداختی بیشتر از مبلغ پرداخت شده در این فاکتور است.',
|
||||
)
|
||||
}
|
||||
|
||||
await this.bankAccountsWorkflow.addTransaction(tx, {
|
||||
bankAccountId: bankAccountId,
|
||||
amount: amount,
|
||||
type: type === 'PAYMENT' ? 'WITHDRAWAL' : 'DEPOSIT',
|
||||
referenceId: receiptId,
|
||||
referenceType: type === 'PAYMENT' ? 'PURCHASE_PAYMENT' : 'PURCHASE_REFUND',
|
||||
})
|
||||
|
||||
const newPaidAmount =
|
||||
type === 'PAYMENT'
|
||||
? Number(paidAmount) + Number(amount)
|
||||
: Number(paidAmount) - Number(amount)
|
||||
const newStatus =
|
||||
newPaidAmount === 0
|
||||
? 'UNPAID'
|
||||
: newPaidAmount >= Number(totalAmount)
|
||||
? 'PAID'
|
||||
: 'PARTIALLY_PAID'
|
||||
|
||||
await tx.purchaseReceipt.update({
|
||||
where: { id: receiptId },
|
||||
data: {
|
||||
paidAmount: newPaidAmount,
|
||||
status: newStatus,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { SupplierLedgerModule } from '../suppliers/ledger/supplier-ledger.module'
|
||||
import { PurchaseReceiptsController } from './index/purchase-receipts.controller'
|
||||
import { PurchaseReceiptsService } from './index/purchase-receipts.service'
|
||||
import { PurchaseReceiptWorkflow } from './index/purchase-receipts.workflow'
|
||||
import { PurchaseReceiptItemsModule } from './purchase-receipt-items/purchase-receipt-items.module'
|
||||
import { PurchaseReceiptPaymentsModule } from './purchase-receipt-payments/purchase-receipt-payments.module'
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
PrismaModule,
|
||||
PurchaseReceiptItemsModule,
|
||||
PurchaseReceiptPaymentsModule,
|
||||
SupplierLedgerModule,
|
||||
],
|
||||
controllers: [PurchaseReceiptsController],
|
||||
providers: [PurchaseReceiptsService, PurchaseReceiptWorkflow],
|
||||
exports: [PurchaseReceiptWorkflow],
|
||||
})
|
||||
export class PurchaseReceiptsModule {}
|
||||
@@ -22,11 +22,6 @@ export class SuppliersController {
|
||||
return this.suppliersService.findOne(Number(supplierId))
|
||||
}
|
||||
|
||||
@Get(':supplierId/ledger')
|
||||
getLedger(@Param('supplierId') supplierId: string) {
|
||||
return this.suppliersService.getLedger(Number(supplierId))
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateSupplierDto) {
|
||||
return this.suppliersService.update(Number(id), dto)
|
||||
|
||||
@@ -37,21 +37,6 @@ export class SuppliersService {
|
||||
})
|
||||
}
|
||||
|
||||
async getLedger(supplierId: number) {
|
||||
const items = await this.prisma.supplierLedger.findMany({
|
||||
where: {
|
||||
supplierId,
|
||||
},
|
||||
omit: {
|
||||
supplierId: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
})
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
|
||||
async update(id: number, data: any) {
|
||||
const item = await this.prisma.supplier.update({ where: { id }, data })
|
||||
return ResponseMapper.update(item)
|
||||
|
||||
@@ -21,10 +21,15 @@ export class SupplierInvoicesController {
|
||||
|
||||
@Post(':invoiceId/pay')
|
||||
createPayment(
|
||||
@Param('supplierId') supplierId: string,
|
||||
@Param('invoiceId') invoiceId: string,
|
||||
@Body() data: CreateReceiptPaymentDto,
|
||||
) {
|
||||
return this.supplierInvoicesService.createPayment(Number(invoiceId), data)
|
||||
return this.supplierInvoicesService.createPayment(
|
||||
Number(supplierId),
|
||||
Number(invoiceId),
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
@Get(':invoiceId/payments')
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../../prisma/prisma.module'
|
||||
import { PurchaseReceiptPaymentsModule } from '../../purchase-receipts/purchase-receipt-payments/purchase-receipt-payments.module'
|
||||
import { SupplierLedgerModule } from '../ledger/supplier-ledger.module'
|
||||
import { SupplierInvoicesController } from './invoices.controller'
|
||||
import { SupplierInvoicesService } from './invoices.service'
|
||||
import { SupplierInvoicesWorkflow } from './invoices.workflow'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, PurchaseReceiptPaymentsModule, SupplierLedgerModule],
|
||||
controllers: [SupplierInvoicesController],
|
||||
providers: [SupplierInvoicesService],
|
||||
providers: [SupplierInvoicesService, SupplierInvoicesWorkflow],
|
||||
})
|
||||
export class SupplierInvoicesModule {}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { withTransaction } from '../../../common/database/transaction.helper'
|
||||
import { ResponseMapper } from '../../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../../prisma/prisma.service'
|
||||
import { CreateReceiptPaymentDto } from './dto/create-receipt-payment.dto'
|
||||
import { SupplierInvoicesWorkflow } from './invoices.workflow'
|
||||
|
||||
@Injectable()
|
||||
export class SupplierInvoicesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private supplierInvoicesWorkflow: SupplierInvoicesWorkflow,
|
||||
) {}
|
||||
|
||||
async findAll(supplierId: number) {
|
||||
const items = await this.prisma.purchaseReceipt.findMany({
|
||||
@@ -147,32 +152,45 @@ export class SupplierInvoicesService {
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
|
||||
async createPayment(invoiceId: number, data: CreateReceiptPaymentDto) {
|
||||
async createPayment(
|
||||
supplierId: number,
|
||||
invoiceId: number,
|
||||
data: CreateReceiptPaymentDto,
|
||||
) {
|
||||
const { bankAccountId, ...rest } = data
|
||||
console.log(data)
|
||||
|
||||
const payment = await this.prisma.purchaseReceiptPayments.create({
|
||||
data: {
|
||||
...rest,
|
||||
payedAt: new Date(data.payedAt),
|
||||
receipt: {
|
||||
connect: {
|
||||
id: invoiceId,
|
||||
return withTransaction(this.prisma, async tx => {
|
||||
await this.supplierInvoicesWorkflow.onPaymentCreated(
|
||||
tx,
|
||||
supplierId,
|
||||
data.amount,
|
||||
invoiceId,
|
||||
bankAccountId,
|
||||
)
|
||||
|
||||
const payment = await tx.purchaseReceiptPayments.create({
|
||||
data: {
|
||||
...rest,
|
||||
payedAt: new Date(data.payedAt),
|
||||
receipt: {
|
||||
connect: {
|
||||
id: invoiceId,
|
||||
},
|
||||
},
|
||||
bankAccount: {
|
||||
connect: {
|
||||
id: bankAccountId,
|
||||
},
|
||||
},
|
||||
},
|
||||
bankAccount: {
|
||||
connect: {
|
||||
id: bankAccountId,
|
||||
},
|
||||
include: {
|
||||
receipt: true,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
receipt: true,
|
||||
},
|
||||
omit: {
|
||||
receiptId: true,
|
||||
},
|
||||
omit: {
|
||||
receiptId: true,
|
||||
},
|
||||
})
|
||||
return ResponseMapper.create(payment)
|
||||
})
|
||||
return ResponseMapper.create(payment)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Prisma } from '../../../generated/prisma/client'
|
||||
import { PurchaseReceiptPaymentsWorkflow } from '../../purchase-receipts/purchase-receipt-payments/purchase-receipt-payments.workflow'
|
||||
import { SupplierLedgerWorkflow } from '../ledger/supplier-ledger.workflow'
|
||||
|
||||
@Injectable()
|
||||
export class SupplierInvoicesWorkflow {
|
||||
constructor(
|
||||
private supplierLedgerWorkflow: SupplierLedgerWorkflow,
|
||||
private purchaseReceiptPaymentWorkflow: PurchaseReceiptPaymentsWorkflow,
|
||||
) {}
|
||||
|
||||
async onPaymentCreated(
|
||||
tx: Prisma.TransactionClient,
|
||||
supplierId: number,
|
||||
amount: number,
|
||||
invoiceId: number,
|
||||
bankAccountId: number,
|
||||
) {
|
||||
await this.supplierLedgerWorkflow.updateLedgerBalance(
|
||||
tx,
|
||||
supplierId,
|
||||
amount,
|
||||
invoiceId,
|
||||
'PAYMENT',
|
||||
)
|
||||
|
||||
await this.purchaseReceiptPaymentWorkflow.onAddPayment(
|
||||
tx,
|
||||
bankAccountId,
|
||||
invoiceId,
|
||||
amount,
|
||||
'PAYMENT',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Controller, Get, Param } from '@nestjs/common'
|
||||
import { SupplierLedgerService } from './supplier-ledger.service'
|
||||
|
||||
@Controller('suppliers/:supplierId/ledger')
|
||||
export class SupplierLedgerController {
|
||||
constructor(private readonly supplierLedgerService: SupplierLedgerService) {}
|
||||
|
||||
@Get('')
|
||||
getLedger(@Param('supplierId') supplierId: string) {
|
||||
return this.supplierLedgerService.findAll(Number(supplierId))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { PrismaModule } from '../../../prisma/prisma.module'
|
||||
import { SupplierLedgerController } from './supplier-ledger.controller'
|
||||
import { SupplierLedgerService } from './supplier-ledger.service'
|
||||
import { SupplierLedgerWorkflow } from './supplier-ledger.workflow'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [SupplierLedgerController],
|
||||
providers: [SupplierLedgerService, SupplierLedgerWorkflow],
|
||||
exports: [SupplierLedgerWorkflow],
|
||||
})
|
||||
export class SupplierLedgerModule {}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../../../common/response/response-mapper'
|
||||
import { PrismaService } from '../../../prisma/prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class SupplierLedgerService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(supplierId: number) {
|
||||
const items = await this.prisma.supplierLedger.findMany({
|
||||
where: {
|
||||
supplierId,
|
||||
},
|
||||
omit: {
|
||||
supplierId: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
})
|
||||
return ResponseMapper.list(items)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Prisma } from '../../../generated/prisma/client'
|
||||
import { LedgerSourceType } from '../../../generated/prisma/enums'
|
||||
|
||||
@Injectable()
|
||||
export class SupplierLedgerWorkflow {
|
||||
async updateLedgerBalance(
|
||||
tx: Prisma.TransactionClient,
|
||||
supplierId: number,
|
||||
amount: number,
|
||||
sourceId: number,
|
||||
sourceType: LedgerSourceType,
|
||||
) {
|
||||
const lastSupplierLedgerBalance = await tx.supplierLedger
|
||||
.findFirstOrThrow({
|
||||
where: { supplierId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
.then(sl => (sl ? sl.balance : 0))
|
||||
|
||||
let newBalance = Number(lastSupplierLedgerBalance)
|
||||
let credit = 0
|
||||
let debit = 0
|
||||
let description = ''
|
||||
|
||||
switch (sourceType) {
|
||||
case 'PURCHASE':
|
||||
newBalance += amount
|
||||
credit = amount
|
||||
description = `خرید به مبلغ ${amount} بابت خرید شماره ${sourceId}`
|
||||
break
|
||||
case 'REFUND':
|
||||
case 'PAYMENT':
|
||||
newBalance -= amount
|
||||
debit = amount
|
||||
description = `پرداخت به مبلغ ${amount} بابت پرداخت شماره ${sourceId}`
|
||||
break
|
||||
case 'ADJUSTMENT':
|
||||
newBalance = amount
|
||||
description = `اصلاح حساب به مبلغ ${amount} بابت پرداخت شماره ${sourceId}`
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return await tx.supplierLedger.create({
|
||||
data: {
|
||||
supplierId,
|
||||
debit,
|
||||
credit,
|
||||
balance: newBalance,
|
||||
sourceId,
|
||||
sourceType,
|
||||
description,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ import { PrismaModule } from '../../prisma/prisma.module'
|
||||
import { SuppliersController } from './index/suppliers.controller'
|
||||
import { SuppliersService } from './index/suppliers.service'
|
||||
import { SupplierInvoicesModule } from './invoices/invoices.module'
|
||||
import { SupplierLedgerModule } from './ledger/supplier-ledger.module'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, SupplierInvoicesModule],
|
||||
imports: [PrismaModule, SupplierInvoicesModule, SupplierLedgerModule],
|
||||
controllers: [SuppliersController],
|
||||
providers: [SuppliersService],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user