From a1e8f4041781d0b135fc93c225a2d1382ec3f2fc Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Sun, 10 May 2026 14:14:01 +0330 Subject: [PATCH] feat: enhance sales invoice functionality with inquiry, send, retry, and revoke actions --- .../sale-invoice-access.service.ts | 29 +++++++ .../sale-invoice-actions.service.ts | 55 +++++++++++++ .../sales-invoices.controller.ts | 41 +++++++++- .../sales-invoices/sales-invoices.module.ts | 10 ++- .../sales-invoices/sales-invoices.service.ts | 79 ++++++++++++++++++- .../sales-invoices/sales-invoices.module.ts | 9 ++- .../sales-invoices/sales-invoices.service.ts | 60 ++++---------- 7 files changed, 232 insertions(+), 51 deletions(-) create mode 100644 src/common/services/saleInvoices/sale-invoice-access.service.ts create mode 100644 src/common/services/saleInvoices/sale-invoice-actions.service.ts diff --git a/src/common/services/saleInvoices/sale-invoice-access.service.ts b/src/common/services/saleInvoices/sale-invoice-access.service.ts new file mode 100644 index 0000000..381e588 --- /dev/null +++ b/src/common/services/saleInvoices/sale-invoice-access.service.ts @@ -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 { + 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 + } +} diff --git a/src/common/services/saleInvoices/sale-invoice-actions.service.ts b/src/common/services/saleInvoices/sale-invoice-actions.service.ts new file mode 100644 index 0000000..7980dbd --- /dev/null +++ b/src/common/services/saleInvoices/sale-invoice-actions.service.ts @@ -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) + } +} diff --git a/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.controller.ts b/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.controller.ts index daed5de..1ee1cd0 100644 --- a/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.controller.ts +++ b/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.controller.ts @@ -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 { ApiTags } from '@nestjs/swagger' @@ -26,4 +26,43 @@ export class SalesInvoicesController { ) { 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) + } } diff --git a/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.module.ts b/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.module.ts index ad47e25..9442d26 100644 --- a/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.module.ts +++ b/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.module.ts @@ -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 { SalesInvoicesController } from './sales-invoices.controller' import { SalesInvoicesService } from './sales-invoices.service' @Module({ + imports: [SaleInvoiceTspModule], controllers: [SalesInvoicesController], - providers: [SalesInvoicesService], + providers: [ + SalesInvoicesService, + SharedSaleInvoiceActionsService, + SharedSaleInvoiceAccessService, + ], }) export class ConsumerPosSalesInvoicesModule {} diff --git a/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.service.ts b/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.service.ts index 281fbc2..63f5586 100644 --- a/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.service.ts +++ b/src/modules/consumer/business-activities/complexes/poses/sales-invoices/sales-invoices.service.ts @@ -1,11 +1,15 @@ +import { SharedSaleInvoiceActionsService } from '@/common/services/saleInvoices/sale-invoice-actions.service' import { ResponseMapper } from '@/common/response/response-mapper' import { SalesInvoiceWhereInput } from '@/generated/prisma/models' import { PrismaService } from '@/prisma/prisma.service' -import { Injectable } from '@nestjs/common' +import { Injectable, NotFoundException } from '@nestjs/common' @Injectable() 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) { const defaultWhere: SalesInvoiceWhereInput = { @@ -117,4 +121,75 @@ export class SalesInvoicesService { // TODO: Implement fetching a single sales invoice 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) + } } diff --git a/src/modules/pos/sales-invoices/sales-invoices.module.ts b/src/modules/pos/sales-invoices/sales-invoices.module.ts index e3b58be..7fc2eab 100644 --- a/src/modules/pos/sales-invoices/sales-invoices.module.ts +++ b/src/modules/pos/sales-invoices/sales-invoices.module.ts @@ -1,4 +1,6 @@ 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 { Module } from '@nestjs/common' import { SalesInvoicesController } from './sales-invoices.controller' @@ -7,6 +9,11 @@ import { SalesInvoicesService } from './sales-invoices.service' @Module({ imports: [SaleInvoiceTspModule], controllers: [SalesInvoicesController], - providers: [SalesInvoicesService, SharedSaleInvoiceCreateService], + providers: [ + SalesInvoicesService, + SharedSaleInvoiceCreateService, + SharedSaleInvoiceActionsService, + SharedSaleInvoiceAccessService, + ], }) export class PosSalesInvoicesModule {} diff --git a/src/modules/pos/sales-invoices/sales-invoices.service.ts b/src/modules/pos/sales-invoices/sales-invoices.service.ts index 7fc3b6b..b73b0ab 100644 --- a/src/modules/pos/sales-invoices/sales-invoices.service.ts +++ b/src/modules/pos/sales-invoices/sales-invoices.service.ts @@ -1,15 +1,12 @@ import { IPosPayload } from '@/common/models/posPayload.model' 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 { 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 { Prisma } from 'generated/prisma/client' -import { - ConsumerRole, - TspProviderRequestType, - TspProviderResponseStatus, -} from 'generated/prisma/enums' +import { TspProviderRequestType, TspProviderResponseStatus } from 'generated/prisma/enums' import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.service' import { CreateSalesInvoiceDto } from './dto/create-sales-invoice.dto' import { SalesInvoicesFilterDto } from './dto/sales-invoices-filter.dto' @@ -20,6 +17,7 @@ export class SalesInvoicesService { private prisma: PrismaService, private salesInvoiceTaxService: SalesInvoiceTspService, private sharedSaleInvoiceCreateService: SharedSaleInvoiceCreateService, + private sharedSaleInvoiceActionsService: SharedSaleInvoiceActionsService, ) {} async findAll(posInfo: IPosPayload, query: SalesInvoicesFilterDto = {}) { @@ -253,9 +251,8 @@ export class SalesInvoicesService { } async send(invoiceId: string, posInfo: IPosPayload) { - await this.checkAccessToInvoice(posInfo.consumer_account_id, posInfo.pos_id) - - const invoice = await this.salesInvoiceTaxService.originalSend( + const invoice = await this.sharedSaleInvoiceActionsService.send( + posInfo.consumer_account_id, posInfo.pos_id, invoiceId, ) @@ -264,9 +261,8 @@ export class SalesInvoicesService { } async retry(invoiceId: string, posInfo: IPosPayload) { - await this.checkAccessToInvoice(posInfo.consumer_account_id, posInfo.pos_id) - - const invoice = await this.salesInvoiceTaxService.originalSend( + const invoice = await this.sharedSaleInvoiceActionsService.retry( + posInfo.consumer_account_id, posInfo.pos_id, invoiceId, ) @@ -275,11 +271,9 @@ export class SalesInvoicesService { } 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 invoice = await this.salesInvoiceTaxService.revoke( + const invoice = await this.sharedSaleInvoiceActionsService.revoke( consumer_account_id, pos_id, complex_id, @@ -291,8 +285,11 @@ export class SalesInvoicesService { } 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.salesInvoiceTaxService.get(invoiceId, pos_id, consumer_id) + const invoice = await this.sharedSaleInvoiceActionsService.inquiry( + consumer_account_id, + pos_id, + invoiceId, + ) return ResponseMapper.single(invoice) } @@ -447,33 +444,4 @@ export class SalesInvoicesService { return where } - - private async checkAccessToInvoice( - consumer_account_id: string, - pos_id: string, - ): Promise { - 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 - } }