mirror of
https://github.com/ellite/Wallos.git
synced 2025-12-23 23:18:07 -05:00
feat: add payment cycle to csv/json export
feat: run db migration after restoring database feat: run db migration after importing db feat: store weekly the total yearly cost of subscriptions fix: double encoding in statistics labels
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Use the php:8.0.5-fpm-alpine base image
|
||||
# Use the php:8.2-fpm-alpine base image
|
||||
FROM php:8.2-fpm-alpine
|
||||
|
||||
# Set working directory to /var/www/html
|
||||
|
||||
@@ -98,6 +98,7 @@ See instructions to run Wallos below.
|
||||
*/2 * * * * php /var/www/html/endpoints/cronjobs/sendverificationemails.php >> /var/log/cron/sendverificationemail.log 2>&1
|
||||
*/2 * * * * php /var/www/html/endpoints/cronjobs/sendresetpasswordemails.php >> /var/log/cron/sendresetpasswordemails.log 2>&1
|
||||
0 */6 * * * php /var/www/html/endpoints/cronjobs/checkforupdates.php >> /var/log/cron/checkforupdates.log 2>&1
|
||||
30 1 * * 1 php /var/www/html/endpoints/cronjobs/storetotalyearlycost.php >> /var/log/cron/storetotalyearlycost.log 2>&1
|
||||
```
|
||||
|
||||
5. If your web root is not `/var/www/html/` adjust the cronjobs above accordingly.
|
||||
|
||||
@@ -362,6 +362,7 @@ $loginDisabledAllowed = $userCount == 1 && $settings['registrations_open'] == 0;
|
||||
<input type="button" value="Send Verification Emails" class="button tiny mobile-grow" onclick="executeCronJob('sendverificationemails')">
|
||||
<input type="button" value="Update Exchange Rates" class="button tiny mobile-grow" onclick="executeCronJob('updateexchange')">
|
||||
<input type="button" value="Update Next Payments" class="button tiny mobile-grow" onclick="executeCronJob('updatenextpayment')">
|
||||
<input type="button" value="Store Total Yearly Cost" class="button tiny mobile-grow" onclick="executeCronJob('storetotalyearlycost')">
|
||||
</div>
|
||||
<div class="inline-row">
|
||||
<textarea id="cronjobResult" class="thin" readonly></textarea>
|
||||
|
||||
1
cronjobs
1
cronjobs
@@ -6,3 +6,4 @@
|
||||
*/2 * * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/sendverificationemails.php >> /var/log/cron/sendverificationemails.log 2>&1
|
||||
*/2 * * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/sendresetpasswordemails.php >> /var/log/cron/sendresetpasswordemails.log 2>&1
|
||||
0 */6 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/checkforupdates.php >> /var/log/cron/checkforupdates.log 2>&1
|
||||
30 1 * * 1 /usr/local/bin/php /var/www/html/endpoints/cronjobs/storetotalyearlycost.php >> /var/log/cron/storetotalyearlycost.log 2>&1
|
||||
72
endpoints/cronjobs/storetotalyearlycost.php
Normal file
72
endpoints/cronjobs/storetotalyearlycost.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../includes/connect_endpoint_crontabs.php';
|
||||
|
||||
if (php_sapi_name() == 'cli') {
|
||||
$date = new DateTime('now');
|
||||
echo "\n" . $date->format('Y-m-d') . " " . $date->format('H:i:s') . "<br />\n";
|
||||
}
|
||||
|
||||
$currentDate = new DateTime();
|
||||
$currentDateString = $currentDate->format('Y-m-d');
|
||||
|
||||
function getPriceConverted($price, $currency, $database, $userId)
|
||||
{
|
||||
$query = "SELECT rate FROM currencies WHERE id = :currency AND user_id = :userId";
|
||||
$stmt = $database->prepare($query);
|
||||
$stmt->bindParam(':currency', $currency, SQLITE3_INTEGER);
|
||||
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
|
||||
$result = $stmt->execute();
|
||||
|
||||
$exchangeRate = $result->fetchArray(SQLITE3_ASSOC);
|
||||
if ($exchangeRate === false) {
|
||||
return $price;
|
||||
} else {
|
||||
$fromRate = $exchangeRate['rate'];
|
||||
return $price / $fromRate;
|
||||
}
|
||||
}
|
||||
|
||||
// Get all users
|
||||
|
||||
$query = "SELECT id, main_currency FROM user";
|
||||
$stmt = $db->prepare($query);
|
||||
$result = $stmt->execute();
|
||||
|
||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
$userId = $row['id'];
|
||||
$userCurrencyId = $row['main_currency'];
|
||||
$totalYearlyCost = 0;
|
||||
|
||||
$query = "SELECT * FROM subscriptions WHERE user_id = :userId";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
|
||||
$resultSubscriptions = $stmt->execute();
|
||||
|
||||
while ($rowSubscriptions = $resultSubscriptions->fetchArray(SQLITE3_ASSOC)) {
|
||||
$price = getPriceConverted($rowSubscriptions['price'], $rowSubscriptions['currency_id'], $db, $userId);
|
||||
$totalYearlyCost += $price;
|
||||
}
|
||||
|
||||
$query = "INSERT INTO total_yearly_cost (user_id, date, cost, currency) VALUES (:userId, :date, :cost, :currency)";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
|
||||
$stmt->bindParam(':date', $currentDateString, SQLITE3_TEXT);
|
||||
$stmt->bindParam(':cost', $totalYearlyCost, SQLITE3_FLOAT);
|
||||
$stmt->bindParam(':currency', $userCurrencyId, SQLITE3_INTEGER);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo "Inserted total yearly cost for user " . $userId . " with cost " . $totalYearlyCost . "<br />\n";
|
||||
} else {
|
||||
echo "Error inserting total yearly cost for user " . $userId . "<br />\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
?>
|
||||
@@ -18,8 +18,25 @@ $stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
|
||||
$result = $stmt->execute();
|
||||
|
||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
$cycle = $cycles[$row['cycle']]['name'];
|
||||
$frequency =$row['frequency'];
|
||||
|
||||
$cyclesMap = array(
|
||||
'Daily' => 'Days',
|
||||
'Weekly' => 'Weeks',
|
||||
'Monthly' => 'Months',
|
||||
'Yearly' => 'Years'
|
||||
);
|
||||
|
||||
if ($frequency == 1) {
|
||||
$cyclePrint = $cycle;
|
||||
} else {
|
||||
$cyclePrint = "Every " . $frequency . " " . $cyclesMap[$cycle];
|
||||
}
|
||||
|
||||
$subscriptionDetails = array(
|
||||
'Name' => str_replace(',', ' ', $row['name']),
|
||||
'Payment Cycle' => $cyclePrint,
|
||||
'Next Payment' => $row['next_payment'],
|
||||
'Renewal' => $row['auto_renew'] ? 'Automatic' : 'Manual',
|
||||
'Category' => str_replace(',', ' ', $categories[$row['category_id']]['name']),
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
$version = "v2.40.0";
|
||||
$version = "v2.41.0";
|
||||
?>
|
||||
@@ -148,8 +148,15 @@ function restoreDB() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showSuccessMessage(data.message)
|
||||
window.location.href = 'logout.php';
|
||||
showSuccessMessage(data.message);
|
||||
fetch('endpoints/db/migrate.php')
|
||||
.then(response => response.text())
|
||||
.then(() => {
|
||||
window.location.href = 'logout.php';
|
||||
})
|
||||
.catch(error => {
|
||||
window.location.href = 'logout.php';
|
||||
});
|
||||
} else {
|
||||
showErrorMessage(data.message);
|
||||
}
|
||||
|
||||
@@ -1,155 +1,162 @@
|
||||
function setCookie(name, value, days) {
|
||||
var expires = "";
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + value + expires + "; SameSite=Strict";
|
||||
var expires = "";
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + value + expires + "; SameSite=Strict";
|
||||
}
|
||||
|
||||
function storeFormFieldValue(fieldId) {
|
||||
var fieldElement = document.getElementById(fieldId);
|
||||
if (fieldElement) {
|
||||
localStorage.setItem(fieldId, fieldElement.value);
|
||||
}
|
||||
var fieldElement = document.getElementById(fieldId);
|
||||
if (fieldElement) {
|
||||
localStorage.setItem(fieldId, fieldElement.value);
|
||||
}
|
||||
}
|
||||
|
||||
function storeFormFields() {
|
||||
storeFormFieldValue('username');
|
||||
storeFormFieldValue('email');
|
||||
storeFormFieldValue('password');
|
||||
storeFormFieldValue('confirm_password');
|
||||
storeFormFieldValue('currency');
|
||||
storeFormFieldValue('username');
|
||||
storeFormFieldValue('email');
|
||||
storeFormFieldValue('password');
|
||||
storeFormFieldValue('confirm_password');
|
||||
storeFormFieldValue('currency');
|
||||
}
|
||||
|
||||
function restoreFormFieldValue(fieldId) {
|
||||
var fieldElement = document.getElementById(fieldId);
|
||||
if (localStorage.getItem(fieldId)) {
|
||||
fieldElement.value = localStorage.getItem(fieldId) || '';
|
||||
}
|
||||
var fieldElement = document.getElementById(fieldId);
|
||||
if (localStorage.getItem(fieldId)) {
|
||||
fieldElement.value = localStorage.getItem(fieldId) || '';
|
||||
}
|
||||
}
|
||||
|
||||
function restoreFormFields() {
|
||||
restoreFormFieldValue('username');
|
||||
restoreFormFieldValue('email');
|
||||
restoreFormFieldValue('password');
|
||||
restoreFormFieldValue('confirm_password');
|
||||
restoreFormFieldValue('currency');
|
||||
restoreFormFieldValue('username');
|
||||
restoreFormFieldValue('email');
|
||||
restoreFormFieldValue('password');
|
||||
restoreFormFieldValue('confirm_password');
|
||||
restoreFormFieldValue('currency');
|
||||
}
|
||||
|
||||
function removeFromStorage() {
|
||||
localStorage.removeItem('username');
|
||||
localStorage.removeItem('email');
|
||||
localStorage.removeItem('password');
|
||||
localStorage.removeItem('confirm_password');
|
||||
localStorage.removeItem('currency');
|
||||
localStorage.removeItem('username');
|
||||
localStorage.removeItem('email');
|
||||
localStorage.removeItem('password');
|
||||
localStorage.removeItem('confirm_password');
|
||||
localStorage.removeItem('currency');
|
||||
}
|
||||
|
||||
function changeLanguage(selectedLanguage) {
|
||||
storeFormFields();
|
||||
setCookie("language", selectedLanguage, 365);
|
||||
location.reload();
|
||||
storeFormFields();
|
||||
setCookie("language", selectedLanguage, 365);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
function runDatabaseMigration() {
|
||||
let url = "endpoints/db/migrate.php";
|
||||
fetch(url)
|
||||
let url = "endpoints/db/migrate.php";
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showErrorMessage(message) {
|
||||
const toast = document.querySelector(".toast#errorToast");
|
||||
(closeIcon = document.querySelector(".close-error")),
|
||||
const toast = document.querySelector(".toast#errorToast");
|
||||
(closeIcon = document.querySelector(".close-error")),
|
||||
(errorMessage = document.querySelector(".errorMessage")),
|
||||
(progress = document.querySelector(".progress.error"));
|
||||
let timer1, timer2;
|
||||
errorMessage.textContent = message;
|
||||
toast.classList.add("active");
|
||||
progress.classList.add("active");
|
||||
timer1 = setTimeout(() => {
|
||||
toast.classList.remove("active");
|
||||
closeIcon.removeEventListener("click", () => {});
|
||||
}, 5000);
|
||||
|
||||
timer2 = setTimeout(() => {
|
||||
let timer1, timer2;
|
||||
errorMessage.textContent = message;
|
||||
toast.classList.add("active");
|
||||
progress.classList.add("active");
|
||||
timer1 = setTimeout(() => {
|
||||
toast.classList.remove("active");
|
||||
closeIcon.removeEventListener("click", () => { });
|
||||
}, 5000);
|
||||
|
||||
timer2 = setTimeout(() => {
|
||||
progress.classList.remove("active");
|
||||
}, 5300);
|
||||
|
||||
closeIcon.addEventListener("click", () => {
|
||||
toast.classList.remove("active");
|
||||
|
||||
setTimeout(() => {
|
||||
progress.classList.remove("active");
|
||||
}, 5300);
|
||||
|
||||
closeIcon.addEventListener("click", () => {
|
||||
toast.classList.remove("active");
|
||||
|
||||
setTimeout(() => {
|
||||
progress.classList.remove("active");
|
||||
}, 300);
|
||||
|
||||
clearTimeout(timer1);
|
||||
clearTimeout(timer2);
|
||||
closeIcon.removeEventListener("click", () => {});
|
||||
});
|
||||
}, 300);
|
||||
|
||||
clearTimeout(timer1);
|
||||
clearTimeout(timer2);
|
||||
closeIcon.removeEventListener("click", () => { });
|
||||
});
|
||||
}
|
||||
|
||||
function showSuccessMessage(message) {
|
||||
const toast = document.querySelector(".toast#successToast");
|
||||
(closeIcon = document.querySelector(".close-success")),
|
||||
const toast = document.querySelector(".toast#successToast");
|
||||
(closeIcon = document.querySelector(".close-success")),
|
||||
(successMessage = document.querySelector(".successMessage")),
|
||||
(progress = document.querySelector(".progress.success"));
|
||||
let timer1, timer2;
|
||||
successMessage.textContent = message;
|
||||
toast.classList.add("active");
|
||||
progress.classList.add("active");
|
||||
timer1 = setTimeout(() => {
|
||||
toast.classList.remove("active");
|
||||
closeIcon.removeEventListener("click", () => {});
|
||||
}, 5000);
|
||||
|
||||
timer2 = setTimeout(() => {
|
||||
let timer1, timer2;
|
||||
successMessage.textContent = message;
|
||||
toast.classList.add("active");
|
||||
progress.classList.add("active");
|
||||
timer1 = setTimeout(() => {
|
||||
toast.classList.remove("active");
|
||||
closeIcon.removeEventListener("click", () => { });
|
||||
}, 5000);
|
||||
|
||||
timer2 = setTimeout(() => {
|
||||
progress.classList.remove("active");
|
||||
}, 5300);
|
||||
|
||||
closeIcon.addEventListener("click", () => {
|
||||
toast.classList.remove("active");
|
||||
|
||||
setTimeout(() => {
|
||||
progress.classList.remove("active");
|
||||
}, 5300);
|
||||
|
||||
closeIcon.addEventListener("click", () => {
|
||||
toast.classList.remove("active");
|
||||
|
||||
setTimeout(() => {
|
||||
progress.classList.remove("active");
|
||||
}, 300);
|
||||
|
||||
clearTimeout(timer1);
|
||||
clearTimeout(timer2);
|
||||
closeIcon.removeEventListener("click", () => {});
|
||||
});
|
||||
}, 300);
|
||||
|
||||
clearTimeout(timer1);
|
||||
clearTimeout(timer2);
|
||||
closeIcon.removeEventListener("click", () => { });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function openRestoreDBFileSelect() {
|
||||
document.getElementById('restoreDBFile').click();
|
||||
document.getElementById('restoreDBFile').click();
|
||||
};
|
||||
|
||||
|
||||
function restoreDB() {
|
||||
const input = document.getElementById('restoreDBFile');
|
||||
const file = input.files[0];
|
||||
|
||||
if (!file) {
|
||||
console.error('No file selected');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
fetch('endpoints/db/import.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
const input = document.getElementById('restoreDBFile');
|
||||
const file = input.files[0];
|
||||
|
||||
if (!file) {
|
||||
console.error('No file selected');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
fetch('endpoints/db/import.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showSuccessMessage(data.message)
|
||||
window.location.href = 'logout.php';
|
||||
showSuccessMessage(data.message);
|
||||
fetch('endpoints/db/migrate.php')
|
||||
.then(response => response.text())
|
||||
.then(() => {
|
||||
window.location.href = 'logout.php';
|
||||
})
|
||||
.catch(error => {
|
||||
window.location.href = 'logout.php';
|
||||
});
|
||||
} else {
|
||||
showErrorMessage(data.message);
|
||||
}
|
||||
@@ -170,8 +177,8 @@ function checkThemeNeedsUpdate() {
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
restoreFormFields();
|
||||
removeFromStorage();
|
||||
runDatabaseMigration();
|
||||
checkThemeNeedsUpdate();
|
||||
restoreFormFields();
|
||||
removeFromStorage();
|
||||
runDatabaseMigration();
|
||||
checkThemeNeedsUpdate();
|
||||
};
|
||||
|
||||
@@ -485,7 +485,7 @@ if ($usesMultipleCurrencies) {
|
||||
foreach ($categoryCost as $category) {
|
||||
if ($category['cost'] != 0) {
|
||||
$categoryDataPoints[] = [
|
||||
"label" => $category['name'],
|
||||
"label" => html_entity_decode($category['name']),
|
||||
"y" => $category["cost"],
|
||||
];
|
||||
}
|
||||
@@ -499,7 +499,7 @@ if ($usesMultipleCurrencies) {
|
||||
foreach ($memberCost as $member) {
|
||||
if ($member['cost'] != 0) {
|
||||
$memberDataPoints[] = [
|
||||
"label" => $member['name'],
|
||||
"label" => html_entity_decode($member['name']),
|
||||
"y" => $member["cost"],
|
||||
];
|
||||
|
||||
@@ -513,7 +513,7 @@ if ($usesMultipleCurrencies) {
|
||||
foreach ($paymentMethodsCount as $paymentMethod) {
|
||||
if ($paymentMethod['count'] != 0) {
|
||||
$paymentMethodDataPoints[] = [
|
||||
"label" => $paymentMethod['name'],
|
||||
"label" => html_entity_decode($paymentMethod['name']),
|
||||
"y" => $paymentMethod["count"],
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user