diff --git a/client/scripts/database.js b/client/scripts/database.js index 6f87354..44823cf 100644 --- a/client/scripts/database.js +++ b/client/scripts/database.js @@ -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; } @@ -1304,8 +1306,7 @@ function initDatabase() { if ((oldVersion < 10) && (newVersion >= 10)) { log('[db] to version 10'); var osExpenditures = db.createObjectStore('expenditures', { keyPath: 'id' }); - osExpenditures.createIndex('user_from', 'user_from', { unique: false }); - osExpenditures.createIndex('user_to', 'user_to', { unique: false }); + osExpenditures.createIndex('user', 'user', { unique: false }); var osUpdateTimes = upgradeTransaction.objectStore('update_times'); osUpdateTimes.add({ table: 'expenditures', time: 0 }); } 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..8d6a884 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..fb57ba1 --- /dev/null +++ b/server/scripts/expenditures-add.js @@ -0,0 +1,319 @@ +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(); + $('#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(); + $('#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}); + } +} + +async function expendituresShowAdd(defaultUser = 0) { + $('#item-add-user-from').data('userid', USER_ID).find('span').html('bezahlt von: ' + USER_NAME); + $('#input-add-date').val(formatDate('Y-m-d')).trigger('focusin'); + $('#input-add-amount').val(''); + $('#select-add-purpose').val('entryfee'); + $('#input-add-regatta-name').val(''); // TODO: datalist? + $('#input-add-purpose-text').val(''); + $('.item-user-to').remove(); + let item = ''; + item += '' + USER_NAME + ''; + item += ''; + item += ''; + $('#item-add-user-to').before(item); + if (defaultUser > 0) { + item = ''; + item += '' + (await dbGetData('users', defaultUser)).username + ''; + item += ''; + item += ''; + $('#item-add-user-to').before(item); + } + $('#menu-add').showMenu(); +} + +async function expendituresShowAddTransfer(defaultUser = 0) { + $('#switch-add-transfer-received').prop('checked', true).parent().prev().text('Geld gegeben').prev().removeClass('fa-arrow-left').addClass('fa-arrow-right'); + if (defaultUser == 0) { + $('#item-add-transfer-user').data('userid', 0).find('span').html('an: bitte auswählen'); + } else { + $('#item-add-transfer-user').data('userid', defaultUser).find('span').html('an: ' + (await dbGetData('users', defaultUser)).username); + } + $('#input-add-transfer-date').val(formatDate('Y-m-d')).trigger('focusin'); + $('#input-add-transfer-amount').val(''); + $('#input-add-transfer-purpose-text').val(''); + $('#menu-add-transfer').showMenu(); +} diff --git a/server/scripts/expenditures-user.js b/server/scripts/expenditures-user.js new file mode 100644 index 0000000..24cacde --- /dev/null +++ b/server/scripts/expenditures-user.js @@ -0,0 +1,317 @@ +let firstCall = true; +let rows = []; +let displayed = []; +let page = 1; +let pageCount = 0; +const showCount = 25; +let sumsDisplayed = true; + +function createPurpose(exp) { + let purpose = ''; + let extraText = ': '; + switch (exp.purpose) { + case 'transfer': purpose = 'Geldtransfer'; break; + case 'entryfee': purpose = 'Meldegeld'; break; + case 'camping': purpose = 'Camping'; break; + case 'food': purpose = 'Essen'; break; + case 'other': extraText = ''; break; + } + if (exp.purpose_text != '') { + purpose += extraText + exp.purpose_text; + } + return purpose; +} + +async function onListClicked(id) { + let exp = await dbGetData('expenditures', id); + console.log(exp); + + $('#menu-expenditure').find('.menu-title').find('p').text(createPurpose(exp) + ' (' + (exp.direction * exp.amount / 100).toFixed(2) + ' €)'); + + if (exp.approved == 0 && (exp.direction < 0) == (exp.canceled == 0)) { + $('#menu-item-approve').show(); + $('#menu-item-approve').attr('onclick', 'expenditureAction("approve", ' + exp['id'] + ', "' + (exp.canceled == 1 ? 'Stornierung' : 'Ausgabe') + ' genehmigt")'); + $('#menu-item-approve').text(exp.canceled == 1 ? 'Storno akzeptieren' : 'Akzeptieren'); + $('#menu-item-decline').show(); + $('#menu-item-decline').attr('onclick', 'expenditureAction("decline", ' + exp['id'] + ', "Ausgabe ' + (exp.canceled == 1 ? 'erneut angefragt' : 'abgelehnt') + '")'); + $('#menu-item-decline').text(exp.canceled == 1 ? 'Erneut anfragen' : 'Ablehnen'); + } else { + $('#menu-item-approve').hide(); + $('#menu-item-decline').hide(); + } + + if (exp.canceled == 0 && (exp.approved == 1 || exp.direction > 0)) { + $('#menu-item-cancel').show(); + $('#menu-item-cancel').attr('onclick', 'expenditureAction("cancel", ' + exp['id'] + ', "' + (exp.direction > 0 ? 'Ausgabe storniert' : 'Stornierung beantragt') + '")'); + } else { + $('#menu-item-cancel').hide(); + } + + $('#menu-expenditure').showMenu(); + $('#menu-expenditure').scrollTop(0); +} + +function expenditureAction(action, expId, successStr) { + $('#menu-expenditure').hideMenu(); + showLoader(); + let auth = { + id: localStorage.getItem('auth_id'), + hash: localStorage.getItem('auth_hash') + } + $.ajax({ + url: QUERY_URL + 'expenditure_' + action, + method: 'POST', + data: { + auth: auth, + expenditure: expId + }, + 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 stornieren.'); + } else { + log('unbekannter Fehler', status, error); + log(xhr); + toastError('Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal', 5000); + } + hideLoader(); + }, + success: function (data, status, xhr) { + sync(); + hideLoader(); + toastOk(successStr); + } + }); +} + +function pageChange() { + $('#p-count')[0].scrollIntoView({ behavior: "smooth" }); + drawList(); +} + +async function drawList() { + window.setTimeout(function () { + let list = ''; + + if (displayed.length > 0) { + let offset = (page - 1) * (sumsDisplayed ? showCount * 2 : showCount); + let count = (page == pageCount ? (displayed.length - offset) : (sumsDisplayed ? showCount * 2 + 1 : showCount)); + + for (i = 0; i < count; i ++) { + list += displayed[i + offset]; + } + } else { + list = '
Keine Ergebnisse, die der Suche entsprechen
'; + } + + $('#div-list').html(list); + }, 0); +} + +async function reSearch() { + window.setTimeout(function () { + displayed = []; + rows.forEach(function (entry) { + if (search($('#input-search').val(), entry.keywords)) { + displayed.push(entry.content); + } + }); + sumsDisplayed = displayed.length == rows.length; + $('#div-list').toggleClass('display-border', !sumsDisplayed); + pageCount = Math.ceil((displayed.length - (sumsDisplayed ? 1 : 0)) / (sumsDisplayed ? showCount * 2 : showCount)); + if ((page < 1) || (page > pageCount)) { + if (page < 1) { + page = 1; + } else { + page = pageCount; + } + } + drawPagination(); + drawList(); + }, 0); +} + +function cancelCause(cancel_cause) { + switch (cancel_cause) { + case 'not approved': + return 'nicht genehmigt'; + case 'canceled': + return 'storniert'; + } + return 'unbekannter Grund'; +} + +let siteScript = async function() { + if (!isLoggedIn()) { + hideLoader(); + return; + } + + userid = findGetParameter('user'); + let user = null; + if (userid !== null) { + user = await dbGetData('users', userid); + } + if (user === null) { + location.href = LINK_PRE + 'expenditures'; + return; + } + + $('#p-username').text(user.username); + $('.span-username').text(user.username); + + if (firstCall) { + firstCall = false; + initPagination(); + $('#input-search').on('input', reSearch); + $('#button-add').click((e) => expendituresShowAdd(userid)); + $('#button-add-transfer').click((e) => expendituresShowAddTransfer(userid)); + expendituresInitModals(); + } + + let exps = await dbGetDataIndex('expenditures', 'user', user.id); + exps.sort(function (a, b) { + return a.created - b.created; + }); + + let sum = 0; + for (i in exps) { + let exp = exps[i]; + if (exp.canceled == 0) { + sum += exp.direction * exp.amount; + } + exps[i].sum = sum; + exps[i].purpose_html = createPurpose(exp); + exps[i].purpose_date = formatDate("d.m.Y", parseDate(exp.purpose_date)); + } + + let sumText = ''; + if (sum == 0) sumText = 'Du und ' + user.username + ' seid quitt.'; + else if (sum > 0) sumText = user.username + ' schuldet Dir noch ' + (sum / 100).toFixed(2) + ' €.'; // TODO: Ausgleichen Button? + else sumText = 'Du schuldest ' + user.username + ' noch ' + (-sum / 100).toFixed(2) + ' €.'; // TODO: Ausgleichen Button? + + results = exps.reverse(); + + // TODO: open approvals + + let count = results.length; + if (count > 0) { + $('#p-count').text(sumText); + $('#div-list').show(); + $('#input-search').parent().show(); + + rows = []; + + for (id in results) { + let entry = results[id]; + + let rowSum = { keywords: [], content: '' }; + + rowSum.content += '
'; + + // ZEILE SUM + rowSum.content += '
'; + + // sum + rowSum.content += '
'; + if (entry.sum < 0) rowSum.content += ''; + rowSum.content += (entry.sum / 100).toFixed(2); + if (entry.sum < 0) rowSum.content += ''; + rowSum.content += ' €
'; + + rowSum.content += '
'; + + rows.push(rowSum); + + + let row = { keywords: [], content: '' }; + + row.keywords.push((entry.amount / 100).toFixed(2)); + row.keywords.push(entry.purpose_html); + row.keywords.push(entry.purpose_date); + row.keywords.push(entry.regatta_name); + + row.content += '
'; + + // ZEILE 1 + row.content += '
'; + + // purpose + row.content += '' + entry.purpose_html + '
'; + + // amount + row.content += '
'; + if (entry.canceled == 1) row.content += '('; + if (entry.direction < 0) row.content += ''; + row.content += (entry.direction * entry.amount / 100).toFixed(2); + if (entry.direction < 0) row.content += ''; + if (entry.canceled == 1) row.content += ')'; + row.content += ' €
'; + + // icons + if (entry.approved == 1) { + if (entry.canceled == 1) { + row.content += '
'; + } else { + row.content += '
'; + } + } else if ((entry.direction < 0) == (entry.canceled == 1)) { + row.content += '
'; + } else { + row.content += '
'; + } + + row.content += '
'; + + // ZEILE 2 + row.content += '
'; + + // date + row.content += '
' + entry.purpose_date + '
'; + + // regatta + row.content += '
' + entry.regatta_name + '
'; + + // cancel_cause + row.content += '
' + (entry.canceled == 1 ? cancelCause(entry.cancel_cause) : '') + '
'; + + row.content += '
'; + + rows.push(row); + } + + let rowSum = { keywords: [], content: '' }; + + rowSum.content += '
'; + + // ZEILE SUM + rowSum.content += '
'; + + // sum + rowSum.content += '
'; + rowSum.content += (0).toFixed(2); + rowSum.content += ' €
'; + + rowSum.content += '
'; + + rows.push(rowSum); + + reSearch(); + + } else { + $('#p-count').html('Keine Ausgaben gefunden!'); + $('#div-list').hide(); + $('#input-search').parent().hide(); + } + + hideLoader(); +} diff --git a/server/scripts/expenditures.js b/server/scripts/expenditures.js new file mode 100644 index 0000000..9f13014 --- /dev/null +++ b/server/scripts/expenditures.js @@ -0,0 +1,149 @@ +var firstCall = true; +var rows = []; +var displayed = []; +var page = 1; +var pageCount = 0; +const showCount = 25; + +async function onListClicked(id) { + window.location = LINK_PRE + 'expenditures-user?user=' + id; +} + +function pageChange() { + $('#p-count')[0].scrollIntoView({ behavior: "smooth" }); + drawList(); +} + +async function drawList() { + window.setTimeout(function () { + var list = ''; + + if (displayed.length > 0) { + var offset = (page - 1) * showCount; + var count = (page == pageCount ? (displayed.length % showCount) : showCount); + if (count == 0) count = showCount; + + for (i = 0; i < count; i ++) { + list += displayed[i + offset]; + } + } else { + list = '
Keine Ergebnisse, die der Suche entsprechen
'; + } + + $('#div-list').html(list); + }, 0); +} + +async function reSearch() { + window.setTimeout(function () { + displayed = []; + rows.forEach(function (entry) { + if (search($('#input-search').val(), entry.keywords)) { + displayed.push(entry.content); + } + }); + pageCount = Math.ceil(displayed.length / showCount); + if ((page < 1) || (page > pageCount)) { + if (page < 1) { + page = 1; + } else { + page = pageCount; + } + } + drawPagination(); + drawList(); + }, 0); +} + +var siteScript = async function() { + if (!isLoggedIn()) { + hideLoader(); + return; + } + + if (firstCall) { + firstCall = false; + initPagination(); + $('#input-search').on('input', reSearch); + $('#button-add').click((e) => expendituresShowAdd()); + $('#button-add-transfer').click((e) => expendituresShowAddTransfer()); + expendituresInitModals(); + } + + 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, + balance: 0, + openApprovals: 0, + cnt: 0 + }; + } + if (exp.canceled == 0) { + expUsers[eUId].balance += exp.direction * parseInt(exp.amount); + } + if (exp.approved == 0) { + if (exp.direction < 0 && exp.canceled == 0) expUsers[eUId].openApprovals++; + if (exp.direction > 0 && exp.canceled == 1) expUsers[eUId].openApprovals++; + } + expUsers[eUId].cnt++; + } + let results = Object.values(expUsers); + + let count = results.length; + if (count > 0) { + $('#p-count').hide(); + $('#div-list').show(); + $('#input-search').parent().show(); + + results.sort(function (a, b) { + return b.cnt - a.cnt; + }); + + rows = []; + + for (id in results) { + var entry = results[id]; + + var row = { keywords: [], content: '' }; + row.keywords.push(entry.username); + + row.content += '
'; + + // ZEILE 1 + // Username + row.content += '
' + entry.username + '
'; + + // ZEILE 2 + row.content += '
'; + + // open approvals + row.content += '
' + ((entry.openApprovals > 0) ? (entry.openApprovals + ' offene Genehmigung' + (entry.openApprovals > 1 ? 'en' : '')) : '') + '
'; + + // balance + row.content += '
'; + if (entry.balance < 0) row.content += ''; + row.content += (entry.balance / 100).toFixed(2); + if (entry.balance < 0) row.content += ''; + row.content += ' €
'; + + row.content += '
'; + + rows.push(row); + } + + reSearch(); + + } else { + $('#p-count').show().html('Keine Ausgaben gefunden!'); + $('#div-list').hide(); + $('#input-search').parent().hide(); + } + + hideLoader(); +}