2026-05-05 22:42:09 +03:30
|
|
|
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,
|
|
|
|
|
) {
|
2026-05-07 20:30:24 +03:30
|
|
|
return `${businessId.substring(businessId.length - 2, businessId.length)}${complexId.substring(complexId.length - 2, complexId.length)}${posId.substring(posId.length - 2, posId.length)}${invoiceNumber.toString()}`
|
2026-05-05 22:42:09 +03:30
|
|
|
}
|
|
|
|
|
}
|