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