Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cdf825bcb | |||
| 71c86e512d | |||
| 9d8013bc33 | |||
| 05733d3b7a | |||
| 71c071bbdd | |||
| 1241fee61d | |||
| d64cb886c9 | |||
| ac8149e6bb | |||
| 493b61465b | |||
| df372e9cfd | |||
| ff58f3ae82 | |||
| b71db96b47 | |||
| 02434419f4 | |||
| bd096dc61f | |||
| 9f286f3121 | |||
| 2211f4f39d | |||
| a6b988df3a | |||
| 1316d3fb1e | |||
| 99fa75a980 | |||
| 6fc590d795 | |||
| 7afc804586 | |||
| f40692dd3d | |||
| 1c91699bf5 | |||
| 9f5e1e0853 | |||
| 55dfea65b0 |
@@ -53,8 +53,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
TAGS="$(curl -s -u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_PASS }}" https://${{ secrets.REGISTRY_PATH }}/v2/umbrella/tags/list | jq -r ".tags[]")"
|
TAGS="$(curl -s -u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_PASS }}" https://${{ secrets.REGISTRY_PATH }}/v2/umbrella/tags/list | jq -r ".tags[]")"
|
||||||
COUNT=$(echo "$TAGS" | wc -l)
|
COUNT=$(echo "$TAGS" | wc -l)
|
||||||
|
echo found $COUNT tags: $TAGS
|
||||||
if [ $COUNT -gt 10 ]; then
|
if [ $COUNT -gt 10 ]; then
|
||||||
REMAIN=$((COUNT - 10))
|
REMAIN=$((COUNT - 10))
|
||||||
|
echo $REMAIN tags will be kept!
|
||||||
echo "$TAGS" | head -n $REMAIN > /tmp/old_tags
|
echo "$TAGS" | head -n $REMAIN > /tmp/old_tags
|
||||||
else
|
else
|
||||||
echo less than 10 tags, skipping cleanup
|
echo less than 10 tags, skipping cleanup
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ import de.srsoftware.umbrella.core.model.Account;
|
|||||||
import de.srsoftware.umbrella.core.model.Transaction;
|
import de.srsoftware.umbrella.core.model.Transaction;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface AccountDb {
|
public interface AccountDb {
|
||||||
void dropTransactionTag(long transactionId, String tag);
|
void dropTransactionTag(long transactionId, String tag);
|
||||||
|
|
||||||
Optional<Transaction> lastTransaction(long accountId, String source, String dest, double amount);
|
|
||||||
|
|
||||||
Collection<Account> listAccounts(long userId);
|
Collection<Account> listAccounts(long userId);
|
||||||
|
|
||||||
Collection<String> listTags(long accountId, String source, String destination);
|
Collection<String> listTags(long accountId, String source, String destination);
|
||||||
|
|||||||
@@ -295,22 +295,6 @@ public class AccountingModule extends BaseHandler implements AccountingService {
|
|||||||
return sendContent(ex,newAccount != null ? newAccount : transaction);
|
return sendContent(ex,newAccount != null ? newAccount : transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean postGetLastTransaction(long accountId, UmbrellaUser user, HttpExchange ex) throws IOException {
|
|
||||||
var json = json(ex);
|
|
||||||
if (!json.has(Field.SOURCE)) throw missingField(Field.SOURCE);
|
|
||||||
if (!(json.get(Field.SOURCE) instanceof JSONObject src)) throw invalidField(Field.SOURCE,JSON);
|
|
||||||
var source = src.get(src.has(Field.ID) ? Field.ID : Field.DISPLAY).toString();
|
|
||||||
if (!json.has(Field.DESTINATION)) throw missingField(Field.DESTINATION);
|
|
||||||
if (!(json.get(Field.DESTINATION) instanceof JSONObject dst)) throw invalidField(Field.SOURCE,JSON);
|
|
||||||
var dest = dst.get(dst.has(Field.ID) ? Field.ID : Field.DISPLAY).toString();
|
|
||||||
if (!json.has(Field.AMOUNT)) throw missingField(Field.AMOUNT);
|
|
||||||
if (!(json.get(Field.AMOUNT) instanceof Number amt)) throw invalidField(Field.AMOUNT,Text.NUMBER);
|
|
||||||
var amount = amt.doubleValue();
|
|
||||||
|
|
||||||
var transaction = accountDb.lastTransaction(accountId, source, dest, amount);
|
|
||||||
return transaction.isPresent() ? sendContent(ex,transaction.get()) : notFound(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean postSearchDestinations(UmbrellaUser user, HttpExchange ex) throws IOException {
|
public boolean postSearchDestinations(UmbrellaUser user, HttpExchange ex) throws IOException {
|
||||||
return sendContent(ex,searchOptions(user, Field.DESTINATION, body(ex)));
|
return sendContent(ex,searchOptions(user, Field.DESTINATION, body(ex)));
|
||||||
}
|
}
|
||||||
@@ -343,9 +327,7 @@ public class AccountingModule extends BaseHandler implements AccountingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
var head = path.pop();
|
return switch (path.pop()) {
|
||||||
return switch (head) {
|
|
||||||
case PURPOSES -> postGetLastTransaction(accountId,user,ex);
|
|
||||||
case TAGS -> postSearchTags(accountId,user,ex);
|
case TAGS -> postSearchTags(accountId,user,ex);
|
||||||
case null, default -> super.doPost(path,ex);
|
case null, default -> super.doPost(path,ex);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
package de.srsoftware.umbrella.accounting;
|
package de.srsoftware.umbrella.accounting;
|
||||||
|
|
||||||
import static de.srsoftware.tools.NotImplemented.notImplemented;
|
import static de.srsoftware.tools.NotImplemented.notImplemented;
|
||||||
import static de.srsoftware.tools.Optionals.nullable;
|
|
||||||
import static de.srsoftware.tools.jdbc.Condition.*;
|
import static de.srsoftware.tools.jdbc.Condition.*;
|
||||||
import static de.srsoftware.tools.jdbc.Query.*;
|
import static de.srsoftware.tools.jdbc.Query.*;
|
||||||
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
||||||
@@ -131,29 +130,6 @@ public class SqliteDb extends BaseDb implements AccountDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Transaction> lastTransaction(long accountId, String source, String dest, double amount) {
|
|
||||||
try {
|
|
||||||
var rs = select(ALL).from(TABLE_TRANSACTIONS)
|
|
||||||
.where(ACCOUNT,equal(accountId)).where(SOURCE,equal(source)).where(DESTINATION,equal(dest)).where(AMOUNT,equal(amount))
|
|
||||||
.sort(ID+" DESC")
|
|
||||||
.limit(1)
|
|
||||||
.exec(db);
|
|
||||||
Transaction ta = null;
|
|
||||||
if (rs.next()) ta = Transaction.of(rs);
|
|
||||||
rs.close();
|
|
||||||
if (ta != null){
|
|
||||||
var tags = ta.tags();
|
|
||||||
rs = select(TAG).from(TABLE_TAGS_TRANSACTIONS).leftJoin(TAG_ID,TABLE_TAGS,ID).where(TRANSACTION_ID,equal(ta.id())).exec(db);
|
|
||||||
while (rs.next()) tags.add(rs.getString(1));
|
|
||||||
rs.close();
|
|
||||||
}
|
|
||||||
return nullable(ta);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw failedToSearchDb(t(Text.ACCOUNTING));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HashSet<Account> listAccounts(long userId) {
|
public HashSet<Account> listAccounts(long userId) {
|
||||||
try {
|
try {
|
||||||
@@ -295,7 +271,7 @@ public class SqliteDb extends BaseDb implements AccountDb {
|
|||||||
}
|
}
|
||||||
} else if (transaction.isDirty()) {
|
} else if (transaction.isDirty()) {
|
||||||
try {
|
try {
|
||||||
if (transaction.amount() == 0 || transaction.source().isEmpty() || transaction.destination().isEmpty()) {
|
if (transaction.amount() == 0) {
|
||||||
delete().from(TABLE_TRANSACTIONS).where(Field.ID, equal(transaction.id())).where(ACCOUNT, equal(transaction.accountId())).execute(db);
|
delete().from(TABLE_TRANSACTIONS).where(Field.ID, equal(transaction.id())).where(ACCOUNT, equal(transaction.accountId())).execute(db);
|
||||||
} else {
|
} else {
|
||||||
replaceInto(TABLE_TRANSACTIONS, Field.ID, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION)
|
replaceInto(TABLE_TRANSACTIONS, Field.ID, Field.ACCOUNT, Field.TIMESTAMP, Field.SOURCE, Field.DESTINATION, Field.AMOUNT, Field.DESCRIPTION)
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ import de.srsoftware.umbrella.core.constants.Field;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static de.srsoftware.tools.Optionals.isSet;
|
|
||||||
import static de.srsoftware.tools.Optionals.nullIfEmpty;
|
|
||||||
|
|
||||||
public class IdOrString implements Mappable {
|
public class IdOrString implements Mappable {
|
||||||
private final Long id;
|
private final Long id;
|
||||||
private final String value;
|
private final String value;
|
||||||
@@ -55,10 +52,6 @@ public class IdOrString implements Mappable {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return id == null && !isSet(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -14,10 +14,9 @@
|
|||||||
|
|
||||||
const ignore = ['ArrowLeft','ArrowRight'];
|
const ignore = ['ArrowLeft','ArrowRight'];
|
||||||
//let candidate = $state({ display : '' });
|
//let candidate = $state({ display : '' });
|
||||||
let selected = $state(null);
|
let selected = $state([]);
|
||||||
let candidates = $state([]);
|
let candidates = $state([]);
|
||||||
let timer = null;
|
let timer = null;
|
||||||
let list_elem;
|
|
||||||
|
|
||||||
async function dummyGetCandidates(text){
|
async function dummyGetCandidates(text){
|
||||||
console.warn(`getCandidates(${text}) not overridden!`);
|
console.warn(`getCandidates(${text}) not overridden!`);
|
||||||
@@ -54,43 +53,41 @@
|
|||||||
const idx = select.value;
|
const idx = select.value;
|
||||||
candidate = candidates[idx];
|
candidate = candidates[idx];
|
||||||
candidates = [];
|
candidates = [];
|
||||||
selected = null;
|
selected = [];
|
||||||
onSelect(candidate);
|
onSelect(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchCandidates(){
|
async function fetchCandidates(){
|
||||||
candidates = candidate.display ? await getCandidates(candidate.display) : [];
|
candidates = await getCandidates(candidate.display);
|
||||||
selected = null;
|
if (selected>candidates.length) selected = candidates.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onkeyup(ev){
|
async function onkeyup(ev){
|
||||||
if (ignore.includes(ev.key)) return;
|
if (ignore.includes(ev.key)) return;
|
||||||
if (ev.key == 'ArrowDown'){
|
if (ev.key == 'ArrowDown'){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
selected = selected == null ? 0: selected +1;
|
selected = selected.length < 1 ? [0] : [selected[0]+1]
|
||||||
if (selected >= candidates.length) selected = 0;
|
if (selected[0] >= candidates.length) selected = [0];
|
||||||
scrollTo(selected);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ev.key == 'ArrowUp'){
|
if (ev.key == 'ArrowUp'){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
selected = selected == null ? candidates.length -1 : selected -1;
|
selected = selected.length < 1 ? [-1] : [selected[0]-1]
|
||||||
if (selected < 0) selected = candidates.length -1;
|
if (selected[0] < 0) selected = [candidates.length-1];
|
||||||
scrollTo(selected);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ev.key == 'Enter'|| ev.key == 'Tab'){
|
if (ev.key == 'Enter'|| ev.key == 'Tab'){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (selected != null && selected < candidates.length) {
|
if (selected.length>0) {
|
||||||
candidate = candidates[selected];
|
candidate = candidates[selected[0]];
|
||||||
candidates = [];
|
candidates = [];
|
||||||
selected = null;
|
selected = [];
|
||||||
onSelect(candidate);
|
onSelect(candidate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ev.key == 'Enter') {
|
if (ev.key == 'Enter') {
|
||||||
candidates = [];
|
candidates = [];
|
||||||
selected = null;
|
selected = [];
|
||||||
if (onCommit(candidate)) candidate = { display : '' };
|
if (onCommit(candidate)) candidate = { display : '' };
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -98,7 +95,7 @@
|
|||||||
if (ev.key == 'Escape'){
|
if (ev.key == 'Escape'){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
candidates = [];
|
candidates = [];
|
||||||
selected = null;
|
selected = [];
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,50 +104,23 @@
|
|||||||
timer = setTimeout(fetchCandidates,400);
|
timer = setTimeout(fetchCandidates,400);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(index){
|
|
||||||
candidate = candidates[index];
|
|
||||||
selected = null;
|
|
||||||
candidates = [];
|
|
||||||
onSelect(candidate);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollTo(index){
|
|
||||||
let list_elements = list_elem.children;
|
|
||||||
if (list_elements) list_elements[index].scrollIntoView({block:'center'});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
span { position : relative }
|
span { position : relative }
|
||||||
ul {
|
select { position : absolute; top: 30px; left: 3px; }
|
||||||
position : absolute;
|
|
||||||
top: 30px;
|
|
||||||
left: 3px;
|
|
||||||
background: black;
|
|
||||||
color: orange;
|
|
||||||
border: 1px solid orange;
|
|
||||||
border-radius: 5px;
|
|
||||||
z-index: 50;
|
|
||||||
list-style: none;
|
|
||||||
padding: 4px;
|
|
||||||
margin: 0;
|
|
||||||
min-width: 400px;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow: scroll;
|
|
||||||
}
|
|
||||||
.highlight { background: orange; color: black; }
|
|
||||||
|
|
||||||
|
select { background: black; color: orange; border: 1px solid orange; border-radius: 5px; z-index: 50; }
|
||||||
|
option:checked { background: orange; color: black; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<span class="autocomplete">
|
<span>
|
||||||
<input type="text" bind:value={candidate.display} {onkeyup} autofocus={autofocus} {id} />
|
<input type="text" bind:value={candidate.display} {onkeyup} autofocus={autofocus} />
|
||||||
{#if candidates && candidates.length > 0}
|
{#if candidates && candidates.length > 0}
|
||||||
<ul bind:this={list_elem} class="suggestions">
|
<select bind:value={selected} {ondblclick} multiple tabindex="-1" class="autocomplete" size={Math.min(candidates.length,10)}>
|
||||||
{#each candidates as candidate,i}
|
{#each candidates as candidate,i}
|
||||||
<li class="option {selected==i?'highlight':''}" onclick={e => select(i)} ondblclick={e => select(i)}>{candidate.display}</li>
|
<option value={i}>{candidate.display}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</select>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
@@ -155,8 +155,5 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<Display classes={{editable}} markdown={value} {onclick} {oncontextmenu} title={t('right_click_to_edit')} wrapper={type} />
|
<Display classes={{editable}} markdown={value} {onclick} {oncontextmenu} title={t('right_click_to_edit')} wrapper={type} />
|
||||||
{#if !value.display}
|
|
||||||
<button onclick={oncontextmenu}>{t('add_object',{object:t('content')})}</button>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
if (!transaction.destination.id) sums[0] += transaction.amount;
|
if (!transaction.destination.id) sums[0] += transaction.amount;
|
||||||
if (!transaction.source.id) sums[0] -= transaction.amount;
|
if (!transaction.source.id) sums[0] -= transaction.amount;
|
||||||
}
|
}
|
||||||
window.setTimeout(scrollToBottom,100);
|
|
||||||
return sums;
|
return sums;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +65,6 @@
|
|||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToBottom(){
|
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(load);
|
onMount(load);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -77,12 +72,6 @@
|
|||||||
.amount{ text-align: right }
|
.amount{ text-align: right }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
{#if account}
|
|
||||||
<title>Umbrella – {account.name}</title>
|
|
||||||
{/if}
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
{#if filter.length > 0}
|
{#if filter.length > 0}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t('filter by tags')}</legend>
|
<legend>{t('filter by tags')}</legend>
|
||||||
|
|||||||
@@ -42,10 +42,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function focusOnEnter(ev,id){
|
function focusOnEnter(ev,id){
|
||||||
if (ev.key == 'Enter') {
|
if (ev.key == 'Enter') document.getElementById(id).focus();
|
||||||
proposePurpose();
|
|
||||||
document.getElementById(id).focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAccountTags(text){
|
async function getAccountTags(text){
|
||||||
@@ -96,20 +93,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function proposePurpose(){
|
|
||||||
const source = entry.source;
|
|
||||||
const destination = entry.destination;
|
|
||||||
const amount = entry.amount;
|
|
||||||
const url = api(`accounting/${account.id}/purposes`);
|
|
||||||
const res = await post(url,{source,destination,amount});
|
|
||||||
if (res.ok) {
|
|
||||||
yikes();
|
|
||||||
var lastTransaction = await res.json();
|
|
||||||
entry.purpose = { display: lastTransaction.purpose};
|
|
||||||
entry.tags = lastTransaction.tags;
|
|
||||||
} else error(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save(){
|
async function save(){
|
||||||
let data = {
|
let data = {
|
||||||
...entry,
|
...entry,
|
||||||
@@ -123,7 +106,7 @@
|
|||||||
router.navigate('/accounting');
|
router.navigate('/accounting');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//entry.tags = [];
|
entry.tags = [];
|
||||||
onSave();
|
onSave();
|
||||||
document.getElementById('date-input').focus();
|
document.getElementById('date-input').focus();
|
||||||
} else error(res);
|
} else error(res);
|
||||||
|
|||||||
@@ -32,10 +32,6 @@
|
|||||||
onMount(load);
|
onMount(load);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Umbrella – {t('accounts')}</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|
||||||
<span></span>
|
<span></span>
|
||||||
|
|||||||
@@ -200,10 +200,9 @@
|
|||||||
<LineEditor bind:value={project.name} editable={true} onSet={val => update({name:val})} />
|
<LineEditor bind:value={project.name} editable={true} onSet={val => update({name:val})} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t('options')}
|
<button onclick={kanban}>{t('show_kanban')}</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button onclick={kanban}><span class="symbol"></span> {t('show_kanban')}</button>
|
|
||||||
<button onclick={toggleSettings}><span class="symbol"></span> {t('settings')}</button>
|
<button onclick={toggleSettings}><span class="symbol"></span> {t('settings')}</button>
|
||||||
</div>
|
</div>
|
||||||
<div>{t('state')}</div>
|
<div>{t('state')}</div>
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import de.srsoftware.umbrella.core.BaseDb;
|
|||||||
import de.srsoftware.umbrella.messagebus.events.Event;
|
import de.srsoftware.umbrella.messagebus.events.Event;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
|
|
||||||
public class SqliteDb extends BaseDb implements JournalDb{
|
public class SqliteDb extends BaseDb implements JournalDb{
|
||||||
public SqliteDb(Connection connection) {
|
public SqliteDb(Connection connection) {
|
||||||
@@ -35,14 +33,13 @@ public class SqliteDb extends BaseDb implements JournalDb{
|
|||||||
var sql = """
|
var sql = """
|
||||||
CREATE TABLE IF NOT EXISTS {0} (
|
CREATE TABLE IF NOT EXISTS {0} (
|
||||||
{1} INTEGER PRIMARY KEY,
|
{1} INTEGER PRIMARY KEY,
|
||||||
{2} LONG NOT NULL,
|
{2} INTEGER,
|
||||||
{3} INTEGER,
|
{3} VARCHAR(255) NOT NULL,
|
||||||
{4} VARCHAR(255) NOT NULL,
|
{4} VARCHAR(16) NOT NULL,
|
||||||
{5} VARCHAR(16) NOT NULL,
|
{5} TEXT
|
||||||
{6} TEXT
|
|
||||||
);
|
);
|
||||||
""";
|
""";
|
||||||
sql = format(sql,TABLE_JOURNAL,ID,TIMESTAMP,USER_ID,MODULE,ACTION,DESCRIPTION);
|
sql = format(sql,TABLE_JOURNAL,ID,USER_ID,MODULE,ACTION,DESCRIPTION);
|
||||||
try {
|
try {
|
||||||
db.prepareStatement(sql).execute();
|
db.prepareStatement(sql).execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -53,9 +50,8 @@ public class SqliteDb extends BaseDb implements JournalDb{
|
|||||||
@Override
|
@Override
|
||||||
public void logEvent(Event<?> event) {
|
public void logEvent(Event<?> event) {
|
||||||
try {
|
try {
|
||||||
var timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
|
insertInto(TABLE_JOURNAL,USER_ID,MODULE,ACTION,DESCRIPTION)
|
||||||
insertInto(TABLE_JOURNAL,TIMESTAMP,USER_ID,MODULE,ACTION,DESCRIPTION)
|
.values(event.initiator().id(), event.module(), event.eventType(), event.describe())
|
||||||
.values(timestamp,event.initiator().id(), event.module(), event.eventType(), event.describe())
|
|
||||||
.execute(db).close();
|
.execute(db).close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw databaseException(ERROR_WRITE_EVENT,event.eventType(),event.initiator().name());
|
throw databaseException(ERROR_WRITE_EVENT,event.eventType(),event.initiator().name());
|
||||||
|
|||||||
@@ -192,8 +192,6 @@
|
|||||||
"items": "Artikel",
|
"items": "Artikel",
|
||||||
|
|
||||||
"join_objects" : "{objects} zusammenführen",
|
"join_objects" : "{objects} zusammenführen",
|
||||||
|
|
||||||
"kanban": "Kanban",
|
|
||||||
"key": "Suchbegriff",
|
"key": "Suchbegriff",
|
||||||
|
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
@@ -309,7 +307,6 @@
|
|||||||
"project ({id})": "Projekt ({id})",
|
"project ({id})": "Projekt ({id})",
|
||||||
"Project '{project}' was edited": "Projekt '{project}' wurde bearbeitet",
|
"Project '{project}' was edited": "Projekt '{project}' wurde bearbeitet",
|
||||||
"projects": "Projekte",
|
"projects": "Projekte",
|
||||||
"Projects": "Projekte",
|
|
||||||
"properties": "Eigenschaften",
|
"properties": "Eigenschaften",
|
||||||
"property": "Eigenschaft",
|
"property": "Eigenschaft",
|
||||||
"purpose": "Zweck",
|
"purpose": "Zweck",
|
||||||
|
|||||||
@@ -192,8 +192,6 @@
|
|||||||
"items": "items",
|
"items": "items",
|
||||||
|
|
||||||
"join_objects" : "join {objects}",
|
"join_objects" : "join {objects}",
|
||||||
|
|
||||||
"kanban": "Kanban",
|
|
||||||
"key": "search term",
|
"key": "search term",
|
||||||
|
|
||||||
"language": "language",
|
"language": "language",
|
||||||
@@ -309,7 +307,6 @@
|
|||||||
"project ({id})": "project ({id})",
|
"project ({id})": "project ({id})",
|
||||||
"Project '{project}' was edited": "Project '{project}' was edited",
|
"Project '{project}' was edited": "Project '{project}' was edited",
|
||||||
"projects": "projects",
|
"projects": "projects",
|
||||||
"Projects": "projects",
|
|
||||||
"properties": "properties",
|
"properties": "properties",
|
||||||
"property": "property",
|
"property": "property",
|
||||||
"purpose": "purpose",
|
"purpose": "purpose",
|
||||||
|
|||||||
@@ -542,8 +542,7 @@ select.autocomplete{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
.grid2,
|
.grid2{
|
||||||
.grid3{
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto;
|
grid-template-columns: auto;
|
||||||
}
|
}
|
||||||
@@ -583,10 +582,6 @@ select.autocomplete{
|
|||||||
#app nav.expanded .timetracking{
|
#app nav.expanded .timetracking{
|
||||||
grid-column-end: span 2;
|
grid-column-end: span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete .suggestions > *{
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset.vcard{
|
fieldset.vcard{
|
||||||
|
|||||||
@@ -711,10 +711,6 @@ select.autocomplete{
|
|||||||
#app nav.expanded .timetracking{
|
#app nav.expanded .timetracking{
|
||||||
grid-column-end: span 2;
|
grid-column-end: span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete .suggestions > *{
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset.vcard{
|
fieldset.vcard{
|
||||||
|
|||||||
@@ -701,10 +701,6 @@ select.autocomplete{
|
|||||||
#app nav.expanded .timetracking{
|
#app nav.expanded .timetracking{
|
||||||
grid-column-end: span 2;
|
grid-column-end: span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete .suggestions > *{
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset.vcard{
|
fieldset.vcard{
|
||||||
|
|||||||
Reference in New Issue
Block a user