feat: add DTOs and services for tax switch integration

- Created SendBulkSaleInvoicesDto for handling bulk sale invoice requests.
- Implemented TaxSwitchSendPayloadDto and related DTOs for tax switch item payloads and results.
- Developed SalesInvoiceTaxSwitchService to manage tax switch operations, including sending and retrieving tax information.
- Added SalesInvoiceTaxService for handling sales invoice tax logic, including bulk sending and persistence of results.
- Introduced NamaTaxSwitchAdapter to interact with the tax switch service, simulating external API responses.
- Created SendBulkSalesInvoicesDto for POS module to handle bulk sales invoice requests.
This commit is contained in:
2026-04-27 22:11:05 +03:30
parent dee96b6e91
commit 58a7c359d8
68 changed files with 7896 additions and 3534 deletions
@@ -1,10 +1,10 @@
import { IPosPayload } from '@/common/models/posPayload.model'
import { SalesInvoiceCreateInput } from '@/generated/prisma/models'
import { PrismaService } from '@/prisma/prisma.service'
import { BadRequestException, Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper'
import type { CustomerIndividual, CustomerLegal } from 'generated/prisma/client'
import { CustomerType, PaymentMethodType } from 'generated/prisma/enums'
import { SalesInvoiceTaxService } from '../../consumer/saleInvoices/tax/sales-invoice-tax.service'
import { CreateSalesInvoiceDto } from './dto/create-sales-invoice.dto'
// Define type guard for CustomerIndividual
@@ -27,7 +27,10 @@ function isCustomerLegal(customer: any): customer is CustomerLegal {
@Injectable()
export class SalesInvoicesService {
constructor(private prisma: PrismaService) {}
constructor(
private prisma: PrismaService,
private salesInvoiceTaxService: SalesInvoiceTaxService,
) {}
findAll() {
// TODO: Implement fetching all sales invoices
@@ -39,6 +42,28 @@ export class SalesInvoicesService {
return {}
}
// async send(invoiceId: string, posInfo: IPosPayload) {
// await this.salesInvoiceTaxService.send(invoiceId, posInfo.pos_id)
// return ResponseMapper.single({
// invoice_id: invoiceId,
// sent_to_tax: true,
// })
// }
// async sendBulk(invoiceIds: string[], posInfo: IPosPayload) {
// if (!invoiceIds.length) {
// throw new BadRequestException('invoice_ids is required and cannot be empty.')
// }
// await this.salesInvoiceTaxService.sendBulk(invoiceIds, posInfo.pos_id)
// return ResponseMapper.single({
// invoice_ids: invoiceIds,
// sent_to_tax: true,
// })
// }
async create(data: CreateSalesInvoiceDto, posInfo: IPosPayload) {
data.invoice_date = new Date(data.invoice_date).toISOString() as any
@@ -135,7 +160,40 @@ export class SalesInvoicesService {
} else if (data.customer_type === CustomerType.UNKNOWN) {
}
const salesInvoiceData: SalesInvoiceCreateInput = {
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: true,
local_sku: true,
barcode: true,
pricing_model: true,
unit_type: true,
base_sale_price: true,
image_url: true,
category: {
select: {
id: true,
name: true,
},
},
},
})
: []
const goodsById = new Map(goods.map(good => [good.id, good]))
const salesInvoiceData: any = {
...invoiceData,
total_amount: data.total_amount,
code: 'INV-' + Date.now(),
@@ -150,6 +208,13 @@ export class SalesInvoicesService {
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,
unit_type: item.unit_type,
// pricing_model: item.pricingModel,
})),
@@ -192,6 +257,10 @@ export class SalesInvoicesService {
return salesInvoice
})
if (data.send_to_tax) {
await this.salesInvoiceTaxService.send(salesInvoice.id, pos_id)
}
return ResponseMapper.create(salesInvoice)
}
}