feat: implement SalesInvoiceTspSwitchService and SalesInvoiceTspService for handling TSP provider interactions

- Add SalesInvoiceTspSwitchService to manage TSP provider selection and sending invoices.
- Introduce SalesInvoiceTspService for creating, sending, and retrieving sales invoices.
- Implement NamaProviderSwitchAdapter for communication with the NAMA TSP provider API.
- Define DTOs for request and response structures specific to the NAMA provider.
- Enhance error handling and logging for TSP provider interactions.
This commit is contained in:
2026-05-03 16:23:17 +03:30
parent ad470d2166
commit a486127ade
62 changed files with 4136 additions and 5015 deletions
+6 -6
View File
@@ -1,4 +1,4 @@
import { FiscalResponseStatus } from '@/generated/prisma/enums'
import { TspProviderResponseStatus } from '@/generated/prisma/enums'
export default {
PaymentMethodType: {
@@ -132,23 +132,23 @@ export default {
CAFE_BAZAR: 'کافه بازار',
MAYKET: 'مایکت',
},
PartnerFiscalSwitchType: {
TspProviderType: {
NAMA: 'نما',
SUN: 'سان',
},
FiscalResponseStatus: {
TspProviderResponseStatus: {
SUCCESS: 'موفق',
FAILURE: 'ناموفق',
NOT_SEND: 'ارسال نشده',
[FiscalResponseStatus.QUEUED]: 'در صف ارسال',
[TspProviderResponseStatus.QUEUED]: 'در صف ارسال',
},
FiscalRequestType: {
TspProviderRequestType: {
MAIN: 'اصلی',
UPDATE: 'اصلاح',
REVOKE: 'ابطال',
REMOVE: 'حذف',
},
FiscalInvoiceCustomerType: {
TspProviderCustomerType: {
Unknown: 'ناشناس',
Known: 'شناسایی شده',
},
+2 -2
View File
@@ -4,14 +4,14 @@ export enum GoldKarat {
KARAT_24 = '24',
}
export enum FiscalRequestType {
export enum TspProviderRequestType {
MAIN = 'MAIN',
UPDATE = 'UPDATE',
REVOKE = 'REVOKE',
REMOVE = 'REMOVE',
}
export enum FiscalInvoiceCustomerType {
export enum TspProviderCustomerType {
Unknown = 'Unknown',
Known = 'Known',
}
@@ -0,0 +1,63 @@
export interface FetchRequestContext {
url: string
init: RequestInit
}
export interface FetchRequestInterceptor {
onRequest?(
context: FetchRequestContext,
): Promise<FetchRequestContext> | FetchRequestContext
onResponse?(
context: FetchRequestContext,
response: Response,
): Promise<Response> | Response
onError?(context: FetchRequestContext, error: unknown): Promise<void> | void
}
function mergeHeaders(
currentHeaders: HeadersInit | undefined,
newHeaders: HeadersInit,
): Headers {
const mergedHeaders = new Headers(currentHeaders)
const incomingHeaders = new Headers(newHeaders)
incomingHeaders.forEach((value, key) => {
mergedHeaders.set(key, value)
})
return mergedHeaders
}
export function createHeaderRequestInterceptor(
headers: HeadersInit,
): FetchRequestInterceptor {
return {
onRequest(context) {
return {
...context,
init: {
...context.init,
headers: mergeHeaders(context.init.headers, headers),
},
}
},
}
}
export function createEnsureSuccessResponseInterceptor(): FetchRequestInterceptor {
return {
async onResponse(_, response) {
if (response.ok) {
return response
}
const errorBody = await response.text().catch(() => '')
const normalizedBody = errorBody
return Promise.reject({
status: response.status,
statusText: response.statusText,
body: normalizedBody,
})
},
}
}
@@ -4,6 +4,7 @@ export interface SaleInvoiceGoldTypePayload {
karat: keyof typeof GoldKarat
wages: number
profit: number
commission: number
}
export interface SaleInvoiceStandardPayload {}
+47
View File
@@ -0,0 +1,47 @@
import {
FetchRequestContext,
FetchRequestInterceptor,
} from '@/common/interceptors/fetch-request.interceptor'
import { Injectable } from '@nestjs/common'
@Injectable()
export class HttpClientUtil {
async request(
url: string,
init: RequestInit = {},
interceptors: FetchRequestInterceptor[] = [],
): Promise<Response> {
let context: FetchRequestContext = { url, init }
for (const interceptor of interceptors) {
if (!interceptor.onRequest) {
continue
}
context = await interceptor.onRequest(context)
}
try {
let response = await fetch(context.url, context.init)
for (const interceptor of interceptors) {
if (!interceptor.onResponse) {
continue
}
response = await interceptor.onResponse(context, response)
console.log('response')
console.log(response)
}
return response
} catch (error: any) {
for (const interceptor of interceptors) {
if (!interceptor.onError) {
continue
}
await interceptor.onError(context, error)
}
throw error
}
}
}
+1
View File
@@ -3,3 +3,4 @@ export * from './enum-translator.util'
export * from './mappers/consumer_mappers.util'
export * from './password.util'
export * from './tracking-code-generator.util'
export * from './http-client.util'