2026-03-29 18:06:41 +03:30
|
|
|
import { IPosPayload } from '@/common/models/posPayload.model'
|
2026-05-10 19:56:05 +03:30
|
|
|
import { QUERY_CONSTANTS } from '@/common/queryConstants'
|
2026-05-10 14:14:01 +03:30
|
|
|
import { SharedSaleInvoiceActionsService } from '@/common/services/saleInvoices/sale-invoice-actions.service'
|
2026-05-10 19:56:05 +03:30
|
|
|
import { SharedSaleInvoiceCreateService } from '@/common/services/saleInvoices/sale-invoice-create.service'
|
2026-05-01 19:43:59 +03:30
|
|
|
import { translateEnumValue } from '@/common/utils'
|
2026-03-07 11:25:11 +03:30
|
|
|
import { PrismaService } from '@/prisma/prisma.service'
|
2026-05-10 14:14:01 +03:30
|
|
|
import { Injectable, NotFoundException } from '@nestjs/common'
|
2026-03-07 11:25:11 +03:30
|
|
|
import { ResponseMapper } from 'common/response/response-mapper'
|
2026-04-30 16:27:46 +03:30
|
|
|
import { Prisma } from 'generated/prisma/client'
|
2026-05-10 14:14:01 +03:30
|
|
|
import { TspProviderRequestType, TspProviderResponseStatus } from 'generated/prisma/enums'
|
2026-05-03 16:23:17 +03:30
|
|
|
import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.service'
|
2026-06-06 19:53:00 +03:30
|
|
|
import {
|
|
|
|
|
PosCorrectionSalesInvoiceDto,
|
|
|
|
|
PosCreateSalesInvoiceDto,
|
|
|
|
|
} from './dto/create-sales-invoice.dto'
|
2026-05-01 19:43:59 +03:30
|
|
|
import { SalesInvoicesFilterDto } from './dto/sales-invoices-filter.dto'
|
2026-02-24 12:42:10 +03:30
|
|
|
|
2026-01-07 15:25:59 +03:30
|
|
|
@Injectable()
|
|
|
|
|
export class SalesInvoicesService {
|
2026-04-27 22:11:05 +03:30
|
|
|
constructor(
|
|
|
|
|
private prisma: PrismaService,
|
2026-05-03 16:23:17 +03:30
|
|
|
private salesInvoiceTaxService: SalesInvoiceTspService,
|
2026-05-05 22:42:09 +03:30
|
|
|
private sharedSaleInvoiceCreateService: SharedSaleInvoiceCreateService,
|
2026-05-10 14:14:01 +03:30
|
|
|
private sharedSaleInvoiceActionsService: SharedSaleInvoiceActionsService,
|
2026-04-27 22:11:05 +03:30
|
|
|
) {}
|
2026-02-24 12:42:10 +03:30
|
|
|
|
2026-05-01 19:43:59 +03:30
|
|
|
async findAll(posInfo: IPosPayload, query: SalesInvoicesFilterDto = {}) {
|
|
|
|
|
const page = query.page || 1
|
|
|
|
|
const perPage = Math.min(query.perPage || 10, 100)
|
|
|
|
|
|
|
|
|
|
const where = this.buildFindAllWhere(posInfo, query)
|
|
|
|
|
const [items, total] = await this.prisma.$transaction(async tx => [
|
|
|
|
|
await tx.salesInvoice.findMany({
|
|
|
|
|
where,
|
|
|
|
|
orderBy: {
|
|
|
|
|
created_at: 'desc',
|
|
|
|
|
},
|
|
|
|
|
skip: (page - 1) * perPage,
|
|
|
|
|
take: perPage,
|
|
|
|
|
select: {
|
2026-05-10 19:56:05 +03:30
|
|
|
...QUERY_CONSTANTS.SALE_INVOICE.summarySelect,
|
2026-05-01 19:43:59 +03:30
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
await tx.salesInvoice.count({ where }),
|
|
|
|
|
])
|
|
|
|
|
|
2026-05-27 21:55:02 +03:30
|
|
|
const summaryItems = items.map(invoice => {
|
|
|
|
|
const { tsp_attempts, type, ...rest } = invoice
|
|
|
|
|
return {
|
|
|
|
|
...rest,
|
|
|
|
|
type: translateEnumValue('TspProviderRequestType', type),
|
|
|
|
|
status: translateEnumValue(
|
|
|
|
|
'TspProviderResponseStatus',
|
|
|
|
|
invoice.tsp_attempts?.[0]?.status || TspProviderResponseStatus.NOT_SEND,
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
})
|
2026-05-01 19:43:59 +03:30
|
|
|
|
|
|
|
|
return ResponseMapper.paginate(summaryItems, { total, page, perPage })
|
2026-01-07 15:25:59 +03:30
|
|
|
}
|
|
|
|
|
|
2026-05-01 19:43:59 +03:30
|
|
|
async findOne(posInfo: IPosPayload, id: string) {
|
2026-05-03 16:23:17 +03:30
|
|
|
const invoice = await this.prisma.salesInvoice.findUnique({
|
2026-05-01 19:43:59 +03:30
|
|
|
where: {
|
|
|
|
|
id,
|
|
|
|
|
pos: {
|
2026-05-03 16:23:17 +03:30
|
|
|
id: posInfo.pos_id,
|
2026-05-01 19:43:59 +03:30
|
|
|
complex: {
|
|
|
|
|
business_activity_id: posInfo.business_id,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
select: {
|
2026-05-10 19:56:05 +03:30
|
|
|
...QUERY_CONSTANTS.SALE_INVOICE.select,
|
2026-05-01 19:43:59 +03:30
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
2026-05-03 16:23:17 +03:30
|
|
|
if (invoice) {
|
2026-05-27 21:55:02 +03:30
|
|
|
const { tsp_attempts, type, ...rest } = invoice
|
|
|
|
|
const mappedInvoice = {
|
2026-05-03 16:23:17 +03:30
|
|
|
...rest,
|
2026-05-27 21:55:02 +03:30
|
|
|
type: translateEnumValue('TspProviderRequestType', type),
|
2026-05-05 10:14:48 +03:30
|
|
|
status: translateEnumValue(
|
|
|
|
|
'TspProviderResponseStatus',
|
|
|
|
|
invoice.tsp_attempts?.[0]?.status || TspProviderResponseStatus.NOT_SEND,
|
|
|
|
|
),
|
2026-06-01 16:22:35 +03:30
|
|
|
settlement_type: translateEnumValue(
|
|
|
|
|
'InvoiceSettlementType',
|
|
|
|
|
invoice.settlement_type,
|
|
|
|
|
),
|
2026-05-27 21:55:02 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ResponseMapper.single(mappedInvoice)
|
2026-06-11 16:13:17 +03:30
|
|
|
} else throw new NotFoundException('صورتحساب مورد نظر شما یافت نشد.')
|
2026-01-07 15:25:59 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-06 19:53:00 +03:30
|
|
|
async create(data: PosCreateSalesInvoiceDto, posInfo: IPosPayload) {
|
2026-05-08 18:09:13 +03:30
|
|
|
let salesInvoice = await this.sharedSaleInvoiceCreateService.create({
|
2026-05-05 22:42:09 +03:30
|
|
|
data,
|
|
|
|
|
businessId: posInfo.business_id,
|
|
|
|
|
complexId: posInfo.complex_id,
|
|
|
|
|
posId: posInfo.pos_id,
|
|
|
|
|
consumerAccountId: posInfo.consumer_account_id,
|
|
|
|
|
type: TspProviderRequestType.ORIGINAL,
|
|
|
|
|
})
|
2026-02-24 12:42:10 +03:30
|
|
|
|
2026-05-05 22:42:09 +03:30
|
|
|
if (data.send_to_tsp) {
|
2026-05-08 18:09:13 +03:30
|
|
|
const providerResponse = await this.salesInvoiceTaxService.originalSend(
|
|
|
|
|
salesInvoice.id,
|
|
|
|
|
posInfo.pos_id,
|
|
|
|
|
)
|
|
|
|
|
if (providerResponse?.invoice) {
|
|
|
|
|
salesInvoice = providerResponse?.invoice as any
|
|
|
|
|
}
|
2026-04-30 16:27:46 +03:30
|
|
|
}
|
2026-02-24 12:42:10 +03:30
|
|
|
|
2026-05-05 22:42:09 +03:30
|
|
|
return ResponseMapper.create(salesInvoice)
|
2026-04-30 16:27:46 +03:30
|
|
|
}
|
|
|
|
|
|
2026-05-05 10:14:48 +03:30
|
|
|
async send(invoiceId: string, posInfo: IPosPayload) {
|
2026-05-10 14:14:01 +03:30
|
|
|
const invoice = await this.sharedSaleInvoiceActionsService.send(
|
|
|
|
|
posInfo.consumer_account_id,
|
2026-05-10 09:44:49 +03:30
|
|
|
posInfo.pos_id,
|
|
|
|
|
invoiceId,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return ResponseMapper.single(invoice)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async retry(invoiceId: string, posInfo: IPosPayload) {
|
2026-05-10 14:14:01 +03:30
|
|
|
const invoice = await this.sharedSaleInvoiceActionsService.retry(
|
|
|
|
|
posInfo.consumer_account_id,
|
2026-05-05 10:14:48 +03:30
|
|
|
posInfo.pos_id,
|
2026-05-05 22:42:09 +03:30
|
|
|
invoiceId,
|
2026-05-05 10:14:48 +03:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return ResponseMapper.single(invoice)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async revoke(invoiceId: string, posInfo: IPosPayload) {
|
2026-05-05 22:42:09 +03:30
|
|
|
const { consumer_account_id, pos_id, business_id, complex_id } = posInfo
|
|
|
|
|
|
2026-05-10 14:14:01 +03:30
|
|
|
const invoice = await this.sharedSaleInvoiceActionsService.revoke(
|
2026-05-05 22:42:09 +03:30
|
|
|
consumer_account_id,
|
|
|
|
|
pos_id,
|
|
|
|
|
complex_id,
|
|
|
|
|
business_id,
|
|
|
|
|
invoiceId,
|
|
|
|
|
)
|
2026-05-05 10:14:48 +03:30
|
|
|
|
|
|
|
|
return ResponseMapper.single(invoice)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 19:53:00 +03:30
|
|
|
async correction(
|
|
|
|
|
invoiceId: string,
|
|
|
|
|
posInfo: IPosPayload,
|
|
|
|
|
data: PosCorrectionSalesInvoiceDto,
|
|
|
|
|
) {
|
|
|
|
|
const { consumer_account_id, pos_id, business_id, complex_id } = posInfo
|
|
|
|
|
|
|
|
|
|
const invoice = await this.sharedSaleInvoiceActionsService.correction(
|
|
|
|
|
data,
|
|
|
|
|
consumer_account_id,
|
|
|
|
|
pos_id,
|
|
|
|
|
complex_id,
|
|
|
|
|
business_id,
|
|
|
|
|
invoiceId,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return ResponseMapper.single(invoice)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async return(invoiceId: string, posInfo: IPosPayload) {
|
|
|
|
|
const { consumer_account_id, pos_id, business_id, complex_id } = posInfo
|
|
|
|
|
|
|
|
|
|
const invoice = await this.sharedSaleInvoiceActionsService.revoke(
|
|
|
|
|
consumer_account_id,
|
|
|
|
|
pos_id,
|
|
|
|
|
complex_id,
|
|
|
|
|
business_id,
|
|
|
|
|
invoiceId,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return ResponseMapper.single(invoice)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 10:14:48 +03:30
|
|
|
async inquiry(consumer_account_id: string, pos_id: string, invoiceId: string) {
|
2026-05-10 14:14:01 +03:30
|
|
|
const invoice = await this.sharedSaleInvoiceActionsService.inquiry(
|
|
|
|
|
consumer_account_id,
|
|
|
|
|
pos_id,
|
|
|
|
|
invoiceId,
|
|
|
|
|
)
|
2026-05-05 10:14:48 +03:30
|
|
|
return ResponseMapper.single(invoice)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 19:43:59 +03:30
|
|
|
private buildFindAllWhere(posInfo: IPosPayload, query: SalesInvoicesFilterDto) {
|
|
|
|
|
const where: Prisma.SalesInvoiceWhereInput = {
|
|
|
|
|
pos: {
|
2026-05-05 22:42:09 +03:30
|
|
|
id: posInfo.pos_id,
|
2026-05-01 19:43:59 +03:30
|
|
|
complex: {
|
|
|
|
|
business_activity_id: posInfo.business_id,
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-06-09 13:31:58 +03:30
|
|
|
referenced_by: null,
|
2026-05-01 19:43:59 +03:30
|
|
|
}
|
|
|
|
|
|
2026-05-30 19:05:56 +03:30
|
|
|
if (query.invoice_number) {
|
|
|
|
|
where.invoice_number = parseInt(query.invoice_number + '')
|
2026-05-01 19:43:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (query.invoice_date_from || query.invoice_date_to) {
|
|
|
|
|
where.invoice_date = {
|
|
|
|
|
...(query.invoice_date_from ? { gte: new Date(query.invoice_date_from) } : {}),
|
|
|
|
|
...(query.invoice_date_to ? { lte: new Date(query.invoice_date_to) } : {}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (query.created_at_from || query.created_at_to) {
|
|
|
|
|
where.created_at = {
|
|
|
|
|
...(query.created_at_from ? { gte: new Date(query.created_at_from) } : {}),
|
|
|
|
|
...(query.created_at_to ? { lte: new Date(query.created_at_to) } : {}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
query.total_amount !== undefined ||
|
|
|
|
|
query.total_amount_from !== undefined ||
|
|
|
|
|
query.total_amount_to !== undefined
|
|
|
|
|
) {
|
|
|
|
|
where.total_amount = {
|
|
|
|
|
...(query.total_amount !== undefined ? { equals: query.total_amount } : {}),
|
|
|
|
|
...(query.total_amount_from !== undefined
|
|
|
|
|
? { gte: query.total_amount_from }
|
|
|
|
|
: {}),
|
|
|
|
|
...(query.total_amount_to !== undefined ? { lte: query.total_amount_to } : {}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
query.customer_name?.trim() ||
|
|
|
|
|
query.customer_mobile?.trim() ||
|
|
|
|
|
query.customer_national_id?.trim() ||
|
|
|
|
|
query.customer_economic_code?.trim()
|
|
|
|
|
) {
|
|
|
|
|
where.customer = {
|
|
|
|
|
is: {
|
|
|
|
|
OR: [
|
|
|
|
|
...(query.customer_name?.trim()
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
individual: {
|
|
|
|
|
is: {
|
|
|
|
|
OR: [
|
|
|
|
|
{
|
|
|
|
|
first_name: {
|
|
|
|
|
contains: query.customer_name.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
last_name: {
|
|
|
|
|
contains: query.customer_name.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
legal: {
|
|
|
|
|
is: {
|
|
|
|
|
name: {
|
|
|
|
|
contains: query.customer_name.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
...(query.customer_mobile?.trim()
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
individual: {
|
|
|
|
|
is: {
|
|
|
|
|
mobile_number: {
|
|
|
|
|
contains: query.customer_mobile.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
...(query.customer_national_id?.trim()
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
individual: {
|
|
|
|
|
is: {
|
|
|
|
|
national_id: {
|
|
|
|
|
contains: query.customer_national_id.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
...(query.customer_economic_code?.trim()
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
legal: {
|
|
|
|
|
is: {
|
|
|
|
|
OR: [
|
|
|
|
|
{
|
|
|
|
|
economic_code: {
|
|
|
|
|
contains: query.customer_economic_code.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
registration_number: {
|
|
|
|
|
contains: query.customer_economic_code.trim(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (query.status) {
|
2026-05-03 16:23:17 +03:30
|
|
|
if (query.status === TspProviderResponseStatus.NOT_SEND) {
|
2026-06-09 13:31:58 +03:30
|
|
|
where.OR = [
|
|
|
|
|
{
|
|
|
|
|
last_tsp_status: null,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
last_tsp_status: TspProviderResponseStatus.NOT_SEND,
|
2026-05-01 19:43:59 +03:30
|
|
|
},
|
2026-06-09 13:31:58 +03:30
|
|
|
]
|
|
|
|
|
} else {
|
|
|
|
|
where.last_tsp_status = query.status
|
2026-05-01 19:43:59 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return where
|
|
|
|
|
}
|
2026-01-07 15:25:59 +03:30
|
|
|
}
|