feat: implement correction and original send functionality for Nama provider
- Added new DTOs for correction requests and responses in `nama-provider.dto.ts`. - Updated `nama-provider.adapter.ts` to include `originalSend` and `correctionSend` methods. - Enhanced `nama-provider.util.ts` with mapping functions for correction requests. - Created operational guidelines for agents in `AGENT.md`. - Updated Prisma migrations to support new invoice types and relationships. - Introduced new service and DTO for creating sales invoices in `sale-invoice-create.service.ts` and `sale-invoice-create.dto.ts`. - Added utility for handling Prisma errors in `prisma-error.util.ts`.
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
import type { SaleInvoiceType } from '@/common/interfaces/sale-invoice-payload'
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { Type } from 'class-transformer'
|
||||
import {
|
||||
ArrayMinSize,
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
ValidateNested,
|
||||
} from 'class-validator'
|
||||
import type { CustomerIndividual, CustomerLegal } from 'generated/prisma/client'
|
||||
import { CustomerType } from 'generated/prisma/enums'
|
||||
|
||||
export class SharedCreateSalesInvoiceItemDto {
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true })
|
||||
unit_price: number
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true, default: 1 })
|
||||
quantity: number
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true })
|
||||
total_amount: number
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true })
|
||||
discount_amount: number
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ required: true })
|
||||
invoice_id: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
good_id?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
service_id?: string
|
||||
|
||||
// @IsEnum(SalesInvoiceItemPricingModel)
|
||||
// @ApiProperty({ enum: Object.values(SalesInvoiceItemPricingModel) })
|
||||
// @IsOptional()
|
||||
// pricingModel: SalesInvoiceItemPricingModel
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
@ApiProperty({ required: false, default: {} })
|
||||
payload: SaleInvoiceType
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false, default: '' })
|
||||
notes: string
|
||||
}
|
||||
|
||||
export class SharedCreateTerminalPayment {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
amount?: number
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ required: true })
|
||||
terminalId: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ required: true })
|
||||
stan: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiProperty({ required: true })
|
||||
rrn: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ required: true })
|
||||
response_code?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty({ required: true })
|
||||
customer_card_no?: string
|
||||
|
||||
@Type(() => Date)
|
||||
@IsDate()
|
||||
@ApiProperty({ required: true })
|
||||
transaction_date_time: Date
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
description?: string
|
||||
}
|
||||
|
||||
export class SharedCreateSalesInvoicePaymentsDto {
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => SharedCreateTerminalPayment)
|
||||
@ApiProperty({ required: false, type: () => SharedCreateTerminalPayment })
|
||||
terminals?: SharedCreateTerminalPayment
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
cash?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
set_off?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
card?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
bank?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
check?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
other?: number
|
||||
}
|
||||
|
||||
export class SharedCreateSalesInvoiceDto {
|
||||
// @TODO: totalAmount must calculated instead of get from api
|
||||
@IsNumber()
|
||||
@ApiProperty({ required: true, default: 0 })
|
||||
total_amount: number
|
||||
|
||||
@ApiProperty({ required: true })
|
||||
@IsDateString(
|
||||
{ strict: true },
|
||||
{ message: 'invoice_date must be a valid ISO-8601 string' },
|
||||
)
|
||||
invoice_date: Date
|
||||
|
||||
@ApiProperty({ required: true })
|
||||
@IsObject()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => SharedCreateSalesInvoicePaymentsDto)
|
||||
payments: SharedCreateSalesInvoicePaymentsDto
|
||||
|
||||
@ApiProperty()
|
||||
@ArrayMinSize(1)
|
||||
@ValidateNested({ each: true })
|
||||
items: SharedCreateSalesInvoiceItemDto[]
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
notes?: string
|
||||
|
||||
@ApiProperty({ required: false, default: false })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
send_to_tsp?: boolean
|
||||
|
||||
@ApiProperty({ required: true, default: CustomerType.UNKNOWN })
|
||||
@IsEnum(CustomerType)
|
||||
customer_type: CustomerType
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ required: false })
|
||||
customer_id?: string
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
customer?: {
|
||||
customer_individual?: Omit<CustomerIndividual, 'business_activity_id' | 'customer_id'>
|
||||
customer_legal?: Omit<CustomerLegal, 'business_activity_id' | 'customer_id'>
|
||||
customer_unknown?: {
|
||||
first_name: string
|
||||
last_name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
import { SalesInvoiceCreateInput } from '@/generated/prisma/models'
|
||||
import { PrismaService } from '@/prisma/prisma.service'
|
||||
import { BadRequestException, Injectable } from '@nestjs/common'
|
||||
import { Prisma } from 'generated/prisma/client'
|
||||
import {
|
||||
CustomerType,
|
||||
PaymentMethodType,
|
||||
TspProviderRequestType,
|
||||
} from 'generated/prisma/enums'
|
||||
import { SharedCreateSalesInvoiceDto } from './sale-invoice-create.dto'
|
||||
|
||||
interface TerminalPaymentInfo {
|
||||
terminalId: string
|
||||
stan: string
|
||||
rrn: string
|
||||
transactionDateTime: string | Date
|
||||
customerCardNO?: string
|
||||
description?: string
|
||||
amount?: number
|
||||
}
|
||||
|
||||
interface NormalizedPayment {
|
||||
method: PaymentMethodType
|
||||
amount: number
|
||||
}
|
||||
|
||||
interface CreateSharedSaleInvoiceInput {
|
||||
tx?: Prisma.TransactionClient
|
||||
data: SharedCreateSalesInvoiceDto
|
||||
businessId: string
|
||||
complexId: string
|
||||
posId: string
|
||||
consumerAccountId: string
|
||||
type: TspProviderRequestType
|
||||
main_invoice_id?: string
|
||||
ref_invoice_id?: string
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SharedSaleInvoiceCreateService {
|
||||
private readonly createInvoiceRetries = 3
|
||||
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async create(input: CreateSharedSaleInvoiceInput) {
|
||||
const {
|
||||
tx = this.prisma,
|
||||
data,
|
||||
businessId,
|
||||
complexId,
|
||||
posId,
|
||||
consumerAccountId,
|
||||
type,
|
||||
main_invoice_id,
|
||||
ref_invoice_id,
|
||||
} = input
|
||||
|
||||
const normalizedInvoiceDate = this.normalizeInvoiceDate(data.invoice_date)
|
||||
const { payments, terminalInfo } = this.buildPaymentsData(
|
||||
data.payments,
|
||||
data.total_amount,
|
||||
)
|
||||
|
||||
for (let attempt = 1; attempt <= this.createInvoiceRetries; attempt++) {
|
||||
try {
|
||||
return await tx.$transaction(async $tx => {
|
||||
const invoiceNumber = await this.getNextInvoiceNumber($tx, businessId)
|
||||
const customerId = await this.resolveCustomerId($tx, data, businessId)
|
||||
const goodsById = await this.getGoodsById($tx, data)
|
||||
const salesInvoiceData = this.buildSalesInvoiceData({
|
||||
data,
|
||||
normalizedInvoiceDate,
|
||||
invoiceNumber,
|
||||
consumerAccountId,
|
||||
businessId,
|
||||
complexId,
|
||||
posId,
|
||||
goodsById,
|
||||
customerId,
|
||||
type,
|
||||
main_invoice_id,
|
||||
ref_invoice_id,
|
||||
})
|
||||
|
||||
const salesInvoice = await $tx.salesInvoice.create({
|
||||
data: salesInvoiceData,
|
||||
})
|
||||
|
||||
await this.createPayments(
|
||||
$tx,
|
||||
salesInvoice.id,
|
||||
payments,
|
||||
terminalInfo,
|
||||
normalizedInvoiceDate,
|
||||
)
|
||||
|
||||
return salesInvoice
|
||||
})
|
||||
} catch (error) {
|
||||
if (
|
||||
this.isRetryableInvoiceConflict(error) &&
|
||||
attempt < this.createInvoiceRetries
|
||||
) {
|
||||
continue
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
throw new BadRequestException('ایجاد فاکتور با خطا مواجه شد.')
|
||||
}
|
||||
|
||||
private isRetryableInvoiceConflict(error: unknown) {
|
||||
if (!(error instanceof Prisma.PrismaClientKnownRequestError)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (error.code !== 'P2002') {
|
||||
return false
|
||||
}
|
||||
|
||||
const target = (error.meta?.target as string[]) || []
|
||||
return (
|
||||
target.includes('invoice_number') ||
|
||||
target.includes('sales_invoices_invoice_number_pos_id_key')
|
||||
)
|
||||
}
|
||||
|
||||
private normalizeInvoiceDate(invoiceDate: Date | string) {
|
||||
return new Date(invoiceDate).toISOString() as any
|
||||
}
|
||||
|
||||
private buildPaymentsData(
|
||||
paymentsData: SharedCreateSalesInvoiceDto['payments'],
|
||||
totalAmount: number,
|
||||
) {
|
||||
const paymentMethodMap: Record<string, PaymentMethodType> = {
|
||||
cash: PaymentMethodType.CASH,
|
||||
set_off: PaymentMethodType.SET_OFF,
|
||||
card: PaymentMethodType.CARD,
|
||||
payment_gateway: PaymentMethodType.PAYMENT_GATEWAY,
|
||||
bank: PaymentMethodType.BANK,
|
||||
check: PaymentMethodType.CHEQUE,
|
||||
other: PaymentMethodType.OTHER,
|
||||
terminal: PaymentMethodType.TERMINAL,
|
||||
}
|
||||
|
||||
const rawPayments = (paymentsData || {}) as Record<string, unknown>
|
||||
const terminalInfo = rawPayments.terminals as TerminalPaymentInfo | undefined
|
||||
|
||||
const payments: NormalizedPayment[] = Object.entries(rawPayments)
|
||||
.filter(([key]) => key !== 'terminals')
|
||||
.map(([key, value]) => ({
|
||||
method: paymentMethodMap[key.toLowerCase()],
|
||||
amount: typeof value === 'number' ? value : Number(value || 0),
|
||||
}))
|
||||
.filter(payment => payment.method && payment.amount > 0) as NormalizedPayment[]
|
||||
|
||||
const hasTerminalPayment = payments.some(
|
||||
payment => payment.method === PaymentMethodType.TERMINAL,
|
||||
)
|
||||
const nonTerminalTotal = payments
|
||||
.filter(payment => payment.method !== PaymentMethodType.TERMINAL)
|
||||
.reduce((sum, payment) => sum + payment.amount, 0)
|
||||
|
||||
if (!hasTerminalPayment && terminalInfo) {
|
||||
const terminalAmount =
|
||||
typeof terminalInfo.amount === 'number'
|
||||
? terminalInfo.amount
|
||||
: Math.max(0, Number(totalAmount) - nonTerminalTotal)
|
||||
|
||||
if (terminalAmount > 0) {
|
||||
payments.push({
|
||||
method: PaymentMethodType.TERMINAL,
|
||||
amount: terminalAmount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.validatePayments(payments, totalAmount, terminalInfo)
|
||||
|
||||
return {
|
||||
payments,
|
||||
terminalInfo,
|
||||
}
|
||||
}
|
||||
|
||||
private validatePayments(
|
||||
payments: NormalizedPayment[],
|
||||
totalAmount: number,
|
||||
terminalInfo?: TerminalPaymentInfo,
|
||||
) {
|
||||
const totalPayments = payments.reduce((sum, payment) => sum + payment.amount, 0)
|
||||
const roundedTotalPayments = Number(totalPayments.toFixed(2))
|
||||
const roundedTotalAmount = Number(Number(totalAmount).toFixed(2))
|
||||
|
||||
if (roundedTotalPayments !== roundedTotalAmount) {
|
||||
throw new BadRequestException('مبلغ پرداختی باید برابر با مبلغ کل فاکتور باشد.')
|
||||
}
|
||||
|
||||
const terminalPayments = payments.filter(
|
||||
payment => payment.method === PaymentMethodType.TERMINAL,
|
||||
)
|
||||
|
||||
if (terminalPayments.length > 0 && !terminalInfo) {
|
||||
throw new BadRequestException('برای پرداخت ترمینال اطلاعات ترمینال الزامی است.')
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveCustomerId(
|
||||
tx: Prisma.TransactionClient,
|
||||
data: SharedCreateSalesInvoiceDto,
|
||||
businessId: string,
|
||||
) {
|
||||
if (data.customer_id) {
|
||||
return data.customer_id
|
||||
}
|
||||
|
||||
if (
|
||||
data.customer_type === CustomerType.INDIVIDUAL &&
|
||||
data.customer?.customer_individual
|
||||
) {
|
||||
const { national_id, mobile_number, ...rest } = data.customer.customer_individual
|
||||
|
||||
const customerIndividual = await tx.customerIndividual.upsert({
|
||||
where: {
|
||||
business_activity_id_national_id: {
|
||||
business_activity_id: businessId,
|
||||
national_id: data.customer.customer_individual.national_id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
...data.customer.customer_individual,
|
||||
customer: {
|
||||
create: {
|
||||
type: CustomerType.INDIVIDUAL,
|
||||
},
|
||||
},
|
||||
business_activity: {
|
||||
connect: {
|
||||
id: businessId,
|
||||
},
|
||||
},
|
||||
},
|
||||
update: { ...rest },
|
||||
select: {
|
||||
customer_id: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!customerIndividual) {
|
||||
throw new BadRequestException('متاسفانه مشکلی در اطلاعات مشتری وجود دارد.')
|
||||
}
|
||||
|
||||
return customerIndividual.customer_id
|
||||
}
|
||||
|
||||
if (data.customer_type === CustomerType.LEGAL && data.customer?.customer_legal) {
|
||||
const { registration_number, ...rest } = data.customer.customer_legal
|
||||
const customerLegal = await tx.customerLegal.upsert({
|
||||
where: {
|
||||
business_activity_id_economic_code: {
|
||||
business_activity_id: businessId,
|
||||
economic_code: data.customer.customer_legal.economic_code,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
...data.customer.customer_legal,
|
||||
customer: {
|
||||
create: {
|
||||
type: CustomerType.LEGAL,
|
||||
},
|
||||
},
|
||||
business_activity: {
|
||||
connect: {
|
||||
id: businessId,
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {
|
||||
...rest,
|
||||
},
|
||||
select: {
|
||||
customer_id: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!customerLegal) {
|
||||
throw new BadRequestException('متاسفانه مشکلی در اطلاعات مشتری وجود دارد.')
|
||||
}
|
||||
|
||||
return customerLegal.customer_id
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private async getGoodsById(
|
||||
tx: Prisma.TransactionClient,
|
||||
data: SharedCreateSalesInvoiceDto,
|
||||
) {
|
||||
const itemGoodIds = data.items
|
||||
.map(item => item.good_id)
|
||||
.filter((goodId): goodId is string => Boolean(goodId))
|
||||
|
||||
const goods = itemGoodIds.length
|
||||
? await tx.good.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: itemGoodIds,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
sku: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
code: true,
|
||||
VAT: true,
|
||||
},
|
||||
},
|
||||
local_sku: true,
|
||||
barcode: true,
|
||||
pricing_model: true,
|
||||
measure_unit: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
code: true,
|
||||
},
|
||||
},
|
||||
base_sale_price: true,
|
||||
image_url: true,
|
||||
category: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
: []
|
||||
|
||||
return new Map(goods.map(good => [good.id, good]))
|
||||
}
|
||||
|
||||
private buildSalesInvoiceData(params: {
|
||||
data: SharedCreateSalesInvoiceDto
|
||||
normalizedInvoiceDate: Date
|
||||
invoiceNumber: number
|
||||
consumerAccountId: string
|
||||
businessId: string
|
||||
complexId: string
|
||||
posId: string
|
||||
goodsById: Map<string, any>
|
||||
customerId: string | null
|
||||
type: TspProviderRequestType
|
||||
main_invoice_id?: string
|
||||
ref_invoice_id?: string
|
||||
}) {
|
||||
const {
|
||||
data,
|
||||
normalizedInvoiceDate,
|
||||
invoiceNumber,
|
||||
consumerAccountId,
|
||||
posId,
|
||||
goodsById,
|
||||
customerId,
|
||||
businessId,
|
||||
complexId,
|
||||
type,
|
||||
main_invoice_id,
|
||||
ref_invoice_id,
|
||||
} = params
|
||||
const { customer_id, customer_type, customer, payments, ...invoiceData } = data
|
||||
|
||||
if (
|
||||
type !== TspProviderRequestType.ORIGINAL &&
|
||||
!(main_invoice_id || ref_invoice_id)
|
||||
) {
|
||||
throw new BadRequestException('متاسفانه مشکلی در اطلاعات فاکتور وجود دارد.')
|
||||
}
|
||||
|
||||
const salesInvoiceData: SalesInvoiceCreateInput = {
|
||||
...invoiceData,
|
||||
invoice_date: normalizedInvoiceDate,
|
||||
invoice_number: invoiceNumber,
|
||||
total_amount: data.total_amount,
|
||||
code: this.generateInvoiceCode(businessId, complexId, posId, invoiceNumber),
|
||||
type,
|
||||
items: {
|
||||
createMany: {
|
||||
data: data.items.map(item => ({
|
||||
good_id: item.good_id!,
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price,
|
||||
total_amount: item.total_amount,
|
||||
measure_unit_text: goodsById.get(item.good_id!)?.measure_unit.name || null,
|
||||
measure_unit_code: goodsById.get(item.good_id!)?.measure_unit.code || null,
|
||||
sku_code: goodsById.get(item.good_id!)?.sku.code || null,
|
||||
sku_vat: goodsById.get(item.good_id!)?.sku.VAT || null,
|
||||
payload: item.payload ? JSON.parse(JSON.stringify(item.payload)) : undefined,
|
||||
good_snapshot: item.good_id
|
||||
? JSON.parse(
|
||||
JSON.stringify({
|
||||
good: goodsById.get(item.good_id) || null,
|
||||
}),
|
||||
)
|
||||
: undefined,
|
||||
})),
|
||||
},
|
||||
},
|
||||
unknown_customer: {},
|
||||
consumer_account: {
|
||||
connect: {
|
||||
id: consumerAccountId,
|
||||
},
|
||||
},
|
||||
pos: {
|
||||
connect: {
|
||||
id: posId,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (customerId) {
|
||||
salesInvoiceData.customer = {
|
||||
connect: {
|
||||
id: customerId,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (type !== TspProviderRequestType.ORIGINAL) {
|
||||
salesInvoiceData.main_id = main_invoice_id
|
||||
salesInvoiceData.reference_invoice = {
|
||||
connect: {
|
||||
id: ref_invoice_id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return salesInvoiceData
|
||||
}
|
||||
|
||||
private async getNextInvoiceNumber(tx: Prisma.TransactionClient, businessId: string) {
|
||||
const latestInvoice = await tx.salesInvoice.findFirst({
|
||||
where: {
|
||||
pos: {
|
||||
complex: {
|
||||
business_activity_id: businessId,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
invoice_number: 'desc',
|
||||
},
|
||||
select: {
|
||||
invoice_number: true,
|
||||
pos: {
|
||||
select: {
|
||||
complex: {
|
||||
select: {
|
||||
business_activity: {
|
||||
select: {
|
||||
invoice_number_sequence: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const latestSequence =
|
||||
latestInvoice?.pos?.complex?.business_activity?.invoice_number_sequence.toNumber() ||
|
||||
0
|
||||
|
||||
return Math.max(latestInvoice?.invoice_number || 0, latestSequence) + 1
|
||||
}
|
||||
|
||||
private async createPayments(
|
||||
tx: Prisma.TransactionClient,
|
||||
invoiceId: string,
|
||||
payments: NormalizedPayment[],
|
||||
terminalInfo: TerminalPaymentInfo | undefined,
|
||||
paidAt: Date,
|
||||
) {
|
||||
for (const payment of payments) {
|
||||
if (payment.amount <= 0) {
|
||||
continue
|
||||
}
|
||||
const createdPayment = await tx.salesInvoicePayment.create({
|
||||
data: {
|
||||
invoice_id: invoiceId,
|
||||
amount: payment.amount,
|
||||
payment_method: payment.method,
|
||||
paid_at: paidAt,
|
||||
},
|
||||
})
|
||||
|
||||
if (payment.method === PaymentMethodType.TERMINAL && terminalInfo) {
|
||||
await tx.salesInvoicePaymentTerminalInfo.create({
|
||||
data: {
|
||||
payment_id: createdPayment.id,
|
||||
terminal_id: terminalInfo.terminalId,
|
||||
stan: terminalInfo.stan,
|
||||
rrn: terminalInfo.rrn,
|
||||
transaction_date_time: new Date(terminalInfo.transactionDateTime || ''),
|
||||
customer_card_no: terminalInfo.customerCardNO || null,
|
||||
description: terminalInfo.description || null,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private generateInvoiceCode(
|
||||
businessId: string,
|
||||
complexId: string,
|
||||
posId: string,
|
||||
invoiceNumber: number,
|
||||
) {
|
||||
return `${businessId.substring(businessId.length - 2, businessId.length)}-${complexId.substring(complexId.length - 2, complexId.length)}-${posId.substring(posId.length - 2, posId.length)}-${invoiceNumber.toString()}`
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user