set invoke in nama

This commit is contained in:
2026-05-04 11:21:49 +03:30
parent a486127ade
commit 6a48f203a9
17 changed files with 711 additions and 240 deletions
@@ -7,6 +7,8 @@ import {
} from 'generated/prisma/client'
import { CreateSalesInvoiceDto } from '../pos/sales-invoices/dto/create-sales-invoice.dto'
import {
TspProviderRevokePayloadDto,
TspProviderRevokeResponseDto,
TspProviderSendItemResultDto,
TspProviderSendPayloadDto,
} from './dto/provider-switch.dto'
@@ -239,25 +241,40 @@ export class SalesInvoiceTspService {
},
})
if (!lastAttempt || lastAttempt?.status === TspProviderResponseStatus.QUEUED) {
if (!lastAttempt) {
throw new NotFoundException('صورت‌حساب ارسالی فاکتور شما به سامانه یافت نشد.')
}
if (lastAttempt.status === TspProviderResponseStatus.QUEUED) {
throw new BadRequestException(
'فاکتور هنوز به سامانه مالیاتی ارسال نشده است یا در صف ارسال قرار دارد. لطفا بعدا تلاش کنید.',
)
}
let isUpdated = false
let isBackFromSale = false
for (let item of dataToUpdate.items) {
const lastAttemptInvoiceItem = lastAttempt.invoice.items.find(
prevItem => prevItem.good_id === item.good_id,
)
if (!lastAttemptInvoiceItem) {
isBackFromSale = true
break
const countByGoodId = (goodIds: string[]): Map<string, number> => {
const counts = new Map<string, number>()
for (const goodId of goodIds) {
counts.set(goodId, (counts.get(goodId) ?? 0) + 1)
}
return counts
}
if (dataToUpdate.total_amount !== lastAttempt.invoice.total_amount.toNumber()) {
isUpdated = true
const updatedGoodIds = dataToUpdate.items.map(item => item.good_id!)
const previousGoodIds = lastAttempt.invoice.items.map(item => item.good_id)
const updatedCounts = countByGoodId(updatedGoodIds)
const previousCounts = countByGoodId(previousGoodIds)
let isBackFromSale = false
if (updatedCounts.size !== previousCounts.size) {
isBackFromSale = true
} else {
for (const [goodId, count] of updatedCounts) {
if (previousCounts.get(goodId) !== count) {
isBackFromSale = true
break
}
}
}
const attempt = await this.prisma.saleInvoiceTspAttempts.create({
@@ -308,6 +325,62 @@ export class SalesInvoiceTspService {
}
}
async revoke(
pos_id: string,
invoice_id: string,
): Promise<TspProviderRevokeResponseDto> {
const attempt = await this.prisma.saleInvoiceTspAttempts.findFirst({
where: {
invoice_id,
},
orderBy: {
attempt_no: 'desc',
},
take: 1,
select: {
tax_id: true,
status: true,
attempt_no: true,
invoice: {
select: {
invoice_number: true,
},
},
},
})
if (!attempt) {
throw new NotFoundException('صورت‌حساب ارسالی فاکتور شما به سامانه یافت نشد.')
}
if (attempt.status === TspProviderResponseStatus.QUEUED) {
throw new BadRequestException(
'فاکتور هنوز به سامانه مالیاتی ارسال نشده است یا در صف ارسال قرار دارد. لطفا بعدا تلاش کنید.',
)
}
const payload = await this.buildRevokePayload(invoice_id, pos_id)
const result = await this.tspSwitchService.revoke(payload)
if (result) {
await this.prisma.saleInvoiceTspAttempts.create({
data: {
attempt_no: attempt.attempt_no + 1,
invoice_id,
status: TspProviderResponseStatus.REVOKED,
type: TspProviderRequestType.REVOKE,
received_at: new Date(),
request_payload: JSON.parse(JSON.stringify(result.request_payload)),
sent_at: result.sent_at ? new Date(result.sent_at) : new Date(),
response_payload: JSON.parse(JSON.stringify(result)),
tax_id: result.tax_id,
},
})
}
return result
}
private async getPosId(consumer_id: string, invoice_id: string): Promise<string> {
const now = new Date()
const saleInvoice = await this.prisma.salesInvoice.findUnique({
@@ -374,6 +447,94 @@ export class SalesInvoiceTspService {
return saleInvoice.pos.id
}
private async buildRevokePayload(
invoiceId: string,
posId: string,
): Promise<TspProviderRevokePayloadDto> {
const invoice = await this.prisma.salesInvoice.findUnique({
where: {
id: invoiceId,
pos_id: posId,
},
select: {
id: true,
code: true,
total_amount: true,
invoice_date: true,
invoice_number: true,
tsp_attempts: {
orderBy: {
created_at: 'asc',
},
take: 1,
select: {
id: true,
status: true,
},
},
pos: {
select: {
complex: {
select: {
business_activity: {
select: {
fiscal_id: true,
economic_code: true,
partner_token: true,
guild: {
select: {
invoice_template: true,
},
},
consumer: {
select: {
legal: {
select: {
partner: {
select: {
tsp_provider: true,
},
},
},
},
individual: {
select: {
partner: {
select: {
tsp_provider: true,
},
},
},
},
},
},
},
},
},
},
},
},
},
})
if (!invoice) {
throw new NotFoundException('فاکتور مورد نظر یافت نشد.')
}
const { partner } = (invoice.pos.complex.business_activity.consumer.legal ||
invoice.pos.complex.business_activity.consumer.individual)!
return {
invoice_id: invoice.id,
invoice_number: invoice.invoice_number,
economic_code: invoice.pos.complex.business_activity.economic_code,
fiscal_id: invoice.pos.complex.business_activity.fiscal_id,
tsp_token: invoice.pos.complex.business_activity.partner_token,
tsp_provider: partner.tsp_provider!,
last_attempt_tax_id: invoice.tsp_attempts[0].id,
}
}
private async buildPayload(
invoiceId: string,
posId: string,