diff --git a/client/scripts/database.js b/client/scripts/database.js index 6a73136..44823cf 100644 --- a/client/scripts/database.js +++ b/client/scripts/database.js @@ -1,4 +1,4 @@ -const DB_VERSION = 9; +const DB_VERSION = 10; const USER_ID = localStorage.getItem('auth_user'); const USER_NAME = localStorage.getItem('auth_username'); @@ -44,15 +44,17 @@ function search(string, fields) { var keywords = string.split(' '); for (kid in keywords) { var keyword = keywords[kid].toLowerCase(); - var found = false; - for (fid in fields) { - var field = fields[fid].toLowerCase(); - if (field.indexOf(keyword) >= 0) { - found = true; - break; + if (keyword != '') { + var found = false; + for (fid in fields) { + var field = fields[fid].toLowerCase(); + if (field.indexOf(keyword) >= 0) { + found = true; + break; + } } + if (!found) return false; } - if (!found) return false; } return true; } @@ -619,7 +621,7 @@ function sync() { localTimes[entry['table']] = entry['time']; }); - syncInProgress = 14; + syncInProgress = 15; var syncOkay = true; log("[db] Sync Start"); $('#i-sync').addClass('fa-spin'); @@ -1035,8 +1037,41 @@ function sync() { } }); + // EXPENDITURES + if (localTimes['expenditures'] < serverTimes['expenditures']) { + getJSON(QUERY_URL + 'get_expenditures?changed-after=' + localTimes['expenditures'], function (code, data) { + if (code == 200) { + var os = db.transaction('expenditures', 'readwrite').objectStore('expenditures'); + data.data.forEach(function (entry) { + os.put(entry); + }); + os.openCursor().onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + if (!data.keys.includes(parseInt(cursor.key))) { + os.delete(cursor.key); + } + cursor.continue(); + } else { + var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); + osUpdateTimes.put({ table: 'expenditures', time: serverTimes['expenditures'] }); + syncInProgress --; + log('[db] expenditures synced, remaining:', syncInProgress); + } + }; + } else { + log("[db] expenditures: Something went wrong (HTTP " + code + ")"); + syncOkay = false; + syncInProgress --; + log('[db] expenditures failed, remaining:', syncInProgress); + } + }); + } else { + syncInProgress --; + } + } else { - syncInProgress -= 4; + syncInProgress -= 5; } // NEWS @@ -1268,6 +1303,14 @@ function initDatabase() { var osFollows = db.createObjectStore('follows', { keyPath: 'id' }); } + if ((oldVersion < 10) && (newVersion >= 10)) { + log('[db] to version 10'); + var osExpenditures = db.createObjectStore('expenditures', { keyPath: 'id' }); + osExpenditures.createIndex('user', 'user', { unique: false }); + var osUpdateTimes = upgradeTransaction.objectStore('update_times'); + osUpdateTimes.add({ table: 'expenditures', time: 0 }); + } + // Force resync after db update if (oldVersion >= 1) { var osUpdateTimes = upgradeTransaction.objectStore('update_times'); @@ -1284,6 +1327,22 @@ function initDatabase() { function resetDb() { if (canUseLocalDB) { showLoader(); + db.transaction('clubs', 'readwrite').objectStore('clubs').clear(); + db.transaction('boats', 'readwrite').objectStore('boats').clear(); + db.transaction('sailors', 'readwrite').objectStore('sailors').clear(); + db.transaction('regattas', 'readwrite').objectStore('regattas').clear(); + db.transaction('results', 'readwrite').objectStore('results').clear(); + db.transaction('plannings', 'readwrite').objectStore('plannings').clear(); + db.transaction('trim_boats', 'readwrite').objectStore('trim_boats').clear(); + db.transaction('trim_users', 'readwrite').objectStore('trim_users').clear(); + db.transaction('trim_trims', 'readwrite').objectStore('trim_trims').clear(); + db.transaction('users', 'readwrite').objectStore('users').clear(); + db.transaction('years', 'readwrite').objectStore('years').clear(); + db.transaction('news', 'readwrite').objectStore('news').clear(); + db.transaction('class', 'readwrite').objectStore('class').clear(); + db.transaction('rankings', 'readwrite').objectStore('rankings').clear(); + db.transaction('follows', 'readwrite').objectStore('follows').clear(); + db.transaction('expenditures', 'readwrite').objectStore('expenditures').clear(); var osUpdateTimes = db.transaction('update_times', 'readwrite').objectStore('update_times'); osUpdateTimes.put({ table: 'last_sync', time: 1 }); osUpdateTimes.put({ table: 'clubs', time: 0 }); @@ -1297,6 +1356,7 @@ function resetDb() { osUpdateTimes.put({ table: 'trim_trims', time: 0 }); osUpdateTimes.put({ table: 'news', time: 0 }); osUpdateTimes.put({ table: 'users', time: 0 }); + osUpdateTimes.put({ table: 'expenditures', time: 0 }); log('[db] DB update times reset'); hideLoader(); } diff --git a/client/scripts/regatten.js.php b/client/scripts/regatten.js.php index 2ee1134..83bb367 100644 --- a/client/scripts/regatten.js.php +++ b/client/scripts/regatten.js.php @@ -62,7 +62,11 @@ var badges = { news: { id: 'badge-more-news', cnt: 0 - } + }, + expenditures: { + id: 'badge-more-expenditures', + cnt: 0 + } } } }; @@ -512,6 +516,24 @@ async function updateNewsBadge() { updateBadge('more/news', sum); } +async function updateExpendituresBadge() { + let allExps = await dbGetData('expenditures'); + let sum = 0; + for (let i in allExps) { + let exp = allExps[i]; + if (exp.approved == 0) { + if (exp.direction < 0 && exp.canceled == 0) sum++; + if (exp.direction > 0 && exp.canceled == 1) sum++; + } + } + updateBadge('more/expenditures', sum); +} + +function updateBadges() { + updateNewsBadge(); + updateExpendituresBadge(); +} + var initRegatten = function() { showLoader(); @@ -568,11 +590,11 @@ var onDatabaseLoaded = function() { onServiceWorkerLoaded(); initPushSettings(); - updateNewsBadge(); + updateBadges(); } var onAfterSync = function() { - updateNewsBadge(); + updateBadges(); } function sendErrorReport() { diff --git a/client/styles/regatten.css b/client/styles/regatten.css index 951cde7..ece6131 100644 --- a/client/styles/regatten.css +++ b/client/styles/regatten.css @@ -144,6 +144,69 @@ text-align: right; } +/*** EXPENDITURES LIST ***/ +.expenditures-list.display-border > div { + border-bottom: 1px solid #dee2e6; +} +.expenditures-list > div { + padding-top: 1rem; + padding-bottom: 1rem; + cursor: pointer; +} + +.expenditures-list > div:last-child { + border: 0; + padding-bottom: 0; +} + +.expenditures-list div { + white-space: nowrap; +} + +.expenditures-list > div > div > div { + display: inline-block; +} + +.expenditures-list > div > div:nth-child(1) > div:nth-child(1) { + width: calc(75% - 1.5em); +} +.expenditures-list > div > div:nth-child(1) > div:nth-child(2) { + width: 25%; + text-align: right; +} +.expenditures-list > div > div:nth-child(1) > div:nth-child(3) { + width: 1.5em; + text-align: right; +} + +.expenditures-list > div > div:nth-child(2) > div:nth-child(1) { + width: 6em; +} +.expenditures-list > div > div:nth-child(2) > div:nth-child(2) { + width: calc(70% - 6em); + overflow-x: visible; +} +.expenditures-list > div > div:nth-child(2) > div:nth-child(3) { + width: 30%; + text-align: right; +} + +.expenditures-list > div.sum { + padding: 0; +} +.expenditures-list > div.sum > div:nth-child(1) { + background: #dee2e6; + height: 1px; + display: flex; + justify-content: center; +} +.expenditures-list > div.sum > div:nth-child(1) > div:nth-child(1) { + background: white; + width: initial; + margin-top: -1em; + padding: 0 .5em; +} + /*** NORMAL LIST ***/ .normal-list > div { padding-top: 1rem; diff --git a/server/content/expenditures-user.php b/server/content/expenditures-user.php new file mode 100644 index 0000000..c4334bd --- /dev/null +++ b/server/content/expenditures-user.php @@ -0,0 +1,80 @@ +Ausgaben-Verwaltung"; +$content .= '

'; +$content .= $tpl->load('button', [' Neue Ausgabe', '#', 'html-id' => 'button-add', 'css-class' => 'mt-3 mb-2']); +$content .= $tpl->load('button', [' Neuer Geldtransfer', '#', 'html-id' => 'button-add-transfer', 'css-class' => 'mt-3 mb-0']); + +$sp['output'] .= $tpl->load('card', [$content, 'css-class' => 'show-loggedin']); + +// Not loggedin +$content = '

Ausgaben-Verwaltung

'; +$content .= '

Um die Ausgaben-Verwaltung nutzen zu können, musst Du angemeldet sein.
Melde Dich hier an oder registriere Dich jetzt kostenlos.

'; + +$sp['output'] .= $tpl->load('card', [$content, 'css-class' => 'show-notloggedin']); + +// List +$content = '

'; +$content .= $tpl->load('input', ['html-id' => 'input-search', 'placeholder' => 'Suche', 'type' => 'text', 'css-class' => 'mt-2']); +$content .= '
'; + +$sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-list', 'css-class' => 'show-loggedin']); + +// Pagination +$sp['output'] .= $tpl->load('pagination', ['html-id' => 'pagination', 'css-class' => 'show-loggedin']); + +// Legend +$content = '

'; +$content .= ' genehmigt
'; +$content .= ' storniert
'; +$content .= ' wartet auf Zustimmung von
'; +$content .= ' wartet auf Deine Zustimmung'; +$content .= '

'; + +$sp['output'] .= $tpl->load('card', [$content, 'css-class' => 'show-loggedin']); + +// Menu +$items = $tpl->load('menu/item-icon', ['Akzeptieren', '#', 'html-id' => 'menu-item-approve', 'icon' => 'fa-check']); +$items .= $tpl->load('menu/item-icon', ['Ablehnen', '#', 'html-id' => 'menu-item-decline', 'icon' => 'fa-times']); +$items .= $tpl->load('menu/item-icon', ['Stornieren', '#', 'html-id' => 'menu-item-cancel', 'icon' => 'fa-times']); +$sp['menus'] .= $tpl->load('menu/bottom', [$items, 'html-id' => 'menu-expenditure', 'title' => 'Ausgabe', 'height' => 200]); + +// Menu Add +$items = $tpl->load('menu/item-simple', ['bezahlt von: ', '#', 'html-id' => 'item-add-user-from']); +$items .= $tpl->load('input', ['html-id' => 'input-add-date', 'placeholder' => 'Datum des Transfers', 'type' => 'date', 'css-class' => 'mt-3']); +$items .= $tpl->load('input', ['html-id' => 'input-add-amount', 'placeholder' => 'Betrag in Euro', 'type' => 'number" min="0.01" step="0.01']); +$options = ''; +$options .= ''; +$options .= ''; +$options .= ''; +$items .= $tpl->load('select', ['html-id' => 'select-add-purpose', 'placeholder' => 'Verwendung', 'options' => $options]); +$items .= $tpl->load('input', ['html-id' => 'input-add-regatta-name', 'placeholder' => 'Name der Regatta (optional)', 'type' => 'text']); +$items .= $tpl->load('input', ['html-id' => 'input-add-purpose-text', 'placeholder' => 'Verwendungszweck (optional)', 'type' => 'text']); +$items .= '

Für wen wurde das Geld ausgegeben? (z.B. Du und Dein Segelpartner)
Hinweis: Der angegebene Betrag wird durch die Personen geteilt, die Du hier auswählst.

'; +$items .= $tpl->load('menu/item-simple', ['Weiteren Benutzer hinzufügen', '#', 'html-id' => 'item-add-user-to']); +$items .= $tpl->load('button', ['Speichern', '#', 'html-id' => 'button-add-save', 'css-class' => 'mb-3']); +$sp['menus'] .= $tpl->load('menu/modal', [$items, 'html-id' => 'menu-add', 'title' => 'Neue Ausgabe', 'height' => '90vh', 'width' => '90vw']); + +// Menu Add Transfer +$items = $tpl->load('menu/item-switch', ['Geld bekommen', 'html-id' => 'switch-add-transfer-received', 'icon' => 'fa-arrow-left']); +$items .= $tpl->load('menu/item-simple', ['von: bitte auswählen', '#', 'html-id' => 'item-add-transfer-user']); +$items .= $tpl->load('input', ['html-id' => 'input-add-transfer-date', 'placeholder' => 'Datum des Transfers', 'type' => 'date', 'css-class' => 'mt-3']); +$items .= $tpl->load('input', ['html-id' => 'input-add-transfer-amount', 'placeholder' => 'Betrag in Euro', 'type' => 'number" min="0.01" step="0.01']); +$items .= $tpl->load('input', ['html-id' => 'input-add-transfer-purpose-text', 'placeholder' => 'Verwendungszweck (optional)', 'type' => 'text']); +$items .= $tpl->load('button', ['Speichern', '#', 'html-id' => 'button-add-transfer-save']); +$sp['menus'] .= $tpl->load('menu/modal', [$items, 'html-id' => 'menu-add-transfer', 'title' => 'Neuer Geldtransfer', 'height' => 470, 'width' => '90vw']); + +// Select user +$items = $tpl->load('input', ['html-id' => 'input-user-search', 'placeholder' => 'Suche...', 'type' => 'text']); +$sp['menus'] .= $tpl->load('menu/modal', [$items, 'html-id' => 'menu-select-user', 'title' => 'Benutzer auswählen', 'height' => 500, 'width' => '90vw']); + +$sp['scripts'] .= $scripts->load('pagination', ['pageChange', 'page', 'pageCount', 'pagination']); +$sp['scripts'] .= $scripts->load('expenditures-add'); +$sp['scripts'] .= $scripts->load('expenditures-user'); + +?> \ No newline at end of file diff --git a/server/content/expenditures.php b/server/content/expenditures.php new file mode 100644 index 0000000..eb1d2a8 --- /dev/null +++ b/server/content/expenditures.php @@ -0,0 +1,63 @@ +Ausgaben-Verwaltung"; +$content .= $tpl->load('button', [' Neue Ausgabe', '#', 'html-id' => 'button-add', 'css-class' => 'mt-3 mb-2']); +$content .= $tpl->load('button', [' Neuer Geldtransfer', '#', 'html-id' => 'button-add-transfer', 'css-class' => 'mt-3 mb-0']); + +$sp['output'] .= $tpl->load('card', [$content, 'css-class' => 'show-loggedin']); + +// Not loggedin +$content = '

Ausgaben-Verwaltung

'; +$content .= '

Um die Ausgaben-Verwaltung nutzen zu können, musst Du angemeldet sein.
Melde Dich hier an oder registriere Dich jetzt kostenlos.

'; + +$sp['output'] .= $tpl->load('card', [$content, 'css-class' => 'show-notloggedin']); + +// List +$content = '

'; +$content .= $tpl->load('input', ['html-id' => 'input-search', 'placeholder' => 'Suche', 'type' => 'text', 'css-class' => 'mt-2']); +$content .= '
'; + +$sp['output'] .= $tpl->load('card', [$content, 'html-id' => 'card-list', 'css-class' => 'show-loggedin']); + +// Pagination +$sp['output'] .= $tpl->load('pagination', ['html-id' => 'pagination', 'css-class' => 'show-loggedin']); + +// Menu Add +$items = $tpl->load('menu/item-simple', ['bezahlt von: ', '#', 'html-id' => 'item-add-user-from']); +$items .= $tpl->load('input', ['html-id' => 'input-add-date', 'placeholder' => 'Datum des Transfers', 'type' => 'date', 'css-class' => 'mt-3']); +$items .= $tpl->load('input', ['html-id' => 'input-add-amount', 'placeholder' => 'Betrag in Euro', 'type' => 'number" min="0.01" step="0.01']); +$options = ''; +$options .= ''; +$options .= ''; +$options .= ''; +$items .= $tpl->load('select', ['html-id' => 'select-add-purpose', 'placeholder' => 'Verwendung', 'options' => $options]); +$items .= $tpl->load('input', ['html-id' => 'input-add-regatta-name', 'placeholder' => 'Name der Regatta (optional)', 'type' => 'text']); +$items .= $tpl->load('input', ['html-id' => 'input-add-purpose-text', 'placeholder' => 'Verwendungszweck (optional)', 'type' => 'text']); +$items .= '

Für wen wurde das Geld ausgegeben? (z.B. Du und Dein Segelpartner)
Hinweis: Der angegebene Betrag wird durch die Personen geteilt, die Du hier auswählst.

'; +$items .= $tpl->load('menu/item-simple', ['Weiteren Benutzer hinzufügen', '#', 'html-id' => 'item-add-user-to']); +$items .= $tpl->load('button', ['Speichern', '#', 'html-id' => 'button-add-save', 'css-class' => 'mb-3']); +$sp['menus'] .= $tpl->load('menu/modal', [$items, 'html-id' => 'menu-add', 'title' => 'Neue Ausgabe', 'height' => '90vh', 'width' => '90vw']); + +// Menu Add Transfer +$items = $tpl->load('menu/item-switch', ['Geld bekommen', 'html-id' => 'switch-add-transfer-received', 'icon' => 'fa-arrow-left']); +$items .= $tpl->load('menu/item-simple', ['von: bitte auswählen', '#', 'html-id' => 'item-add-transfer-user']); +$items .= $tpl->load('input', ['html-id' => 'input-add-transfer-date', 'placeholder' => 'Datum des Transfers', 'type' => 'date', 'css-class' => 'mt-3']); +$items .= $tpl->load('input', ['html-id' => 'input-add-transfer-amount', 'placeholder' => 'Betrag in Euro', 'type' => 'number" min="0.01" step="0.01']); +$items .= $tpl->load('input', ['html-id' => 'input-add-transfer-purpose-text', 'placeholder' => 'Verwendungszweck (optional)', 'type' => 'text']); +$items .= $tpl->load('button', ['Speichern', '#', 'html-id' => 'button-add-transfer-save']); +$sp['menus'] .= $tpl->load('menu/modal', [$items, 'html-id' => 'menu-add-transfer', 'title' => 'Neuer Geldtransfer', 'height' => 470, 'width' => '90vw']); + +// Select user +$items = $tpl->load('input', ['html-id' => 'input-user-search', 'placeholder' => 'Suche...', 'type' => 'text']); +$sp['menus'] .= $tpl->load('menu/modal', [$items, 'html-id' => 'menu-select-user', 'title' => 'Benutzer auswählen', 'height' => 500, 'width' => '90vw']); + +$sp['scripts'] .= $scripts->load('pagination', ['pageChange', 'page', 'pageCount', 'pagination']); +$sp['scripts'] .= $scripts->load('expenditures-add'); +$sp['scripts'] .= $scripts->load('expenditures'); + +?> \ No newline at end of file diff --git a/server/page/menus.php b/server/page/menus.php index 9e59834..a654a43 100644 --- a/server/page/menus.php +++ b/server/page/menus.php @@ -68,6 +68,12 @@ + + + Ausgaben-Verwaltung + + + Trimm-Bücher diff --git a/server/scripts/expenditures-add.js b/server/scripts/expenditures-add.js new file mode 100644 index 0000000..ea508a0 --- /dev/null +++ b/server/scripts/expenditures-add.js @@ -0,0 +1,338 @@ +let users = []; +let known = []; +let userSelectedCallback = null; +let excludeUsers = {}; + +function userSelected(id) { + if (typeof userSelectedCallback === 'function') { + userSelectedCallback(id); + } +} + +async function usersSearch() { + $('.item-user-search').remove(); + if ($('#input-user-search').val().length == 0) { + known.forEach(function (entry) { + if (!(entry.id in excludeUsers)) { + $('#menu-select-user').find('.content').find('.list-group').append(entry.content); + } + }); + } + if ($('#input-user-search').val().length >= 3) { + let cnt = 0; + users.forEach(function (entry) { + if (!(entry.id in excludeUsers) && search($('#input-user-search').val(), entry.keywords)) { + $('#menu-select-user').find('.content').find('.list-group').append(entry.content); + cnt++; + } + }); + if (cnt == 0) { + let item = ''; + $('#menu-select-user').find('.content').find('.list-group').append(item); + } + } else { + let item = ''; + $('#menu-select-user').find('.content').find('.list-group').append(item); + } +} + +function addRemoveToUser(userid) { + $('.item-user-to[data-userid=' + userid + ']').remove(); +} + +async function expendituresInitModals() { + $('#button-add-save').click(function () { + showLoader(); + const jqUserFrom = $('#item-add-user-from'); + const userFrom = jqUserFrom.data('userid'); + const jqDate = $('#input-add-date'); + const purposeDate = jqDate.val(); + if (purposeDate == '') { + hideLoader(); + toastError('Es wurde kein Datum ausgewählt!'); + jqDate.focus(); + return; + } + const jqAmount = $('#input-add-amount'); + const amount = jqAmount.val(); + if (amount == '') { + hideLoader(); + toastError('Es wurde kein Betrag ausgewählt!'); + jqAmount.focus(); + return; + } + const purpose = $('#select-add-purpose').val(); + const regattaName = $('#input-add-regatta-name').val(); + const purposeText = $('#input-add-purpose-text').val(); + let usersTo = []; + $('.item-user-to[data-userid]').each(function (index) { + usersTo.push($(this).data('userid')); + }); + let auth = { + id: localStorage.getItem('auth_id'), + hash: localStorage.getItem('auth_hash') + } + $.ajax({ + url: QUERY_URL + 'expenditures_add', + method: 'POST', + data: { + auth: auth, + user_from: userFrom, + date: purposeDate, + amount: Math.round(parseFloat(amount) * 100), + purpose: purpose, + regatta_name: regattaName, + purpose_text: purposeText, + users_to: usersTo + }, + error: function (xhr, status, error) { + if (xhr.status == 401) { + log('authentification failed'); + toastError('Authentifizierung fehlgeschlagen. Versuche es erneut.'); + } else if (xhr.status == 0) { + toastError('Du bist momentan offline.
Stelle eine Internetverbindung her, um die Ausgabe zu speichern'); + } else { + log('expenditures_add: unbekannter Fehler', status, error); + log(xhr); + toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000); + } + hideLoader(); + }, + success: async function (data, status, xhr) { + await sync(); + updateExpendituresBadge(); + $('#menu-add').hideMenu(); + hideLoader(); + toastOk('Ausgabe gespeichert. Betrag wurde durch ' + data.count + ' Personen geteilt.'); + } + }); + }); + $('#item-add-user-from').click(async function () { + excludeUsers = {}; + $('#input-user-search').val('').trigger('focusin').trigger('focusout'); + usersSearch(); + userSelectedCallback = async function (userid) { + $('#item-add-user-from').data('userid', userid).find('span').html('bezahlt von: ' + (await dbGetData('users', userid)).username); + $('#menu-select-user').hideMenu(); + $('#menu-add').showMenu(); + } + $('#menu-add').hideMenu(); + $('#menu-select-user').showMenu(); + $('#input-user-search').focus(); + }); + $('#item-add-user-to').click(async function () { + excludeUsers = {}; + $('.item-user-to[data-userid]').each(function (index) { + excludeUsers[$(this).data('userid')] = true; + }); + $('#input-user-search').val('').trigger('focusin').trigger('focusout'); + usersSearch(); + userSelectedCallback = async function (userid) { + let item = '
'; + item += '' + (await dbGetData('users', userid)).username + ''; + item += ''; + item += ''; + $('#item-add-user-to').before(item); + $('#menu-select-user').hideMenu(); + $('#menu-add').showMenu(); + } + $('#menu-add').hideMenu(); + $('#menu-select-user').showMenu(); + $('#input-user-search').focus(); + }); + + $('#button-add-transfer-save').click(function () { + showLoader(); + const jqUser = $('#item-add-transfer-user'); + const selectedUser = jqUser.data('userid'); + if (selectedUser == 0) { + hideLoader(); + toastError('Es wurde keine Person ausgewählt!'); + return; + } + const jqDate = $('#input-add-transfer-date'); + const selectedDate = jqDate.val(); + if (selectedDate == '') { + hideLoader(); + toastError('Es wurde kein Datum ausgewählt!'); + jqDate.focus(); + return; + } + const jqAmount = $('#input-add-transfer-amount'); + const selectedAmount = jqAmount.val(); + if (selectedAmount == '') { + hideLoader(); + toastError('Es wurde kein Betrag ausgewählt!'); + jqAmount.focus(); + return; + } + const jqPurposeText = $('#input-add-transfer-purpose-text'); + const selectedPurposeText = jqPurposeText.val(); + const jqSwitch = $('#switch-add-transfer-received'); + const direction = jqSwitch.prop('checked'); + let auth = { + id: localStorage.getItem('auth_id'), + hash: localStorage.getItem('auth_hash') + } + $.ajax({ + url: QUERY_URL + 'expenditures_add_transfer', + method: 'POST', + data: { + auth: auth, + direction: direction ? 1 : -1, + userid: selectedUser, + date: selectedDate, + amount: Math.round(parseFloat(selectedAmount) * 100), + purpose_text: selectedPurposeText + }, + error: function (xhr, status, error) { + if (xhr.status == 401) { + log('authentification failed'); + toastError('Authentifizierung fehlgeschlagen. Versuche es erneut.'); + } else if (xhr.status == 0) { + toastError('Du bist momentan offline.
Stelle eine Internetverbindung her, um den Geldtransfer zu speichern'); + } else { + log('expenditures_add_transfer: unbekannter Fehler', status, error); + log(xhr); + toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000); + } + hideLoader(); + }, + success: async function (data, status, xhr) { + await sync(); + updateExpendituresBadge(); + $('#menu-add-transfer').hideMenu(); + hideLoader(); + toastOk(direction ? 'Geldtransfer gespeichert. ' + (await dbGetData('users', selectedUser)).username + ' muss dies noch bestätigen' : 'Geldtransfer wurde gespeichert'); + } + }); + }); + $('#switch-add-transfer-received').parent().parent().click(async function () { + const jqSwitch = $('#switch-add-transfer-received'); + const jqUser = $('#item-add-transfer-user'); + const direction = jqSwitch.prop('checked'); + jqSwitch.parent().prev().text(direction ? 'Geld gegeben' : 'Geld bekommen').prev().removeClass('fa-arrow-' + (direction ? 'left' : 'right')).addClass('fa-arrow-' + (direction ? 'right' : 'left')); + const selectedUser = jqUser.data('userid'); + if (selectedUser == 0) { + jqUser.find('span').html((direction ? 'an: ' : 'von: ') + 'bitte auswählen'); + } else { + jqUser.find('span').text((direction ? 'an: ' : 'von: ') + (await dbGetData('users', selectedUser)).username); + } + }); + $('#item-add-transfer-user').click(async function () { + excludeUsers = {}; + excludeUsers[USER_ID] = true; + $('#input-user-search').val('').trigger('focusin').trigger('focusout'); + usersSearch(); + userSelectedCallback = async function (userid) { + const direction = $('#switch-add-transfer-received').prop('checked'); + $('#item-add-transfer-user').data('userid', userid).find('span').html((direction ? 'an: ' : 'von: ') + (await dbGetData('users', userid)).username); + $('#menu-select-user').hideMenu(); + $('#menu-add-transfer').showMenu(); + } + $('#menu-add-transfer').hideMenu(); + $('#menu-select-user').showMenu(); + $('#input-user-search').focus(); + }); + + $('#input-user-search').on('input', usersSearch); + + users = []; + known = []; + let itemMe = ''; + itemMe += 'ICH (' + USER_NAME + ')'; + itemMe += ''; + itemMe += ''; + known.push({id: USER_ID, content: itemMe}); + let knownIds = {}; + let allExps = await dbGetData('expenditures'); + let expUsers = {}; + for (let i in allExps) { + let exp = allExps[i]; + let eUId = exp.user; + if (!(eUId in expUsers)) { + expUsers[eUId] = { + userId: eUId, + username: (await dbGetData('users', eUId)).username, + cnt: 0 + }; + } + expUsers[eUId].cnt++; + } + expUsers = Object.values(expUsers); + expUsers.sort(function (a, b) { + return a.username.localeCompare(b.username); + }); + for (let i in expUsers) { + knownIds[expUsers[i].userId] = true; + } + const dbUsers = await dbGetData('users'); + dbUsers.sort(function (a, b) { + return a.username.localeCompare(b.username); + }); + for (let i in dbUsers) { + let item = ''; + item += '' + dbUsers[i].username + ''; + item += ''; + item += ''; + users.push({ + keywords: [dbUsers[i].username], + id: dbUsers[i].id, + content: item + }); + if (dbUsers[i].id in knownIds) known.push({id: dbUsers[i].id, content: item}); + } + + $('#input-add-regatta-name').attr('list', 'list-regattas'); + let listRegattas = $('').appendTo('body'); + let plannings = await dbGetDataIndex('plannings', 'user', USER_ID); + let regattas = []; + for (let i in plannings) { + let regatta = await dbGetData('regattas', plannings[i].regatta); + regatta.year = formatDate('Y', parseDate(regatta.date)); + regattas.push(regatta); + } + regattas.sort(function (a, b) { + if (a.year != b.year) return b.year - a.year; + return a.name.localeCompare(b.name); + }); + for (let i in regattas) { + $('