Fixed pending problmes
This commit is contained in:
@ -10,6 +10,14 @@ const db = postgres({
|
|||||||
|
|
||||||
export { db };
|
export { db };
|
||||||
|
|
||||||
|
export async function setBudgetTransactionTransactionId(transactionId, budgetTransactionId) {
|
||||||
|
return await db`
|
||||||
|
update budget_transaction
|
||||||
|
set transaction_id = ${transactionId}
|
||||||
|
where id = ${budgetTransactionId}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getTotal() {
|
export async function getTotal() {
|
||||||
const result = await db`
|
const result = await db`
|
||||||
select sum(balance) as total
|
select sum(balance) as total
|
||||||
@ -94,7 +102,7 @@ export async function getBudgetTransactions(id) {
|
|||||||
budget_transaction.amount as budget_amount,
|
budget_transaction.amount as budget_amount,
|
||||||
budget_transaction.id as budget_transaction_id
|
budget_transaction.id as budget_transaction_id
|
||||||
from budget_transaction
|
from budget_transaction
|
||||||
join transaction on budget_transaction.transaction_id = transaction.id
|
left join transaction on budget_transaction.transaction_id = transaction.id
|
||||||
where budget_transaction.budget_id = ${id}
|
where budget_transaction.budget_id = ${id}
|
||||||
order by transaction.date desc
|
order by transaction.date desc
|
||||||
`;
|
`;
|
||||||
@ -262,7 +270,7 @@ export async function getHiddenAccounts(age) {
|
|||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTransactions(accountId) {
|
export async function getTransactionsForAccount(accountId) {
|
||||||
let transactions = await db`
|
let transactions = await db`
|
||||||
select
|
select
|
||||||
transaction.id as id,
|
transaction.id as id,
|
||||||
@ -283,6 +291,28 @@ export async function getTransactions(accountId) {
|
|||||||
return transactions;
|
return transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getTransactions(pattern = '', limit = 100) {
|
||||||
|
let transactions = await db`
|
||||||
|
select
|
||||||
|
transaction.id as id,
|
||||||
|
transaction.account_id as account_id,
|
||||||
|
transaction.amount as amount,
|
||||||
|
transaction.description as description,
|
||||||
|
transaction.pending as pending,
|
||||||
|
transaction.notes as notes,
|
||||||
|
transaction.payee as payee,
|
||||||
|
transaction.date as date,
|
||||||
|
transaction.statement_description as statement_description,
|
||||||
|
transaction.out_of_budget as out_of_budget
|
||||||
|
from transaction
|
||||||
|
where transaction.description ILIKE ${`%${pattern}%`}
|
||||||
|
order by date desc
|
||||||
|
LIMIT ${limit}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return transactions;
|
||||||
|
}
|
||||||
|
|
||||||
export async function setTransactionNote(transactionId, note) {
|
export async function setTransactionNote(transactionId, note) {
|
||||||
const result = await db`
|
const result = await db`
|
||||||
update transaction
|
update transaction
|
||||||
|
|||||||
@ -207,10 +207,36 @@ export async function pullData(amount = 100) {
|
|||||||
state.push(`Found ${transactions.length} transactions for account ID: ${account.id}`);
|
state.push(`Found ${transactions.length} transactions for account ID: ${account.id}`);
|
||||||
|
|
||||||
const cardRegEx = /\d{4}$/;
|
const cardRegEx = /\d{4}$/;
|
||||||
|
|
||||||
|
// Check if any pending need to be updated
|
||||||
|
|
||||||
|
state.push(`Checking for pending transactions for account ID: ${account.id}`);
|
||||||
|
let currentPend =
|
||||||
|
await db`SELECT id, amount, description, date from transaction where account_id = ${account.id} AND pending=true`;
|
||||||
|
for (const pend of currentPend) {
|
||||||
|
const found = transactions.find((t) => t.transactionId === pend.id);
|
||||||
|
if (!found) {
|
||||||
|
const updated = transactions.find(
|
||||||
|
(t) => t.amount == pend.amount && new Date(t.postedDate) == pend.date
|
||||||
|
);
|
||||||
|
if (updated) {
|
||||||
|
state.push(
|
||||||
|
`I think I found an updated transaction: ${updated.statementDescription} ${updated.amount} for ${pend.description} ${pend.amount}`
|
||||||
|
);
|
||||||
|
|
||||||
|
await db`UPDATE budget_transaction SET transaction_id = ${updated.transactionId} WHERE transaction_id = ${pend.id}`;
|
||||||
|
} else {
|
||||||
|
state.push(`Orphaning no longer pending budget transaction with no new parent`);
|
||||||
|
await db`UPDATE budget_transaction SET transaction_id = null WHERE transaction_id = ${pend.id}`;
|
||||||
|
}
|
||||||
|
state.push(`Removing pending transaction: ${pend.id}`);
|
||||||
|
await db`DELETE FROM transaction WHERE id = ${pend.id}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const transaction of transactions) {
|
for (const transaction of transactions) {
|
||||||
const amount = Number(transaction.amount);
|
const amount = Number(transaction.amount);
|
||||||
const date = new Date(transaction.postedDate);
|
const date = new Date(transaction.postedDate);
|
||||||
const id = transaction.hostTranNumber;
|
|
||||||
const payee = transaction.description || '';
|
const payee = transaction.description || '';
|
||||||
const statementDescription = transaction.statementDescription;
|
const statementDescription = transaction.statementDescription;
|
||||||
const card = cardRegEx.test(statementDescription)
|
const card = cardRegEx.test(statementDescription)
|
||||||
@ -218,6 +244,11 @@ export async function pullData(amount = 100) {
|
|||||||
: null;
|
: null;
|
||||||
const pending = transaction.extended?.allTransactionType == 1 ? true : false;
|
const pending = transaction.extended?.allTransactionType == 1 ? true : false;
|
||||||
const accountId = transaction.accountId;
|
const accountId = transaction.accountId;
|
||||||
|
|
||||||
|
const id = pending
|
||||||
|
? `${transaction.postedDate}:${transaction.amount}`
|
||||||
|
: transaction.hostTranNumber;
|
||||||
|
|
||||||
await db`INSERT INTO transaction (
|
await db`INSERT INTO transaction (
|
||||||
id,
|
id,
|
||||||
account_id,
|
account_id,
|
||||||
@ -248,6 +279,17 @@ export async function pullData(amount = 100) {
|
|||||||
pending = EXCLUDED.pending`;
|
pending = EXCLUDED.pending`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.push('Orphaning transactions');
|
||||||
|
|
||||||
|
const orphaned = await db`SELECT bt.id as id
|
||||||
|
FROM budget_transaction bt
|
||||||
|
LEFT OUTER JOIN transaction t ON bt.transaction_id = t.id
|
||||||
|
WHERE t.id IS NULL;`;
|
||||||
|
for (const orphan of orphaned) {
|
||||||
|
state.push(`Orphaning transaction: ${orphan.id}`);
|
||||||
|
await db`UPDATE budget_transaction set transaction_id = null where id = ${orphan.id}`;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in pullData:', error);
|
console.error('Error in pullData:', error);
|
||||||
state.push(`Error: ${error.message}`);
|
state.push(`Error: ${error.message}`);
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { getAccount, getTransactions, getBudgets, getBudgetTransactionsForAccount } from '$lib/db';
|
import {
|
||||||
|
getAccount,
|
||||||
|
getTransactionsForAccount,
|
||||||
|
getBudgets,
|
||||||
|
getBudgetTransactionsForAccount
|
||||||
|
} from '$lib/db';
|
||||||
|
|
||||||
/** @type {import('./$types').PageServerLoad} */
|
/** @type {import('./$types').PageServerLoad} */
|
||||||
export async function load({ params, depends }) {
|
export async function load({ params, depends }) {
|
||||||
const slug = params.slug;
|
const slug = params.slug;
|
||||||
const transactions = await getTransactions(slug);
|
const transactions = await getTransactionsForAccount(slug);
|
||||||
const account = await getAccount(slug);
|
const account = await getAccount(slug);
|
||||||
const budgets = await getBudgets();
|
const budgets = await getBudgets();
|
||||||
const budgetTransactions = await getBudgetTransactionsForAccount(slug);
|
const budgetTransactions = await getBudgetTransactionsForAccount(slug);
|
||||||
|
|||||||
18
src/routes/api/budget/transaction/[slug]/+server.js
Normal file
18
src/routes/api/budget/transaction/[slug]/+server.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { setBudgetTransactionTransactionId } from '$lib/db.js';
|
||||||
|
|
||||||
|
export async function PATCH({ request, params }) {
|
||||||
|
const body = await request.json();
|
||||||
|
const { slug } = params;
|
||||||
|
const { transactionId } = body;
|
||||||
|
|
||||||
|
if (!slug || !transactionId) {
|
||||||
|
return new Response('Missing transactionId or budgetTransactionId', { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await setBudgetTransactionTransactionId(transactionId, slug);
|
||||||
|
return new Response('Budget transaction updated successfully', { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
return new Response(`Error updating budget transaction: ${error.message}`, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/routes/api/transactions/+server.js
Normal file
8
src/routes/api/transactions/+server.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { getTransactions } from '$lib/db.js';
|
||||||
|
|
||||||
|
export async function GET({ url }) {
|
||||||
|
const count = url.searchParams.get('c') || '100';
|
||||||
|
const pattern = url.searchParams.get('p') || '';
|
||||||
|
const transacations = await getTransactions(pattern, count);
|
||||||
|
return new Response(JSON.stringify(transacations));
|
||||||
|
}
|
||||||
@ -18,6 +18,52 @@
|
|||||||
let toDelete = $state('');
|
let toDelete = $state('');
|
||||||
let toDeleteName = $state('');
|
let toDeleteName = $state('');
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
|
let newTransaction = $state({
|
||||||
|
name: '',
|
||||||
|
id: null
|
||||||
|
});
|
||||||
|
let searchString = $state('');
|
||||||
|
let debounceString = $state('');
|
||||||
|
let searchResults = $state([]);
|
||||||
|
let searching = $state(false);
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
if (searching) return; // Prevent multiple searches at the same time
|
||||||
|
searching = true;
|
||||||
|
const res = await fetch(`/api/transactions?c=100&p=${searchString}`);
|
||||||
|
const data = await res.json();
|
||||||
|
searchResults = data;
|
||||||
|
searching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateTransactionID() {
|
||||||
|
loading = true;
|
||||||
|
EditBudgetTransactionModal.close();
|
||||||
|
if (!newTransaction.id) {
|
||||||
|
addToast('Please select a transaction to update', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newTransaction.id == newData.id) {
|
||||||
|
addToast('No changes made to the transaction', 'info');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const response = await fetch(`/api/budget/transaction/${newData.id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
transactionId: newTransaction.id
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
addToast('Transaction updated successfully', 'success');
|
||||||
|
invalidateAll();
|
||||||
|
} else {
|
||||||
|
addToast('Failed to update transaction', 'error');
|
||||||
|
}
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function saveTransaction() {
|
async function saveTransaction() {
|
||||||
loading = true;
|
loading = true;
|
||||||
@ -97,15 +143,19 @@
|
|||||||
{#each transactions as tras}
|
{#each transactions as tras}
|
||||||
<li class="list-row">
|
<li class="list-row">
|
||||||
<div>
|
<div>
|
||||||
<div>{tras.description}</div>
|
{#if tras.id == null}
|
||||||
<div class="text-xs uppercase font-semibold opacity-60">
|
<span class="badge badge-warning">Orphan</span>
|
||||||
{tras.date.toDateString()}
|
{:else}
|
||||||
</div>
|
<div>{tras?.description}</div>
|
||||||
|
<div class="text-xs uppercase font-semibold opacity-60">
|
||||||
|
{tras?.date?.toDateString()}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center">{tras.notes}</div>
|
<div class="text-center">{tras.notes}</div>
|
||||||
|
|
||||||
<div class="text-lg uppercase font-semibold text-right w-32">{tras.budget_amount}</div>
|
<div class="text-lg uppercase font-semibold text-right w-32">{tras?.budget_amount}</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-square btn-ghost" onclick={() => edit(tras)}
|
<button class="btn btn-square btn-ghost" onclick={() => edit(tras)}
|
||||||
>{@render EditSymbol()}</button
|
>{@render EditSymbol()}</button
|
||||||
@ -123,6 +173,42 @@
|
|||||||
<dialog id="EditBudgetTransactionModal" class="modal">
|
<dialog id="EditBudgetTransactionModal" class="modal">
|
||||||
<div class="modal-box">
|
<div class="modal-box">
|
||||||
<h1>{newData.name}</h1>
|
<h1>{newData.name}</h1>
|
||||||
|
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4">
|
||||||
|
<legend class="fieldset-legend">Reassign</legend>
|
||||||
|
<div class="join">
|
||||||
|
<label class="input">
|
||||||
|
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
<path d="m21 21-4.3-4.3"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<input type="search" required placeholder="Search" bind:value={searchString} />
|
||||||
|
</label>
|
||||||
|
<button class="btn btn-neutral join-item" onclick={() => search()} disabled={searching}
|
||||||
|
>Search</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="join">
|
||||||
|
<select class="select" bind:value={newTransaction.id} disabled={searching}>
|
||||||
|
<option value="" disabled selected>Select a Transaction</option>
|
||||||
|
{#each searchResults as res}
|
||||||
|
<option value={res?.id}
|
||||||
|
>{res?.description} - {new Date(res?.date).toDateString()} - {res?.amount}}</option
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-neutral join-item" onclick={() => updateTransactionID()}
|
||||||
|
>Update</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4">
|
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4">
|
||||||
<legend class="fieldset-legend">Edit</legend>
|
<legend class="fieldset-legend">Edit</legend>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user