Files
CRM_Montzar/apps/api/src/integrations/tahesab/tahesab-methods.ts
T

517 lines
12 KiB
TypeScript
Raw Normal View History

import { randomBytes } from 'crypto';
import type { TahesabHttpClient, TahesabCallOptions } from './tahesab-http.client';
export type ConnCreds = {
baseUrl: string;
token: string;
dbName: string;
allowInsecureTls?: boolean;
};
/** حداقل طول Factor_Code طبق مستندات ته‌حساب */
export const FACTOR_CODE_MIN_LEN = 20;
export function buildFactorCode(prefix = 'crm'): string {
const stamp = Date.now().toString(36);
const rand = randomBytes(12).toString('hex');
const code = `${prefix}${stamp}${rand}`;
return code.length >= FACTOR_CODE_MIN_LEN
? code
: code.padEnd(FACTOR_CODE_MIN_LEN, '0');
}
export function assertFactorCode(code: string): void {
if (!code || code.trim().length < FACTOR_CODE_MIN_LEN) {
throw new Error(
`Factor_Code باید حداقل ${FACTOR_CODE_MIN_LEN} کاراکتر باشد.`,
);
}
}
/** نرمال‌سازی موبایل ایران برای جستجو در ته‌حساب */
export function normalizeIranMobile(input: string | null | undefined): string | null {
if (!input) return null;
let d = input.replace(/[^\d]/g, '');
if (d.startsWith('98') && d.length >= 12) d = d.slice(2);
if (d.startsWith('0') && d.length >= 11) d = d.slice(1);
if (d.length < 10) return null;
return d;
}
export type MandehValue = {
amount: string | number;
unit: string;
status: 'بس' | 'بد';
};
export type MandehDetailItem = {
Name: string;
values: MandehValue[];
};
export type NormalizedAssets = {
goldBalance: number | null;
rialBalance: number | null;
currencyBalance: number | null;
totalGoldEquiv: number | null;
mandehDetail: MandehDetailItem[];
raw: unknown;
};
function num(v: unknown): number | null {
if (v === null || v === undefined || v === '') return null;
const n = Number(v);
return Number.isFinite(n) ? n : null;
}
function detailStatus(amount: number): 'بس' | 'بد' {
return amount < 0 ? 'بس' : 'بد';
}
/** نرمال‌سازی پاسخ getmandehesabbycode مثل همکار */
export function normalizeMandehResponse(raw: unknown): NormalizedAssets {
const root = raw as {
MandeHesab?: Array<Record<string, unknown>>;
};
const row = root?.MandeHesab?.[0];
if (!row) {
return {
goldBalance: null,
rialBalance: null,
currencyBalance: null,
totalGoldEquiv: null,
mandehDetail: [],
raw,
};
}
const details = Array.isArray(row.details) ? row.details : [];
const mandehDetail: MandehDetailItem[] = details.map((item) => {
const it = item as Record<string, unknown>;
const values: MandehValue[] = [];
if (it.Value1 !== null && it.Value1 !== undefined) {
const a = Number(it.Value1);
values.push({
amount: it.Value1 as string | number,
unit: 'گرم',
status: detailStatus(a),
});
}
if (it.Value2 !== null && it.Value2 !== undefined) {
const a = Number(it.Value2);
values.push({
amount: it.Value2 as string | number,
unit: 'عدد',
status: detailStatus(a),
});
}
if (it.Value3 !== null && it.Value3 !== undefined) {
const a = Number(it.Value3);
values.push({
amount: it.Value3 as string | number,
unit: 'ارز',
status: detailStatus(a),
});
}
if (it.Value4 !== null && it.Value4 !== undefined) {
const a = Number(it.Value4);
values.push({
amount: it.Value4 as string | number,
unit: 'ریال',
status: detailStatus(a),
});
}
return { Name: String(it.Name ?? ''), values };
});
return {
goldBalance: num(row.MandeyeVazni),
rialBalance: num(row.MandeyeMali),
currencyBalance: num(row.MandeyeKolBePool),
totalGoldEquiv: num(row.MandeyeKolBeTALA),
mandehDetail,
raw,
};
}
export class TahesabApi {
constructor(
private readonly http: TahesabHttpClient,
private readonly creds: ConnCreds,
) {}
private call<T>(
path: string,
bodyKey?: string,
args?: unknown[],
method?: 'GET' | 'POST',
timeoutMs?: number,
extra?: Pick<TahesabCallOptions, 'emptyResultOnError'>,
) {
return this.http.call<T>({
...this.creds,
path,
bodyKey,
args,
method,
timeoutMs,
...extra,
});
}
checkHealthSsl() {
return this.call<{ Status?: string; Api_Status?: string }>(
'CheckHealth-SSL',
undefined,
undefined,
'GET',
);
}
checkHealthNoSsl() {
return this.call<{ Status?: string; Api_Status?: string }>(
'CheckHealth-No-SSL',
undefined,
undefined,
'GET',
);
}
/** ثبت مشتری — پاسخ { OK: moshtariCode } */
doNewMoshtari(args: unknown[]) {
return this.call<{ OK?: number }>('DoNewMoshtari', 'DoNewMoshtari', args);
}
doEditMoshtari(args: unknown[]) {
return this.call('DoEditMoshtari', 'DoEditMoshtari', args);
}
doListMoshtariByCode(
fromCode: number,
toCode: number,
opts?: { timeoutMs?: number },
) {
// OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف)
return this.call(
'DoListMoshtari-ByCode',
'Dolistmoshtari',
[fromCode, toCode],
'POST',
opts?.timeoutMs,
);
}
doListMoshtariByPhone(phone: string | number) {
return this.call('DoListMoshtari-ByPhone', 'DoListMoshtari', [phone]);
}
doListMoshtariByName(name: string) {
return this.call('DoListMoshtari-ByName', 'DoListMoshtari', [name]);
}
/** body key حساس به حروف کوچک مثل همکار */
getMandeHesabByCode(moshtariCode: number | string) {
return this.call(
'GetMandeHesabByCode',
'getmandehesabbycode',
[String(moshtariCode)],
);
}
doListAsnad(
countLast: number,
moshtariCode: number,
fromShamsi: string,
toShamsi: string,
) {
// همکار: POST به baseUrl بدون path، با کلید body = DoListAsnad و ۴ آرگومان
const args = [countLast, moshtariCode, fromShamsi, toShamsi];
const empty = { emptyResultOnError: /رکوردی یافت نشد/i };
const timeoutMs = 55_000;
const rootCall = () =>
this.http.call({
...this.creds,
path: '',
bodyKey: 'DoListAsnad',
args,
method: 'POST' as const,
timeoutMs,
...empty,
});
const pathCall = () =>
this.http.call({
...this.creds,
path: 'DoListAsnad',
bodyKey: 'DoListAsnad',
args,
method: 'POST' as const,
timeoutMs,
...empty,
});
return rootCall().catch(async (err) => {
try {
return await pathCall();
} catch {
throw err;
}
});
}
doListNameSekeh() {
return this.call('DoListNameSekeh', 'DoListNameSekeh', []);
}
doListNameArz() {
return this.call('DoListNameARZ', 'DoListNameARZ', []);
}
doListHesabBanki() {
return this.call('DoListHesabBanki', 'DoListHesabBanki', []);
}
doNewSanadBuySaleGold(args: unknown[]) {
return this.call('DoNewSanadBuySaleGOLD', 'DoNewSanadBuySaleGOLD', args);
}
doNewSanadBuySaleSekeh(args: unknown[]) {
return this.call('DoNewSanadBuySaleSEKEH', 'DoNewSanadBuySaleSEKEH', args);
}
doNewSanadTalabBedehi(args: unknown[]) {
return this.call('DoNewSanadTalabBedehi', 'DoNewSanadTalabBedehi', args);
}
doNewSanadVkhGold(args: unknown[]) {
return this.call('DoNewSanadVKHGOLD', 'DoNewSanadVKHGOLD', args);
}
doNewSanadVkhBank(args: unknown[]) {
return this.call('DoNewSanadVKHBank', 'DoNewSanadVKHBank', args);
}
}
/** سازنده‌های آرایه سند — ترتیب پارامترها قفل‌شده با OpenAPI/همکار */
export const SanadBuilders = {
buySaleGold(p: {
moshtariCode: number;
year: number;
month: number;
day: number;
vazn: number;
ayar: number;
buyOrSale: 0 | 1;
mazaneh: number;
mazanehIsGeram: 0 | 1;
isAbshode: 0 | 1;
mablaghKol: number;
sharh: string;
factorCode: string;
havalehBeMcode?: number;
jensFelez?: number;
zamanTasvie?: string;
}): unknown[] {
assertFactorCode(p.factorCode);
return [
1,
p.moshtariCode,
-1,
1,
p.year,
p.month,
p.day,
p.vazn,
p.ayar,
123456,
'Reygiri',
p.buyOrSale,
p.mazaneh,
p.mazanehIsGeram,
p.isAbshode,
p.mablaghKol,
p.sharh,
p.factorCode,
p.havalehBeMcode ?? -1,
-1,
p.jensFelez ?? 0,
p.zamanTasvie ?? '',
];
},
buySaleSekeh(p: {
moshtariCode: number;
year: number;
month: number;
day: number;
vazn: number;
ayar: number;
count: number;
nameSekeh: string;
buyOrSale: 0 | 1;
mazaneh: number;
mablaghKol: number;
sharh: string;
factorCode: string;
jensFelez?: number;
arzName?: string;
}): unknown[] {
assertFactorCode(p.factorCode);
return [
1,
p.moshtariCode,
-1,
1,
p.year,
p.month,
p.day,
p.vazn,
p.ayar,
p.count,
p.nameSekeh,
p.buyOrSale,
p.mazaneh,
p.mablaghKol,
p.sharh,
p.factorCode,
-1,
-1,
p.jensFelez ?? 0,
p.arzName ?? 'IRR',
];
},
/** قرارداد همکار HavalehTahesabService */
talabBedehi(p: {
moshtariCode: number;
year: number;
month: number;
day: number;
isBedehiOrTalab: 0 | 1;
mablagh: number;
sharh: string;
baabat: string;
vazn: number;
factorCode: string;
havalehBeMcode?: number;
jensFelez?: number;
arzName?: string;
}): unknown[] {
assertFactorCode(p.factorCode);
return [
1,
p.moshtariCode,
-1,
0,
p.year,
p.month,
p.day,
p.isBedehiOrTalab,
p.mablagh,
p.sharh,
p.baabat,
p.vazn,
p.factorCode,
p.havalehBeMcode ?? -1,
0,
p.jensFelez ?? 0,
p.arzName ?? 'ریال',
];
},
vkhGold(p: {
moshtariCode: number;
year: number;
month: number;
day: number;
vazn: number;
ayar: number;
angNumber: string | number;
nameAz: string;
isVoroodOrKhorooj: 0 | 1;
isAbshode: 0 | 1;
sharh: string;
factorCode: string;
isSharti?: boolean;
}): unknown[] {
assertFactorCode(p.factorCode);
return [
1,
p.moshtariCode,
-1,
1,
p.year,
p.month,
p.day,
p.vazn,
p.ayar,
p.angNumber,
p.nameAz,
p.isVoroodOrKhorooj,
p.isAbshode,
p.sharh,
p.factorCode,
-1,
-1,
0,
p.isSharti ?? false,
];
},
vkhBank(p: {
moshtariCode: number;
year: number;
month: number;
day: number;
isDaryaftOrPardakht: 0 | 1;
mablagh: number;
sharh: string;
nameBank: string;
codeRahgiri: string;
factorCode: string;
}): unknown[] {
assertFactorCode(p.factorCode);
return [
1,
p.moshtariCode,
-1,
1,
p.year,
p.month,
p.day,
p.isDaryaftOrPardakht,
p.mablagh,
p.sharh,
p.nameBank,
p.codeRahgiri,
p.factorCode,
-1,
0,
];
},
newMoshtari(p: {
name: string;
groupName?: string;
tel?: string;
address?: string;
codeMeli?: string;
tarikhTavalod?: string;
moaref?: string;
codeMoaref?: string;
moshtariCode?: number;
jensFelez?: number;
}): unknown[] {
return [
p.name,
p.groupName ?? 'CRM',
p.tel ?? '',
p.address ?? '',
p.codeMeli ?? '',
p.tarikhTavalod ?? '',
p.moaref ?? '',
p.codeMoaref ?? '-1',
p.moshtariCode ?? -1,
p.jensFelez ?? 0,
];
},
};