BillTracker/services/transactionService.js

104 lines
3.5 KiB
JavaScript
Raw Normal View History

2026-05-16 20:26:09 -05:00
const SOURCE_TYPE_LABELS = {
manual: 'Manual',
file_import: 'File import',
provider_sync: 'Provider sync',
};
function sourceTypeLabel(type) {
return SOURCE_TYPE_LABELS[type] || String(type || 'Unknown');
}
function sourceLabel(source = {}) {
if (source.type === 'manual' || source.provider === 'manual') return source.name || 'Manual Entry';
if (source.name && source.provider) return `${source.name} (${source.provider})`;
return source.name || source.provider || sourceTypeLabel(source.type);
}
function decorateDataSource(row) {
if (!row) return null;
const safe = { ...row };
delete safe.encrypted_secret;
return {
...safe,
source_label: sourceLabel(row),
source_type_label: sourceTypeLabel(row.type),
};
}
function decorateTransaction(row) {
if (!row) return null;
const source = row.data_source_id ? {
id: row.data_source_id,
user_id: row.user_id,
type: row.data_source_type || row.source_type,
provider: row.data_source_provider,
name: row.data_source_name,
status: row.data_source_status,
} : null;
return {
...row,
source_label: source ? sourceLabel(source) : sourceTypeLabel(row.source_type),
source_type_label: sourceTypeLabel(row.source_type),
data_source: source ? decorateDataSource(source) : null,
};
}
function ensureManualDataSource(db, userId) {
const existing = db.prepare(`
SELECT *
FROM data_sources
WHERE user_id = ? AND type = 'manual' AND provider = 'manual'
ORDER BY id ASC
LIMIT 1
`).get(userId);
if (existing) {
if (existing.name !== 'Manual Entry' || existing.status !== 'active') {
db.prepare(`
UPDATE data_sources
SET name = 'Manual Entry', status = 'active', updated_at = datetime('now')
WHERE id = ? AND user_id = ?
`).run(existing.id, userId);
return db.prepare('SELECT * FROM data_sources WHERE id = ? AND user_id = ?').get(existing.id, userId);
}
return existing;
}
const result = db.prepare(`
INSERT INTO data_sources (user_id, type, provider, name, status)
VALUES (?, 'manual', 'manual', 'Manual Entry', 'active')
`).run(userId);
return db.prepare('SELECT * FROM data_sources WHERE id = ? AND user_id = ?').get(result.lastInsertRowid, userId);
}
function getTransactionForUser(db, userId, id) {
return db.prepare(`
SELECT
t.id, t.user_id, t.data_source_id, t.account_id, t.provider_transaction_id,
t.source_type, t.transaction_type, t.posted_date, t.transacted_at, t.amount,
t.currency, t.description, t.payee, t.memo, t.category, t.matched_bill_id,
t.match_status, t.ignored, t.created_at, t.updated_at,
ds.type AS data_source_type, ds.provider AS data_source_provider,
ds.name AS data_source_name, ds.status AS data_source_status,
fa.name AS account_name, fa.org_name AS account_org_name,
fa.account_type AS account_type,
b.name AS matched_bill_name
FROM transactions t
LEFT JOIN data_sources ds ON ds.id = t.data_source_id AND ds.user_id = t.user_id
LEFT JOIN financial_accounts fa ON fa.id = t.account_id AND fa.user_id = t.user_id
LEFT JOIN bills b ON b.id = t.matched_bill_id AND b.user_id = t.user_id AND b.deleted_at IS NULL
WHERE t.id = ? AND t.user_id = ?
AND (t.account_id IS NULL OR fa.id IS NULL OR fa.monitored = 1)
2026-05-16 20:26:09 -05:00
`).get(id, userId);
}
module.exports = {
SOURCE_TYPE_LABELS,
decorateDataSource,
decorateTransaction,
ensureManualDataSource,
getTransactionForUser,
sourceLabel,
sourceTypeLabel,
};