actual/packages/loot-core/src/server/accounts/transfer.js
2022-07-24 08:53:05 +01:00

163 lines
4.7 KiB
JavaScript

import * as db from '../db';
async function getPayee(acct) {
return db.first('SELECT * FROM payees WHERE transfer_acct = ?', [acct]);
}
async function getTransferredAccount(transaction) {
if (transaction.payee) {
let {
transfer_acct,
id
} = await db.first('SELECT id, transfer_acct FROM v_payees WHERE id = ?', [
transaction.payee
]);
return transfer_acct;
}
return null;
}
async function clearCategory(transaction, transferAcct) {
const {
offbudget: fromOffBudget
} = await db.first('SELECT offbudget FROM accounts WHERE id = ?', [
transaction.account
]);
const {
offbudget: toOffBudget
} = await db.first('SELECT offbudget FROM accounts WHERE id = ?', [
transferAcct
]);
// We should clear the category to make sure it's not being
// accounted for in the budget, unless it should be in the case of
// transferring from an on-budget to off-budget account
if (fromOffBudget === toOffBudget) {
await db.updateTransaction({ id: transaction.id, category: null });
return true;
}
return false;
}
export async function addTransfer(transaction, transferredAccount) {
let {
id: fromPayee
} = await db.first('SELECT id FROM payees WHERE transfer_acct = ?', [
transaction.account
]);
// We need to enforce certain constraints with child transaction transfers
if (transaction.parent_id) {
let row = await db.first(
`
SELECT p.id, p.transfer_acct FROM v_transactions t
LEFT JOIN payees p ON p.id = t.payee
WHERE t.id = ?
`,
[transaction.parent_id]
);
if (row.transfer_acct) {
if (row.id !== transaction.payee) {
// This child transaction is trying to use a transfer payee,
// but the parent is already using a different transfer payee.
// This is not allowed, so not only do we do nothing, we clear
// the payee of the child transaction to make it clear
await db.updateTransaction({ id: transaction.id, payee: null });
return { id: transaction.id, payee: null };
}
// The parent has the same transfer payee, so it "owns" the
// transfer logic
return null;
}
}
const id = await db.insertTransaction({
account: transferredAccount,
amount: -transaction.amount,
payee: fromPayee,
date: transaction.date,
transfer_id: transaction.id,
notes: transaction.notes || null,
cleared: false
});
await db.updateTransaction({ id: transaction.id, transfer_id: id });
const categoryCleared = await clearCategory(transaction, transferredAccount);
return {
id: transaction.id,
transfer_id: id,
...(categoryCleared ? { category: null } : {})
};
}
export async function removeTransfer(transaction) {
let transferTrans = await db.getTransaction(transaction.transfer_id);
if (transferTrans.is_child) {
// If it's a child transaction, we don't delete it because that
// would invalidate the whole split transaction. Instead of turn
// it into a normal transaction
await db.updateTransaction({
id: transaction.transfer_id,
transfer_id: null,
payee: null
});
} else {
await db.deleteTransaction({ id: transaction.transfer_id });
}
await db.updateTransaction({ id: transaction.id, transfer_id: null });
return { id: transaction.id, transfer_id: null };
}
export async function updateTransfer(transaction, transferredAccount) {
let payee = await getPayee(transaction.account);
await db.updateTransaction({
id: transaction.transfer_id,
account: transferredAccount,
// Make sure to update the payee on the other side in case the
// user moved this transaction into another account
payee: payee.id,
date: transaction.date,
notes: transaction.notes,
amount: -transaction.amount
});
const categoryCleared = await clearCategory(transaction, transferredAccount);
if (categoryCleared) {
return { id: transaction.id, category: null };
}
}
export async function onInsert(transaction) {
let transferredAccount = await getTransferredAccount(transaction);
if (transferredAccount) {
return addTransfer(transaction, transferredAccount);
}
}
export async function onDelete(transaction) {
if (transaction.transfer_id) {
await removeTransfer(transaction);
}
}
export async function onUpdate(transaction) {
const transferredAccount = await getTransferredAccount(transaction);
if (transferredAccount && !transaction.transfer_id) {
return addTransfer(transaction, transferredAccount);
}
if (!transferredAccount && transaction.transfer_id) {
return removeTransfer(transaction);
}
if (transferredAccount && transaction.transfer_id) {
return updateTransfer(transaction, transferredAccount);
}
}