2026-03-29 18:06:41 +03:30
|
|
|
import { IPosPayload } from '@/common/models/posPayload.model'
|
2026-03-30 13:16:36 +03:30
|
|
|
import { SalesInvoiceCreateInput } from '@/generated/prisma/models'
|
2026-03-07 11:25:11 +03:30
|
|
|
import { PrismaService } from '@/prisma/prisma.service'
|
2026-03-30 13:16:36 +03:30
|
|
|
import { BadRequestException, Injectable } from '@nestjs/common'
|
2026-03-07 11:25:11 +03:30
|
|
|
import { ResponseMapper } from 'common/response/response-mapper'
|
|
|
|
|
import type { CustomerIndividual, CustomerLegal } from 'generated/prisma/client'
|
|
|
|
|
import { CustomerType, PaymentMethodType } from 'generated/prisma/enums'
|
2026-02-24 12:42:10 +03:30
|
|
|
import { CreateSalesInvoiceDto } from './dto/create-sales-invoice.dto'
|
|
|
|
|
|
|
|
|
|
// Define type guard for CustomerIndividual
|
|
|
|
|
function isCustomerIndividual(customer: any): customer is CustomerIndividual {
|
|
|
|
|
return (
|
|
|
|
|
customer &&
|
|
|
|
|
typeof customer.first_name === 'string' &&
|
|
|
|
|
typeof customer.last_name === 'string'
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Define type guard for CustomerLegal
|
|
|
|
|
function isCustomerLegal(customer: any): customer is CustomerLegal {
|
|
|
|
|
return (
|
|
|
|
|
customer &&
|
|
|
|
|
typeof customer.company_name === 'string' &&
|
|
|
|
|
typeof customer.registration_number === 'string'
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-01-07 15:25:59 +03:30
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class SalesInvoicesService {
|
2026-02-24 12:42:10 +03:30
|
|
|
constructor(private prisma: PrismaService) {}
|
|
|
|
|
|
2026-02-12 20:31:04 +03:30
|
|
|
findAll() {
|
|
|
|
|
// TODO: Implement fetching all sales invoices
|
|
|
|
|
return []
|
2026-01-07 15:25:59 +03:30
|
|
|
}
|
|
|
|
|
|
2026-02-12 20:31:04 +03:30
|
|
|
findOne(id: number) {
|
|
|
|
|
// TODO: Implement fetching a single sales invoice
|
|
|
|
|
return {}
|
2026-01-07 15:25:59 +03:30
|
|
|
}
|
|
|
|
|
|
2026-03-29 18:06:41 +03:30
|
|
|
async create(data: CreateSalesInvoiceDto, posInfo: IPosPayload) {
|
2026-02-24 12:42:10 +03:30
|
|
|
data.invoice_date = new Date(data.invoice_date).toISOString() as any
|
|
|
|
|
|
2026-03-29 18:06:41 +03:30
|
|
|
const { complex_id, pos_id, consumer_account_id } = posInfo
|
|
|
|
|
|
2026-02-24 12:42:10 +03:30
|
|
|
const salesInvoice = await this.prisma.$transaction(async $tx => {
|
|
|
|
|
const payments = Object.entries(data.payments)
|
|
|
|
|
.filter(([_, value]) => value > 0 && PaymentMethodType[_.toUpperCase()])
|
|
|
|
|
.map(([key, value]) => {
|
|
|
|
|
return {
|
|
|
|
|
amount: value,
|
|
|
|
|
payment_method: key.toLocaleUpperCase() as PaymentMethodType,
|
|
|
|
|
paid_at: data.invoice_date,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const totalPayments = payments.reduce((sum, payment) => sum + payment.amount, 0)
|
2026-03-29 18:06:41 +03:30
|
|
|
|
2026-02-24 12:42:10 +03:30
|
|
|
if (totalPayments !== data.total_amount) {
|
|
|
|
|
throw new Error('مبلغ پرداختی باید برابر با مبلغ کل فاکتور باشد.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { customer_id, customer_type, customer, ...invoiceData } = data
|
|
|
|
|
let newCustomerId: string | null = customer_id || null
|
|
|
|
|
if (customer_id) {
|
|
|
|
|
} else if (
|
|
|
|
|
customer_type === CustomerType.INDIVIDUAL &&
|
2026-03-30 13:16:36 +03:30
|
|
|
customer?.customer_individual
|
2026-02-24 12:42:10 +03:30
|
|
|
) {
|
2026-03-30 13:16:36 +03:30
|
|
|
const customer_individual = customer.customer_individual
|
|
|
|
|
const customerIndividual = await $tx.customerIndividual.upsert({
|
2026-02-24 12:42:10 +03:30
|
|
|
where: {
|
|
|
|
|
complex_id_national_id: {
|
2026-03-29 18:06:41 +03:30
|
|
|
complex_id,
|
2026-03-30 13:16:36 +03:30
|
|
|
national_id: customer_individual.national_id,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
|
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
create: {
|
|
|
|
|
...customer_individual,
|
|
|
|
|
customer: {
|
|
|
|
|
create: {
|
|
|
|
|
type: CustomerType.INDIVIDUAL,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
|
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
complex: {
|
|
|
|
|
connect: {
|
|
|
|
|
id: complex_id,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
|
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
},
|
|
|
|
|
update: {},
|
|
|
|
|
select: {
|
|
|
|
|
customer_id: true,
|
|
|
|
|
},
|
|
|
|
|
})
|
2026-02-24 12:42:10 +03:30
|
|
|
|
2026-03-30 13:16:36 +03:30
|
|
|
if (!customerIndividual) {
|
|
|
|
|
throw new BadRequestException('متاسفانه مشکلی در اطلاعات مشتری وجود دارد.')
|
2026-02-24 12:42:10 +03:30
|
|
|
}
|
2026-03-30 13:16:36 +03:30
|
|
|
newCustomerId = customerIndividual.customer_id
|
|
|
|
|
} else if (data.customer_type === CustomerType.LEGAL && customer?.customer_legal) {
|
|
|
|
|
const customer_legal = customer.customer_legal
|
|
|
|
|
|
|
|
|
|
const customerLegal = await $tx.customerLegal.upsert({
|
2026-02-24 12:42:10 +03:30
|
|
|
where: {
|
|
|
|
|
complex_id_registration_number: {
|
2026-03-29 18:06:41 +03:30
|
|
|
complex_id,
|
2026-03-30 13:16:36 +03:30
|
|
|
registration_number: customer_legal.registration_number,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
|
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
create: {
|
|
|
|
|
...customer_legal,
|
|
|
|
|
customer: {
|
|
|
|
|
create: {
|
|
|
|
|
type: CustomerType.LEGAL,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
|
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
complex: {
|
|
|
|
|
connect: {
|
|
|
|
|
id: complex_id,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
|
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
},
|
|
|
|
|
update: {},
|
|
|
|
|
select: {
|
|
|
|
|
customer_id: true,
|
|
|
|
|
},
|
|
|
|
|
})
|
2026-02-24 12:42:10 +03:30
|
|
|
|
2026-03-30 13:16:36 +03:30
|
|
|
if (!customerLegal) {
|
|
|
|
|
throw new BadRequestException('متاسفانه مشکلی در اطلاعات مشتری وجود دارد.')
|
2026-02-24 12:42:10 +03:30
|
|
|
}
|
2026-03-30 13:16:36 +03:30
|
|
|
newCustomerId = customerLegal.customer_id
|
2026-03-29 18:06:41 +03:30
|
|
|
} else if (data.customer_type === CustomerType.UNKNOWN) {
|
2026-02-24 12:42:10 +03:30
|
|
|
}
|
|
|
|
|
|
2026-03-30 13:16:36 +03:30
|
|
|
const salesInvoiceData: SalesInvoiceCreateInput = {
|
|
|
|
|
...invoiceData,
|
|
|
|
|
total_amount: data.total_amount,
|
|
|
|
|
code: 'INV-' + Date.now(),
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
payload: item.payload
|
|
|
|
|
? JSON.parse(JSON.stringify(item.payload))
|
|
|
|
|
: undefined,
|
|
|
|
|
unit_type: item.unit_type,
|
|
|
|
|
// pricing_model: item.pricingModel,
|
|
|
|
|
})),
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
},
|
|
|
|
|
unknown_customer: {},
|
|
|
|
|
payments: {
|
|
|
|
|
createMany: {
|
|
|
|
|
data: payments,
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
},
|
|
|
|
|
// customer: {
|
|
|
|
|
// connect: {
|
|
|
|
|
// id: newCustomerId || undefined,
|
|
|
|
|
// },
|
|
|
|
|
// },
|
2026-04-13 13:21:50 +03:30
|
|
|
consumer_account: {
|
2026-03-30 13:16:36 +03:30
|
|
|
connect: {
|
|
|
|
|
id: consumer_account_id,
|
2026-03-29 18:06:41 +03:30
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
},
|
|
|
|
|
pos: {
|
|
|
|
|
connect: {
|
|
|
|
|
id: pos_id,
|
2026-03-29 18:06:41 +03:30
|
|
|
},
|
2026-02-24 12:42:10 +03:30
|
|
|
},
|
2026-03-30 13:16:36 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newCustomerId) {
|
|
|
|
|
salesInvoiceData.customer = {
|
|
|
|
|
connect: {
|
|
|
|
|
id: newCustomerId || undefined,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const salesInvoice = await $tx.salesInvoice.create({
|
|
|
|
|
data: salesInvoiceData,
|
2026-02-24 12:42:10 +03:30
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return salesInvoice
|
|
|
|
|
})
|
|
|
|
|
return ResponseMapper.create(salesInvoice)
|
2026-01-07 15:25:59 +03:30
|
|
|
}
|
|
|
|
|
}
|