feat: assign deterministic operation UUIDs
This commit is contained in:
@@ -133,6 +133,16 @@ const migrations: { name: string; sql: string }[] = [
|
||||
AND NOT EXISTS (SELECT 1 FROM category_rules LIMIT 1);
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: '006_transaction_operation_uuid',
|
||||
sql: `
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
ALTER TABLE transactions
|
||||
ADD COLUMN IF NOT EXISTS operation_id UUID NOT NULL DEFAULT gen_random_uuid();
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_transactions_account_operation_id
|
||||
ON transactions(account_id, operation_id);
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: '005_imports_table',
|
||||
sql: `
|
||||
|
||||
@@ -9,6 +9,7 @@ const TRANSFER_PHRASES = [
|
||||
'внутри втб',
|
||||
];
|
||||
const CASHBACK_KEYWORD = 'зачисление';
|
||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
|
||||
function computeFingerprint(
|
||||
accountNumber: string,
|
||||
@@ -99,6 +100,9 @@ function validateStructure(body: unknown): ValidationError | null {
|
||||
if (t.sourceId !== undefined && (typeof t.sourceId !== 'string' || !t.sourceId.trim())) {
|
||||
return { status: 400, error: 'BAD_REQUEST', message: `transactions[${i}].sourceId must be a non-empty string when provided` };
|
||||
}
|
||||
if (t.operationId !== undefined && (typeof t.operationId !== 'string' || !UUID_RE.test(t.operationId))) {
|
||||
return { status: 400, error: 'BAD_REQUEST', message: `transactions[${i}].operationId must be a valid UUID when provided` };
|
||||
}
|
||||
if (typeof t.amountSigned !== 'number' || !Number.isInteger(t.amountSigned)) {
|
||||
return { status: 400, error: 'BAD_REQUEST', message: `transactions[${i}].amountSigned must be an integer` };
|
||||
}
|
||||
@@ -212,11 +216,11 @@ export async function importStatement(
|
||||
|
||||
const result = await client.query(
|
||||
`INSERT INTO transactions
|
||||
(account_id, operation_at, amount_signed, commission, description, direction, fingerprint, category_id, is_category_confirmed, import_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
(account_id, operation_id, operation_at, amount_signed, commission, description, direction, fingerprint, category_id, is_category_confirmed, import_id)
|
||||
VALUES ($1, COALESCE($2::uuid, gen_random_uuid()), $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT (account_id, fingerprint) DO NOTHING
|
||||
RETURNING id`,
|
||||
[accountId, tx.operationAt, tx.amountSigned, tx.commission, tx.description, dir, fp, categoryId, isCategoryConfirmed, importId],
|
||||
[accountId, tx.operationId ?? null, tx.operationAt, tx.amountSigned, tx.commission, tx.description, dir, fp, categoryId, isCategoryConfirmed, importId],
|
||||
);
|
||||
|
||||
if (result.rows.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user