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 = ? `).get(id, userId); } module.exports = { SOURCE_TYPE_LABELS, decorateDataSource, decorateTransaction, ensureManualDataSource, getTransactionForUser, sourceLabel, sourceTypeLabel, };