fix: make broker XLSX import atomic

This commit is contained in:
2026-08-26 23:42:26 +03:00
parent 000c77f162
commit 12d096beeb
6 changed files with 86 additions and 18 deletions

View File

@@ -1,4 +1,5 @@
import crypto from 'crypto';
import type { PoolClient } from 'pg';
import { pool } from '../db/pool';
import type { ImportPortfolioResponse, PortfolioFile, PortfolioTrade } from '@family-budget/shared';
@@ -45,13 +46,14 @@ export function validatePortfolio(body: unknown): asserts body is PortfolioFile
}
}
export async function importPortfolio(body: unknown, db: Pick<typeof pool, 'connect'> = pool): Promise<ImportPortfolioResponse> {
export async function importPortfolio(body: unknown, db: Pick<typeof pool, 'connect'> = pool, transactionClient?: PoolClient): Promise<ImportPortfolioResponse> {
validatePortfolio(body);
const data = body;
const sourceHash = crypto.createHash('sha256').update(JSON.stringify(data)).digest('hex');
const client = await db.connect();
const client = transactionClient ?? await db.connect();
const ownsTransaction = transactionClient == null;
try {
await client.query('BEGIN');
if (ownsTransaction) await client.query('BEGIN');
const accountResult = await client.query(
`INSERT INTO accounts (bank, account_number, currency, account_type)
VALUES ($1, $2, 'RUB', 'brokerage')
@@ -69,7 +71,7 @@ export async function importPortfolio(body: unknown, db: Pick<typeof pool, 'conn
);
if (reportResult.rows.length === 0) {
const existing = await client.query('SELECT id FROM portfolio_reports WHERE account_id = $1 AND source_hash = $2', [accountId, sourceHash]);
await client.query('COMMIT');
if (ownsTransaction) await client.query('COMMIT');
return { accountId, reportId: Number(existing.rows[0].id), importedTrades: 0, duplicateTrades: data.trades.length, positions: 0 };
}
const reportId = Number(reportResult.rows[0].id);
@@ -92,12 +94,12 @@ export async function importPortfolio(body: unknown, db: Pick<typeof pool, 'conn
);
importedTrades += result.rowCount ?? 0;
}
await client.query('COMMIT');
if (ownsTransaction) await client.query('COMMIT');
return { accountId, reportId, importedTrades, duplicateTrades: data.trades.length - importedTrades, positions: data.positions.length };
} catch (error) {
await client.query('ROLLBACK');
if (ownsTransaction) await client.query('ROLLBACK');
throw error;
} finally {
client.release();
if (ownsTransaction) client.release();
}
}