feat: new statistics per payment method (#124)

* feat: new statistics per payment method

* fix: fix typo on english language file

* fix: use translation text for deleting subscriptions
This commit is contained in:
Miguel Ribeiro 2024-02-18 16:26:54 +01:00 committed by GitHub
parent fc2ca267e0
commit 6200fa5e87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 67 additions and 4 deletions

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Aufgeteilte Ansichten", 'split_views' => "Aufgeteilte Ansichten",
'category_split' => "Kategorien", 'category_split' => "Kategorien",
'household_split' => "Haushalt", 'household_split' => "Haushalt",
'payment_method_split' => "Zahlungsmethode",
// About page // About page
'about_and_credits' => "Informationen und Danksagungen", 'about_and_credits' => "Informationen und Danksagungen",
'license' => "Lizenz", 'license' => "Lizenz",

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Διαχωρισμένες προβολές", 'split_views' => "Διαχωρισμένες προβολές",
'category_split' => "Διαχωρισμός κατηγορίας", 'category_split' => "Διαχωρισμός κατηγορίας",
'household_split' => "Διαχωρισμός νοικοκυριού", 'household_split' => "Διαχωρισμός νοικοκυριού",
'payment_method_split' => "Διαχωρισμός τρόπου πληρωμής",
// About page // About page
'about_and_credits' => "Σχετικά και Credits", 'about_and_credits' => "Σχετικά και Credits",
'license' => "License", 'license' => "License",

View File

@ -44,7 +44,7 @@ $i18n = [
"weeks" => "weeks", "weeks" => "weeks",
"months" => "months", "months" => "months",
"years" => "years", "years" => "years",
"external_url" => "Visit Externarl URL", "external_url" => "Visit External URL",
"empty_page" => "Empty Page", "empty_page" => "Empty Page",
// Subscription form // Subscription form
"add_subscription" => "Add subscription", "add_subscription" => "Add subscription",
@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Split Views", 'split_views' => "Split Views",
'category_split' => "Category Split", 'category_split' => "Category Split",
'household_split' => "Household Split", 'household_split' => "Household Split",
'payment_method_split' => "Payment Method Split",
// About page // About page
'about_and_credits' => "About and Credits", 'about_and_credits' => "About and Credits",
'license' => "License", 'license' => "License",

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Vistas Divididas", 'split_views' => "Vistas Divididas",
'category_split' => "División por Categoría", 'category_split' => "División por Categoría",
'household_split' => "División por Hogar", 'household_split' => "División por Hogar",
'payment_method_split' => "División por Método de Pago",
// About page // About page
'about_and_credits' => "Acerca de y Créditos", 'about_and_credits' => "Acerca de y Créditos",
'license' => "Licencia", 'license' => "Licencia",

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Vues partagées", 'split_views' => "Vues partagées",
'category_split' => "Répartition par catégorie", 'category_split' => "Répartition par catégorie",
'household_split' => "Répartition du ménage", 'household_split' => "Répartition du ménage",
'payment_method_split' => "Répartition par méthode de paiement",
// Page À propos // Page À propos
'about_and_credits' => "À propos et crédits", 'about_and_credits' => "À propos et crédits",
'license' => "Licence", 'license' => "Licence",

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "分割表示", 'split_views' => "分割表示",
'category_split' => "カテゴリ別", 'category_split' => "カテゴリ別",
'household_split' => "世帯別", 'household_split' => "世帯別",
'payment_method_split' => "支払い方法別",
// About page // About page
'about_and_credits' => "概要とクレジット", 'about_and_credits' => "概要とクレジット",
'license' => "License", 'license' => "License",

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Vistas Divididas", 'split_views' => "Vistas Divididas",
'category_split' => "Por Categoria", 'category_split' => "Por Categoria",
'household_split' => "Por Membro", 'household_split' => "Por Membro",
'payment_method_split' => "Por Método de Pagamento",
// About page // About page
'about_and_credits' => "Sobre e Créditos", 'about_and_credits' => "Sobre e Créditos",
'license' => "Licença", 'license' => "Licença",

View File

@ -78,6 +78,7 @@ $i18n = [
'split_views' => "Bölünmüş Görünümler", 'split_views' => "Bölünmüş Görünümler",
'category_split' => "Kategori Bölümü", 'category_split' => "Kategori Bölümü",
'household_split' => "Hane Bölümü", 'household_split' => "Hane Bölümü",
'payment_method_split' => "Ödeme Yöntemi Bölümü",
// About page // About page
'about_and_credits' => "Hakkında ve Teşekkürler", 'about_and_credits' => "Hakkında ve Teşekkürler",
'license' => "Lisans", 'license' => "Lisans",

View File

@ -83,6 +83,7 @@ $i18n = [
'split_views' => "拆分视图", 'split_views' => "拆分视图",
'category_split' => "分类视图", 'category_split' => "分类视图",
'household_split' => "家庭视图", 'household_split' => "家庭视图",
'payment_method_split' => "支付方式视图",
// 关于页面 // 关于页面
'about_and_credits' => "关于和鸣谢", 'about_and_credits' => "关于和鸣谢",

View File

@ -83,6 +83,7 @@ $i18n = [
'split_views' => "分割表示", 'split_views' => "分割表示",
'category_split' => "類別表示", 'category_split' => "類別表示",
'household_split' => "家庭表示", 'household_split' => "家庭表示",
'payment_method_split' => "付款方式表示",
// 關於頁面 // 關於頁面
'about_and_credits' => "關於和致謝", 'about_and_credits' => "關於和致謝",

View File

@ -145,7 +145,7 @@ function handleFileSelect(event) {
} }
function deleteSubscription(id) { function deleteSubscription(id) {
if (confirm("Are you sure you want to delete this subscription?")) { if (confirm(translate('confirm_delete_subscription'))) {
fetch(`endpoints/subscription/delete.php?id=${id}`, { fetch(`endpoints/subscription/delete.php?id=${id}`, {
method: 'DELETE', method: 'DELETE',
}) })

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "Fehler beim Laden des Abonnements", failed_to_load_subscription: "Fehler beim Laden des Abonnements",
edit_subscription: "Abonnement bearbeiten", edit_subscription: "Abonnement bearbeiten",
add_subscription: "Abonnement hinzufügen", add_subscription: "Abonnement hinzufügen",
confirm_delete_subscription: "Sind Sie sicher, dass Sie dieses Abonnement löschen möchten?",
// Settings // Settings
network_response_error: "Netzwerkfehler", network_response_error: "Netzwerkfehler",
failed_add_member: "Hinzufügen von Mitglied fehlgeschlagen", failed_add_member: "Hinzufügen von Mitglied fehlgeschlagen",

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "Απέτυχε η φόρτωση της συνδρομής", failed_to_load_subscription: "Απέτυχε η φόρτωση της συνδρομής",
edit_subscription: "Επεξεργασία συνδρομής", edit_subscription: "Επεξεργασία συνδρομής",
add_subscription: "Προσθήκη συνδρομής", add_subscription: "Προσθήκη συνδρομής",
confirm_delete_subscription: "Είστε σίγουρος ότι θέλετε να διαγράψετε αυτή τη συνδρομή;",
// Settings // Settings
network_response_error: "Η ανταπόκριση του δικτύου δεν ήταν εντάξει", network_response_error: "Η ανταπόκριση του δικτύου δεν ήταν εντάξει",
failed_add_member: "Αποτυχία προσθήκης μέλους", failed_add_member: "Αποτυχία προσθήκης μέλους",

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "Failed to load subscription", failed_to_load_subscription: "Failed to load subscription",
edit_subscription: "Edit subscription", edit_subscription: "Edit subscription",
add_subscription: "Add subscription", add_subscription: "Add subscription",
confirm_delete_subscription: "Are you sure you want to delete this subscription?",
// Settings // Settings
network_response_error: "Network response was not ok", network_response_error: "Network response was not ok",
failed_add_member: "Failed to add member", failed_add_member: "Failed to add member",

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "Error al cargar la suscripción", failed_to_load_subscription: "Error al cargar la suscripción",
edit_subscription: "Editar suscripción", edit_subscription: "Editar suscripción",
add_subscription: "Añadir suscripción", add_subscription: "Añadir suscripción",
confirm_delete_subscription: "¿Estás seguro de que quieres eliminar esta suscripción?",
// Settings // Settings
network_response_error: "Error en la respuesta de la red", network_response_error: "Error en la respuesta de la red",
failed_add_member: "Error al añadir miembro", failed_add_member: "Error al añadir miembro",

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "Impossible de charger l'abonnement", failed_to_load_subscription: "Impossible de charger l'abonnement",
edit_subscription: "Modifier l'abonnement", edit_subscription: "Modifier l'abonnement",
add_subscription: "Ajouter un abonnement", add_subscription: "Ajouter un abonnement",
confirm_delete_subscription: "Êtes-vous sûr de vouloir supprimer cet abonnement ?",
// Paramètres // Paramètres
network_response_error: "La réponse du réseau n'était pas correcte", network_response_error: "La réponse du réseau n'était pas correcte",
failed_add_member: "Échec de l'ajout du membre", failed_add_member: "Échec de l'ajout du membre",

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "定期購入の読み込みに失敗しました", failed_to_load_subscription: "定期購入の読み込みに失敗しました",
edit_subscription: "定期購入の編集", edit_subscription: "定期購入の編集",
add_subscription: "定期購入の追加", add_subscription: "定期購入の追加",
confirm_delete_subscription: "この定期購入を削除してもよろしいですか?",
// Settings // Settings
network_response_error: "ネットワークの応答異常", network_response_error: "ネットワークの応答異常",
failed_add_member: "世帯員の追加に失敗", failed_add_member: "世帯員の追加に失敗",

View File

@ -7,6 +7,7 @@ let i18n = {
'failed_to_load_subscription': 'Falha ao carregar a subscrição', 'failed_to_load_subscription': 'Falha ao carregar a subscrição',
'edit_subscription': 'Editar subscrição', 'edit_subscription': 'Editar subscrição',
'add_subscription': 'Adicionar subscrição', 'add_subscription': 'Adicionar subscrição',
'confirm_delete_subscription': 'Tem a certeza de que deseja eliminar esta subscrição?',
// Settings // Settings
'network_response_error': 'Erro de resposta de rede', 'network_response_error': 'Erro de resposta de rede',
'failed_add_member': 'Falha ao adicionar membro', 'failed_add_member': 'Falha ao adicionar membro',

View File

@ -7,6 +7,7 @@ let i18n = {
failed_to_load_subscription: "Abonelik yüklenemedi", failed_to_load_subscription: "Abonelik yüklenemedi",
edit_subscription: "Aboneliği Düzenle", edit_subscription: "Aboneliği Düzenle",
add_subscription: "Abonelik Ekle", add_subscription: "Abonelik Ekle",
confirm_delete_subscription: "Bu aboneliği silmek istediğinizden emin misiniz?",
// Ayarlar // Ayarlar
network_response_error: "Ağ yanıtı kabul edilmedi", network_response_error: "Ağ yanıtı kabul edilmedi",
failed_add_member: "Üye eklenemedi", failed_add_member: "Üye eklenemedi",

View File

@ -7,6 +7,7 @@ let i18n = {
'failed_to_load_subscription': "加载订阅失败", 'failed_to_load_subscription': "加载订阅失败",
'edit_subscription': "编辑订阅", 'edit_subscription': "编辑订阅",
'add_subscription': "添加订阅", 'add_subscription': "添加订阅",
'confirm_delete_subscription': "您确定要删除此订阅吗?",
// Settings // Settings
'network_response_error': "网络响应不正常", 'network_response_error': "网络响应不正常",
'failed_add_member': '添加成员失败', 'failed_add_member': '添加成员失败',

View File

@ -7,6 +7,7 @@ let i18n = {
'failed_to_load_subscription': "讀取訂閱失敗", 'failed_to_load_subscription': "讀取訂閱失敗",
'edit_subscription': "編輯訂閱", 'edit_subscription': "編輯訂閱",
'add_subscription': "新增訂閱", 'add_subscription': "新增訂閱",
'confirm_delete_subscription': "您確定要刪除此訂閱嗎?",
// Settings // Settings
'network_response_error': "網路無回應", 'network_response_error': "網路無回應",
'failed_add_member': '新增成員失敗', 'failed_add_member': '新增成員失敗',

View File

@ -8,7 +8,13 @@ function loadGraph(container, dataPoints, currency, run) {
datasets: [{ datasets: [{
data: dataPoints.map(point => point.y), data: dataPoints.map(point => point.y),
}], }],
labels: dataPoints.map(point => `${point.label} (${new Intl.NumberFormat(navigator.language, { style: 'currency', currency }).format(point.y)})`), labels: dataPoints.map(point => {
if (currency) {
return `${point.label} (${new Intl.NumberFormat(navigator.language, { style: 'currency', currency }).format(point.y)})`;
} else {
return `${point.label} (${new Intl.NumberFormat(navigator.language).format(point.y)})`;
}
}),
}, },
options: { options: {
animation: { animation: {

View File

@ -60,6 +60,17 @@ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$categoryCost[$categoryId]['name'] = $row['name']; $categoryCost[$categoryId]['name'] = $row['name'];
} }
// Get payment methods
$categories = array();
$query = "SELECT * FROM payment_methods WHERE enabled = 1";
$result = $db->query($query);
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$paymentMethodId = $row['id'];
$paymentMethodCount[$paymentMethodId] = $row;
$paymentMethodCount[$paymentMethodId]['count'] = 0;
$paymentMethodCount[$paymentMethodId]['name'] = $row['name'];
}
// Get code of main currency to display on statistics // Get code of main currency to display on statistics
$query = "SELECT c.code $query = "SELECT c.code
FROM currencies c FROM currencies c
@ -84,7 +95,7 @@ $mostExpensiveSubscription = 0;
$amountDueThisMonth = 0; $amountDueThisMonth = 0;
$totalCostPerMonth = 0; $totalCostPerMonth = 0;
$query = "SELECT name, price, frequency, cycle, currency_id, next_payment, payer_user_id, category_id FROM subscriptions"; $query = "SELECT name, price, frequency, cycle, currency_id, next_payment, payer_user_id, category_id, payment_method_id FROM subscriptions";
$result = $db->query($query); $result = $db->query($query);
if ($result) { if ($result) {
while ($row = $result->fetchArray(SQLITE3_ASSOC)) { while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
@ -100,11 +111,13 @@ if ($result) {
$next_payment = $subscription['next_payment']; $next_payment = $subscription['next_payment'];
$payerId = $subscription['payer_user_id']; $payerId = $subscription['payer_user_id'];
$categoryId = $subscription['category_id']; $categoryId = $subscription['category_id'];
$paymentMethodId = $subscription['payment_method_id'];
$originalSubscriptionPrice = getPriceConverted($price, $currency, $db); $originalSubscriptionPrice = getPriceConverted($price, $currency, $db);
$price = getPricePerMonth($cycle, $frequency, $originalSubscriptionPrice); $price = getPricePerMonth($cycle, $frequency, $originalSubscriptionPrice);
$totalCostPerMonth += $price; $totalCostPerMonth += $price;
$memberCost[$payerId]['cost'] += $price; $memberCost[$payerId]['cost'] += $price;
$categoryCost[$categoryId]['cost'] += $price; $categoryCost[$categoryId]['cost'] += $price;
$paymentMethodCount[$paymentMethodId]['count'] += 1;
if ($price > $mostExpensiveSubscription) { if ($price > $mostExpensiveSubscription) {
$mostExpensiveSubscription = $price; $mostExpensiveSubscription = $price;
} }
@ -207,6 +220,18 @@ if ($result) {
$showMemberCostGraph = count($memberDataPoints) > 1; $showMemberCostGraph = count($memberDataPoints) > 1;
$paymentMethodDataPoints = [];
foreach ($paymentMethodCount as $paymentMethod) {
if ($paymentMethod['count'] != 0) {
$paymentMethodDataPoints[] = [
"label" => $paymentMethod['name'],
"y" => $paymentMethod["count"],
];
}
}
$showPaymentMethodCountGraph = count($paymentMethodDataPoints) > 1;
if ($showMemberCostGraph) { if ($showMemberCostGraph) {
?> ?>
<section class="graph"> <section class="graph">
@ -231,6 +256,17 @@ if ($result) {
<?php <?php
} }
if ($showPaymentMethodCountGraph) {
?>
<section class="graph">
<header>
<?= translate('payment_method_split', $i18n) ?>
</header>
<canvas id="paymentMethidSplitChart" style="height: 370px; width: 100%;"></canvas>
</section>
<?php
}
?> ?>
</div> </div>
</section> </section>
@ -242,6 +278,7 @@ if ($result) {
window.onload = function() { window.onload = function() {
loadGraph("categorySplitChart", <?php echo json_encode($categoryDataPoints, JSON_NUMERIC_CHECK); ?>, "<?= $code ?>", <?= $showCategoryCostGraph ?>); loadGraph("categorySplitChart", <?php echo json_encode($categoryDataPoints, JSON_NUMERIC_CHECK); ?>, "<?= $code ?>", <?= $showCategoryCostGraph ?>);
loadGraph("memberSplitChart", <?php echo json_encode($memberDataPoints, JSON_NUMERIC_CHECK); ?>, "<?= $code ?>", <?= $showMemberCostGraph ?>); loadGraph("memberSplitChart", <?php echo json_encode($memberDataPoints, JSON_NUMERIC_CHECK); ?>, "<?= $code ?>", <?= $showMemberCostGraph ?>);
loadGraph("paymentMethidSplitChart", <?php echo json_encode($paymentMethodDataPoints, JSON_NUMERIC_CHECK); ?>, "", <?= $showPaymentMethodCountGraph ?>);
} }
</script> </script>
<?php <?php