Encrypt tokens server-side, sync customers both ways, show balances/documents on the person Assets tab, and push sanads with idempotent Factor_Code logging. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.2 KiB
TypeScript
133 lines
4.2 KiB
TypeScript
import {
|
|
BadRequestException,
|
|
Injectable,
|
|
NotFoundException,
|
|
} from '@nestjs/common';
|
|
import { Prisma } from '@prisma/client';
|
|
import { PrismaService } from '../../prisma/prisma.service';
|
|
import { TahesabConnectionsService } from './connections.service';
|
|
import { normalizeMandehResponse } from './tahesab-methods';
|
|
|
|
@Injectable()
|
|
export class TahesabBalanceService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly connections: TahesabConnectionsService,
|
|
) {}
|
|
|
|
async getAssets(
|
|
organizationId: string,
|
|
personId: string,
|
|
connectionId?: string,
|
|
) {
|
|
const person = await this.prisma.person.findFirst({
|
|
where: { id: personId, organizationId },
|
|
});
|
|
if (!person) throw new NotFoundException('شخص یافت نشد.');
|
|
|
|
const resolvedId = await this.connections.resolveConnectionId(
|
|
organizationId,
|
|
connectionId,
|
|
);
|
|
|
|
const link = await this.prisma.personAccountingLink.findFirst({
|
|
where: { personId, connectionId: resolvedId },
|
|
include: {
|
|
connection: {
|
|
select: { id: true, label: true, dbName: true, isDefault: true },
|
|
},
|
|
balanceCache: true,
|
|
},
|
|
});
|
|
if (!link) {
|
|
throw new BadRequestException(
|
|
'این شخص به اتصال تهحساب لینک نشده است. ابتدا تطبیق یا ثبت در تهحساب را انجام دهید.',
|
|
);
|
|
}
|
|
|
|
const { api } = await this.connections.getApi(organizationId, resolvedId);
|
|
const raw = await api.getMandeHesabByCode(link.moshtariCode);
|
|
const normalized = normalizeMandehResponse(raw);
|
|
|
|
const cache = await this.prisma.accountingBalanceCache.upsert({
|
|
where: { linkId: link.id },
|
|
create: {
|
|
linkId: link.id,
|
|
connectionId: resolvedId,
|
|
goldBalance: normalized.goldBalance,
|
|
rialBalance: normalized.rialBalance,
|
|
currencyBalance: normalized.currencyBalance,
|
|
totalGoldEquiv: normalized.totalGoldEquiv,
|
|
mandehDetail: normalized.mandehDetail as unknown as Prisma.InputJsonValue,
|
|
rawResponse: normalized.raw as Prisma.InputJsonValue,
|
|
fetchedAt: new Date(),
|
|
},
|
|
update: {
|
|
goldBalance: normalized.goldBalance,
|
|
rialBalance: normalized.rialBalance,
|
|
currencyBalance: normalized.currencyBalance,
|
|
totalGoldEquiv: normalized.totalGoldEquiv,
|
|
mandehDetail: normalized.mandehDetail as unknown as Prisma.InputJsonValue,
|
|
rawResponse: normalized.raw as Prisma.InputJsonValue,
|
|
fetchedAt: new Date(),
|
|
},
|
|
});
|
|
|
|
await this.prisma.personAccountingLink.update({
|
|
where: { id: link.id },
|
|
data: { lastBalanceSyncedAt: cache.fetchedAt },
|
|
});
|
|
|
|
return {
|
|
connectionId: resolvedId,
|
|
connectionLabel: link.connection.label,
|
|
dbName: link.connection.dbName,
|
|
moshtariCode: link.moshtariCode,
|
|
goldBalance: normalized.goldBalance,
|
|
rialBalance: normalized.rialBalance,
|
|
currencyBalance: normalized.currencyBalance,
|
|
totalGoldEquiv: normalized.totalGoldEquiv,
|
|
mandehDetail: normalized.mandehDetail,
|
|
fetchedAt: cache.fetchedAt.toISOString(),
|
|
};
|
|
}
|
|
|
|
async getCachedOrFetch(
|
|
organizationId: string,
|
|
personId: string,
|
|
connectionId?: string,
|
|
refresh = true,
|
|
) {
|
|
if (refresh) {
|
|
return this.getAssets(organizationId, personId, connectionId);
|
|
}
|
|
const resolvedId = await this.connections.resolveConnectionId(
|
|
organizationId,
|
|
connectionId,
|
|
);
|
|
const link = await this.prisma.personAccountingLink.findFirst({
|
|
where: { personId, connectionId: resolvedId },
|
|
include: {
|
|
connection: { select: { label: true, dbName: true } },
|
|
balanceCache: true,
|
|
},
|
|
});
|
|
if (!link?.balanceCache) {
|
|
return this.getAssets(organizationId, personId, connectionId);
|
|
}
|
|
const c = link.balanceCache;
|
|
return {
|
|
connectionId: resolvedId,
|
|
connectionLabel: link.connection.label,
|
|
dbName: link.connection.dbName,
|
|
moshtariCode: link.moshtariCode,
|
|
goldBalance: c.goldBalance,
|
|
rialBalance: c.rialBalance,
|
|
currencyBalance: c.currencyBalance,
|
|
totalGoldEquiv: c.totalGoldEquiv,
|
|
mandehDetail: c.mandehDetail,
|
|
fetchedAt: c.fetchedAt.toISOString(),
|
|
};
|
|
}
|
|
}
|