rename /user/ to /agent/ and update links to use agent/ instead

This commit is contained in:
johnnyq
2025-09-23 18:04:23 -04:00
parent 6b6c70f1df
commit edabc5c33f
373 changed files with 93 additions and 21 deletions

107
agent/reports/budget.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
$sql_expense_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(expense_date) AS expense_year FROM expenses WHERE expense_category_id > 0 ORDER BY expense_year DESC");
$categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Expense' ORDER BY category_name ASC");
$monthlyTotals = array_fill(1, 12, 0); // Initialize monthly totals for each month
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-balance-scale mr-2"></i>Annual Budget</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_expense_years)) {
$expense_year = $row['expense_year'];
?>
<option <?php if ($year == $expense_year) { ?> selected <?php } ?> > <?php echo $expense_year; ?></option>
<?php } ?>
</select>
</form>
<canvas id="cashFlow" width="100%" height="20"></canvas>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead class="text-dark">
<tr>
<th>Category</th>
<th class="text-right">January</th>
<th class="text-right">February</th>
<th class="text-right">March</th>
<th class="text-right">April</th>
<th class="text-right">May</th>
<th class="text-right">June</th>
<th class="text-right">July</th>
<th class="text-right">August</th>
<th class="text-right">September</th>
<th class="text-right">October</th>
<th class="text-right">November</th>
<th class="text-right">December</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
if ($categories->num_rows > 0) {
while($category = $categories->fetch_assoc()) {
echo "<tr>";
echo "<td>" . nullable_htmlentities($category['category_name']) . "</td>";
$categoryTotal = 0;
for ($month = 1; $month <= 12; $month++) {
// Fetch the monthly budget for this category for 2022
$sql = "SELECT budget_amount FROM budget WHERE budget_category_id = " . $category['category_id'] . " AND budget_month = $month AND budget_year = $year";
$result = $mysqli->query($sql);
if ($result->num_rows > 0) {
$budget = $result->fetch_assoc();
$amount = $budget['budget_amount'];
$categoryTotal += $amount;
$monthlyTotals[$month] += $amount;
echo "<td class='text-right'>" . $amount . "</td>";
} else {
echo "<td class='text-right'>0</td>";
}
}
echo "<td class='text-right'>" . $categoryTotal . "</td>";
echo "</tr>";
}
// Displaying the monthly totals row
echo "<tr><td><strong>Total</strong></td>";
$grandTotal = 0;
for ($month = 1; $month <= 12; $month++) {
$grandTotal += $monthlyTotals[$month];
echo "<td class='text-right'>" . $monthlyTotals[$month] . "</td>";
}
echo "<td class='text-right'>" . $grandTotal . "</td>";
echo "</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php";
?>

View File

@@ -0,0 +1,85 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>Clients with a Balance</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<?php
$sql_clients = mysqli_query($mysqli, "
SELECT
clients.client_id,
clients.client_name,
IFNULL(SUM(invoices.invoice_amount), 0) - IFNULL(SUM(payments.payment_amount), 0) AS balance
FROM
clients
LEFT JOIN
invoices
ON
clients.client_id = invoices.invoice_client_id
AND invoices.invoice_status != 'Draft'
AND invoices.invoice_status != 'Cancelled'
AND invoice_status != 'Non-Billable'
LEFT JOIN
(SELECT
payment_invoice_id,
SUM(payment_amount) as payment_amount
FROM payments
GROUP BY payment_invoice_id) as payments
ON
invoices.invoice_id = payments.payment_invoice_id
GROUP BY
clients.client_id,
clients.client_name
HAVING
balance > 0
ORDER BY
balance DESC
");
?>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Balance</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_clients)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$balance = floatval($row['balance']);
?>
<tr>
<td><a href="../../agent/invoices.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?></a></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $balance, $session_company_currency); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "../../includes/footer.php";

View File

@@ -0,0 +1,75 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_credential');
// TODO: Default to 90 but allow input field to change this
if (isset($_GET['days'])) {
$days = intval($_GET['days']);
} else {
$days = 90;
}
$passwords_not_rotated_sql = mysqli_query($mysqli,
"SELECT credential_id, credential_name, credential_description, credential_password_changed_at, credential_client_id, client_id, client_name
FROM credentials
LEFT JOIN clients ON credential_client_id = client_id
WHERE DATE(credential_password_changed_at) < DATE_SUB(CURDATE(), INTERVAL $days DAY)
ORDER BY client_name"
);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-life-ring mr-2"></i>Client credentials not changed/rotated in the last 90 days</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Credential Name</th>
<th class="text-right">Credential Description</th>
<th class="text-right">Credential Password Last Changed</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($passwords_not_rotated_sql)) {
$credential_id = intval($row['credential_id']);
$credential_name = nullable_htmlentities($row['credential_name']);
$credential_description = nullable_htmlentities($row['credential_description']);
$credential_password_changed = nullable_htmlentities($row['credential_password_changed_at']);
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
?>
<tr>
<td><?php echo $client_name; ?></td>
<td class="text-right"><?php echo $credential_name; ?></td>
<td class="text-right"><?php echo $credential_description; ?></td>
<td class="text-right"><?php echo timeAgo($credential_password_changed) . " (" . $credential_password_changed . ")" ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "../../includes/footer.php";

View File

@@ -0,0 +1,97 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
if (isset($_GET['year'])) {
if ($_GET['year'] === 'all') {
$year = 'all';
} else {
$year = intval($_GET['year']);
}
} else {
$year = date('Y');
}
$sql_payment_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(payment_date) AS payment_year FROM payments
UNION SELECT DISTINCT YEAR(revenue_date) AS payment_year FROM revenues
ORDER BY payment_year DESC"
);
$year_condition = ($year == 'all') ? "" : "AND YEAR(expense_date) = $year";
$sql_vendor_expenses = mysqli_query($mysqli, "
SELECT
vendors.*,
SUM(expenses.expense_amount) AS amount_paid
FROM
vendors
LEFT JOIN
expenses ON vendors.vendor_id = expenses.expense_vendor_id $year_condition
GROUP BY
vendors.vendor_id
HAVING
amount_paid > 599
ORDER BY
amount_paid DESC
");
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-building mr-2"></i>Expense By Vendor <small>(With expense amounts of 600 or more)</small></h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<option value="all" <?php if ($year == 'all') { ?> selected <?php } ?> >All Years</option>
<?php
while ($row = mysqli_fetch_array($sql_payment_years)) {
$payment_year = intval($row['payment_year']);
?>
<option <?php if ($year == $payment_year) { ?> selected <?php } ?> > <?php echo $payment_year; ?></option>
<?php
}
?>
</select>
</form>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Vendor</th>
<th class="text-right">Paid</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_vendor_expenses)) {
$vendor_id = intval($row['vendor_id']);
$vendor_name = nullable_htmlentities($row['vendor_name']);
$amount_paid = floatval($row['amount_paid']); ?>
<tr>
<td><?php echo $vendor_name; ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $amount_paid, $session_company_currency); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php";
?>

View File

@@ -0,0 +1,195 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
$sql_expense_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(expense_date) AS expense_year FROM expenses WHERE expense_category_id > 0 ORDER BY expense_year DESC");
$sql_categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Expense' ORDER BY category_name ASC");
// For chart Y-axis max
$largest_expense_month = 0;
?>
<!-- Responsive chart helpers -->
<style>
.chart-h-320 { position: relative; height: 320px; }
@media (max-width: 576px) { .chart-h-320 { height: 260px; } }
</style>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-coins mr-2"></i>Expense Summary</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php while ($row = mysqli_fetch_array($sql_expense_years)) {
$expense_year = intval($row['expense_year']); ?>
<option <?php if ($year == $expense_year) { ?> selected <?php } ?>><?php echo $expense_year; ?></option>
<?php } ?>
</select>
</form>
<div class="chart-h-320 mb-3">
<canvas id="cashFlow"></canvas>
</div>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead class="text-dark">
<tr>
<th>Category</th>
<th class="text-right">January</th>
<th class="text-right">February</th>
<th class="text-right">March</th>
<th class="text-right">April</th>
<th class="text-right">May</th>
<th class="text-right">June</th>
<th class="text-right">July</th>
<th class="text-right">August</th>
<th class="text-right">September</th>
<th class="text-right">October</th>
<th class="text-right">November</th>
<th class="text-right">December</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php while ($row = mysqli_fetch_array($sql_categories)) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']); ?>
<tr>
<td><?php echo $category_name; ?></td>
<?php
$total_expense_for_all_months = 0;
for ($month = 1; $month <= 12; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE expense_category_id = $category_id AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month");
$rowm = mysqli_fetch_array($sql_expenses);
$expense_amount_for_month = floatval($rowm['expense_amount_for_month']);
$total_expense_for_all_months += $expense_amount_for_month;
?>
<td class="text-right">
<a class="text-dark" href="expenses.php?q=<?php echo $category_name; ?>&dtf=<?php echo "$year-$month"; ?>-01&dtt=<?php echo "$year-$month"; ?>-31">
<?php echo numfmt_format_currency($currency_format, $expense_amount_for_month, $session_company_currency); ?>
</a>
</td>
<?php } ?>
<th class="text-right">
<a class="text-dark" href="expenses.php?q=<?php echo $category_name; ?>&dtf=<?php echo $year; ?>-01-01&dtt=<?php echo $year; ?>-12-31">
<?php echo numfmt_format_currency($currency_format, $total_expense_for_all_months, $session_company_currency); ?>
</a>
</th>
</tr>
<?php } ?>
<tr>
<th>Total</th>
<?php
$grand_total_all_months = 0;
for ($month = 1; $month <= 12; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_total_amount_for_month FROM expenses WHERE YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
$rowt = mysqli_fetch_array($sql_expenses);
$expense_total_amount_for_month = floatval($rowt['expense_total_amount_for_month']);
$grand_total_all_months += $expense_total_amount_for_month;
?>
<th class="text-right">
<a class="text-dark" href="expenses.php?dtf=<?php echo "$year-$month"; ?>-01&dtt=<?php echo "$year-$month"; ?>-31">
<?php echo numfmt_format_currency($currency_format, $expense_total_amount_for_month, $session_company_currency); ?>
</a>
</th>
<?php } ?>
<th class="text-right">
<a class="text-dark" href="expenses.php?dtf=<?php echo $year; ?>-01-01&dtt=<?php echo $year; ?>-12-31">
<?php echo numfmt_format_currency($currency_format, $grand_total_all_months, $session_company_currency); ?>
</a>
</th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php"; ?>
<script>
// Bootstrap-like defaults for Chart.js v4
Chart.defaults.font.family = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.color = '#292b2c';
// EXPENSES LINE CHART
(function () {
var ctx = document.getElementById("cashFlow");
if (!ctx) return;
var dataPoints = [
<?php
// Build series and track the largest month for axis max
for ($month = 1; $month <= 12; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
$rowm = mysqli_fetch_array($sql_expenses);
$expenses_for_month = floatval($rowm['expense_amount_for_month']);
if ($expenses_for_month > 0 && $expenses_for_month > $largest_expense_month) {
$largest_expense_month = $expenses_for_month;
}
echo "$expenses_for_month,";
}
?>
];
new Chart(ctx, {
type: 'line',
data: {
labels: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
datasets: [{
label: "Expense",
tension: 0.3, // v4 name (v2: lineTension)
fill: false,
borderColor: "#dc3545",
pointBackgroundColor: "#dc3545",
pointBorderColor: "#dc3545",
pointHoverRadius: 5,
pointHoverBackgroundColor: "#dc3545",
pointBorderWidth: 2,
data: dataPoints
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
grid: { display: false },
ticks: { maxTicksLimit: 12 }
},
y: {
beginAtZero: true,
min: 0,
max: <?php
$max = max(1000, $largest_expense_month);
echo roundUpToNearestMultiple($max);
?>,
ticks: { maxTicksLimit: 5 },
grid: { color: "rgba(0, 0, 0, .125)" }
}
},
plugins: {
legend: { display: false }
}
}
});
})();
</script>

View File

@@ -0,0 +1,20 @@
<?php
require_once "../../config.php";
require_once "../../functions.php";
require_once "../../includes/router.php";
require_once "../../includes/check_login.php";
require_once "../../includes/page_title.php";
// Reporting Perms
enforceUserPermission('module_reporting');
require_once "../../includes/header.php";
require_once "../../includes/top_nav.php";
require_once "includes/reports_side_nav.php";
require_once "../../includes/inc_wrapper.php";
require_once "../../includes/inc_alert_feedback.php";
require_once "../../includes/filter_header.php";
// Set variable default values
$largest_income_month = 0;
$largest_invoice_month = 0;
$recurring_total = 0;

View File

@@ -0,0 +1,121 @@
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary d-print-none">
<a class="pb-1 mt-1 brand-link" href="../../agent/<?php echo $config_start_page ?>">
<p class="h5"><i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i>
<span class="brand-text ">Back | <strong>Reports</strong>
</p>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar Menu -->
<nav>
<ul class="nav nav-pills nav-sidebar flex-column mt-2" data-widget="treeview" data-accordion="false">
<li class="nav-header">FINANCIAL</li>
<?php if ($config_module_enable_accounting == 1 && lookupUserPermission("module_financial") >= 1) { ?>
<li class="nav-item">
<a href="income_summary.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "income_summary.php") { echo "active"; } ?>">
<i class="far fa-circle nav-icon"></i>
<p>Income</p>
</a>
</li>
<li class="nav-item">
<a href="income_by_client.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "income_by_client.php") { echo "active"; } ?>">
<i class="far fa-user nav-icon"></i>
<p>Income By Client</p>
</a>
</li>
<li class="nav-item">
<a href="recurring_by_client.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "recurring_by_client.php") { echo "active"; } ?>">
<i class="fa fa-sync nav-icon"></i>
<p>Recurring Income By Client</p>
</a>
</li>
<li class="nav-item">
<a href="clients_with_balance.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "clients_with_balance.php") { echo "active"; } ?>">
<i class="fa fa-exclamation-triangle nav-icon"></i>
<p>Clients with a Balance</p>
</a>
</li>
<li class="nav-item">
<a href="expense_summary.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "expense_summary.php") { echo "active"; } ?>">
<i class="far fa-credit-card nav-icon"></i>
<p>Expense</p>
</a>
</li>
<li class="nav-item">
<a href="expense_by_vendor.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "expense_by_vendor.php") { echo "active"; } ?>">
<i class="far fa-building nav-icon"></i>
<p>Expense By Vendor</p>
</a>
</li>
<li class="nav-item">
<a href="tax_summary.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "tax_summary.php") { echo "active"; } ?>">
<i class="fas fa-percent nav-icon"></i>
<p>Tax Summary</p>
</a>
</li>
<li class="nav-item">
<a href="profit_loss.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "profit_loss.php") { echo "active"; } ?>">
<i class="fas fa-file-invoice-dollar nav-icon"></i>
<p>Profit & Loss</p>
</a>
</li>
<li class="nav-item">
<a href="tickets_unbilled.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "tickets_unbilled.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Unbilled Tickets</p>
</a>
</li>
<?php } // End financial reports IF statement ?>
<li class="nav-header">TECHNICAL</li>
<?php if ($config_module_enable_ticketing && lookupUserPermission("module_support") >= 1) { ?>
<li class="nav-item">
<a href="ticket_summary.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "ticket_summary.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Tickets</p>
</a>
</li>
<li class="nav-item">
<a href="ticket_by_client.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "ticket_by_client.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Tickets by Client</p>
</a>
</li>
<li class="nav-item">
<a href="time_by_tech.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "time_by_tech.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Time by Technician</p>
</a>
</li>
<?php } ?>
<?php if (lookupUserPermission("module_credential") >= 1) { ?>
<li class="nav-item">
<a href="credential_rotation.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "credential_rotation.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-key"></i>
<p>Credential rotation</p>
</a>
</li>
<?php } ?>
</ul>
</nav>
<!-- /.sidebar-menu -->
<div class="sidebar-custom mb-3">
</div>
</div>
<!-- /.sidebar -->
</aside>

View File

@@ -0,0 +1,95 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
if (isset($_GET['year'])) {
if ($_GET['year'] === 'all') {
$year = 'all';
} else {
$year = intval($_GET['year']);
}
} else {
$year = date('Y');
}
$sql_payment_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(payment_date) AS payment_year FROM payments
UNION SELECT DISTINCT YEAR(revenue_date) AS payment_year FROM revenues
ORDER BY payment_year DESC"
);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-users mr-2"></i>Income By Client <small>(With payments of 600 or more)</small></h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<option value="all" <?php if ($year == 'all') { ?> selected <?php } ?> >All Years</option>
<?php
while ($row = mysqli_fetch_array($sql_payment_years)) {
$payment_year = intval($row['payment_year']);
?>
<option <?php if ($year == $payment_year) { ?> selected <?php } ?> > <?php echo $payment_year; ?></option>
<?php } ?>
</select>
</form>
<?php
$sql_clients = "SELECT c.client_id, c.client_name, SUM(p.payment_amount) AS amount_paid
FROM clients AS c
JOIN invoices AS i ON c.client_id = i.invoice_client_id
JOIN payments AS p ON i.invoice_id = p.payment_invoice_id";
if ($year != 'all') {
$sql_clients .= " WHERE YEAR(p.payment_date) = $year";
}
$sql_clients .= " GROUP BY c.client_id
HAVING amount_paid > 599
ORDER BY amount_paid DESC";
$sql_clients = mysqli_query($mysqli, $sql_clients);
?>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Paid</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_clients)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$amount_paid = floatval($row['amount_paid']);
?>
<tr>
<td><a href="../../agent/client_overview.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?></a></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $amount_paid, $session_company_currency); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "../../includes/footer.php";

View File

@@ -0,0 +1,201 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
$sql_payment_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(payment_date) AS payment_year FROM payments
UNION SELECT DISTINCT YEAR(revenue_date) AS payment_year FROM revenues
ORDER BY payment_year DESC");
$sql_categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Income' ORDER BY category_name ASC");
// Used for chart y-axis max calculation
$largest_income_month = 0;
?>
<!-- Responsive chart helpers -->
<style>
.chart-h-320 { position: relative; height: 320px; }
@media (max-width: 576px) { .chart-h-320 { height: 260px; } }
</style>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-coins mr-2"></i>Income Summary</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body p-0">
<form class="p-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php while ($row = mysqli_fetch_array($sql_payment_years)) {
$payment_year = intval($row['payment_year']); ?>
<option <?php if ($year == $payment_year) { ?> selected <?php } ?>><?php echo $payment_year; ?></option>
<?php } ?>
</select>
</form>
<div class="px-3 pb-3">
<div class="chart-h-320">
<canvas id="cashFlow"></canvas>
</div>
</div>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Category</th>
<th class="text-right">January</th>
<th class="text-right">February</th>
<th class="text-right">March</th>
<th class="text-right">April</th>
<th class="text-right">May</th>
<th class="text-right">June</th>
<th class="text-right">July</th>
<th class="text-right">August</th>
<th class="text-right">September</th>
<th class="text-right">October</th>
<th class="text-right">November</th>
<th class="text-right">December</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php while ($row = mysqli_fetch_array($sql_categories)) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']); ?>
<tr>
<td><?php echo $category_name; ?></td>
<?php
$total_payment_for_all_months = 0;
for ($month = 1; $month <= 12; $month++) {
// Payments to Invoices
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_category_id = $category_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row2 = mysqli_fetch_array($sql_payments);
$payment_amount_for_month = floatval($row2['payment_amount_for_month']);
// Revenues
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id = $category_id AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row3 = mysqli_fetch_array($sql_revenues);
$revenues_amount_for_month = floatval($row3['revenue_amount_for_month']);
$payment_amount_for_month = $payment_amount_for_month + $revenues_amount_for_month;
$total_payment_for_all_months += $payment_amount_for_month;
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_amount_for_month, $session_company_currency); ?></td>
<?php } ?>
<td class="text-right text-bold"><?php echo numfmt_format_currency($currency_format, $total_payment_for_all_months, $session_company_currency); ?></td>
</tr>
<?php } ?>
<tr>
<th>Total</th>
<?php
$grand_total_all_months = 0;
for ($month = 1; $month <= 12; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_total_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row4 = mysqli_fetch_array($sql_payments);
$payment_total_amount_for_month = floatval($row4['payment_total_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id > 0 AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row5 = mysqli_fetch_array($sql_revenues);
$revenues_total_amount_for_month = floatval($row5['revenue_amount_for_month']);
$payment_total_amount_for_month += $revenues_total_amount_for_month;
$grand_total_all_months += $payment_total_amount_for_month;
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_total_amount_for_month, $session_company_currency); ?></th>
<?php } ?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $grand_total_all_months, $session_company_currency); ?></th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php"; ?>
<script>
// Bootstrap-like defaults for Chart.js v4
Chart.defaults.font.family = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.color = '#292b2c';
// INCOME (Line)
(function () {
var ctx = document.getElementById("cashFlow");
if (!ctx) return;
var myLineChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
datasets: [{
label: "Income",
fill: false,
borderColor: "#007bff",
pointBackgroundColor: "#007bff",
pointBorderColor: "#007bff",
pointHoverRadius: 5,
pointHoverBackgroundColor: "#007bff",
pointBorderWidth: 2,
data: [
<?php
// Build series and track the largest month for axis max
for ($month = 1; $month <= 12; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$r1 = mysqli_fetch_array($sql_payments);
$payments_for_month = floatval($r1['payment_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id > 0 AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$r2 = mysqli_fetch_array($sql_revenues);
$revenues_for_month = floatval($r2['revenue_amount_for_month']);
$income_for_month = $payments_for_month + $revenues_for_month;
if ($income_for_month > 0 && $income_for_month > $largest_income_month) {
$largest_income_month = $income_for_month;
}
echo "$income_for_month,";
}
?>
],
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
grid: { display: false },
ticks: { maxTicksLimit: 12 }
},
y: {
beginAtZero: true,
min: 0,
max: <?php
$max = max(1000, $largest_income_month);
echo roundUpToNearestMultiple($max);
?>,
ticks: { maxTicksLimit: 5 },
grid: { color: "rgba(0, 0, 0, .125)" }
}
},
plugins: {
legend: { display: false }
}
}
});
})();
</script>

20
agent/reports/index.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
require_once "includes/inc_all_reports.php";
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-coins mr-2"></i>Reports</h3>
</div>
<div class="card-body p-0">
<form class="p-3">
<h3>Access different reports using the menu on the left</h3>
<small class="text-muted">In addition to the general reporting permission, you must have read permissions to the reporting area you wish to view (e.g. support/financial).</small>
</div>
</div>
<?php require_once "../../includes/footer.php"; ?>

View File

@@ -0,0 +1,437 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
//GET unique years from expenses, payments and revenues
$sql_all_years = mysqli_query($mysqli, "SELECT YEAR(expense_date) AS all_years FROM expenses
UNION DISTINCT SELECT YEAR(payment_date) FROM payments
UNION DISTINCT SELECT YEAR(revenue_date) FROM revenues
ORDER BY all_years DESC"
);
$sql_categories_income = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Income' ORDER BY category_name ASC");
$sql_categories_expense = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Expense' ORDER BY category_name ASC");
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-balance-scale mr-2"></i>Profit & Loss</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body p-0">
<form class="p-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_all_years)) {
$all_years = intval($row['all_years']);
?>
<option <?php if ($year == $all_years) { ?> selected <?php } ?> > <?php echo $all_years; ?></option>
<?php
}
?>
</select>
</form>
<div class="table-responsive-sm">
<table class="table table-sm">
<thead class="text-dark">
<tr>
<th></th>
<th class="text-right">Jan-Mar</th>
<th class="text-right">Apr-Jun</th>
<th class="text-right">Jul-Sep</th>
<th class="text-right">Oct-Dec</th>
<th class="text-right">Total</th>
</tr>
<tr>
<th><br><br>Income</th>
<th colspan="5"></th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_categories_income)) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']);
?>
<tr>
<td><?php echo $category_name; ?></td>
<?php
$payment_amount_for_quarter_one = 0;
for($month = 1; $month<=3; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_category_id = $category_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_amount_for_month = floatval($row['payment_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id = $category_id AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_amount_for_month = floatval($row['revenue_amount_for_month']);
$payment_amount_for_month = $payment_amount_for_month + $revenue_amount_for_month;
$payment_amount_for_quarter_one = $payment_amount_for_quarter_one + $payment_amount_for_month;
}
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_amount_for_quarter_one, $session_company_currency); ?></td>
<?php
$payment_amount_for_quarter_two = 0;
for($month = 4; $month<=6; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_category_id = $category_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_amount_for_month = floatval($row['payment_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id = $category_id AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_amount_for_month = floatval($row['revenue_amount_for_month']);
$payment_amount_for_month = $payment_amount_for_month + $revenue_amount_for_month;
$payment_amount_for_quarter_two = $payment_amount_for_quarter_two + $payment_amount_for_month;
}
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_amount_for_quarter_two, $session_company_currency); ?></td>
<?php
$payment_amount_for_quarter_three = 0;
for($month = 7; $month<=9; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_category_id = $category_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_amount_for_month = floatval($row['payment_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id = $category_id AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_amount_for_month = floatval($row['revenue_amount_for_month']);
$payment_amount_for_month = $payment_amount_for_month + $revenue_amount_for_month;
$payment_amount_for_quarter_three = $payment_amount_for_quarter_three + $payment_amount_for_month;
}
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_amount_for_quarter_three, $session_company_currency); ?></td>
<?php
$payment_amount_for_quarter_four = 0;
for($month = 10; $month<=12; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_category_id = $category_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_amount_for_month = floatval($row['payment_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_amount_for_month FROM revenues WHERE revenue_category_id = $category_id AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_amount_for_month = floatval($row['revenue_amount_for_month']);
$payment_amount_for_month = $payment_amount_for_month + $revenue_amount_for_month;
$payment_amount_for_quarter_four = $payment_amount_for_quarter_four + $payment_amount_for_month;
}
$total_payments_for_all_four_quarters = $payment_amount_for_quarter_one + $payment_amount_for_quarter_two + $payment_amount_for_quarter_three + $payment_amount_for_quarter_four;
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_amount_for_quarter_four, $session_company_currency); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $total_payments_for_all_four_quarters, $session_company_currency); ?></td>
</tr>
<?php
$total_payment_for_all_months = 0;
}
?>
<tr>
<th>Gross Profit</th>
<?php
$payment_total_amount_for_quarter_one = 0;
for($month = 1; $month<=3; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_total_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_total_amount_for_month = floatval($row['payment_total_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_total_amount_for_month FROM revenues WHERE revenue_category_id > 0 AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_total_amount_for_month = floatval($row['revenue_total_amount_for_month']);
$payment_total_amount_for_month = $payment_total_amount_for_month + $revenue_total_amount_for_month;
$payment_total_amount_for_quarter_one = $payment_total_amount_for_quarter_one + $payment_total_amount_for_month;
}
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_total_amount_for_quarter_one, $session_company_currency); ?></th>
<?php
$payment_total_amount_for_quarter_two = 0;
for($month = 4; $month<=6; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_total_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_total_amount_for_month = floatval($row['payment_total_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_total_amount_for_month FROM revenues WHERE revenue_category_id > 0 AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_total_amount_for_month = floatval($row['revenue_total_amount_for_month']);
$payment_total_amount_for_month = $payment_total_amount_for_month + $revenue_total_amount_for_month;
$payment_total_amount_for_quarter_two = $payment_total_amount_for_quarter_two + $payment_total_amount_for_month;
}
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_total_amount_for_quarter_two, $session_company_currency); ?></th>
<?php
$payment_total_amount_for_quarter_three = 0;
for($month = 7; $month<=9; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_total_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_total_amount_for_month = floatval($row['payment_total_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_total_amount_for_month FROM revenues WHERE revenue_category_id > 0 AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_total_amount_for_month = floatval($row['revenue_total_amount_for_month']);
$payment_total_amount_for_month = $payment_total_amount_for_month + $revenue_total_amount_for_month;
$payment_total_amount_for_quarter_three = $payment_total_amount_for_quarter_three + $payment_total_amount_for_month;
}
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_total_amount_for_quarter_three, $session_company_currency); ?></th>
<?php
$payment_total_amount_for_quarter_four = 0;
for($month = 10; $month<=12; $month++) {
$sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS payment_total_amount_for_month FROM payments, invoices WHERE payment_invoice_id = invoice_id AND YEAR(payment_date) = $year AND MONTH(payment_date) = $month");
$row = mysqli_fetch_array($sql_payments);
$payment_total_amount_for_month = floatval($row['payment_total_amount_for_month']);
$sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS revenue_total_amount_for_month FROM revenues WHERE revenue_category_id > 0 AND YEAR(revenue_date) = $year AND MONTH(revenue_date) = $month");
$row = mysqli_fetch_array($sql_revenues);
$revenue_total_amount_for_month = floatval($row['revenue_total_amount_for_month']);
$payment_total_amount_for_month = $payment_total_amount_for_month + $revenue_total_amount_for_month;
$payment_total_amount_for_quarter_four = $payment_total_amount_for_quarter_four + $payment_total_amount_for_month;
}
$total_payments_for_all_four_quarters = $payment_total_amount_for_quarter_one + $payment_total_amount_for_quarter_two + $payment_total_amount_for_quarter_three + $payment_total_amount_for_quarter_four;
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_total_amount_for_quarter_four, $session_company_currency); ?></th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $total_payments_for_all_four_quarters, $session_company_currency); ?></th>
</tr>
<tr>
<th><br><br>Expenses</th>
<th colspan="5"></th>
</tr>
<?php
while ($row = mysqli_fetch_array($sql_categories_expense)) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']);
?>
<tr>
<td><?php echo $category_name; ?></td>
<?php
$expense_amount_for_quarter_one = 0;
for($month = 1; $month<=3; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE expense_category_id = $category_id AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month");
$row = mysqli_fetch_array($sql_expenses);
$expense_amount_for_quarter_one = $expense_amount_for_quarter_one + floatval($row['expense_amount_for_month']);
}
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount_for_quarter_one, $session_company_currency); ?></td>
<?php
$expense_amount_for_quarter_two = 0;
for($month = 4; $month<=6; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE expense_category_id = $category_id AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month");
$row = mysqli_fetch_array($sql_expenses);
$expense_amount_for_quarter_two = $expense_amount_for_quarter_two + floatval($row['expense_amount_for_month']);
}
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount_for_quarter_two, $session_company_currency); ?></td>
<?php
$expense_amount_for_quarter_three = 0;
for($month = 7; $month<=9; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE expense_category_id = $category_id AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month");
$row = mysqli_fetch_array($sql_expenses);
$expense_amount_for_quarter_three = $expense_amount_for_quarter_three + floatval($row['expense_amount_for_month']);
}
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount_for_quarter_three, $session_company_currency); ?></td>
<?php
$expense_amount_for_quarter_four = 0;
for($month = 10; $month<=12; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE expense_category_id = $category_id AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month");
$row = mysqli_fetch_array($sql_expenses);
$expense_amount_for_quarter_four = $expense_amount_for_quarter_four + floatval($row['expense_amount_for_month']);
}
$total_expenses_for_all_four_quarters = $expense_amount_for_quarter_one + $expense_amount_for_quarter_two + $expense_amount_for_quarter_three + $expense_amount_for_quarter_four;
?>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount_for_quarter_four, $session_company_currency); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $total_expenses_for_all_four_quarters, $session_company_currency); ?></td>
</tr>
<?php
$total_expense_for_all_months = 0;
}
?>
<tr>
<th>Total Expenses<br><br><br></th>
<?php
$expense_total_amount_for_quarter_one = 0;
for($month = 1; $month<=3; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_total_amount_for_month FROM expenses WHERE expense_category_id > 0 AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
$row = mysqli_fetch_array($sql_expenses);
$expense_total_amount_for_quarter_one = $expense_total_amount_for_quarter_one + floatval($row['expense_total_amount_for_month']);
}
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_total_amount_for_quarter_one, $session_company_currency); ?></th>
<?php
$expense_total_amount_for_quarter_two = 0;
for($month = 4; $month<=6; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_total_amount_for_month FROM expenses WHERE expense_category_id > 0 AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
$row = mysqli_fetch_array($sql_expenses);
$expense_total_amount_for_quarter_two = $expense_total_amount_for_quarter_two + floatval($row['expense_total_amount_for_month']);
}
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_total_amount_for_quarter_two, $session_company_currency); ?></th>
<?php
$expense_total_amount_for_quarter_three = 0;
for($month = 7; $month<=9; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_total_amount_for_month FROM expenses WHERE expense_category_id > 0 AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
$row = mysqli_fetch_array($sql_expenses);
$expense_total_amount_for_quarter_three = $expense_total_amount_for_quarter_three + floatval($row['expense_total_amount_for_month']);
}
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_total_amount_for_quarter_three, $session_company_currency); ?></th>
<?php
$expense_total_amount_for_quarter_four = 0;
for($month = 10; $month<=12; $month++) {
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_total_amount_for_month FROM expenses WHERE expense_category_id > 0 AND YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
$row = mysqli_fetch_array($sql_expenses);
$expense_total_amount_for_quarter_four = $expense_total_amount_for_quarter_four + floatval($row['expense_total_amount_for_month']);
}
$total_expenses_for_all_four_quarters = $expense_total_amount_for_quarter_one + $expense_total_amount_for_quarter_two + $expense_total_amount_for_quarter_three + $expense_total_amount_for_quarter_four;
?>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_total_amount_for_quarter_four, $session_company_currency); ?></th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $total_expenses_for_all_four_quarters, $session_company_currency); ?></th>
</tr>
<tr>
<?php
$net_profit_quarter_one = $payment_total_amount_for_quarter_one - $expense_total_amount_for_quarter_one;
$net_profit_quarter_two = $payment_total_amount_for_quarter_two - $expense_total_amount_for_quarter_two;
$net_profit_quarter_three = $payment_total_amount_for_quarter_three - $expense_total_amount_for_quarter_three;
$net_profit_quarter_four = $payment_total_amount_for_quarter_four - $expense_total_amount_for_quarter_four;
$net_profit_year = $total_payments_for_all_four_quarters - $total_expenses_for_all_four_quarters;
?>
<th>Net Profit</th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $net_profit_quarter_one, $session_company_currency); ?></th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $net_profit_quarter_two, $session_company_currency); ?></th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $net_profit_quarter_three, $session_company_currency); ?></th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $net_profit_quarter_four, $session_company_currency); ?></th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $net_profit_year, $session_company_currency); ?></th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php";

View File

@@ -0,0 +1,67 @@
<?php
require_once "includes/inc_all_reports.php";
validateAccountantRole();
$sql = mysqli_query($mysqli, "
SELECT client_id, client_name,
SUM(CASE WHEN recurring_invoice_frequency = 'month' THEN recurring_invoice_amount
WHEN recurring_invoice_frequency = 'year' THEN recurring_invoice_amount / 12 END) AS recurring_monthly_total
FROM clients
LEFT JOIN recurring_invoices ON client_id = recurring_invoice_client_id
WHERE recurring_invoice_status = 1
GROUP BY clients.client_id
HAVING recurring_monthly_total > 0
ORDER BY recurring_monthly_total DESC
");
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-sync mr-2"></i>Recurring Income By Client</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Monthly Recurring</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$recurring_monthly_total = floatval($row['recurring_monthly_total']);
$recurring_total = $recurring_total + $recurring_monthly_total;
?>
<tr>
<td><a href="../../agent/client_overview.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?></a></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $recurring_monthly_total, $session_company_currency); ?></td>
</tr>
<?php
}
?>
<tr>
<th>Total Monthly Income</th>
<th class="text-right"><?php echo numfmt_format_currency($currency_format, $recurring_total, $session_company_currency); ?></th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php";
?>

View File

@@ -0,0 +1,121 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_financial');
$year = isset($_GET['year']) ? intval($_GET['year']) : date('Y');
$view = isset($_GET['view']) ? $_GET['view'] : 'quarterly';
$currency_row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT company_currency FROM companies WHERE company_id = 1"));
$company_currency = nullable_htmlentities($currency_row['company_currency']);
// GET unique years from expenses, payments and revenues
$sql_all_years = mysqli_query($mysqli, "SELECT DISTINCT(YEAR(item_created_at)) AS all_years FROM invoice_items ORDER BY all_years DESC");
$sql_tax = mysqli_query($mysqli, "SELECT `tax_name` FROM `taxes`");
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-balance-scale mr-2"></i>Collected Tax Summary</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body p-0">
<form class="p-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_all_years)) {
$all_years = intval($row['all_years']);
?>
<option <?php if ($year == $all_years) { echo "selected"; } ?> > <?php echo $all_years; ?></option>
<?php
}
?>
</select>
<!-- View Selection Dropdown -->
<select onchange="this.form.submit()" class="form-control" name="view">
<option value="monthly" <?php if ($view == 'monthly') echo "selected"; ?>>Monthly</option>
<option value="quarterly" <?php if ($view == 'quarterly') echo "selected"; ?>>Quarterly</option>
</select>
</form>
<div class="table-responsive-sm">
<table class="table table-sm">
<thead class="text-dark">
<tr>
<th>Tax</th>
<?php
if ($view == 'monthly') {
for ($i = 1; $i <= 12; $i++) {
echo "<th class='text-right'>" . date('M', mktime(0, 0, 0, $i, 10)) . "</th>";
}
} else {
echo "<th class='text-right'>Jan-Mar</th>";
echo "<th class='text-right'>Apr-Jun</th>";
echo "<th class='text-right'>Jul-Sep</th>";
echo "<th class='text-right'>Oct-Dec</th>";
}
?>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_tax)) {
$tax_name = sanitizeInput($row['tax_name']);
echo "<tr>";
echo "<td>" . $row['tax_name'] . "</td>";
if ($view == 'monthly') {
for ($i = 1; $i <= 12; $i++) {
$monthly_tax = getMonthlyTax($tax_name, $i, $year, $mysqli);
echo "<td class='text-right'>" . numfmt_format_currency($currency_format, $monthly_tax, $company_currency) . "</td>";
}
} else {
for ($q = 1; $q <= 4; $q++) {
$quarterly_tax = getQuarterlyTax($tax_name, $q, $year, $mysqli);
echo "<td class='text-right'>" . numfmt_format_currency($currency_format, $quarterly_tax, $company_currency) . "</td>";
}
}
// Calculate total for row and echo bold
$total_tax = getTotalTax($tax_name, $year, $mysqli);
echo "<td class='text-right text-bold'>" . numfmt_format_currency($currency_format, $total_tax, $company_currency) . "</td>";
echo "</tr>";
}
?>
<tr>
<th>Total</th>
<?php
if ($view == 'monthly') {
for ($i = 1; $i <= 12; $i++) {
$monthly_tax = getMonthlyTax($tax_name, $i, $year, $mysqli);
echo "<th class='text-right'>" . numfmt_format_currency($currency_format, $monthly_tax, $company_currency) . "</th>";
}
} else {
for ($q = 1; $q <= 4; $q++) {
$quarterly_tax = getQuarterlyTax($tax_name, $q, $year, $mysqli);
echo "<th class='text-right'>" . numfmt_format_currency($currency_format, $quarterly_tax, $company_currency) . "</th>";
}
}
?>
<td class="text-right"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php";

View File

@@ -0,0 +1,343 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_support');
function secondsToTime($inputSeconds) {
$inputSeconds = floor($inputSeconds);
$secondsInAMinute = 60;
$secondsInAnHour = 60 * $secondsInAMinute;
$secondsInADay = 24 * $secondsInAnHour;
// Extract days
$days = floor($inputSeconds / $secondsInADay);
// Extract hours
$hourSeconds = $inputSeconds % $secondsInADay;
$hours = floor($hourSeconds / $secondsInAnHour);
// Extract minutes
$minuteSeconds = $hourSeconds % $secondsInAnHour;
$minutes = floor($minuteSeconds / $secondsInAMinute);
// Extract the remaining seconds
$remainingSeconds = $minuteSeconds % $secondsInAMinute;
$seconds = ceil($remainingSeconds);
// Format and return
$timeParts = [];
$sections = [
'day' => (int)$days,
'hour' => (int)$hours,
'minute' => (int)$minutes,
'second' => (int)$seconds,
];
foreach ($sections as $name => $value){
if ($value > 0) {
$timeParts[] = $value. ' '.$name.($value == 1 ? '' : 's');
}
}
return implode(', ', $timeParts);
}
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
if (isset($_GET['month'])) {
$month = intval($_GET['month']);
} else {
$month = date('m');
}
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-life-ring mr-2"></i>Tickets By Client</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_ticket_years)) {
$ticket_year = intval($row['ticket_year']); ?>
<option <?php if ($year == $ticket_year) { ?> selected <?php } ?> > <?php echo $ticket_year; ?></option>
<?php } ?>
</select>
<select onchange="this.form.submit()" class="form-control" name="month">
<option <?php if ($month == 1) { echo 'selected'; } ?> value="1">January</option>
<option <?php if ($month == 2) { echo 'selected'; } ?> value="2">February</option>
<option <?php if ($month == 3) { echo 'selected'; } ?> value="3">March</option>
<option <?php if ($month == 4) { echo 'selected'; } ?> value="4">April</option>
<option <?php if ($month == 5) { echo 'selected'; } ?> value="5">May</option>
<option <?php if ($month == 6) { echo 'selected'; } ?> value="6">June</option>
<option <?php if ($month == 7) { echo 'selected'; } ?> value="7">July</option>
<option <?php if ($month == 8) { echo 'selected'; } ?> value="8">August</option>
<option <?php if ($month == 9) { echo 'selected'; } ?> value="9">September</option>
<option <?php if ($month == 10) { echo 'selected'; } ?> value="10">October</option>
<option <?php if ($month == 11) { echo 'selected'; } ?> value="11">November</option>
<option <?php if ($month == 12) { echo 'selected'; } ?> value="12">December</option>
</select>
</form>
<div class="card card-dark mb-3">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-fw fa-chart-area mr-2"></i>Yearly (<?php echo $year; ?>)</h3>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Raised</th>
<th class="text-right">Priority: Low</th>
<th class="text-right">Priority: Med</th>
<th class="text-right">Priority: High</th>
<th class="text-right">Resolved</th>
<th class="text-right">Total Time worked <i>(H:M:S)</i></th>
<th class="text-right">Avg time to respond</th>
<th class="text-right">Avg time to resolve</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_clients)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
// Calculate total tickets raised in period
$sql_ticket_raised_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_raised_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id");
$row = mysqli_fetch_array($sql_ticket_raised_count);
$ticket_raised_count = intval($row['ticket_raised_count']);
// Calculate total tickets raised in period that are resolved
$sql_ticket_resolved_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_resolved_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_resolved_at IS NOT NULL");
$row = mysqli_fetch_array($sql_ticket_resolved_count);
$ticket_resolved_count = intval($row['ticket_resolved_count']);
// Breakdown tickets for each priority - Low
$sql_low_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS low_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_priority = 'Low'");
$row = mysqli_fetch_array($sql_low_ticket_count);
$low_ticket_count = intval($row['low_ticket_count']);
// Breakdown tickets for each priority - Low
$sql_med_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS med_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_priority = 'Medium'");
$row = mysqli_fetch_array($sql_med_ticket_count);
$med_ticket_count = intval($row['med_ticket_count']);
// Breakdown tickets for each priority - Low
$sql_high_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS high_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_priority = 'High'");
$row = mysqli_fetch_array($sql_high_ticket_count);
$high_ticket_count = intval($row['high_ticket_count']);
// Used to calculate average time to respond to tickets that were raised in period specified
$sql_tickets_respond = mysqli_query($mysqli, "SELECT ticket_created_at, ticket_first_response_at FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id");
// Used to calculate average time to resolve tickets that were raised in period specified
$sql_tickets_resolved = mysqli_query($mysqli, "SELECT ticket_created_at, ticket_resolved_at FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_resolved_at IS NOT NULL");
// Calculate total time tracked towards tickets in the period
$sql_time = mysqli_query($mysqli, "SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(ticket_reply_time_worked))) as total_time FROM ticket_replies LEFT JOIN tickets ON tickets.ticket_id = ticket_replies.ticket_reply_ticket_id WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_reply_time_worked IS NOT NULL");
$row = mysqli_fetch_array($sql_time);
$ticket_total_time_worked = nullable_htmlentities($row['total_time']);
if ($ticket_raised_count > 0 || $ticket_resolved_count > 0) {
// Calculate average time to respond
$avg_time_to_respond = '-';
$count = 0;
$total = 0;
while ($row = mysqli_fetch_array($sql_tickets_respond)) {
if (!empty($row['ticket_first_response_at'])) {
$openedTime = new DateTime($row['ticket_created_at']);
$respondTime = new DateTime($row['ticket_first_response_at']);
$total += ($respondTime->getTimestamp() - $openedTime->getTimestamp());
$count++;
}
}
if ($count > 0) {
$avg_time_to_respond = secondsToTime($total / $count); // Avoids DivisionByZeroError
}
// Calculate average time to solve
$avg_time_to_resolve = '-';
if ($ticket_resolved_count > 0) {
$count = 0;
$total = 0;
while ($row = mysqli_fetch_array($sql_tickets_resolved)) {
$openedTime = new DateTime($row['ticket_created_at']);
$resolvedTime = new DateTime($row['ticket_resolved_at']);
$total += ($resolvedTime->getTimestamp() - $openedTime->getTimestamp());
$count++;
}
$avg_time_to_resolve = secondsToTime($total / $count);
}
?>
<tr>
<td><?php echo $client_name; ?></td>
<td class="text-right"><?php echo $ticket_raised_count; ?></td>
<td class="text-right"><?php echo $low_ticket_count; ?></td>
<td class="text-right"><?php echo $med_ticket_count; ?></td>
<td class="text-right"><?php echo $high_ticket_count; ?></td>
<td class="text-right"><?php echo $ticket_resolved_count; ?></td>
<td class="text-right"><?php echo $ticket_total_time_worked; ?></td>
<td class="text-right"><?php echo $avg_time_to_respond; ?></td>
<td class="text-right"><?php echo $avg_time_to_resolve; ?></td>
</tr>
<?php
}
}
?>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-dark mb-3">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-fw fa-chart-area mr-2"></i>Monthly (<?php echo date("F", mktime(1, 1, 1, $month, 1)) . ' ' . $year; ?>)</h3>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Raised</th>
<th class="text-right">Priority: Low</th>
<th class="text-right">Priority: Med</th>
<th class="text-right">Priority: High</th>
<th class="text-right">Resolved</th>
<th class="text-right">Total Time worked <i>(H:M:S)</i></th>
<th class="text-right">Avg time to respond</th>
<th class="text-right">Avg time to resolve</th>
</tr>
</thead>
<tbody>
<?php
mysqli_data_seek($sql_clients, 0); // Reset
while ($row = mysqli_fetch_array($sql_clients)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
// Calculate total tickets raised in period
$sql_ticket_raised_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_raised_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id");
$row = mysqli_fetch_array($sql_ticket_raised_count);
$ticket_raised_count = intval($row['ticket_raised_count']);
// Calculate total tickets raised in period that are resolved
$sql_ticket_resolved_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_resolved_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_resolved_at IS NOT NULL");
$row = mysqli_fetch_array($sql_ticket_resolved_count);
$ticket_resolved_count = intval($row['ticket_resolved_count']);
// Breakdown tickets for each priority - Low
$sql_low_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS low_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_priority = 'Low'");
$row = mysqli_fetch_array($sql_low_ticket_count);
$low_ticket_count = intval($row['low_ticket_count']);
// Breakdown tickets for each priority - Low
$sql_med_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS med_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_priority = 'Medium'");
$row = mysqli_fetch_array($sql_med_ticket_count);
$med_ticket_count = intval($row['med_ticket_count']);
// Breakdown tickets for each priority - Low
$sql_high_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS high_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_priority = 'High'");
$row = mysqli_fetch_array($sql_high_ticket_count);
$high_ticket_count = intval($row['high_ticket_count']);
// Used to calculate average time to respond to tickets that were raised in period specified
$sql_tickets_respond = mysqli_query($mysqli, "SELECT ticket_created_at, ticket_first_response_at FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id");
// Used to calculate average time to resolve tickets that were raised in period specified
$sql_tickets_resolved = mysqli_query($mysqli, "SELECT ticket_created_at, ticket_resolved_at FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_resolved_at IS NOT NULL");
// Calculate total time tracked towards tickets in the period
$sql_time = mysqli_query($mysqli, "SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(ticket_reply_time_worked))) as total_time FROM ticket_replies LEFT JOIN tickets ON tickets.ticket_id = ticket_replies.ticket_reply_ticket_id WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_reply_time_worked IS NOT NULL");
$row = mysqli_fetch_array($sql_time);
$ticket_total_time_worked = nullable_htmlentities($row['total_time']);
if ($ticket_raised_count > 0 || $ticket_resolved_count > 0) {
// Calculate average time to respond
$avg_time_to_respond = '-';
$count = 0;
$total = 0;
while ($row = mysqli_fetch_array($sql_tickets_respond)) {
if (!empty($row['ticket_first_response_at'])) {
$openedTime = new DateTime($row['ticket_created_at']);
$respondTime = new DateTime($row['ticket_first_response_at']);
$total += ($respondTime->getTimestamp() - $openedTime->getTimestamp());
$count++;
}
}
if ($count > 0) {
$avg_time_to_respond = secondsToTime($total / $count); // Avoids DivisionByZeroError
}
// Calculate average time to solve
$avg_time_to_resolve = '-';
if ($ticket_resolved_count > 0) {
$count = 0;
$total = 0;
while ($row = mysqli_fetch_array($sql_tickets_resolved)) {
$openedTime = new DateTime($row['ticket_created_at']);
$resolvedTime = new DateTime($row['ticket_resolved_at']);
$total += ($resolvedTime->getTimestamp() - $openedTime->getTimestamp());
$count++;
}
$avg_time_to_resolve = secondsToTime($total / $count);
}
?>
<tr>
<td><?php echo $client_name; ?></td>
<td class="text-right"><?php echo $ticket_raised_count; ?></td>
<td class="text-right"><?php echo $low_ticket_count; ?></td>
<td class="text-right"><?php echo $med_ticket_count; ?></td>
<td class="text-right"><?php echo $high_ticket_count; ?></td>
<td class="text-right"><?php echo $ticket_resolved_count; ?></td>
<td class="text-right"><?php echo $ticket_total_time_worked; ?></td>
<td class="text-right"><?php echo $avg_time_to_respond; ?></td>
<td class="text-right"><?php echo $avg_time_to_resolve; ?></td>
</tr>
<?php
}
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php
require_once "../../includes/footer.php";

View File

@@ -0,0 +1,163 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_support');
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
$sql_tickets = mysqli_query($mysqli, "SELECT ticket_id FROM tickets");
// Track largest month for chart y-axis max
$largest_ticket_month = 0;
?>
<!-- Responsive chart helpers -->
<style>
.chart-h-320 { position: relative; height: 320px; }
@media (max-width: 576px) { .chart-h-320 { height: 260px; } }
</style>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-life-ring mr-2"></i>Ticket Summary</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body p-0">
<form class="p-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_ticket_years)) {
$ticket_year = intval($row['ticket_year']); ?>
<option <?php if ($year == $ticket_year) { ?> selected <?php } ?>><?php echo $ticket_year; ?></option>
<?php } ?>
</select>
</form>
<div class="px-3 pb-3">
<div class="chart-h-320">
<canvas id="tickets"></canvas>
</div>
</div>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th class="text-right">January</th>
<th class="text-right">February</th>
<th class="text-right">March</th>
<th class="text-right">April</th>
<th class="text-right">May</th>
<th class="text-right">June</th>
<th class="text-right">July</th>
<th class="text-right">August</th>
<th class="text-right">September</th>
<th class="text-right">October</th>
<th class="text-right">November</th>
<th class="text-right">December</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
$total_tickets_for_all_months = 0;
for ($month = 1; $month <= 12; $month++) {
$sql_tickets = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS tickets_for_month FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month");
$row = mysqli_fetch_array($sql_tickets);
$tickets_for_month = intval($row['tickets_for_month']);
if ($tickets_for_month > 0 && $tickets_for_month > $largest_ticket_month) {
$largest_ticket_month = $tickets_for_month;
}
$total_tickets_for_all_months += $tickets_for_month; ?>
<td class="text-right"><?php echo $tickets_for_month; ?></td>
<?php } ?>
<td class="text-right"><b><?php echo $total_tickets_for_all_months; ?></b></td>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once "../../includes/footer.php"; ?>
<script>
// Bootstrap-like defaults for Chart.js v4
Chart.defaults.font.family = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.color = '#292b2c';
(function () {
var ctx = document.getElementById("tickets");
if (!ctx) return;
var dataPoints = [
<?php
// Recompute for the chart dataset (values already gathered above, but we echo directly again)
for ($month = 1; $month <= 12; $month++) {
$sql_tickets = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS tickets_for_month FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month");
$row = mysqli_fetch_array($sql_tickets);
$tickets_for_month = intval($row['tickets_for_month']);
echo "$tickets_for_month,";
}
?>
];
new Chart(ctx, {
type: 'line',
data: {
labels: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
datasets: [{
label: "Tickets Raised",
fill: false,
borderColor: "#007bff",
pointBackgroundColor: "#007bff",
pointBorderColor: "#007bff",
pointHoverRadius: 5,
pointHoverBackgroundColor: "#007bff",
pointBorderWidth: 2,
data: dataPoints
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
grid: { display: false },
ticks: { maxTicksLimit: 12 }
},
y: {
beginAtZero: true,
min: 0,
max: <?php
// use your helper if available, otherwise largest_ticket_month as-is
$max = max(5, $largest_ticket_month);
echo function_exists('roundUpToNearestMultiple') ? roundUpToNearestMultiple($max) : $max;
?>,
ticks: { maxTicksLimit: 5, precision: 0 },
grid: { color: "rgba(0, 0, 0, .125)" }
}
},
plugins: {
legend: { display: false }
}
}
});
})();
</script>

View File

@@ -0,0 +1,182 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_sales', 1);
function secondsToTime($inputSeconds) {
$secondsInAMinute = 60;
$secondsInAnHour = 60 * $secondsInAMinute;
$secondsInADay = 24 * $secondsInAnHour;
// Extract days
$days = floor($inputSeconds / $secondsInADay);
// Extract hours
$hourSeconds = $inputSeconds % $secondsInADay;
$hours = floor($hourSeconds / $secondsInAnHour);
// Extract minutes
$minuteSeconds = $hourSeconds % $secondsInAnHour;
$minutes = floor($minuteSeconds / $secondsInAMinute);
// Extract the remaining seconds
$remainingSeconds = $minuteSeconds % $secondsInAMinute;
$seconds = ceil($remainingSeconds);
// Format and return
$timeParts = [];
$sections = [
'day' => (int)$days,
'hour' => (int)$hours,
'minute' => (int)$minutes,
'second' => (int)$seconds,
];
foreach ($sections as $name => $value){
if ($value > 0){
$timeParts[] = $value. ' '.$name.($value == 1 ? '' : 's');
}
}
return implode(', ', $timeParts);
}
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients ORDER BY client_name ASC");
$rows = 0;
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-life-ring mr-2"></i>Unbilled Tickets By Client</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_ticket_years)) {
$ticket_year = intval($row['ticket_year']); ?>
<option <?php if ($year == $ticket_year) { ?> selected <?php } ?> > <?php echo $ticket_year; ?></option>
<?php } ?>
</select>
</form>
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Client</th>
<th class="text-right">Tickets Raised</th>
<th class="text-right">Billable Tickets</th>
<th class="text-right">Unbilled Tickets</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_clients)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
// Calculate total tickets raised in period
$sql_ticket_raised_count = mysqli_query(
$mysqli,
"SELECT
COUNT(ticket_id) AS ticket_raised_count
FROM
tickets
WHERE
YEAR(ticket_created_at) = $year
AND
ticket_client_id = $client_id"
);
$row = mysqli_fetch_array($sql_ticket_raised_count);
$ticket_raised_count = intval($row['ticket_raised_count']);
// Calculate total tickets raised in period that are closed and billable
$sql_ticket_closed_count = mysqli_query(
$mysqli,
"SELECT
COUNT(ticket_id) AS ticket_closed_count
FROM
tickets
WHERE
YEAR(ticket_created_at) = $year
AND
ticket_client_id = $client_id
AND
ticket_closed_at IS NOT NULL
AND
ticket_billable = 1
");
$row = mysqli_fetch_array($sql_ticket_closed_count);
$ticket_closed_count = intval($row['ticket_closed_count']);
// Calculate total tickets raised in period that are closed and billable, but not invoiced
$sql_ticket_unbilled_count = mysqli_query(
$mysqli,
"SELECT
COUNT(ticket_id) AS ticket_unbilled_count
FROM
tickets
WHERE
YEAR(ticket_created_at) = $year
AND
ticket_client_id = $client_id
AND
ticket_closed_at IS NOT NULL
AND
ticket_billable = 1
AND
ticket_invoice_id = 0");
$row = mysqli_fetch_array($sql_ticket_unbilled_count);
$ticket_unbilled_count = intval($row['ticket_unbilled_count']);
if ($ticket_unbilled_count > 0) {
$rows = $rows + 1;
?>
<tr>
<td>
<a href="../../agent/tickets.php?client_id=<?php echo $client_id; ?>&billable=1&unbilled"><?php echo $client_name; ?></a>
</td>
<td class="text-right"><?php echo $ticket_raised_count; ?></td>
<td class="text-right"><?php echo $ticket_closed_count; ?></td>
<td class="text-right"><?php echo $ticket_unbilled_count; ?></td>
</tr>
<?php
}
}
if ($rows == 0) {
?>
<tr>
<td colspan="4">You are all caught up! There are no unbilled tickets for this year.
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "../../includes/footer.php";

View File

@@ -0,0 +1,181 @@
<?php
require_once "includes/inc_all_reports.php";
enforceUserPermission('module_support');
function secondsToTime($inputSeconds) {
$inputSeconds = floor($inputSeconds);
$secondsInAMinute = 60;
$secondsInAnHour = 60 * $secondsInAMinute;
$secondsInADay = 24 * $secondsInAnHour;
// Extract days
$days = floor($inputSeconds / $secondsInADay);
// Extract hours
$hourSeconds = $inputSeconds % $secondsInADay;
$hours = floor($hourSeconds / $secondsInAnHour);
// Extract minutes
$minuteSeconds = $hourSeconds % $secondsInAnHour;
$minutes = floor($minuteSeconds / $secondsInAMinute);
// Extract the remaining seconds
$remainingSeconds = $minuteSeconds % $secondsInAMinute;
$seconds = ceil($remainingSeconds);
// Format and return
$timeParts = [];
$sections = [
'day' => (int)$days,
'hour' => (int)$hours,
'minute' => (int)$minutes,
'second' => (int)$seconds,
];
foreach ($sections as $name => $value){
if ($value > 0){
$timeParts[] = $value. ' '.$name.($value == 1 ? '' : 's');
}
}
return implode(', ', $timeParts);
}
if (isset($_GET['year'])) {
$year = intval($_GET['year']);
} else {
$year = date('Y');
}
if (isset($_GET['month'])) {
$month = intval($_GET['month']);
} else {
$month = date('m');
}
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
$sql_users = mysqli_query($mysqli, "
SELECT users.user_id, user_name FROM users
LEFT JOIN user_settings on users.user_id = user_settings.user_id
WHERE user_type = 1
AND user_status = 1
AND user_archived_at IS NULL
ORDER BY user_name DESC"
);
// TODO: Maybe try and filter this to just users with the support module perm
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-life-ring mr-2"></i>Time Logged By Technician</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary d-print-none" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
</div>
</div>
<div class="card-body">
<form class="mb-3">
<select onchange="this.form.submit()" class="form-control" name="year">
<?php
while ($row = mysqli_fetch_array($sql_ticket_years)) {
$ticket_year = intval($row['ticket_year']); ?>
<option <?php if ($year == $ticket_year) { ?> selected <?php } ?> > <?php echo $ticket_year; ?></option>
<?php } ?>
</select>
</form>
<div class="card card-dark mb-3">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-fw fa-chart-area mr-2"></i>Yearly (<?php echo $year; ?>)</h3>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped">
<thead>
<tr>
<th>Technician</th>
<th class="text-right">Tickets assigned</th>
<th class="text-right">Tickets touched</th>
<th class="text-right">Total time worked <i>(H:M:S)</i></th>
</tr>
</thead>
<tbody>
<?php
while ($agent_row = mysqli_fetch_array($sql_users)) {
$user_id = intval($agent_row['user_id']);
$user_name = nullable_htmlentities($agent_row['user_name']);
// Get tickets in period that are still assigned to the technician/agent
$sql_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_assigned_to = $user_id");
$row = mysqli_fetch_array($sql_ticket_count);
$ticket_raised_count = intval($row['ticket_count']);
// Get unique tickets in period that the agent replied to/touched
$sql_tickets_touched = mysqli_query($mysqli, "
SELECT COUNT(DISTINCT ticket_id) AS tickets_touched
FROM (
-- Tickets the agent replied to
SELECT ticket_reply_ticket_id AS ticket_id
FROM ticket_replies
WHERE YEAR(ticket_reply_created_at) = $year AND ticket_reply_by = $user_id
UNION
-- Tickets the agent opened
SELECT ticket_id
FROM tickets
WHERE YEAR(ticket_created_at) = $year AND ticket_created_by = $user_id
UNION
-- Tickets the agent closed
SELECT ticket_id
FROM tickets
WHERE YEAR(ticket_created_at) = $year AND ticket_closed_by = $user_id
)
AS tickets_touched
");
$row = mysqli_fetch_array($sql_tickets_touched);
$tickets_touched = intval($row['tickets_touched']);
// Calculate total time tracked towards tickets in the period (for this agent)
$sql_time = mysqli_query($mysqli, "SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(ticket_reply_time_worked))) as total_time FROM ticket_replies LEFT JOIN tickets ON tickets.ticket_id = ticket_replies.ticket_reply_ticket_id WHERE YEAR(ticket_created_at) = $year AND ticket_reply_by = $user_id AND ticket_reply_time_worked IS NOT NULL");
$row = mysqli_fetch_array($sql_time);
$ticket_total_time_worked = nullable_htmlentities($row['total_time']);
?>
<tr>
<td><?php echo $user_name; ?></td>
<td class="text-right"><?php echo $ticket_raised_count; ?></td>
<td class="text-right"><?php echo $tickets_touched; ?></td>
<td class="text-right"><?php echo $ticket_total_time_worked; ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<!-- TODO: Monthly version of this report -->
</div>
</div>
<?php
require_once "../../includes/footer.php";