Files
Umbrella/frontend/src/routes/accounting/account.svelte
T
2026-05-06 08:20:37 +02:00

167 lines
4.2 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
import { onMount } from 'svelte';
import { api, eventStream, get } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import EntryForm from './add_entry.svelte';
import Transaction from './transaction.svelte';
let { id } = $props();
let account = $state(null);
let eventSource = null;
let filter = $state([]);
let transactions = $state([]);
let filtered = $derived(transactions.filter(t => checker(t.tags,filter)));
let users = {};
let sums = $derived.by(calcSums);
function calcSums(){
let sums = {};
sums[0] = 0;
for (let user of Object.values(users)) sums[user.id] = 0;
for (let transaction of filtered) {
for (let user of Object.values(users)){
if (user.id == transaction.destination.id) sums[user.id] += transaction.amount;
if (user.id == transaction.source.id) sums[user.id] -= transaction.amount;
}
if (!transaction.destination.id) sums[0] += transaction.amount;
if (!transaction.source.id) sums[0] -= transaction.amount;
}
return sums;
}
function addToFilter(tag){
filter.push(tag.toLowerCase());
}
function checker(taglist, filter){
for (var f of filter){
var included = false;
for (var t of taglist){
if (t.toLowerCase() == f && (included = true)) break;
}
if (!included) return false;
}
return true;
}
function dropTag(tag){
filter = filter.filter(x => x != tag.toLowerCase());
}
function handleCreateEvent(evt){
const event_data = JSON.parse(evt.data);
const new_transaction = event_data.transaction;
transactions.push(new_transaction);
window.setTimeout(scrollToBottom,100);
}
function handleDeleteEvent(evt){
const event_data = JSON.parse(evt.data);
transactions = transactions.filter(t => t.id != event_data.transaction.id);
}
function handleUpdateEvent(evt){
const event_data = JSON.parse(evt.data);
const updated_transaction = event_data.transaction;
for (var idx in transactions){
if (transactions[idx].id == updated_transaction.id) {
updated_transaction.tags = transactions[idx].tags;
transactions[idx] = updated_transaction;
break;
}
}
}
async function load(){
let url = api(`accounting/${id}`);
let res = await get(url);
if (res.ok) {
yikes();
let json = await res.json();
transactions = json.transactions;
users = json.user_list;
account = json.account;
try {
eventSource = eventStream(handleCreateEvent,handleUpdateEvent,handleDeleteEvent);
} catch (ignored) {}
window.setTimeout(scrollToBottom,100);
} else error(res);
}
function onSave(){
// load();
}
function scrollToBottom(){
window.scrollTo(0, document.body.scrollHeight);
}
onMount(load);
</script>
<style>
.amount{ text-align: right }
</style>
<svelte:head>
{#if account}
<title>Umbrella {account.name}</title>
{/if}
</svelte:head>
{#if filter.length > 0}
<fieldset>
<legend>{t('filter by tags')}</legend>
<div class="taglist">
{#each filter as tag,i}
<span class="tag">{tag}&nbsp;<button onclick={() => dropTag(tag)} class="symbol"></button></span>
{/each}
</div>
</fieldset>
{/if}
{#if account}
<fieldset>
<legend>{account.name}</legend>
<table class="account">
<thead>
<tr>
<th>{t('date')}</th>
{#each Object.entries(users) as [id,user]}
<th>{user.name}</th>
{/each}
<th>{t('other party')}</th>
<th>{t('purpose')}</th>
<th>{t('tags')}</th>
<th></th>
</tr>
</thead>
<tbody>
{#each filtered as transaction, i}
<Transaction {account} {addToFilter} {transaction} {users} />
{/each}
<tr class="sums">
<td>
<br/>
{t('sums')}
</td>
{#each Object.entries(users) as [id,user]}
<th class="amount">
{user.name}<br/>
{sums[id].toFixed(2)}&nbsp;{account.currency}
</th>
{/each}
<td class="amount">
<br/>
{sums[0].toFixed(2)}&nbsp;{account.currency}
</td>
<td colspan="3"></td>
</tr>
</tbody>
</table>
</fieldset>
<EntryForm {account} {onSave} />
{/if}