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 tags = accountDb.searchTagsContaining(key,accountId);
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 {
@@ -223,7 +223,7 @@ public class SqliteDb extends BaseDb implements AccountDb {
var timestamp = transaction.date().toEpochSecond(ZoneOffset.UTC);
if (transaction.id() == 0) {
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())
.execute(db).getGeneratedKeys();
if (rs.next()) transaction = transaction.withId(rs.getLong(1));
@@ -233,9 +233,13 @@ public class SqliteDb extends BaseDb implements AccountDb {
}
} else if (transaction.isDirty()) {
try {
Query.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();
if (transaction.amount() == 0) {
delete().from(TABLE_TRANSACTIONS).where(Field.ID, equal(transaction.id())).where(ACCOUNT, equal(transaction.accountId())).execute(db);
} 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();
} catch (SQLException e) {
throw failedToStoreObject(transaction);
@@ -131,7 +131,7 @@ public class Transaction implements Mappable {
Field.DESTINATION, destination.toMap(),
Field.AMOUNT, amount,
Field.PURPOSE, purpose,
Field.TAGS, tags
Field.TAGS, tags.stream().sorted().toList()
);
}
+27 -2
View File
@@ -9,9 +9,9 @@
let { id } = $props();
let account = $state(null);
let filter = $state([]);
let transactions = $state([]);
let users = {};
let sums = {};
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(){
let url = api(`accounting/${id}`);
let res = await get(url);
@@ -51,6 +63,17 @@
.amount{ text-align: right }
</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}
<fieldset>
<legend>{account.name}</legend>
@@ -68,7 +91,9 @@
</thead>
<tbody>
{#each transactions as transaction, i}
<Transaction {account} {transaction} {users} />
{#if checker(transaction.tags,filter)}
<Transaction {account} {addToFilter} {transaction} {users} />
{/if}
{/each}
<tr>
<td>
@@ -4,7 +4,9 @@
import { api, drop, patch, post } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let { account, addToFilter = tag => {}, transaction, users } = $props();
let hidden = $state(false);
async function dropTag(tag){
var url = api(`accounting/transaction/${transaction.id}/tag`)
@@ -47,6 +49,7 @@
if (res.ok) {
yikes();
transaction.tags.push(tag.display);
transaction.tags.sort();
return true;
}
error(res);
@@ -54,7 +57,9 @@
}
async function setAmount(amount){
return await update({amount});
let result = await update({amount});
hidden = (amount == 0);
return result;
}
async function setDate(date){
return await update({date});
@@ -85,6 +90,7 @@
}
</script>
{#if !hidden}
<tr>
<td>
<LineEditor type="date" wrapper="span" editable="true" value={transaction.date} onSet={setDate} />
@@ -92,10 +98,10 @@
{#each Object.entries(users) as [id,user]}
<td class="amount">
{#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 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}
</td>
{/each}
@@ -121,3 +127,4 @@
</span>
</td>
</tr>
{/if}