update return from sale invoices jurney and set nama return from sale
This commit is contained in:
@@ -44,6 +44,12 @@ export const summarySelect: SalesInvoiceSelect = {
|
||||
invoice_number: true,
|
||||
},
|
||||
},
|
||||
referenced_by: {
|
||||
select: {
|
||||
id: true,
|
||||
invoice_number: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const select: SalesInvoiceSelect = {
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
import { SharedSaleInvoiceAccessService } from '@/common/services/saleInvoices/sale-invoice-access.service'
|
||||
import { PosCorrectionSalesInvoiceDto } from '@/modules/pos/sales-invoices/dto/create-sales-invoice.dto'
|
||||
import {
|
||||
PosCorrectionSalesInvoiceDto,
|
||||
PosReturnSalesInvoiceDto,
|
||||
} from '@/modules/pos/sales-invoices/dto/create-sales-invoice.dto'
|
||||
import { SalesInvoiceTspService } from '@/modules/tspProviders/sales-invoice-tsp.service'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { PrismaService } from '@/prisma/prisma.service'
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'
|
||||
import {
|
||||
CustomerType,
|
||||
TspProviderRequestType,
|
||||
TspProviderResponseStatus,
|
||||
} from '@prisma/client'
|
||||
import { SharedSaleInvoiceCreateService } from './sale-invoice-create.service'
|
||||
|
||||
@Injectable()
|
||||
export class SharedSaleInvoiceActionsService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private readonly salesInvoiceTspService: SalesInvoiceTspService,
|
||||
private readonly saleInvoiceAccessService: SharedSaleInvoiceAccessService,
|
||||
private sharedSaleInvoiceCreateService: SharedSaleInvoiceCreateService,
|
||||
) {}
|
||||
|
||||
async send(consumerAccountId: string, posId: string, invoiceId: string) {
|
||||
@@ -58,14 +70,464 @@ export class SharedSaleInvoiceActionsService {
|
||||
consumerAccountId,
|
||||
posId,
|
||||
)
|
||||
return this.salesInvoiceTspService.correctionSend(
|
||||
|
||||
const newInvoice = await this.prisma.$transaction(async tx => {
|
||||
const relatedInvoice = await tx.salesInvoice.findUnique({
|
||||
where: {
|
||||
id: invoiceId,
|
||||
pos_id: posId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
invoice_date: true,
|
||||
settlement_type: true,
|
||||
customer_id: true,
|
||||
main_id: true,
|
||||
last_tsp_status: true,
|
||||
referenced_by: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
customer: {
|
||||
select: {
|
||||
type: true,
|
||||
},
|
||||
},
|
||||
total_amount: true,
|
||||
items: {
|
||||
select: {
|
||||
id: true,
|
||||
good_id: true,
|
||||
service_id: true,
|
||||
quantity: true,
|
||||
unit_price: true,
|
||||
total_amount: true,
|
||||
discount_amount: true,
|
||||
tax_amount: true,
|
||||
payload: true,
|
||||
notes: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!relatedInvoice) {
|
||||
throw new NotFoundException('صورتحساب مورد نظر شما یافت نشد.')
|
||||
}
|
||||
|
||||
if (relatedInvoice.last_tsp_status !== TspProviderResponseStatus.SUCCESS) {
|
||||
throw new BadRequestException('امکان اصلاح این صورتحساب وجود ندارد.')
|
||||
}
|
||||
|
||||
if (relatedInvoice.referenced_by) {
|
||||
throw new BadRequestException(
|
||||
'این صورتحساب قبلا با یک صورتحساب دیگر جایگزین شده است.',
|
||||
)
|
||||
}
|
||||
|
||||
const normalizedInvoiceDate = this.normalizeInvoiceDate(data.invoice_date)
|
||||
const originalInvoiceDate = this.normalizeInvoiceDate(relatedInvoice.invoice_date)
|
||||
|
||||
const originalItemsByKey = new Map(
|
||||
relatedInvoice.items.map(item => [this.getItemKey(item), item]),
|
||||
)
|
||||
|
||||
const usedItemKeys = new Set<string>()
|
||||
const normalizedItems = data.items.map(item => {
|
||||
const itemKey = this.getItemKey(item)
|
||||
|
||||
if (!itemKey) {
|
||||
throw new BadRequestException('هر قلم باید شناسه کالا یا خدمت داشته باشد.')
|
||||
}
|
||||
|
||||
usedItemKeys.add(itemKey)
|
||||
|
||||
const originalItem = originalItemsByKey.get(itemKey)
|
||||
|
||||
if (!originalItem) {
|
||||
throw new BadRequestException(
|
||||
'فقط اقلام موجود در صورتحساب اصلی قابل اصلاح هستند.',
|
||||
)
|
||||
}
|
||||
|
||||
const originalQuantity = Number(originalItem.quantity)
|
||||
const requestedQuantity = Number(item.quantity)
|
||||
const originalUnitPrice = Number(originalItem.unit_price)
|
||||
const requestedUnitPrice = Number(item.unit_price)
|
||||
const originalTotalAmount = this.roundAmount(Number(originalItem.total_amount))
|
||||
const requestedTotalAmount = this.roundAmount(Number(item.total_amount))
|
||||
|
||||
if (requestedQuantity <= 0) {
|
||||
throw new BadRequestException(
|
||||
'تعداد/مقدار اقلام اصلاحی باید بیشتر از صفر باشد.',
|
||||
)
|
||||
}
|
||||
|
||||
// if (requestedUnitPrice < originalUnitPrice) {
|
||||
// throw new BadRequestException(
|
||||
// 'مبلغ واحد اقلام اصلاحی نمیتواند کمتر از صورتحساب اصلی باشد.',
|
||||
// )
|
||||
// }
|
||||
|
||||
return {
|
||||
invoice_id: originalItem.id,
|
||||
good_id: originalItem.good_id,
|
||||
service_id: originalItem.service_id || undefined,
|
||||
quantity: requestedQuantity,
|
||||
unit_price: requestedUnitPrice,
|
||||
total_amount: requestedTotalAmount,
|
||||
discount_amount: this.roundAmount(Number(item.discount_amount || 0)),
|
||||
tax_amount: this.roundAmount(Number(item.tax_amount || 0)),
|
||||
payload: item.payload ? JSON.parse(JSON.stringify(item.payload)) : undefined,
|
||||
notes: item.notes || undefined,
|
||||
}
|
||||
})
|
||||
|
||||
if (usedItemKeys.size !== relatedInvoice.items.length) {
|
||||
throw new BadRequestException(
|
||||
'فقط اقلام موجود در صورتحساب اصلی قابل اصلاح هستند.',
|
||||
)
|
||||
}
|
||||
|
||||
const hasChangedQuantity = normalizedItems.some(item => {
|
||||
const originalItem = relatedInvoice.items.find(
|
||||
relatedItem => this.getItemKey(relatedItem) === this.getItemKey(item),
|
||||
)
|
||||
|
||||
if (!originalItem) {
|
||||
return false
|
||||
}
|
||||
|
||||
return Number(originalItem.quantity) !== Number(item.quantity)
|
||||
})
|
||||
const hasChangedAmount = normalizedItems.some(item => {
|
||||
const originalItem = relatedInvoice.items.find(
|
||||
relatedItem => this.getItemKey(relatedItem) === this.getItemKey(item),
|
||||
)
|
||||
|
||||
if (!originalItem) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
this.roundAmount(Number(originalItem.total_amount)) !==
|
||||
this.roundAmount(Number(item.total_amount))
|
||||
)
|
||||
})
|
||||
const hasChangedInvoiceDate = normalizedInvoiceDate !== originalInvoiceDate
|
||||
|
||||
if (!hasChangedQuantity && !hasChangedAmount && !hasChangedInvoiceDate) {
|
||||
throw new BadRequestException('مقداری تغییر نکرده است.')
|
||||
}
|
||||
|
||||
const requestedTotalAmount = this.roundAmount(Number(data.total_amount))
|
||||
const originalTotalAmount = this.roundAmount(Number(relatedInvoice.total_amount))
|
||||
const totalDiff = this.roundAmount(requestedTotalAmount - originalTotalAmount)
|
||||
const paymentsAmount = this.roundAmount(this.getPaymentsAmount(data.payments))
|
||||
|
||||
if (totalDiff > 0 && paymentsAmount !== totalDiff) {
|
||||
throw new BadRequestException(
|
||||
'جمع پرداختی باید برابر با اختلاف مبلغ صورتحساب اصلاحی و صورتحساب مرجع باشد.',
|
||||
)
|
||||
}
|
||||
|
||||
if (totalDiff === 0 && paymentsAmount !== 0) {
|
||||
throw new BadRequestException('در صورت عدم افزایش مبلغ، پرداختی نباید ثبت شود.')
|
||||
}
|
||||
|
||||
const newInvoice = await this.sharedSaleInvoiceCreateService.create({
|
||||
tx,
|
||||
data: {
|
||||
invoice_date: new Date(normalizedInvoiceDate),
|
||||
items: normalizedItems as any,
|
||||
total_amount: requestedTotalAmount,
|
||||
discount_amount: this.roundAmount(Number(data.discount_amount)),
|
||||
tax_amount: this.roundAmount(Number(data.tax_amount)),
|
||||
customer_id: relatedInvoice.customer_id || undefined,
|
||||
customer_type: relatedInvoice.customer?.type || CustomerType.UNKNOWN,
|
||||
settlement_type: relatedInvoice.settlement_type,
|
||||
payments: data.payments,
|
||||
} as any,
|
||||
businessId,
|
||||
complexId,
|
||||
posId,
|
||||
consumerAccountId,
|
||||
main_invoice_id: relatedInvoice.main_id || relatedInvoice.id,
|
||||
ref_invoice_id: relatedInvoice.id,
|
||||
type: TspProviderRequestType.CORRECTION,
|
||||
})
|
||||
|
||||
await tx.salesInvoice.update({
|
||||
where: {
|
||||
id: relatedInvoice.id,
|
||||
},
|
||||
data: {
|
||||
referenced_by: {
|
||||
connect: {
|
||||
id: newInvoice.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return newInvoice
|
||||
})
|
||||
|
||||
return this.salesInvoiceTspService.correctionSend(posId, newInvoice.id)
|
||||
}
|
||||
|
||||
async return(
|
||||
data: PosReturnSalesInvoiceDto,
|
||||
consumerAccountId: string,
|
||||
pos_id: string,
|
||||
complex_id: string,
|
||||
business_id: string,
|
||||
invoiceId: string,
|
||||
) {
|
||||
await this.saleInvoiceAccessService.getConsumerIdWithPosAccess(
|
||||
consumerAccountId,
|
||||
posId,
|
||||
complexId,
|
||||
businessId,
|
||||
invoiceId,
|
||||
data,
|
||||
pos_id,
|
||||
)
|
||||
|
||||
const newInvoice = await this.prisma.$transaction(async tx => {
|
||||
const relatedInvoice = await tx.salesInvoice.findUnique({
|
||||
where: {
|
||||
id: invoiceId,
|
||||
pos_id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
invoice_date: true,
|
||||
settlement_type: true,
|
||||
customer_id: true,
|
||||
main_id: true,
|
||||
last_tsp_status: true,
|
||||
referenced_by: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
customer: {
|
||||
select: {
|
||||
type: true,
|
||||
},
|
||||
},
|
||||
items: {
|
||||
select: {
|
||||
id: true,
|
||||
good_id: true,
|
||||
service_id: true,
|
||||
quantity: true,
|
||||
unit_price: true,
|
||||
total_amount: true,
|
||||
discount_amount: true,
|
||||
tax_amount: true,
|
||||
payload: true,
|
||||
notes: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!relatedInvoice) {
|
||||
throw new NotFoundException('صورتحساب مورد نظر شما یافت نشد.')
|
||||
}
|
||||
|
||||
if (relatedInvoice.last_tsp_status !== TspProviderResponseStatus.SUCCESS) {
|
||||
throw new BadRequestException('امکان برگشت از خرید روی این صورتحساب وجود ندارد.')
|
||||
}
|
||||
|
||||
if (relatedInvoice.referenced_by) {
|
||||
throw new BadRequestException(
|
||||
'این صورتحساب قبلا با یک صورتحساب دیگر جایگزین شده است.',
|
||||
)
|
||||
}
|
||||
|
||||
const normalizedInvoiceDate = this.normalizeInvoiceDate(data.invoice_date)
|
||||
const originalInvoiceDate = this.normalizeInvoiceDate(relatedInvoice.invoice_date)
|
||||
|
||||
const originalItemsByKey = new Map(
|
||||
relatedInvoice.items.map(item => [this.getReturnItemKey(item), item]),
|
||||
)
|
||||
|
||||
const usedItemKeys = new Set<string>()
|
||||
const normalizedItems = data.items.map(item => {
|
||||
const itemKey = this.getReturnItemKey(item)
|
||||
|
||||
if (!itemKey) {
|
||||
throw new BadRequestException('هر قلم باید شناسه کالا یا خدمت داشته باشد.')
|
||||
}
|
||||
|
||||
// if (usedItemKeys.has(itemKey)) {
|
||||
// throw new BadRequestException('اقلام تکراری در صورتحساب بازگشتی مجاز نیستند.')
|
||||
// }
|
||||
|
||||
usedItemKeys.add(itemKey)
|
||||
|
||||
const originalItem = originalItemsByKey.get(itemKey)
|
||||
|
||||
if (!originalItem) {
|
||||
throw new BadRequestException(
|
||||
'فقط اقلام موجود در صورتحساب اصلی قابل بازگشت هستند.',
|
||||
)
|
||||
}
|
||||
|
||||
const originalQuantity = Number(originalItem.quantity)
|
||||
const requestedQuantity = Number(item.quantity)
|
||||
|
||||
if (requestedQuantity <= 0) {
|
||||
throw new BadRequestException(
|
||||
'تعداد/مقدار اقلام بازگشتی باید بیشتر از صفر باشد.',
|
||||
)
|
||||
}
|
||||
|
||||
if (requestedQuantity > originalQuantity) {
|
||||
throw new BadRequestException(
|
||||
'تعداد/مقدار اقلام بازگشتی نمیتواند از مقدار صورتحساب اصلی بیشتر باشد.',
|
||||
)
|
||||
}
|
||||
|
||||
const quantityRatio = requestedQuantity / originalQuantity
|
||||
|
||||
return {
|
||||
invoice_id: originalItem.id,
|
||||
good_id: originalItem.good_id,
|
||||
service_id: originalItem.service_id || undefined,
|
||||
quantity: requestedQuantity,
|
||||
unit_price: Number(originalItem.unit_price),
|
||||
total_amount: this.roundAmount(
|
||||
Number(originalItem.total_amount) * quantityRatio,
|
||||
),
|
||||
discount_amount: this.roundAmount(
|
||||
Number(originalItem.discount_amount || 0) * quantityRatio,
|
||||
),
|
||||
tax_amount: this.roundAmount(
|
||||
Number(originalItem.tax_amount || 0) * quantityRatio,
|
||||
),
|
||||
payload: originalItem.payload
|
||||
? JSON.parse(JSON.stringify(originalItem.payload))
|
||||
: undefined,
|
||||
notes: originalItem.notes || undefined,
|
||||
}
|
||||
})
|
||||
|
||||
if (!normalizedItems.length) {
|
||||
throw new BadRequestException(
|
||||
'حداقل یک قلم باید در صورتحساب بازگشتی باقی بماند.',
|
||||
)
|
||||
}
|
||||
|
||||
const hasRemovedItem = relatedInvoice.items.some(
|
||||
item => !usedItemKeys.has(this.getReturnItemKey(item)),
|
||||
)
|
||||
const hasChangedQuantity = normalizedItems.some(item => {
|
||||
const originalItem = relatedInvoice.items.find(
|
||||
relatedItem =>
|
||||
this.getReturnItemKey(relatedItem) === this.getReturnItemKey(item),
|
||||
)
|
||||
|
||||
if (!originalItem) {
|
||||
return false
|
||||
}
|
||||
|
||||
return Number(originalItem.quantity) !== Number(item.quantity)
|
||||
})
|
||||
const hasChangedInvoiceDate = normalizedInvoiceDate !== originalInvoiceDate
|
||||
|
||||
if (!hasRemovedItem && !hasChangedQuantity && !hasChangedInvoiceDate) {
|
||||
throw new BadRequestException('مقداری تغییر نکرده است.')
|
||||
}
|
||||
|
||||
const totalAmount = normalizedItems.reduce(
|
||||
(sum, item) => sum + Number(item.total_amount),
|
||||
0,
|
||||
)
|
||||
const discountAmount = normalizedItems.reduce(
|
||||
(sum, item) => sum + Number(item.discount_amount || 0),
|
||||
0,
|
||||
)
|
||||
const taxAmount = normalizedItems.reduce(
|
||||
(sum, item) => sum + Number(item.tax_amount || 0),
|
||||
0,
|
||||
)
|
||||
|
||||
const newInvoice = await this.sharedSaleInvoiceCreateService.create({
|
||||
tx,
|
||||
data: {
|
||||
invoice_date: new Date(normalizedInvoiceDate),
|
||||
items: normalizedItems as any,
|
||||
total_amount: totalAmount,
|
||||
discount_amount: discountAmount,
|
||||
tax_amount: taxAmount,
|
||||
customer_id: relatedInvoice.customer_id || undefined,
|
||||
customer_type: relatedInvoice.customer?.type || CustomerType.UNKNOWN,
|
||||
settlement_type: relatedInvoice.settlement_type,
|
||||
payments: undefined,
|
||||
} as any,
|
||||
businessId: business_id,
|
||||
complexId: complex_id,
|
||||
posId: pos_id,
|
||||
consumerAccountId,
|
||||
main_invoice_id: relatedInvoice.main_id || relatedInvoice.id,
|
||||
ref_invoice_id: relatedInvoice.id,
|
||||
type: TspProviderRequestType.RETURN,
|
||||
})
|
||||
|
||||
await tx.salesInvoice.update({
|
||||
where: {
|
||||
id: relatedInvoice.id,
|
||||
},
|
||||
data: {
|
||||
referenced_by: {
|
||||
connect: {
|
||||
id: newInvoice.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return newInvoice
|
||||
})
|
||||
|
||||
return this.salesInvoiceTspService.returnFromSaleSend(
|
||||
pos_id,
|
||||
business_id,
|
||||
newInvoice.id,
|
||||
)
|
||||
}
|
||||
|
||||
private normalizeInvoiceDate(invoiceDate: Date | string) {
|
||||
return new Date(invoiceDate).toISOString()
|
||||
}
|
||||
|
||||
private getItemKey(item: { good_id?: string | null; service_id?: string | null }) {
|
||||
return item.good_id || item.service_id || ''
|
||||
}
|
||||
|
||||
private getPaymentsAmount(payments?: {
|
||||
terminals?: { amount?: number }
|
||||
cash?: number
|
||||
set_off?: number
|
||||
card?: number
|
||||
bank?: number
|
||||
check?: number
|
||||
other?: number
|
||||
}) {
|
||||
return (
|
||||
Number(payments?.terminals?.amount || 0) +
|
||||
Number(payments?.cash || 0) +
|
||||
Number(payments?.set_off || 0) +
|
||||
Number(payments?.card || 0) +
|
||||
Number(payments?.bank || 0) +
|
||||
Number(payments?.check || 0) +
|
||||
Number(payments?.other || 0)
|
||||
)
|
||||
}
|
||||
|
||||
private roundAmount(amount: number) {
|
||||
return Number(amount.toFixed(2))
|
||||
}
|
||||
|
||||
async inquiry(consumerAccountId: string, posId: string, invoiceId: string) {
|
||||
@@ -75,4 +537,11 @@ export class SharedSaleInvoiceActionsService {
|
||||
)
|
||||
return this.salesInvoiceTspService.get(invoiceId, posId, consumerId)
|
||||
}
|
||||
|
||||
private getReturnItemKey(item: {
|
||||
good_id?: string | null
|
||||
service_id?: string | null
|
||||
}) {
|
||||
return item.good_id || item.service_id || ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,11 @@ export class SharedCreateSalesInvoiceDto {
|
||||
@ValidateNested({ each: true })
|
||||
items: SharedCreateSalesInvoiceItemDto[]
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
ref_invoice_id?: string
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@@ -230,5 +235,13 @@ export class SharedCreateSalesInvoiceDto {
|
||||
|
||||
export class SharedCorrectionSalesInvoiceDto extends OmitType(
|
||||
SharedCreateSalesInvoiceDto,
|
||||
['customer', 'customer_id', 'send_to_tsp', 'customer_type', 'settlement_type'],
|
||||
['customer', 'customer_id', 'customer_type', 'settlement_type'],
|
||||
) {}
|
||||
|
||||
export class SharedReturnSalesInvoiceDto extends OmitType(SharedCreateSalesInvoiceDto, [
|
||||
'customer',
|
||||
'customer_id',
|
||||
'customer_type',
|
||||
'settlement_type',
|
||||
'payments',
|
||||
]) {}
|
||||
|
||||
@@ -58,7 +58,10 @@ export class SharedSaleInvoiceCreateService {
|
||||
} = input
|
||||
|
||||
const normalizedInvoiceDate = this.normalizeInvoiceDate(data.invoice_date)
|
||||
const payments = this.buildPaymentsData(data.payments, data.total_amount)
|
||||
const payments =
|
||||
type === TspProviderRequestType.ORIGINAL || data.payments
|
||||
? this.buildPaymentsData(data.payments, data.total_amount)
|
||||
: []
|
||||
|
||||
for (let attempt = 1; attempt <= this.createInvoiceRetries; attempt++) {
|
||||
try {
|
||||
@@ -86,7 +89,14 @@ export class SharedSaleInvoiceCreateService {
|
||||
select: { ...QUERY_CONSTANTS.SALE_INVOICE.select },
|
||||
})
|
||||
|
||||
await this.createPayments($tx, salesInvoice.id, payments, normalizedInvoiceDate)
|
||||
if (payments.length) {
|
||||
await this.createPayments(
|
||||
$tx,
|
||||
salesInvoice.id,
|
||||
payments,
|
||||
normalizedInvoiceDate,
|
||||
)
|
||||
}
|
||||
|
||||
return salesInvoice
|
||||
})
|
||||
@@ -468,7 +478,7 @@ export class SharedSaleInvoiceCreateService {
|
||||
return salesInvoiceData
|
||||
}
|
||||
|
||||
private async getNextInvoiceNumber(tx: Prisma.TransactionClient, businessId: string) {
|
||||
async getNextInvoiceNumber(tx: Prisma.TransactionClient, businessId: string) {
|
||||
const latestInvoice = await tx.salesInvoice.findFirst({
|
||||
where: {
|
||||
pos: {
|
||||
@@ -525,17 +535,26 @@ export class SharedSaleInvoiceCreateService {
|
||||
})
|
||||
|
||||
if (payment.method === PaymentMethodType.TERMINAL && payment.terminalInfo) {
|
||||
const {
|
||||
terminal_id,
|
||||
stan,
|
||||
rrn,
|
||||
transaction_date_time,
|
||||
customer_card_no,
|
||||
description,
|
||||
} = payment.terminalInfo
|
||||
await tx.salesInvoicePaymentTerminalInfo.create({
|
||||
data: {
|
||||
payment_id: createdPayment.id,
|
||||
terminal_id: payment.terminalInfo.terminal_id,
|
||||
stan: payment.terminalInfo.stan,
|
||||
rrn: payment.terminalInfo.rrn,
|
||||
transaction_date_time: new Date(
|
||||
payment.terminalInfo.transaction_date_time || '',
|
||||
),
|
||||
customer_card_no: payment.terminalInfo.customer_card_no || null,
|
||||
description: payment.terminalInfo.description || null,
|
||||
terminal_id,
|
||||
stan: stan,
|
||||
rrn: rrn,
|
||||
transaction_date_time: transaction_date_time
|
||||
? new Date(transaction_date_time)
|
||||
: new Date(),
|
||||
customer_card_no: '1234567890123456',
|
||||
// customer_card_no: customer_card_no || null,
|
||||
description: description || null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,51 @@ import {
|
||||
SharedCorrectionSalesInvoiceDto,
|
||||
SharedCreateSalesInvoiceDto,
|
||||
} from '@/common/services/saleInvoices/sale-invoice-create.dto'
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { Type } from 'class-transformer'
|
||||
import {
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsDateString,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
ValidateNested,
|
||||
} from 'class-validator'
|
||||
|
||||
export class PosCreateSalesInvoiceDto extends SharedCreateSalesInvoiceDto {}
|
||||
export class PosCorrectionSalesInvoiceDto extends SharedCorrectionSalesInvoiceDto {}
|
||||
|
||||
export class PosReturnSalesInvoiceItemDto {
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
good_id?: string
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
service_id?: string
|
||||
|
||||
@ApiProperty({ required: true, default: 1 })
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
quantity: number
|
||||
}
|
||||
|
||||
export class PosReturnSalesInvoiceDto {
|
||||
@ApiProperty({ required: true })
|
||||
@IsDateString(
|
||||
{ strict: true },
|
||||
{ message: 'invoice_date must be a valid ISO-8601 string' },
|
||||
)
|
||||
invoice_date: Date
|
||||
|
||||
@ApiProperty({ required: true, type: [PosReturnSalesInvoiceItemDto] })
|
||||
@IsArray()
|
||||
@ArrayMinSize(1)
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => PosReturnSalesInvoiceItemDto)
|
||||
items: PosReturnSalesInvoiceItemDto[]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { IPosPayload } from '@/common/models/posPayload.model'
|
||||
import {
|
||||
PosCorrectionSalesInvoiceDto,
|
||||
PosCreateSalesInvoiceDto,
|
||||
PosReturnSalesInvoiceDto,
|
||||
} from './dto/create-sales-invoice.dto'
|
||||
import { SalesInvoicesFilterDto } from './dto/sales-invoices-filter.dto'
|
||||
import { SalesInvoicesService } from './sales-invoices.service'
|
||||
@@ -60,9 +61,13 @@ export class SalesInvoicesController {
|
||||
) {
|
||||
return this.salesInvoicesService.correction(id, posInfo, data)
|
||||
}
|
||||
@Post(':id/return')
|
||||
return(@Param('id') id: string, @PosInfo() posInfo: IPosPayload) {
|
||||
return this.salesInvoicesService.return(id, posInfo)
|
||||
@Post(':id/return_from_sale')
|
||||
return(
|
||||
@Param('id') id: string,
|
||||
@PosInfo() posInfo: IPosPayload,
|
||||
@Body() data: PosReturnSalesInvoiceDto,
|
||||
) {
|
||||
return this.salesInvoicesService.return(id, posInfo, data)
|
||||
}
|
||||
|
||||
// @Post('send/bulk')
|
||||
|
||||
@@ -13,6 +13,7 @@ import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.ser
|
||||
import {
|
||||
PosCorrectionSalesInvoiceDto,
|
||||
PosCreateSalesInvoiceDto,
|
||||
PosReturnSalesInvoiceDto,
|
||||
} from './dto/create-sales-invoice.dto'
|
||||
import { SalesInvoicesFilterDto } from './dto/sales-invoices-filter.dto'
|
||||
|
||||
@@ -172,10 +173,11 @@ export class SalesInvoicesService {
|
||||
return ResponseMapper.single(invoice)
|
||||
}
|
||||
|
||||
async return(invoiceId: string, posInfo: IPosPayload) {
|
||||
async return(invoiceId: string, posInfo: IPosPayload, data: PosReturnSalesInvoiceDto) {
|
||||
const { consumer_account_id, pos_id, business_id, complex_id } = posInfo
|
||||
|
||||
const invoice = await this.sharedSaleInvoiceActionsService.revoke(
|
||||
const invoice = await this.sharedSaleInvoiceActionsService.return(
|
||||
data,
|
||||
consumer_account_id,
|
||||
pos_id,
|
||||
complex_id,
|
||||
@@ -183,7 +185,7 @@ export class SalesInvoicesService {
|
||||
invoiceId,
|
||||
)
|
||||
|
||||
return ResponseMapper.single(invoice)
|
||||
return ResponseMapper.create(invoice)
|
||||
}
|
||||
|
||||
async inquiry(consumer_account_id: string, pos_id: string, invoiceId: string) {
|
||||
|
||||
@@ -46,8 +46,9 @@ export class TspProviderCorrectionInvoicePayloadDto {
|
||||
@IsObject()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => SharedCreateSalesInvoicePaymentsDto)
|
||||
payments: SharedCreateSalesInvoicePaymentsDto
|
||||
payments?: SharedCreateSalesInvoicePaymentsDto
|
||||
}
|
||||
|
||||
export class TspProviderCorrectionSendPayloadDto extends TspProviderOriginalSendPayloadDto {
|
||||
@ApiProperty({ required: true })
|
||||
@IsString()
|
||||
|
||||
@@ -3,4 +3,5 @@ export * from './correction.dto'
|
||||
export * from './get.dto'
|
||||
export * from './original.dto'
|
||||
export * from './provider-switch.dto'
|
||||
export * from './return.dto'
|
||||
export * from './revoke.dto'
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { SharedCreateSalesInvoiceItemDto } from '@/common/services/saleInvoices/sale-invoice-create.dto'
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { Type } from 'class-transformer'
|
||||
import {
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsDateString,
|
||||
IsNumber,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from 'class-validator'
|
||||
import {
|
||||
TspProviderOriginalResponseDto,
|
||||
TspProviderOriginalSendPayloadDto,
|
||||
} from './original.dto'
|
||||
|
||||
export class TspProviderReturnInvoicePayloadDto {
|
||||
@ApiProperty({ required: true, nullable: true })
|
||||
@IsDateString()
|
||||
invoice_date: Date
|
||||
|
||||
@ApiProperty({ type: [SharedCreateSalesInvoiceItemDto] })
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => SharedCreateSalesInvoiceItemDto)
|
||||
@ArrayMinSize(1)
|
||||
items: SharedCreateSalesInvoiceItemDto[]
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
total_amount: number
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
discount_amount: number
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
tax_amount: number
|
||||
}
|
||||
|
||||
export class TspProviderReturnSendPayloadDto extends TspProviderOriginalSendPayloadDto {
|
||||
@ApiProperty({ required: true })
|
||||
@IsString()
|
||||
last_tax_id: string
|
||||
}
|
||||
|
||||
export class TspProviderReturnSendResponseDto extends TspProviderOriginalResponseDto {}
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
TspProviderGetResponseDto,
|
||||
TspProviderOriginalResponseDto,
|
||||
TspProviderOriginalSendPayloadDto,
|
||||
TspProviderReturnSendPayloadDto,
|
||||
TspProviderReturnSendResponseDto,
|
||||
TspProviderRevokePayloadDto,
|
||||
TspProviderRevokeResponseDto,
|
||||
} from './dto'
|
||||
@@ -48,6 +50,14 @@ export class SalesInvoiceTspSwitchService {
|
||||
return adapter.correctionSend(payload)
|
||||
}
|
||||
|
||||
async returnFromSale(
|
||||
payload: TspProviderReturnSendPayloadDto,
|
||||
): Promise<TspProviderReturnSendResponseDto> {
|
||||
const adapter = this.resolveSwitch(payload.tsp_provider)
|
||||
|
||||
return adapter.returnSend(payload)
|
||||
}
|
||||
|
||||
async sendBulk(
|
||||
payloads: TspProviderOriginalSendPayloadDto[],
|
||||
): Promise<TspProviderBulkSendResultDto[]> {
|
||||
|
||||
@@ -6,17 +6,14 @@ import {
|
||||
TspProviderRequestType,
|
||||
TspProviderResponseStatus,
|
||||
} from 'generated/prisma/client'
|
||||
import {
|
||||
TspProviderActionResponseDto,
|
||||
TspProviderCorrectionInvoicePayloadDto,
|
||||
} from './dto'
|
||||
import { TspProviderActionResponseDto } from './dto'
|
||||
import { SalesInvoiceTspSwitchService } from './sales-invoice-tsp-switch.service'
|
||||
import {
|
||||
buildCorrectionPayload,
|
||||
buildOriginalPayload,
|
||||
buildReturnFromSalePayload,
|
||||
buildRevokePayload,
|
||||
getOriginalResendAttemptNumber,
|
||||
getRelatedInvoiceForCorrection,
|
||||
onResult,
|
||||
} from './utils/sales-invoice-tsp.utils'
|
||||
|
||||
@@ -176,60 +173,68 @@ export class SalesInvoiceTspService {
|
||||
}
|
||||
|
||||
async correctionSend(
|
||||
consumerAccountId: string,
|
||||
posId: string,
|
||||
complexId: string,
|
||||
businessId: string,
|
||||
ref_invoice_id: string,
|
||||
dataToUpdate: TspProviderCorrectionInvoicePayloadDto,
|
||||
invoice_id: string,
|
||||
): Promise<TspProviderActionResponseDto> {
|
||||
const [newInvoice, attempt, correctionPayload] = await this.prisma.$transaction(
|
||||
async tx => {
|
||||
const relatedInvoice = await getRelatedInvoiceForCorrection(tx, ref_invoice_id)
|
||||
const payload = await buildCorrectionPayload(this.prisma, invoice_id, posId)
|
||||
|
||||
const newInvoice = await this.sharedSaleInvoiceCreateService.create({
|
||||
tx,
|
||||
data: {
|
||||
customer_type: relatedInvoice.customer?.type || CustomerType.UNKNOWN,
|
||||
invoice_date: dataToUpdate.invoice_date,
|
||||
payments: dataToUpdate.payments,
|
||||
items: dataToUpdate.items,
|
||||
total_amount: dataToUpdate.total_amount,
|
||||
discount_amount: dataToUpdate.discount_amount,
|
||||
tax_amount: dataToUpdate.tax_amount,
|
||||
customer_id: relatedInvoice.customer_id || undefined,
|
||||
settlement_type: relatedInvoice.settlement_type,
|
||||
},
|
||||
businessId,
|
||||
complexId,
|
||||
posId,
|
||||
consumerAccountId,
|
||||
main_invoice_id: relatedInvoice.main_id || relatedInvoice.id,
|
||||
ref_invoice_id: relatedInvoice.id,
|
||||
type: TspProviderRequestType.CORRECTION,
|
||||
})
|
||||
const attemptNumber = 1
|
||||
|
||||
const correctionPayload = await buildCorrectionPayload(tx, newInvoice.id)
|
||||
|
||||
const attempt = await tx.saleInvoiceTspAttempts.create({
|
||||
data: {
|
||||
attempt_no: 1,
|
||||
invoice_id: newInvoice.id,
|
||||
message: 'در حال ارسال به سامانه مالیاتی...',
|
||||
status: TspProviderResponseStatus.QUEUED,
|
||||
sent_at: new Date().toISOString(),
|
||||
raw_request_payload: JSON.parse(JSON.stringify(correctionPayload)),
|
||||
provider_request_payload: {},
|
||||
},
|
||||
})
|
||||
|
||||
return [newInvoice, attempt, correctionPayload]
|
||||
const invoice = await this.prisma.salesInvoice.update({
|
||||
where: {
|
||||
id: invoice_id,
|
||||
},
|
||||
)
|
||||
data: {
|
||||
last_tsp_status: TspProviderResponseStatus.QUEUED,
|
||||
last_attempt_no: attemptNumber,
|
||||
type: TspProviderRequestType.CORRECTION,
|
||||
tsp_attempts: {
|
||||
create: {
|
||||
attempt_no: attemptNumber,
|
||||
provider_request_payload: {},
|
||||
status: TspProviderResponseStatus.QUEUED,
|
||||
raw_request_payload: JSON.parse(JSON.stringify(payload)),
|
||||
sent_at: new Date().toISOString(),
|
||||
message: 'در حال ارسال به سامانه مالیاتی...',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await this.tspSwitchService.correction(correctionPayload)
|
||||
const result = await this.tspSwitchService.send(payload)
|
||||
return await onResult(this.prisma, result, invoice.id)
|
||||
}
|
||||
|
||||
return await onResult(this.prisma, result, newInvoice.id)
|
||||
async returnFromSaleSend(
|
||||
pos_id: string,
|
||||
business_id: string,
|
||||
invoice_id: string,
|
||||
): Promise<TspProviderActionResponseDto> {
|
||||
const payload = await buildReturnFromSalePayload(this.prisma, invoice_id, pos_id)
|
||||
|
||||
const attemptNumber = 1
|
||||
|
||||
const invoice = await this.prisma.salesInvoice.update({
|
||||
where: {
|
||||
id: invoice_id,
|
||||
},
|
||||
data: {
|
||||
last_tsp_status: TspProviderResponseStatus.QUEUED,
|
||||
last_attempt_no: attemptNumber,
|
||||
tsp_attempts: {
|
||||
create: {
|
||||
attempt_no: attemptNumber,
|
||||
provider_request_payload: {},
|
||||
status: TspProviderResponseStatus.QUEUED,
|
||||
raw_request_payload: JSON.parse(JSON.stringify(payload)),
|
||||
sent_at: new Date().toISOString(),
|
||||
message: 'در حال ارسال به سامانه مالیاتی...',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await this.tspSwitchService.returnFromSale(payload)
|
||||
return await onResult(this.prisma, result, invoice.id)
|
||||
}
|
||||
|
||||
async revoke(
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from './common.dto'
|
||||
export * from './correction.dto'
|
||||
export * from './get.dto'
|
||||
export * from './original.dto'
|
||||
export * from './returnFromSale.dto'
|
||||
export * from './revoke.dto'
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { Type } from 'class-transformer'
|
||||
import { IsArray, IsString, Length, ValidateNested } from 'class-validator'
|
||||
import {
|
||||
NamaProviderCommonBodyDto,
|
||||
NamaProviderCommonHeaderDto,
|
||||
NamaProviderPaymentInfoDto,
|
||||
NamaProviderResponseDto,
|
||||
} from './common.dto'
|
||||
|
||||
export class NamaProviderReturnHeaderDto extends NamaProviderCommonHeaderDto {
|
||||
@ApiProperty({ required: true, description: 'موضوع صورتحساب (3- برگشت از فروش)' })
|
||||
@IsString()
|
||||
ins: '3'
|
||||
|
||||
@ApiProperty({
|
||||
required: true,
|
||||
description: 'شناسه مالیاتی صورتحساب مرجع',
|
||||
minLength: 22,
|
||||
maxLength: 22,
|
||||
})
|
||||
@IsString()
|
||||
@Length(22, 22)
|
||||
irtaxid: string
|
||||
}
|
||||
|
||||
export class NamaProviderReturnBodyItemDto extends NamaProviderCommonBodyDto {}
|
||||
|
||||
export class NamaProviderReturnRequestDto {
|
||||
@ApiProperty({ type: [NamaProviderReturnBodyItemDto] })
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => NamaProviderReturnBodyItemDto)
|
||||
body: NamaProviderReturnBodyItemDto[]
|
||||
|
||||
@ApiProperty({ type: [NamaProviderPaymentInfoDto] })
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => NamaProviderPaymentInfoDto)
|
||||
payment: NamaProviderPaymentInfoDto[]
|
||||
|
||||
@ApiProperty({ type: NamaProviderReturnHeaderDto })
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => NamaProviderReturnHeaderDto)
|
||||
header: NamaProviderReturnHeaderDto
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
uuid: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
economic_code: string
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
fiscal_id: string
|
||||
}
|
||||
|
||||
export class NamaProviderReturnResponseDto extends NamaProviderResponseDto {}
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
TspProviderGetResponseDto,
|
||||
TspProviderOriginalResponseDto,
|
||||
TspProviderOriginalSendPayloadDto,
|
||||
TspProviderReturnSendPayloadDto,
|
||||
TspProviderReturnSendResponseDto,
|
||||
TspProviderRevokePayloadDto,
|
||||
TspProviderRevokeResponseDto,
|
||||
} from '../../dto'
|
||||
@@ -27,6 +29,7 @@ import {
|
||||
NamaProviderRevokeRequestDto,
|
||||
NamaProviderRevokeResponseDto,
|
||||
} from './dto'
|
||||
import { NamaProviderReturnResponseDto } from './dto/returnFromSale.dto'
|
||||
import { NamaProviderUtils } from './nama-provider.util'
|
||||
|
||||
@Injectable()
|
||||
@@ -40,6 +43,7 @@ export class NamaProviderSwitchAdapter implements IProviderSwitchAdapter {
|
||||
|
||||
private readonly sendPath = '/api/v1/invoices/single-send'
|
||||
private readonly correctionPath = this.sendPath
|
||||
private readonly returnFromSalePath = this.sendPath
|
||||
private readonly sendBulkPath = '/api/v1/invoices'
|
||||
private readonly checkBulkStatus = '/api/v1/invoices/check-by-uuids'
|
||||
private readonly getPath = '/api/v1/invoices'
|
||||
@@ -63,6 +67,10 @@ export class NamaProviderSwitchAdapter implements IProviderSwitchAdapter {
|
||||
return `${this.baseUrl}${this.correctionPath}`
|
||||
}
|
||||
|
||||
private buildReturnFromSaleUrl() {
|
||||
return `${this.baseUrl}${this.returnFromSalePath}`
|
||||
}
|
||||
|
||||
private buildGetUrl(invoiceId: string) {
|
||||
return `${this.baseUrl}${this.getPath}/${invoiceId}`
|
||||
}
|
||||
@@ -176,6 +184,51 @@ export class NamaProviderSwitchAdapter implements IProviderSwitchAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async returnSend(
|
||||
payload: TspProviderReturnSendPayloadDto,
|
||||
): Promise<TspProviderReturnSendResponseDto> {
|
||||
const mappedRequest = this.namaProviderUtils.mapToNamaReturnDto(payload)
|
||||
|
||||
try {
|
||||
const response = await this.httpClient.request(
|
||||
this.buildReturnFromSaleUrl(),
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(mappedRequest),
|
||||
},
|
||||
this.createRequestInterceptors(payload.token),
|
||||
)
|
||||
const providerResponse: NamaProviderReturnResponseDto = await response.json()
|
||||
this.logger.debug('NAMA provider return response', providerResponse)
|
||||
|
||||
const result: TspProviderReturnSendResponseDto = {
|
||||
invoice_id: payload.id,
|
||||
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
|
||||
hasError: !response.ok,
|
||||
message: providerResponse.message,
|
||||
provider_response: JSON.parse(JSON.stringify(providerResponse)),
|
||||
received_at: new Date().toISOString(),
|
||||
tax_id: providerResponse.tax_id,
|
||||
status: this.namaProviderUtils.mapResponseStatus(
|
||||
providerResponse.status as NamaProviderResponseStatus,
|
||||
),
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
this.logger.error('NAMA return from sale send failed', err)
|
||||
const failure: TspProviderReturnSendResponseDto = {
|
||||
invoice_id: payload.id,
|
||||
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
|
||||
hasError: true,
|
||||
message: (err as Error).message,
|
||||
received_at: new Date().toISOString(),
|
||||
status: TspProviderResponseStatus.SEND_FAILURE,
|
||||
}
|
||||
return failure
|
||||
}
|
||||
}
|
||||
|
||||
async sendBulk(
|
||||
payloads: TspProviderOriginalSendPayloadDto[],
|
||||
): Promise<TspProviderBulkSendResultDto[]> {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
PaymentInfoDto,
|
||||
TspProviderCorrectionSendPayloadDto,
|
||||
TspProviderOriginalSendPayloadDto,
|
||||
TspProviderReturnSendPayloadDto,
|
||||
TspProviderRevokePayloadDto,
|
||||
} from '../../dto'
|
||||
import {
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
NamaProviderResponseStatus,
|
||||
NamaProviderRevokeRequestDto,
|
||||
} from './dto'
|
||||
import { NamaProviderReturnRequestDto } from './dto/returnFromSale.dto'
|
||||
|
||||
export class NamaProviderUtils {
|
||||
mapResponseStatus(status: NamaProviderResponseStatus): TspProviderResponseStatus {
|
||||
@@ -143,6 +145,57 @@ export class NamaProviderUtils {
|
||||
}
|
||||
}
|
||||
|
||||
mapToNamaReturnDto(
|
||||
payload: TspProviderReturnSendPayloadDto,
|
||||
): NamaProviderReturnRequestDto {
|
||||
return {
|
||||
uuid: payload.id,
|
||||
economic_code: payload.economic_code,
|
||||
fiscal_id: payload.fiscal_id,
|
||||
payment: this.mapPayments(payload.payments),
|
||||
header: {
|
||||
ins: this.mapRequestType(TspProviderRequestType.RETURN),
|
||||
inp: this.mapInvoiceTemplate(payload.template),
|
||||
inty: this.mapTspProviderCustomerType(payload.customer?.type),
|
||||
inno: payload.invoice_number.toString(),
|
||||
tins: '',
|
||||
indatim: payload.invoice_date.getTime() + '',
|
||||
irtaxid: payload.last_tax_id,
|
||||
...this.mapSettlementBased(
|
||||
payload.settlement_type,
|
||||
payload.payments,
|
||||
payload.total_amount,
|
||||
),
|
||||
...this.mapCustomerInfo(payload.customer),
|
||||
},
|
||||
body: payload.items.map(item => {
|
||||
let data: NamaProviderOriginalBodyItemDto = {
|
||||
sstid: item.sku,
|
||||
vra: item.sku_vat,
|
||||
fee: String(item.unit_price),
|
||||
dis: String(parseInt(item.discount_amount + '')),
|
||||
mu: item.measure_unit,
|
||||
am: String(item.quantity),
|
||||
consfee: '0',
|
||||
bros: '0',
|
||||
spro: '0',
|
||||
}
|
||||
|
||||
if (item.gold_type_payload) {
|
||||
const { profit = '0', commission = '0', wages = '0' } = item.gold_type_payload
|
||||
data = {
|
||||
...data,
|
||||
consfee: wages,
|
||||
bros: commission,
|
||||
spro: profit,
|
||||
tcpbs: (Number(profit) + Number(commission) + Number(wages)).toString(),
|
||||
}
|
||||
}
|
||||
return data
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
mapToNamaRevokeRequestDto(
|
||||
payload: TspProviderRevokePayloadDto,
|
||||
): NamaProviderRevokeRequestDto {
|
||||
|
||||
@@ -9,12 +9,13 @@ import {
|
||||
} from 'generated/prisma/client'
|
||||
import {
|
||||
goldTypePayload,
|
||||
TspProviderCorrectionSendPayloadDto,
|
||||
TspProviderGetResponseDto,
|
||||
TspProviderOriginalResponseDto,
|
||||
TspProviderOriginalSendPayloadDto,
|
||||
TspProviderReturnSendPayloadDto,
|
||||
TspProviderRevokePayloadDto,
|
||||
} from '../dto'
|
||||
import { TspProviderCorrectionSendPayloadDto } from '../dto/correction.dto'
|
||||
import { TspProviderActionResponseDto } from '../dto/provider-switch.dto'
|
||||
|
||||
export async function getOriginalResendAttemptNumber(
|
||||
@@ -160,10 +161,12 @@ export async function buildRevokePayload(
|
||||
export async function buildCorrectionPayload(
|
||||
tx: Prisma.TransactionClient,
|
||||
invoice_id: string,
|
||||
pos_id: string,
|
||||
): Promise<TspProviderCorrectionSendPayloadDto> {
|
||||
const invoice = await tx.salesInvoice.findUnique({
|
||||
where: {
|
||||
id: invoice_id,
|
||||
pos_id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
@@ -323,6 +326,217 @@ export async function buildCorrectionPayload(
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildReturnFromSalePayload(
|
||||
prisma: PrismaService,
|
||||
invoiceId: string,
|
||||
posId: string,
|
||||
): Promise<TspProviderReturnSendPayloadDto> {
|
||||
const invoice = await prisma.salesInvoice.findUnique({
|
||||
where: {
|
||||
id: invoiceId,
|
||||
pos_id: posId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
code: true,
|
||||
total_amount: true,
|
||||
invoice_date: true,
|
||||
invoice_number: true,
|
||||
settlement_type: true,
|
||||
discount_amount: true,
|
||||
tax_amount: true,
|
||||
tax_id: true,
|
||||
items: {
|
||||
select: {
|
||||
id: true,
|
||||
quantity: true,
|
||||
unit_price: true,
|
||||
total_amount: true,
|
||||
measure_unit_code: true,
|
||||
measure_unit_text: true,
|
||||
sku_code: true,
|
||||
sku_vat: true,
|
||||
good_id: true,
|
||||
service_id: true,
|
||||
discount_amount: true,
|
||||
tax_amount: true,
|
||||
payload: true,
|
||||
good_snapshot: 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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
unknown_customer: true,
|
||||
customer: {
|
||||
select: {
|
||||
type: true,
|
||||
individual: true,
|
||||
legal: true,
|
||||
},
|
||||
},
|
||||
payments: {
|
||||
select: {
|
||||
amount: true,
|
||||
payment_method: true,
|
||||
paid_at: true,
|
||||
terminal_info: {
|
||||
select: {
|
||||
stan: true,
|
||||
rrn: true,
|
||||
transaction_date_time: true,
|
||||
customer_card_no: true,
|
||||
terminal_id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
reference_invoice: {
|
||||
select: {
|
||||
tax_id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!invoice) {
|
||||
throw new NotFoundException('صورتحساب مورد نظر یافت نشد.')
|
||||
}
|
||||
if (!invoice.reference_invoice) {
|
||||
throw new NotFoundException('صورتحساب مرجع برای برگشت از فروش یافت نشد.')
|
||||
}
|
||||
|
||||
const {
|
||||
pos,
|
||||
id,
|
||||
invoice_number,
|
||||
invoice_date,
|
||||
total_amount,
|
||||
discount_amount,
|
||||
tax_amount,
|
||||
settlement_type,
|
||||
} = invoice
|
||||
const { business_activity: ba } = pos.complex
|
||||
|
||||
const unknown_customer = (invoice.unknown_customer || {}) as Record<string, string>
|
||||
|
||||
const { partner } = (ba.consumer.legal || ba.consumer.individual)!
|
||||
|
||||
return {
|
||||
id,
|
||||
invoice_number,
|
||||
invoice_date,
|
||||
settlement_type,
|
||||
total_amount: Number(total_amount),
|
||||
tax_amount: Number(tax_amount),
|
||||
discount_amount: Number(discount_amount),
|
||||
economic_code: ba.economic_code,
|
||||
fiscal_id: ba.fiscal_id,
|
||||
template: ba.guild.invoice_template,
|
||||
token: ba.partner_token,
|
||||
tsp_provider: partner.tsp_provider!,
|
||||
last_tax_id: invoice.reference_invoice.tax_id!,
|
||||
|
||||
payments: invoice.payments.map(payment => ({
|
||||
amount: Number(payment.amount),
|
||||
payment_method: payment.payment_method,
|
||||
paid_at: payment.paid_at,
|
||||
terminal_info: {
|
||||
card_number: payment.terminal_info?.customer_card_no
|
||||
? payment.terminal_info.customer_card_no
|
||||
: undefined,
|
||||
tracking_code: payment.terminal_info ? payment.terminal_info.rrn : undefined,
|
||||
},
|
||||
})),
|
||||
customer:
|
||||
invoice.customer && invoice.customer.type !== CustomerType.UNKNOWN
|
||||
? {
|
||||
type: invoice.customer.type,
|
||||
legal_info: invoice.customer.legal
|
||||
? {
|
||||
name: invoice.customer.legal.name ?? undefined,
|
||||
registration_number:
|
||||
invoice.customer.legal.registration_number ?? undefined,
|
||||
postal_code: invoice.customer.legal.postal_code ?? undefined,
|
||||
economic_code: invoice.customer.legal.economic_code ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
individual_info: invoice.customer.individual
|
||||
? {
|
||||
first_name: invoice.customer.individual.first_name ?? undefined,
|
||||
last_name: invoice.customer.individual.last_name ?? undefined,
|
||||
national_id: invoice.customer.individual.national_id ?? undefined,
|
||||
mobile_number: invoice.customer.individual.mobile_number ?? undefined,
|
||||
postal_code: invoice.customer.individual.postal_code ?? undefined,
|
||||
economic_code: invoice.customer.individual.economic_code ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
: {
|
||||
type: CustomerType.UNKNOWN,
|
||||
unknown_info: {
|
||||
name: unknown_customer?.name || '',
|
||||
economic_code: unknown_customer?.economic_code || '',
|
||||
},
|
||||
},
|
||||
items: invoice.items.map(item => ({
|
||||
invoice_item_id: item.id,
|
||||
quantity: Number(item.quantity),
|
||||
unit_price: Number(item.unit_price),
|
||||
total_amount: Number(item.total_amount),
|
||||
tax_amount: Number(tax_amount),
|
||||
discount_amount: Number(discount_amount),
|
||||
measure_unit: item.measure_unit_code,
|
||||
sku: item.sku_code,
|
||||
sku_vat: String(item.sku_vat),
|
||||
discount: String((item.payload as Record<string, any>)?.discount || '0'),
|
||||
good_id: item.good_id,
|
||||
service_id: item.service_id,
|
||||
good_snapshot: item.good_snapshot,
|
||||
gold_type_payload: isGoldTypePayload(item.payload) ? item.payload : undefined,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildOriginalPayload(
|
||||
prisma: PrismaService,
|
||||
invoiceId: string,
|
||||
|
||||
Reference in New Issue
Block a user