feat: enhance sales invoice functionality with inquiry, send, retry, and revoke actions

This commit is contained in:
2026-05-10 14:14:01 +03:30
parent afa83895a2
commit a1e8f40417
7 changed files with 232 additions and 51 deletions
@@ -0,0 +1,29 @@
import { PrismaService } from '@/prisma/prisma.service'
import { BadRequestException, Injectable } from '@nestjs/common'
import { ConsumerRole } from 'generated/prisma/enums'
@Injectable()
export class SharedSaleInvoiceAccessService {
constructor(private readonly prisma: PrismaService) {}
async getConsumerIdWithPosAccess(
consumerAccountId: string,
posId: string,
): Promise<string> {
const consumer = await this.prisma.consumerAccount.findUnique({
where: {
id: consumerAccountId,
OR: [{ pos: { id: posId } }, { role: ConsumerRole.OWNER }],
},
select: {
consumer_id: true,
},
})
if (!consumer) {
throw new BadRequestException('شما دسترسی لازم برای ارسال فاکتور را ندارید.')
}
return consumer.consumer_id
}
}
@@ -0,0 +1,55 @@
import { SharedSaleInvoiceAccessService } from '@/common/services/saleInvoices/sale-invoice-access.service'
import { Injectable } from '@nestjs/common'
import { SalesInvoiceTspService } from '@/modules/tspProviders/sales-invoice-tsp.service'
@Injectable()
export class SharedSaleInvoiceActionsService {
constructor(
private readonly salesInvoiceTspService: SalesInvoiceTspService,
private readonly saleInvoiceAccessService: SharedSaleInvoiceAccessService,
) {}
async send(consumerAccountId: string, posId: string, invoiceId: string) {
await this.saleInvoiceAccessService.getConsumerIdWithPosAccess(
consumerAccountId,
posId,
)
return this.salesInvoiceTspService.originalSend(posId, invoiceId)
}
async retry(consumerAccountId: string, posId: string, invoiceId: string) {
await this.saleInvoiceAccessService.getConsumerIdWithPosAccess(
consumerAccountId,
posId,
)
return this.salesInvoiceTspService.originalSend(posId, invoiceId)
}
async revoke(
consumerAccountId: string,
posId: string,
complexId: string,
businessId: string,
invoiceId: string,
) {
await this.saleInvoiceAccessService.getConsumerIdWithPosAccess(
consumerAccountId,
posId,
)
return this.salesInvoiceTspService.revoke(
consumerAccountId,
posId,
complexId,
businessId,
invoiceId,
)
}
async inquiry(consumerAccountId: string, posId: string, invoiceId: string) {
const consumerId = await this.saleInvoiceAccessService.getConsumerIdWithPosAccess(
consumerAccountId,
posId,
)
return this.salesInvoiceTspService.get(invoiceId, posId, consumerId)
}
}
@@ -1,4 +1,4 @@
import { Controller, Get, Param } from '@nestjs/common' import { Controller, Get, Param, Post } from '@nestjs/common'
import { TokenAccount } from '@/common/decorators/tokenInfo.decorator' import { TokenAccount } from '@/common/decorators/tokenInfo.decorator'
import { ApiTags } from '@nestjs/swagger' import { ApiTags } from '@nestjs/swagger'
@@ -26,4 +26,43 @@ export class SalesInvoicesController {
) { ) {
return this.salesInvoicesService.findOne(complexId, posId, id) return this.salesInvoicesService.findOne(complexId, posId, id)
} }
@Get(':id/inquiry')
inquiry(
@TokenAccount('userId') userId: string,
@Param('posId') posId: string,
@Param('id') id: string,
) {
return this.salesInvoicesService.inquiry(userId, posId, id)
}
@Post(':id/send')
send(
@TokenAccount('userId') userId: string,
@Param('complexId') complexId: string,
@Param('posId') posId: string,
@Param('id') id: string,
) {
return this.salesInvoicesService.send(userId, complexId, posId, id)
}
@Post(':id/retry')
retry(
@TokenAccount('userId') userId: string,
@Param('complexId') complexId: string,
@Param('posId') posId: string,
@Param('id') id: string,
) {
return this.salesInvoicesService.retry(userId, complexId, posId, id)
}
@Post(':id/revoke')
revoke(
@TokenAccount('userId') userId: string,
@Param('complexId') complexId: string,
@Param('posId') posId: string,
@Param('id') id: string,
) {
return this.salesInvoicesService.revoke(userId, complexId, posId, id)
}
} }
@@ -1,9 +1,17 @@
import { SharedSaleInvoiceActionsService } from '@/common/services/saleInvoices/sale-invoice-actions.service'
import { SharedSaleInvoiceAccessService } from '@/common/services/saleInvoices/sale-invoice-access.service'
import { SaleInvoiceTspModule } from '@/modules/tspProviders/sales-invoice-tsp.module'
import { Module } from '@nestjs/common' import { Module } from '@nestjs/common'
import { SalesInvoicesController } from './sales-invoices.controller' import { SalesInvoicesController } from './sales-invoices.controller'
import { SalesInvoicesService } from './sales-invoices.service' import { SalesInvoicesService } from './sales-invoices.service'
@Module({ @Module({
imports: [SaleInvoiceTspModule],
controllers: [SalesInvoicesController], controllers: [SalesInvoicesController],
providers: [SalesInvoicesService], providers: [
SalesInvoicesService,
SharedSaleInvoiceActionsService,
SharedSaleInvoiceAccessService,
],
}) })
export class ConsumerPosSalesInvoicesModule {} export class ConsumerPosSalesInvoicesModule {}
@@ -1,11 +1,15 @@
import { SharedSaleInvoiceActionsService } from '@/common/services/saleInvoices/sale-invoice-actions.service'
import { ResponseMapper } from '@/common/response/response-mapper' import { ResponseMapper } from '@/common/response/response-mapper'
import { SalesInvoiceWhereInput } from '@/generated/prisma/models' import { SalesInvoiceWhereInput } from '@/generated/prisma/models'
import { PrismaService } from '@/prisma/prisma.service' import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common' import { Injectable, NotFoundException } from '@nestjs/common'
@Injectable() @Injectable()
export class SalesInvoicesService { export class SalesInvoicesService {
constructor(private prisma: PrismaService) {} constructor(
private prisma: PrismaService,
private readonly sharedSaleInvoiceActionsService: SharedSaleInvoiceActionsService,
) {}
async findAll(consumer_id: string, complex_id: string, pos_id: string) { async findAll(consumer_id: string, complex_id: string, pos_id: string) {
const defaultWhere: SalesInvoiceWhereInput = { const defaultWhere: SalesInvoiceWhereInput = {
@@ -117,4 +121,75 @@ export class SalesInvoicesService {
// TODO: Implement fetching a single sales invoice // TODO: Implement fetching a single sales invoice
return {} return {}
} }
async send(
consumerAccountId: string,
complexId: string,
posId: string,
invoiceId: string,
) {
const invoice = await this.sharedSaleInvoiceActionsService.send(
consumerAccountId,
posId,
invoiceId,
)
return ResponseMapper.single(invoice)
}
async retry(
consumerAccountId: string,
complexId: string,
posId: string,
invoiceId: string,
) {
const invoice = await this.sharedSaleInvoiceActionsService.retry(
consumerAccountId,
posId,
invoiceId,
)
return ResponseMapper.single(invoice)
}
async revoke(
consumerAccountId: string,
complexId: string,
posId: string,
invoiceId: string,
) {
const pos = await this.prisma.pos.findUnique({
where: {
id: posId,
complex: {
id: complexId,
},
},
select: {
complex: {
select: {
business_activity_id: true,
},
},
},
})
if (!pos) throw new NotFoundException('پایانه فروش مورد نظر یافت نشد.')
const invoice = await this.sharedSaleInvoiceActionsService.revoke(
consumerAccountId,
posId,
complexId,
pos.complex.business_activity_id,
invoiceId,
)
return ResponseMapper.single(invoice)
}
async inquiry(consumerAccountId: string, posId: string, invoiceId: string) {
const invoice = await this.sharedSaleInvoiceActionsService.inquiry(
consumerAccountId,
posId,
invoiceId,
)
return ResponseMapper.single(invoice)
}
} }
@@ -1,4 +1,6 @@
import { SharedSaleInvoiceCreateService } from '@/common/services/saleInvoices/sale-invoice-create.service' import { SharedSaleInvoiceCreateService } from '@/common/services/saleInvoices/sale-invoice-create.service'
import { SharedSaleInvoiceActionsService } from '@/common/services/saleInvoices/sale-invoice-actions.service'
import { SharedSaleInvoiceAccessService } from '@/common/services/saleInvoices/sale-invoice-access.service'
import { SaleInvoiceTspModule } from '@/modules/tspProviders/sales-invoice-tsp.module' import { SaleInvoiceTspModule } from '@/modules/tspProviders/sales-invoice-tsp.module'
import { Module } from '@nestjs/common' import { Module } from '@nestjs/common'
import { SalesInvoicesController } from './sales-invoices.controller' import { SalesInvoicesController } from './sales-invoices.controller'
@@ -7,6 +9,11 @@ import { SalesInvoicesService } from './sales-invoices.service'
@Module({ @Module({
imports: [SaleInvoiceTspModule], imports: [SaleInvoiceTspModule],
controllers: [SalesInvoicesController], controllers: [SalesInvoicesController],
providers: [SalesInvoicesService, SharedSaleInvoiceCreateService], providers: [
SalesInvoicesService,
SharedSaleInvoiceCreateService,
SharedSaleInvoiceActionsService,
SharedSaleInvoiceAccessService,
],
}) })
export class PosSalesInvoicesModule {} export class PosSalesInvoicesModule {}
@@ -1,15 +1,12 @@
import { IPosPayload } from '@/common/models/posPayload.model' import { IPosPayload } from '@/common/models/posPayload.model'
import { SharedSaleInvoiceCreateService } from '@/common/services/saleInvoices/sale-invoice-create.service' import { SharedSaleInvoiceCreateService } from '@/common/services/saleInvoices/sale-invoice-create.service'
import { SharedSaleInvoiceActionsService } from '@/common/services/saleInvoices/sale-invoice-actions.service'
import { translateEnumValue } from '@/common/utils' import { translateEnumValue } from '@/common/utils'
import { PrismaService } from '@/prisma/prisma.service' import { PrismaService } from '@/prisma/prisma.service'
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common' import { Injectable, NotFoundException } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper' import { ResponseMapper } from 'common/response/response-mapper'
import { Prisma } from 'generated/prisma/client' import { Prisma } from 'generated/prisma/client'
import { import { TspProviderRequestType, TspProviderResponseStatus } from 'generated/prisma/enums'
ConsumerRole,
TspProviderRequestType,
TspProviderResponseStatus,
} from 'generated/prisma/enums'
import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.service' import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.service'
import { CreateSalesInvoiceDto } from './dto/create-sales-invoice.dto' import { CreateSalesInvoiceDto } from './dto/create-sales-invoice.dto'
import { SalesInvoicesFilterDto } from './dto/sales-invoices-filter.dto' import { SalesInvoicesFilterDto } from './dto/sales-invoices-filter.dto'
@@ -20,6 +17,7 @@ export class SalesInvoicesService {
private prisma: PrismaService, private prisma: PrismaService,
private salesInvoiceTaxService: SalesInvoiceTspService, private salesInvoiceTaxService: SalesInvoiceTspService,
private sharedSaleInvoiceCreateService: SharedSaleInvoiceCreateService, private sharedSaleInvoiceCreateService: SharedSaleInvoiceCreateService,
private sharedSaleInvoiceActionsService: SharedSaleInvoiceActionsService,
) {} ) {}
async findAll(posInfo: IPosPayload, query: SalesInvoicesFilterDto = {}) { async findAll(posInfo: IPosPayload, query: SalesInvoicesFilterDto = {}) {
@@ -253,9 +251,8 @@ export class SalesInvoicesService {
} }
async send(invoiceId: string, posInfo: IPosPayload) { async send(invoiceId: string, posInfo: IPosPayload) {
await this.checkAccessToInvoice(posInfo.consumer_account_id, posInfo.pos_id) const invoice = await this.sharedSaleInvoiceActionsService.send(
posInfo.consumer_account_id,
const invoice = await this.salesInvoiceTaxService.originalSend(
posInfo.pos_id, posInfo.pos_id,
invoiceId, invoiceId,
) )
@@ -264,9 +261,8 @@ export class SalesInvoicesService {
} }
async retry(invoiceId: string, posInfo: IPosPayload) { async retry(invoiceId: string, posInfo: IPosPayload) {
await this.checkAccessToInvoice(posInfo.consumer_account_id, posInfo.pos_id) const invoice = await this.sharedSaleInvoiceActionsService.retry(
posInfo.consumer_account_id,
const invoice = await this.salesInvoiceTaxService.originalSend(
posInfo.pos_id, posInfo.pos_id,
invoiceId, invoiceId,
) )
@@ -275,11 +271,9 @@ export class SalesInvoicesService {
} }
async revoke(invoiceId: string, posInfo: IPosPayload) { async revoke(invoiceId: string, posInfo: IPosPayload) {
await this.checkAccessToInvoice(posInfo.consumer_account_id, posInfo.pos_id)
const { consumer_account_id, pos_id, business_id, complex_id } = posInfo const { consumer_account_id, pos_id, business_id, complex_id } = posInfo
const invoice = await this.salesInvoiceTaxService.revoke( const invoice = await this.sharedSaleInvoiceActionsService.revoke(
consumer_account_id, consumer_account_id,
pos_id, pos_id,
complex_id, complex_id,
@@ -291,8 +285,11 @@ export class SalesInvoicesService {
} }
async inquiry(consumer_account_id: string, pos_id: string, invoiceId: string) { async inquiry(consumer_account_id: string, pos_id: string, invoiceId: string) {
const consumer_id = await this.checkAccessToInvoice(consumer_account_id, pos_id) const invoice = await this.sharedSaleInvoiceActionsService.inquiry(
const invoice = await this.salesInvoiceTaxService.get(invoiceId, pos_id, consumer_id) consumer_account_id,
pos_id,
invoiceId,
)
return ResponseMapper.single(invoice) return ResponseMapper.single(invoice)
} }
@@ -447,33 +444,4 @@ export class SalesInvoicesService {
return where return where
} }
private async checkAccessToInvoice(
consumer_account_id: string,
pos_id: string,
): Promise<string> {
const consumer = await this.prisma.consumerAccount.findUnique({
where: {
id: consumer_account_id,
OR: [
{
pos: {
id: pos_id,
},
},
{
role: ConsumerRole.OWNER,
},
],
},
select: {
consumer_id: true,
},
})
if (!consumer) {
throw new BadRequestException('شما دسترسی لازم برای ارسال فاکتور را ندارید.')
}
return consumer.consumer_id
}
} }