kinda there
This commit is contained in:
578
src/lib/db.js
578
src/lib/db.js
@ -1,173 +1,211 @@
|
||||
// db.js
|
||||
import postgres from 'postgres'
|
||||
import postgres from 'postgres';
|
||||
|
||||
const db = postgres({ host:'192.168.1.126', username:'budget', password:'budget', database:'budget'}) // will use psql environment variables
|
||||
const db = postgres({
|
||||
host: '192.168.1.126',
|
||||
username: 'budget',
|
||||
password: 'budget',
|
||||
database: 'budget'
|
||||
}); // will use psql environment variables
|
||||
|
||||
export { db };
|
||||
|
||||
export async function getTotal() {
|
||||
const result = await db`
|
||||
select sum(balance) as total
|
||||
from account
|
||||
where in_total is true
|
||||
`;
|
||||
// result = Result [{ total: 1000.00 }]
|
||||
return result[0].total;
|
||||
}
|
||||
|
||||
export async function setAccountInTotal(accountId, total) {
|
||||
return await db`
|
||||
return await db`
|
||||
update account
|
||||
set in_total = ${total}
|
||||
where id = ${accountId}
|
||||
`
|
||||
`;
|
||||
}
|
||||
|
||||
export async function setAccountHide(accountId, hide) {
|
||||
return await db`
|
||||
return await db`
|
||||
update account
|
||||
set hide = ${hide}
|
||||
where id = ${accountId}
|
||||
`
|
||||
`;
|
||||
}
|
||||
|
||||
export async function addBudget(name, notes) {
|
||||
const result = await db`
|
||||
insert into budget (name, notes)
|
||||
values (${name}, ${notes})
|
||||
returning id
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result[0].id;
|
||||
}
|
||||
|
||||
export async function deleteBudget(id) {
|
||||
return await db`
|
||||
UPDATE budget
|
||||
SET delete = true
|
||||
WHERE id = ${id}
|
||||
`
|
||||
}
|
||||
|
||||
export async function createBudgetTable() {
|
||||
return await db`
|
||||
create table if not exists budget (
|
||||
id serial primary key,
|
||||
name text not null,
|
||||
amount numeric(10,2) not null,
|
||||
notes text,
|
||||
delete boolean default false
|
||||
)
|
||||
`
|
||||
}
|
||||
|
||||
export async function createBudgetTransactionTable() {
|
||||
return await db`
|
||||
create table if not exists budget_transaction (
|
||||
id serial primary key,
|
||||
budget_id integer not null references budget(id),
|
||||
transaction_id text not null references transaction(id),
|
||||
amount numeric(10,2) not null
|
||||
)`
|
||||
}
|
||||
|
||||
export async function addBudget(name, amount, notes) {
|
||||
const result = await db`
|
||||
insert into budget (name, amount, notes)
|
||||
values (${name}, ${amount}, ${notes})
|
||||
returning id
|
||||
`
|
||||
// result = Result [{ id: 1 }]
|
||||
return result[0].id
|
||||
}
|
||||
|
||||
export async function deleteBudget(id) {
|
||||
const result = await db`
|
||||
delete from budget
|
||||
const result = await db`
|
||||
update budget
|
||||
set delete = true
|
||||
where id = ${id}
|
||||
`
|
||||
// result = Result [{ id: 1 }]
|
||||
return result
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function restoreBudget(id) {
|
||||
const result = await db`
|
||||
update budget
|
||||
set delete = false
|
||||
where id = ${id}
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function updateBudget(id, name, amount, notes) {
|
||||
const result = await db`
|
||||
const result = await db`
|
||||
update budget
|
||||
set name = ${name},
|
||||
amount = ${amount},
|
||||
notes = ${notes}
|
||||
where id = ${id}
|
||||
`
|
||||
// result = Result [{ id: 1 }]
|
||||
return result
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export async function getBudgetTransactions(id) {
|
||||
// Fetch all transactions associated with a specific budget
|
||||
try {
|
||||
const transactions = await db`
|
||||
// Fetch all transactions associated with a specific budget
|
||||
try {
|
||||
let transactions = await db`
|
||||
select
|
||||
transaction.id as id,
|
||||
transaction.posted as posted,
|
||||
transaction.amount as amount,
|
||||
transaction.description as description,
|
||||
transaction.pending as pending,
|
||||
transaction.notes as notes,
|
||||
budget_transaction.notes as notes,
|
||||
transaction.payee as payee,
|
||||
budget_transaction.amount as budget_amount,
|
||||
budget_transaction.id as budget_transaction_id
|
||||
from budget_transaction
|
||||
join transaction on budget_transaction.transaction_id = transaction.id
|
||||
where budget_transaction.budget_id = ${id}
|
||||
order by transaction.posted desc
|
||||
`
|
||||
|
||||
// transactions = Result [{ id: 1, posted: 1633036800, amount: 50.00, description: "Grocery Store", pending: false, notes: "Weekly groceries" }, ...]
|
||||
return { transactions }
|
||||
}
|
||||
catch {
|
||||
await createBudgetTransactionTable();
|
||||
return []
|
||||
}
|
||||
`;
|
||||
console.log(`Fetched ${transactions.length} transactions for budget ${id}`);
|
||||
transactions = transactions.map((t) => ({
|
||||
...t,
|
||||
date: new Date(t.posted * 1000)
|
||||
}));
|
||||
// transactions = Result [{ id: 1, posted: 1633036800, amount: 50.00, description: "Grocery Store", pending: false, notes: "Weekly groceries" }, ...]
|
||||
return { transactions };
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteBudgetTransaction(budgetId, transactionId) {
|
||||
// Delete a transaction from a budget
|
||||
const result = await db`
|
||||
export async function updateBudgetTransaction(id, amount, notes) {
|
||||
// Delete a transaction from a budget
|
||||
const result = await db`
|
||||
update from budget_transaction
|
||||
where id= ${id}
|
||||
SET amount = ${amount}, notes = ${notes}
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function deleteBudgetTransaction(id) {
|
||||
// Delete a transaction from a budget
|
||||
const result = await db`
|
||||
delete from budget_transaction
|
||||
where budget_id = ${budgetId} and transaction_id = ${transactionId}
|
||||
`
|
||||
// result = Result [{ id: 1 }]
|
||||
return result
|
||||
where id = ${id}
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function addBudgetTransaction(budgetId, transactionId, amount) {
|
||||
// Add a transaction to a budget
|
||||
const result = await db`
|
||||
insert into budget_transaction (budget_id, transaction_id, amount)
|
||||
values (${budgetId}, ${transactionId}, ${amount})
|
||||
export async function addBudgetTransaction(budgetId, transactionId, amount, notes, ruleId = null) {
|
||||
const existingTransactions = await db`
|
||||
select amount from budget_transaction
|
||||
where transaction_id = ${transactionId}
|
||||
`;
|
||||
const realTransactionAmount = await db`
|
||||
select amount from transaction
|
||||
where id = ${transactionId}
|
||||
`;
|
||||
|
||||
if (existingTransactions.length > 0) {
|
||||
if (
|
||||
existingTransactions.reduce((acc, curr) => acc + curr.amount, 0) + amount >
|
||||
realTransactionAmount
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a transaction to a budget
|
||||
const result = await db`
|
||||
insert into budget_transaction (budget_id, transaction_id, amount, notes, rule_id)
|
||||
values (${budgetId}, ${transactionId}, ${amount}, ${notes}, ${ruleId ?? null})
|
||||
returning id
|
||||
`
|
||||
// result = Result [{ id: 1 }]
|
||||
return result[0].id
|
||||
`;
|
||||
// result = Result [{ id: 1 }]
|
||||
return result[0].id;
|
||||
}
|
||||
|
||||
export async function getBudgets() {
|
||||
const budgets = await db`
|
||||
const budgets = await db`
|
||||
select
|
||||
budget.id as id,
|
||||
budget.name as name,
|
||||
budget.amount as amount,
|
||||
budget.sum as sum,
|
||||
budget.notes as notes
|
||||
from budget
|
||||
from budget_with_sum as budget
|
||||
WHERE budget.delete is false
|
||||
`
|
||||
if (!budgets) {
|
||||
await createBudgetTable();
|
||||
return await getBudgets()
|
||||
}
|
||||
// budgets = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return budgets
|
||||
`;
|
||||
// budgets = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return budgets;
|
||||
}
|
||||
|
||||
export async function getBudget(id) {
|
||||
const budget = await db`
|
||||
select
|
||||
budget.id as id,
|
||||
budget.name as name,
|
||||
budget.sum as sum,
|
||||
budget.notes as notes,
|
||||
budget.delete as delete
|
||||
from budget_with_sum as budget
|
||||
where budget.id = ${id}
|
||||
`;
|
||||
// budget = Result [{ id: 1, name: "Groceries", notes: "Monthly grocery budget" }]
|
||||
if (!budget || budget.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return budget[0];
|
||||
}
|
||||
|
||||
export async function getDeletedBudgets() {
|
||||
const budgets = await db`
|
||||
const budgets = await db`
|
||||
select
|
||||
budget.id as id,
|
||||
budget.name as name,
|
||||
budget.amount as amount,
|
||||
budget.notes as notes
|
||||
from budget
|
||||
WHERE budget.delete is true
|
||||
`
|
||||
if (!budgets) {
|
||||
await createBudgetTable();
|
||||
return await getBudgets()
|
||||
}
|
||||
// budgets = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return budgets
|
||||
`;
|
||||
// budgets = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return budgets;
|
||||
}
|
||||
|
||||
export async function getAccount(id) {
|
||||
const account = await db`
|
||||
const account = await db`
|
||||
select
|
||||
account.id as id,
|
||||
account.name as name,
|
||||
@ -184,73 +222,89 @@ export async function getAccount(id) {
|
||||
from account
|
||||
left join org on account.org_id = org.id
|
||||
where account.id = ${id}
|
||||
`
|
||||
if (!account || account.length === 0) {
|
||||
return null
|
||||
}
|
||||
return account[0];
|
||||
`;
|
||||
if (!account || account.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return account[0];
|
||||
}
|
||||
|
||||
export async function getAccounts(age) {
|
||||
const accounts = await db`
|
||||
const accounts = await db`
|
||||
select
|
||||
account.id as id,
|
||||
account.name as name,
|
||||
balance
|
||||
from account
|
||||
where hide is false
|
||||
`
|
||||
// users = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return accounts
|
||||
`;
|
||||
// users = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return accounts;
|
||||
}
|
||||
|
||||
export async function getBudgetTransactionsForAccount(accountID) {
|
||||
const transactions = await db`
|
||||
select budget_transaction.id as id,
|
||||
budget_transaction.budget_id as budget_id,
|
||||
budget_transaction.transaction_id as transaction_id,
|
||||
budget_transaction.amount as amount,
|
||||
budget_transaction.notes as notes
|
||||
from budget_transaction
|
||||
join transaction on budget_transaction.transaction_id = transaction.id
|
||||
where transaction.account_id = ${accountID}
|
||||
`;
|
||||
return transactions;
|
||||
}
|
||||
|
||||
export async function getHiddenAccounts(age) {
|
||||
const accounts = await db`
|
||||
const accounts = await db`
|
||||
select
|
||||
account.id as id,
|
||||
account.name as name
|
||||
from account
|
||||
where account.hide is true
|
||||
`
|
||||
// users = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return accounts
|
||||
`;
|
||||
// users = Result [{ name: "Walter", age: 80 }, { name: 'Murray', age: 68 }, ...]
|
||||
return accounts;
|
||||
}
|
||||
|
||||
export async function getTransactions(accountId) {
|
||||
let transactions = await db`
|
||||
let transactions = await db`
|
||||
select
|
||||
transaction.id as id,
|
||||
transaction.posted as posted,
|
||||
transaction.amount as amount,
|
||||
transaction.description as description,
|
||||
transaction.pending as pending,
|
||||
transaction.notes as notes
|
||||
transaction.notes as notes,
|
||||
transaction.payee as payee
|
||||
from transaction
|
||||
where account_id = ${accountId}
|
||||
order by posted desc
|
||||
`
|
||||
transactions = transactions.map((t) => ({
|
||||
...t, date: new Date(t.posted * 1000)
|
||||
}));
|
||||
return transactions
|
||||
`;
|
||||
transactions = transactions.map((t) => ({
|
||||
...t,
|
||||
date: new Date(t.posted * 1000)
|
||||
}));
|
||||
return transactions;
|
||||
}
|
||||
|
||||
export async function setTransactionNote(transactionId, note) {
|
||||
const result = await db`
|
||||
const result = await db`
|
||||
update transaction
|
||||
set notes = ${note}
|
||||
where id = ${transactionId}
|
||||
`
|
||||
return result
|
||||
`;
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function updateAccounts(data) {
|
||||
try {
|
||||
console.log('Updating accounts with data:', data);
|
||||
for (const account of data.accounts) {
|
||||
// Upsert Org
|
||||
console.log(`Upserting org for account: ${account.id}`, account.org);
|
||||
await db`
|
||||
try {
|
||||
console.log('Updating accounts with data:', data);
|
||||
for (const account of data.accounts) {
|
||||
// Upsert Org
|
||||
console.log(`Upserting org for account: ${account.id}`, account.org);
|
||||
await db`
|
||||
insert into org (id, domain, name, sfin_url, url)
|
||||
values (${account.org.id}, ${account.org.domain ?? null}, ${account.org.name ?? null}, ${account.org.sfin_url ?? null}, ${account.org.url ?? null})
|
||||
on conflict (id) do update set
|
||||
@ -259,9 +313,9 @@ export async function updateAccounts(data) {
|
||||
sfin_url = excluded.sfin_url,
|
||||
url = excluded.url
|
||||
`;
|
||||
console.log(`Upserting account: ${account.id} (${account.name})`);
|
||||
// Upsert Account
|
||||
await db`
|
||||
console.log(`Upserting account: ${account.id} (${account.name})`);
|
||||
// Upsert Account
|
||||
await db`
|
||||
insert into account (id, org_id, name, currency, balance, available_balance, balance_date)
|
||||
values (
|
||||
${account.id},
|
||||
@ -270,7 +324,7 @@ export async function updateAccounts(data) {
|
||||
${account.currency ?? null},
|
||||
${account.balance ?? null},
|
||||
${account.available_balance ?? null},
|
||||
${account.balance_date ?? null}
|
||||
${account.balance_date ?? null}
|
||||
)
|
||||
on conflict (id) do update set
|
||||
org_id = excluded.org_id,
|
||||
@ -280,35 +334,35 @@ export async function updateAccounts(data) {
|
||||
available_balance = excluded.available_balance,
|
||||
balance_date = excluded.balance_date
|
||||
`;
|
||||
|
||||
// Upsert Transactions
|
||||
if (account.transactions && account.transactions.length > 0) {
|
||||
for (const txn of account.transactions) {
|
||||
let extraId = null;
|
||||
console.log(`Upserting transaction: ${txn.id} for account: ${account.id}`);
|
||||
if (txn.extra) {
|
||||
// Upsert TransactionExtra (insert only, update not needed for category)
|
||||
const extraResult = await db`
|
||||
|
||||
// Upsert Transactions
|
||||
if (account.transactions && account.transactions.length > 0) {
|
||||
for (const txn of account.transactions) {
|
||||
let extraId = null;
|
||||
console.log(`Upserting transaction: ${txn.id} for account: ${account.id}`);
|
||||
if (txn.extra) {
|
||||
// Upsert TransactionExtra (insert only, update not needed for category)
|
||||
const extraResult = await db`
|
||||
insert into transaction_extra (category)
|
||||
values (${txn.extra.category ?? null})
|
||||
on conflict (category) do nothing
|
||||
returning id
|
||||
`;
|
||||
if (extraResult.length > 0) {
|
||||
extraId = extraResult[0].id;
|
||||
} else {
|
||||
// If already exists, fetch id
|
||||
const existing = await db`
|
||||
if (extraResult.length > 0) {
|
||||
extraId = extraResult[0].id;
|
||||
} else {
|
||||
// If already exists, fetch id
|
||||
const existing = await db`
|
||||
select id from transaction_extra where category = ${txn.extra.category ?? null}
|
||||
`;
|
||||
if (existing.length > 0) {
|
||||
extraId = existing[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Preparing to upsert transaction: ${txn.id} with data:`, txn);
|
||||
await db`
|
||||
insert into transaction (id, account_id, posted, amount, description, pending, transacted_at)
|
||||
if (existing.length > 0) {
|
||||
extraId = existing[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Preparing to upsert transaction: ${txn.id} with data:`, txn);
|
||||
await db`
|
||||
insert into transaction (id, account_id, posted, amount, description, pending, transacted_at, payee)
|
||||
values (
|
||||
${txn.id},
|
||||
${account.id},
|
||||
@ -316,7 +370,8 @@ export async function updateAccounts(data) {
|
||||
${txn.amount ?? null},
|
||||
${txn.description ?? null},
|
||||
${txn.pending ?? false},
|
||||
${txn.transacted_at ?? 0}
|
||||
${txn.transacted_at ?? 0},
|
||||
${txn.payee ?? null}
|
||||
)
|
||||
on conflict (id) do update set
|
||||
account_id = excluded.account_id,
|
||||
@ -324,17 +379,200 @@ export async function updateAccounts(data) {
|
||||
amount = excluded.amount,
|
||||
description = excluded.description,
|
||||
pending = excluded.pending,
|
||||
transacted_at = excluded.transacted_at
|
||||
transacted_at = excluded.transacted_at,
|
||||
payee = excluded.payee
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('updateAccounts error:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('updateAccounts error:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRules(data) {
|
||||
try {
|
||||
const rules = await db`
|
||||
select
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
payee,
|
||||
amount,
|
||||
transdescription,
|
||||
action,
|
||||
priority,
|
||||
action_amount,
|
||||
account_id,
|
||||
amount_is_precent,
|
||||
use_priority
|
||||
from rules
|
||||
order by priority asc
|
||||
`;
|
||||
return rules;
|
||||
} catch (error) {
|
||||
console.error('getRules error:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export default db
|
||||
export async function addRule(
|
||||
name,
|
||||
description,
|
||||
payee,
|
||||
amount,
|
||||
transdescription,
|
||||
action,
|
||||
actionAmount,
|
||||
account,
|
||||
amount_is_precent,
|
||||
priority,
|
||||
use_priority
|
||||
) {
|
||||
try {
|
||||
const result = await db`
|
||||
insert into rules (name, description, payee, amount, transdescription, action, action_amount, account_id, amount_is_precent, priority, use_priority)
|
||||
values (${name}, ${description}, ${payee}, ${amount}, ${transdescription}, ${action}, ${actionAmount}, ${account}, ${amount_is_precent}, ${priority}, ${use_priority})
|
||||
`;
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('addRule error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRule(id) {
|
||||
try {
|
||||
const result = await db`
|
||||
delete from rules
|
||||
where id = ${id}
|
||||
`;
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('deleteRule error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateRule(
|
||||
name,
|
||||
description,
|
||||
payee,
|
||||
amount,
|
||||
transdescription,
|
||||
action,
|
||||
actionAmount,
|
||||
account,
|
||||
amount_is_precent,
|
||||
priority,
|
||||
use_priority,
|
||||
id
|
||||
) {
|
||||
console.log(`Updating rule with id: ${id}`);
|
||||
try {
|
||||
const result = await db`
|
||||
update rules
|
||||
set name = ${name},
|
||||
description = ${description},
|
||||
payee = ${payee},
|
||||
amount = ${amount},
|
||||
transdescription = ${transdescription},
|
||||
action = ${action},
|
||||
action_amount = ${actionAmount},
|
||||
account_id = ${account ?? null},
|
||||
amount_is_precent = ${amount_is_precent},
|
||||
priority = ${priority},
|
||||
use_priority = ${use_priority}
|
||||
where id = ${id}
|
||||
`;
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('updateRule error:', error);
|
||||
console.log(`
|
||||
update rules
|
||||
set name = ${name},
|
||||
description = ${description},
|
||||
payee = ${payee},
|
||||
amount = ${amount},
|
||||
transdescription = ${transdescription},
|
||||
action = ${action},
|
||||
action_amount = ${actionAmount},
|
||||
account_id = ${account ?? null},
|
||||
amount_is_precent = ${amount_is_precent},
|
||||
use_priority = ${use_priority}
|
||||
where id = ${id}
|
||||
`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function runRules() {
|
||||
try {
|
||||
const rules = await getRules();
|
||||
let transactions = await db`
|
||||
select id, account_id, payee, amount, description from transaction
|
||||
`;
|
||||
let budgetTransactions = await db`
|
||||
select id, transaction_id, budget_id, notes, amount, rule_id from budget_transaction
|
||||
`;
|
||||
|
||||
console.log(`Running ${rules.length} rules on ${transactions.length} transactions`);
|
||||
|
||||
for (const rule of rules.sort((a, b) => a.priority - b.priority)) {
|
||||
console.log(
|
||||
`Running rule: ${rule.name} (${rule.id}, ${rule.payee}, ${rule.amount}, ${rule.transdescription})`
|
||||
);
|
||||
console.log(
|
||||
`Rule: payee: ${rule.payee}, amount: ${rule.amount}, transdescription: ${rule.transdescription}, account: ${rule.account_id}`
|
||||
);
|
||||
|
||||
const amountRE = new RegExp(rule.amount);
|
||||
const descriptionRE = new RegExp(rule.transdescription);
|
||||
console.log(rule.account_id);
|
||||
const accountRE = new RegExp(rule.account_id === null ? '.*' : rule.account_id);
|
||||
console.log(accountRE);
|
||||
const payeeRE = new RegExp(rule.payee);
|
||||
|
||||
for (const transaction of transactions) {
|
||||
if (
|
||||
amountRE.test(transaction.amount) &&
|
||||
descriptionRE.test(transaction.description) &&
|
||||
accountRE.test(transaction.account_id) &&
|
||||
payeeRE.test(transaction.payee)
|
||||
) {
|
||||
if (
|
||||
rule.action &&
|
||||
!budgetTransactions.some(
|
||||
(bt) => bt.transaction_id === transaction.id && bt.rule_id === rule.id
|
||||
)
|
||||
) {
|
||||
const amount = rule.amount_is_precent
|
||||
? (transaction.amount * rule.action_amount) / 100
|
||||
: rule.action_amount;
|
||||
const notes = `Rule - ${rule.name}`;
|
||||
|
||||
const budgetTransactionId = await addBudgetTransaction(
|
||||
rule.action,
|
||||
transaction.id,
|
||||
amount,
|
||||
notes,
|
||||
rule.id
|
||||
);
|
||||
await db`
|
||||
update transaction set processed = true
|
||||
where id = ${transaction.id}`;
|
||||
console.log(
|
||||
`Added budget transaction ${budgetTransactionId} for rule ${rule.name} on transaction ${transaction.id}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('runRules error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export default db;
|
||||
|
||||
38
src/lib/editSymbol.svelte
Normal file
38
src/lib/editSymbol.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<script module>
|
||||
export { EditSymbol };
|
||||
</script>
|
||||
|
||||
{#snippet EditSymbol(onClick)}
|
||||
<svg
|
||||
fill="#000000"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="25px"
|
||||
height="25px"
|
||||
viewBox="0 0 494.936 494.936"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
fill="#FEFEFE"
|
||||
d="M389.844,182.85c-6.743,0-12.21,5.467-12.21,12.21v222.968c0,23.562-19.174,42.735-42.736,42.735H67.157
|
||||
c-23.562,0-42.736-19.174-42.736-42.735V150.285c0-23.562,19.174-42.735,42.736-42.735h267.741c6.743,0,12.21-5.467,12.21-12.21
|
||||
s-5.467-12.21-12.21-12.21H67.157C30.126,83.13,0,113.255,0,150.285v267.743c0,37.029,30.126,67.155,67.157,67.155h267.741
|
||||
c37.03,0,67.156-30.126,67.156-67.155V195.061C402.054,188.318,396.587,182.85,389.844,182.85z"
|
||||
/>
|
||||
<path
|
||||
fill="#FEFEFE"
|
||||
d="M483.876,20.791c-14.72-14.72-38.669-14.714-53.377,0L221.352,229.944c-0.28,0.28-3.434,3.559-4.251,5.396l-28.963,65.069
|
||||
c-2.057,4.619-1.056,10.027,2.521,13.6c2.337,2.336,5.461,3.576,8.639,3.576c1.675,0,3.362-0.346,4.96-1.057l65.07-28.963
|
||||
c1.83-0.815,5.114-3.97,5.396-4.25L483.876,74.169c7.131-7.131,11.06-16.61,11.06-26.692
|
||||
C494.936,37.396,491.007,27.915,483.876,20.791z M466.61,56.897L257.457,266.05c-0.035,0.036-0.055,0.078-0.089,0.107
|
||||
l-33.989,15.131L238.51,247.3c0.03-0.036,0.071-0.055,0.107-0.09L447.765,38.058c5.038-5.039,13.819-5.033,18.846,0.005
|
||||
c2.518,2.51,3.905,5.855,3.905,9.414C470.516,51.036,469.127,54.38,466.61,56.897z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
{/snippet}
|
||||
13
src/lib/loadingModal.svelte
Normal file
13
src/lib/loadingModal.svelte
Normal file
@ -0,0 +1,13 @@
|
||||
<script module>
|
||||
export { loadingModal };
|
||||
</script>
|
||||
|
||||
{#snippet loadingModal()}
|
||||
<dialog id="my_modal_1" class="modal modal-open">
|
||||
<div class="modal-box">
|
||||
<div class="flex items-center justify-center">
|
||||
<span class="loading loading-spinner loading-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{/snippet}
|
||||
51
src/lib/settingsSymbol.svelte
Normal file
51
src/lib/settingsSymbol.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<script module>
|
||||
export { settingsSymbol };
|
||||
</script>
|
||||
|
||||
{#snippet settingsSymbol()}
|
||||
<svg
|
||||
fill="#000000"
|
||||
height="20px"
|
||||
width="20px"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 478.703 478.703"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
fill="#FEFEFE"
|
||||
d="M454.2,189.101l-33.6-5.7c-3.5-11.3-8-22.2-13.5-32.6l19.8-27.7c8.4-11.8,7.1-27.9-3.2-38.1l-29.8-29.8
|
||||
c-5.6-5.6-13-8.7-20.9-8.7c-6.2,0-12.1,1.9-17.1,5.5l-27.8,19.8c-10.8-5.7-22.1-10.4-33.8-13.9l-5.6-33.2
|
||||
c-2.4-14.3-14.7-24.7-29.2-24.7h-42.1c-14.5,0-26.8,10.4-29.2,24.7l-5.8,34c-11.2,3.5-22.1,8.1-32.5,13.7l-27.5-19.8
|
||||
c-5-3.6-11-5.5-17.2-5.5c-7.9,0-15.4,3.1-20.9,8.7l-29.9,29.8c-10.2,10.2-11.6,26.3-3.2,38.1l20,28.1
|
||||
c-5.5,10.5-9.9,21.4-13.3,32.7l-33.2,5.6c-14.3,2.4-24.7,14.7-24.7,29.2v42.1c0,14.5,10.4,26.8,24.7,29.2l34,5.8
|
||||
c3.5,11.2,8.1,22.1,13.7,32.5l-19.7,27.4c-8.4,11.8-7.1,27.9,3.2,38.1l29.8,29.8c5.6,5.6,13,8.7,20.9,8.7c6.2,0,12.1-1.9,17.1-5.5
|
||||
l28.1-20c10.1,5.3,20.7,9.6,31.6,13l5.6,33.6c2.4,14.3,14.7,24.7,29.2,24.7h42.2c14.5,0,26.8-10.4,29.2-24.7l5.7-33.6
|
||||
c11.3-3.5,22.2-8,32.6-13.5l27.7,19.8c5,3.6,11,5.5,17.2,5.5l0,0c7.9,0,15.3-3.1,20.9-8.7l29.8-29.8c10.2-10.2,11.6-26.3,3.2-38.1
|
||||
l-19.8-27.8c5.5-10.5,10.1-21.4,13.5-32.6l33.6-5.6c14.3-2.4,24.7-14.7,24.7-29.2v-42.1
|
||||
C478.9,203.801,468.5,191.501,454.2,189.101z M451.9,260.401c0,1.3-0.9,2.4-2.2,2.6l-42,7c-5.3,0.9-9.5,4.8-10.8,9.9
|
||||
c-3.8,14.7-9.6,28.8-17.4,41.9c-2.7,4.6-2.5,10.3,0.6,14.7l24.7,34.8c0.7,1,0.6,2.5-0.3,3.4l-29.8,29.8c-0.7,0.7-1.4,0.8-1.9,0.8
|
||||
c-0.6,0-1.1-0.2-1.5-0.5l-34.7-24.7c-4.3-3.1-10.1-3.3-14.7-0.6c-13.1,7.8-27.2,13.6-41.9,17.4c-5.2,1.3-9.1,5.6-9.9,10.8l-7.1,42
|
||||
c-0.2,1.3-1.3,2.2-2.6,2.2h-42.1c-1.3,0-2.4-0.9-2.6-2.2l-7-42c-0.9-5.3-4.8-9.5-9.9-10.8c-14.3-3.7-28.1-9.4-41-16.8
|
||||
c-2.1-1.2-4.5-1.8-6.8-1.8c-2.7,0-5.5,0.8-7.8,2.5l-35,24.9c-0.5,0.3-1,0.5-1.5,0.5c-0.4,0-1.2-0.1-1.9-0.8l-29.8-29.8
|
||||
c-0.9-0.9-1-2.3-0.3-3.4l24.6-34.5c3.1-4.4,3.3-10.2,0.6-14.8c-7.8-13-13.8-27.1-17.6-41.8c-1.4-5.1-5.6-9-10.8-9.9l-42.3-7.2
|
||||
c-1.3-0.2-2.2-1.3-2.2-2.6v-42.1c0-1.3,0.9-2.4,2.2-2.6l41.7-7c5.3-0.9,9.6-4.8,10.9-10c3.7-14.7,9.4-28.9,17.1-42
|
||||
c2.7-4.6,2.4-10.3-0.7-14.6l-24.9-35c-0.7-1-0.6-2.5,0.3-3.4l29.8-29.8c0.7-0.7,1.4-0.8,1.9-0.8c0.6,0,1.1,0.2,1.5,0.5l34.5,24.6
|
||||
c4.4,3.1,10.2,3.3,14.8,0.6c13-7.8,27.1-13.8,41.8-17.6c5.1-1.4,9-5.6,9.9-10.8l7.2-42.3c0.2-1.3,1.3-2.2,2.6-2.2h42.1
|
||||
c1.3,0,2.4,0.9,2.6,2.2l7,41.7c0.9,5.3,4.8,9.6,10,10.9c15.1,3.8,29.5,9.7,42.9,17.6c4.6,2.7,10.3,2.5,14.7-0.6l34.5-24.8
|
||||
c0.5-0.3,1-0.5,1.5-0.5c0.4,0,1.2,0.1,1.9,0.8l29.8,29.8c0.9,0.9,1,2.3,0.3,3.4l-24.7,34.7c-3.1,4.3-3.3,10.1-0.6,14.7
|
||||
c7.8,13.1,13.6,27.2,17.4,41.9c1.3,5.2,5.6,9.1,10.8,9.9l42,7.1c1.3,0.2,2.2,1.3,2.2,2.6v42.1H451.9z"
|
||||
/>
|
||||
<path
|
||||
fill="#FEFEFE"
|
||||
d="M239.4,136.001c-57,0-103.3,46.3-103.3,103.3s46.3,103.3,103.3,103.3s103.3-46.3,103.3-103.3S296.4,136.001,239.4,136.001
|
||||
z M239.4,315.601c-42.1,0-76.3-34.2-76.3-76.3s34.2-76.3,76.3-76.3s76.3,34.2,76.3,76.3S281.5,315.601,239.4,315.601z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
{/snippet}
|
||||
@ -1,14 +1,17 @@
|
||||
const url =
|
||||
'https://19443E0E8171E175EC5DA0C69B35DD50197F234B9A74C00D27FD606121257ECF:DAA3702E2100CFFD3B544251E6D755E86B1EDDFBFCC7F6FA9CE77AB3677E60DE@beta-bridge.simplefin.org/simplefin';
|
||||
|
||||
export async function fetchAccounts(url, startDate) {
|
||||
const { username, password, origin, pathname } = new URL(url);
|
||||
const start = Math.floor(startDate.getTime() / 1000);
|
||||
const apiUrl = `${origin}${pathname}/accounts?start-date=${start}`;
|
||||
const headers = {};
|
||||
export async function fetchAccounts(startDate) {
|
||||
const { username, password, origin, pathname } = new URL(url);
|
||||
const apiUrl = `${origin}${pathname}/accounts?start-date=${startDate}`;
|
||||
const headers = {};
|
||||
|
||||
if (username && password) {
|
||||
headers['Authorization'] = 'Basic ' + btoa(`${username}:${password}`);
|
||||
}
|
||||
console.log(`Fetching accounts from: ${apiUrl}`);
|
||||
|
||||
const response = await fetch(apiUrl, { headers });
|
||||
return await response.json();
|
||||
}
|
||||
if (username && password) {
|
||||
headers['Authorization'] = 'Basic ' + btoa(`${username}:${password}`);
|
||||
}
|
||||
|
||||
const response = await fetch(apiUrl, { headers });
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
18
src/lib/trashbin.svelte
Normal file
18
src/lib/trashbin.svelte
Normal file
@ -0,0 +1,18 @@
|
||||
<script module>
|
||||
export { TrashBin };
|
||||
</script>
|
||||
|
||||
{#snippet TrashBin()}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 30 30"
|
||||
width="30px"
|
||||
height="30px"
|
||||
class="cursor-pointer hover:scale-110 transition"
|
||||
>
|
||||
<path
|
||||
fill="#FEFEFE"
|
||||
d="M 14.984375 2.4863281 A 1.0001 1.0001 0 0 0 14 3.5 L 14 4 L 8.5 4 A 1.0001 1.0001 0 0 0 7.4863281 5 L 6 5 A 1.0001 1.0001 0 1 0 6 7 L 24 7 A 1.0001 1.0001 0 1 0 24 5 L 22.513672 5 A 1.0001 1.0001 0 0 0 21.5 4 L 16 4 L 16 3.5 A 1.0001 1.0001 0 0 0 14.984375 2.4863281 z M 6 9 L 7.7929688 24.234375 C 7.9109687 25.241375 8.7633438 26 9.7773438 26 L 20.222656 26 C 21.236656 26 22.088031 25.241375 22.207031 24.234375 L 24 9 L 6 9 z"
|
||||
/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
Reference in New Issue
Block a user