implemented removal of transactions

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-04-14 22:37:19 +02:00
parent f6b854a227
commit 1b6f65e123
5 changed files with 55 additions and 11 deletions
@@ -297,7 +297,15 @@ public class AccountingModule extends BaseHandler implements AccountingService {
var key = body(ex); var key = body(ex);
var tags = accountDb.searchTagsContaining(key,accountId); var tags = accountDb.searchTagsContaining(key,accountId);
if (tags.size()<10) tags.addAll(tagService().search(key,user)); if (tags.size()<10) tags.addAll(tagService().search(key,user));
return sendContent(ex,tags); return sendContent(ex,egalize(tags,key));
}
private List<String> egalize(Set<String> tags, String key) {
var result = new HashSet<String>();
var lower = key.toLowerCase();
var len = key.length();
for (var tag : tags) result.add(tag.toLowerCase().startsWith(lower) ? key + tag.substring(len) : tag);
return result.stream().sorted(String.CASE_INSENSITIVE_ORDER).toList();
} }
private boolean postToAccount(long accountId, Path path, UmbrellaUser user, HttpExchange ex) throws IOException { private boolean postToAccount(long accountId, Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
@@ -223,7 +223,7 @@ public class SqliteDb extends BaseDb implements AccountDb {
var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC); var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC);
if (transaction.id() == 0) { if (transaction.id() == 0) {
try { try {
var rs = Query.insertInto(TABLE_TRANSACTIONS, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION) var rs = insertInto(TABLE_TRANSACTIONS, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION)
.values(transaction.accountId(), timestamp, transaction.source().value(), transaction.destination().value(), transaction.amount(), transaction.purpose()) .values(transaction.accountId(), timestamp, transaction.source().value(), transaction.destination().value(), transaction.amount(), transaction.purpose())
.execute(db).getGeneratedKeys(); .execute(db).getGeneratedKeys();
if (rs.next()) transaction = transaction.withId(rs.getLong(1)); if (rs.next()) transaction = transaction.withId(rs.getLong(1));
@@ -233,9 +233,13 @@ public class SqliteDb extends BaseDb implements AccountDb {
} }
} else if (transaction.isDirty()) { } else if (transaction.isDirty()) {
try { try {
Query.replaceInto(TABLE_TRANSACTIONS, Field.ID, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION) if (transaction.amount() == 0) {
.values(transaction.id(), transaction.accountId(), timestamp, transaction.source().value(), transaction.destination().value(), transaction.amount(), transaction.purpose()) delete().from(TABLE_TRANSACTIONS).where(Field.ID, equal(transaction.id())).where(ACCOUNT, equal(transaction.accountId())).execute(db);
.execute(db).close(); } else {
replaceInto(TABLE_TRANSACTIONS, Field.ID, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION)
.values(transaction.id(), transaction.accountId(), timestamp, transaction.source().value(), transaction.destination().value(), transaction.amount(), transaction.purpose())
.execute(db).close();
}
return transaction.clearDirtyState(); return transaction.clearDirtyState();
} catch (SQLException e) { } catch (SQLException e) {
throw failedToStoreObject(transaction); throw failedToStoreObject(transaction);
@@ -131,7 +131,7 @@ public class Transaction implements Mappable {
Field.DESTINATION, destination.toMap(), Field.DESTINATION, destination.toMap(),
Field.AMOUNT, amount, Field.AMOUNT, amount,
Field.PURPOSE, purpose, Field.PURPOSE, purpose,
Field.TAGS, tags Field.TAGS, tags.stream().sorted().toList()
); );
} }
+27 -2
View File
@@ -9,9 +9,9 @@
let { id } = $props(); let { id } = $props();
let account = $state(null); let account = $state(null);
let filter = $state([]);
let transactions = $state([]); let transactions = $state([]);
let users = {}; let users = {};
let sums = {}; let sums = {};
function calcSums(){ function calcSums(){
@@ -27,6 +27,18 @@
} }
} }
function addToFilter(tag){
filter.push(tag);
}
function checker(taglist, filter){
return filter.every(tag => taglist.includes(tag));
}
function dropTag(tag){
filter = filter.filter(x => x != tag);
}
async function load(){ async function load(){
let url = api(`accounting/${id}`); let url = api(`accounting/${id}`);
let res = await get(url); let res = await get(url);
@@ -51,6 +63,17 @@
.amount{ text-align: right } .amount{ text-align: right }
</style> </style>
{#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} {#if account}
<fieldset> <fieldset>
<legend>{account.name}</legend> <legend>{account.name}</legend>
@@ -68,7 +91,9 @@
</thead> </thead>
<tbody> <tbody>
{#each transactions as transaction, i} {#each transactions as transaction, i}
<Transaction {account} {transaction} {users} /> {#if checker(transaction.tags,filter)}
<Transaction {account} {addToFilter} {transaction} {users} />
{/if}
{/each} {/each}
<tr> <tr>
<td> <td>
@@ -4,7 +4,9 @@
import { api, drop, patch, post } from '../../urls.svelte'; import { api, drop, patch, post } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte'; import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let { account, addToFilter = tag => {}, transaction, users } = $props(); let { account, addToFilter = tag => {}, transaction, users } = $props();
let hidden = $state(false);
async function dropTag(tag){ async function dropTag(tag){
var url = api(`accounting/transaction/${transaction.id}/tag`) var url = api(`accounting/transaction/${transaction.id}/tag`)
@@ -47,6 +49,7 @@
if (res.ok) { if (res.ok) {
yikes(); yikes();
transaction.tags.push(tag.display); transaction.tags.push(tag.display);
transaction.tags.sort();
return true; return true;
} }
error(res); error(res);
@@ -54,7 +57,9 @@
} }
async function setAmount(amount){ async function setAmount(amount){
return await update({amount}); let result = await update({amount});
hidden = (amount == 0);
return result;
} }
async function setDate(date){ async function setDate(date){
return await update({date}); return await update({date});
@@ -85,6 +90,7 @@
} }
</script> </script>
{#if !hidden}
<tr> <tr>
<td> <td>
<LineEditor type="date" wrapper="span" editable="true" value={transaction.date} onSet={setDate} /> <LineEditor type="date" wrapper="span" editable="true" value={transaction.date} onSet={setDate} />
@@ -92,10 +98,10 @@
{#each Object.entries(users) as [id,user]} {#each Object.entries(users) as [id,user]}
<td class="amount"> <td class="amount">
{#if id == transaction.source.id} {#if id == transaction.source.id}
-<LineEditor type="number" wrapper="span" editable="true" value={(+transaction.amount).toFixed(2)} onSet={setAmount} />&nbsp;{account.currency} -<LineEditor type="number" wrapper="span" editable="true" value={(+transaction.amount).toFixed(2)} onSet={setAmount} title={t('Set to zero in order to drop the transaction')} />&nbsp;{account.currency}
{/if} {/if}
{#if id == transaction.destination.id} {#if id == transaction.destination.id}
<LineEditor type="number" wrapper="span" editable="true" value={(+transaction.amount).toFixed(2)} onSet={setAmount} />&nbsp;{account.currency} <LineEditor type="number" wrapper="span" editable="true" value={(+transaction.amount).toFixed(2)} onSet={setAmount} title={t('Set to zero in order to drop the transaction')} />&nbsp;{account.currency}
{/if} {/if}
</td> </td>
{/each} {/each}
@@ -121,3 +127,4 @@
</span> </span>
</td> </td>
</tr> </tr>
{/if}