Files
itflow/client/index.php
johnnyq 422088fb60 Invoices, quotes and recurring invoices makeover stage 2: per user, per device and per license recurring lines that take their quantity from the client's contacts (all or tagged), devices (ticked types or tagged, lost/stolen/retired left out) or a license's seats (in use or bought), counted again for every invoice with an optional contract minimum, set from a Counted choice on the add row or the full Quantity from section in Edit Line with a live count, marked on each line and listed in a new Seats card with Update now when the client has changed, quarterly frequency alongside monthly and yearly counted as a third in MRR on the dashboard, client pages, client portal and reports, period words {month} {quarter} {year} {period} filled into scope, notes and lines, schedule card showing the period the next invoice covers, recurring_invoice_anchor_day so month-end schedules return to the 31st after a short month, nightly run and Invoice Now consolidated into recurringInvoiceGenerate() which claims the run so it is billed once, refreshes seat counts and takes taxes at current rates, nightly run now bills runs missed by up to 7 days, Invoice Now bills the next run early and moves the schedule on from that run instead of restarting from today, recurring email goes to non-archived primary and billing contacts once per address and stays a draft when outgoing mail is not set up. Fixes portal recurring card ignoring yearly-only clients and counting archived ones, edit recurring invoice resetting the billing day. Adds DB 2.9.1, recurring_invoice_sync and recurring_seat_count handlers, itflowBillingSeatForm(), and updates the 26.11 changelog
2026-10-16 13:09:59 -04:00

281 lines
9.1 KiB
PHP

<?php
/*
* Client Portal
* Landing / Home page for the client portal
*/
header("Content-Security-Policy: default-src 'self'");
require_once "includes/inc_all.php";
// Billing Card Queries
//Add up all the payments for the invoice and get the total amount paid to the invoice
$sql_invoice_amounts = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_amounts FROM invoices WHERE invoice_client_id = $session_client_id AND invoice_status != 'Draft' AND invoice_status != 'Cancelled' AND invoice_status != 'Non-Billable'");
$row = mysqli_fetch_assoc($sql_invoice_amounts);
$invoice_amounts = floatval($row['invoice_amounts']);
$sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_client_id = $session_client_id");
$row = mysqli_fetch_assoc($sql_amount_paid);
$amount_paid = floatval($row['amount_paid']);
$balance = $invoice_amounts - $amount_paid;
// What their recurring invoices come to a month - quarterly ones a third, yearly ones a twelfth
$row = mysqli_fetch_row(mysqli_query($mysqli, "SELECT COALESCE(SUM(" . recurringInvoiceMonthlySql() . "), 0) FROM recurring_invoices
WHERE recurring_invoice_status = 1 AND recurring_invoice_archived_at IS NULL AND recurring_invoice_client_id = $session_client_id"));
$recurring_monthly = floatval($row[0]);
// Technical Card Queries
// 8 - 45 Day Warning
// Get Domains Expiring
$sql_domains_expiring = mysqli_query(
$mysqli,
"SELECT domain_expire, domain_id, domain_name FROM domains
WHERE domain_client_id = $session_client_id
AND domain_expire IS NOT NULL
AND domain_archived_at IS NULL
AND domain_expire > CURRENT_DATE
AND domain_expire < CURRENT_DATE + INTERVAL 45 DAY
ORDER BY domain_expire ASC"
);
// Get Certificates Expiring
$sql_certificates_expiring = mysqli_query(
$mysqli,
"SELECT * FROM certificates
WHERE certificate_client_id = $session_client_id
AND certificate_expire IS NOT NULL
AND certificate_archived_at IS NULL
AND certificate_expire > CURRENT_DATE
AND certificate_expire < CURRENT_DATE + INTERVAL 45 DAY
ORDER BY certificate_expire ASC"
);
// Get Licenses Expiring
$sql_licenses_expiring = mysqli_query(
$mysqli,
"SELECT * FROM software
WHERE software_client_id = $session_client_id
AND software_expire IS NOT NULL
AND software_archived_at IS NULL
AND software_expire > CURRENT_DATE
AND software_expire < CURRENT_DATE + INTERVAL 45 DAY
ORDER BY software_expire ASC"
);
// Get Asset Warranties Expiring
$sql_asset_warranties_expiring = mysqli_query(
$mysqli,
"SELECT * FROM assets
WHERE asset_client_id = $session_client_id
AND asset_warranty_expire IS NOT NULL
AND asset_archived_at IS NULL
AND asset_warranty_expire > CURRENT_DATE
AND asset_warranty_expire < CURRENT_DATE + INTERVAL 45 DAY
ORDER BY asset_warranty_expire ASC"
);
// Get Assets Retiring 7 Year
$sql_asset_retire = mysqli_query(
$mysqli,
"SELECT * FROM assets
WHERE asset_client_id = $session_client_id
AND asset_install_date IS NOT NULL
AND asset_archived_at IS NULL
AND asset_install_date + INTERVAL 7 YEAR > CURRENT_DATE
AND asset_install_date + INTERVAL 7 YEAR <= CURRENT_DATE + INTERVAL 45 DAY
ORDER BY asset_install_date ASC"
);
/*
* EXPIRED ITEMS
*/
// Get Domains Expired
$sql_domains_expired = mysqli_query(
$mysqli,
"SELECT * FROM domains
WHERE domain_client_id = $session_client_id
AND domain_expire IS NOT NULL
AND domain_archived_at IS NULL
AND domain_expire < CURRENT_DATE
ORDER BY domain_expire ASC"
);
// Get Certificates Expired
$sql_certificates_expired = mysqli_query(
$mysqli,
"SELECT * FROM certificates
WHERE certificate_client_id = $session_client_id
AND certificate_expire IS NOT NULL
AND certificate_archived_at IS NULL
AND certificate_expire < CURRENT_DATE
ORDER BY certificate_expire ASC"
);
// Get Licenses Expired
$sql_licenses_expired = mysqli_query(
$mysqli,
"SELECT * FROM software
WHERE software_client_id = $session_client_id
AND software_expire IS NOT NULL
AND software_archived_at IS NULL
AND software_expire < CURRENT_DATE
ORDER BY software_expire ASC"
);
// Get Asset Warranties Expired
$sql_asset_warranties_expired = mysqli_query(
$mysqli,
"SELECT * FROM assets
WHERE asset_client_id = $session_client_id
AND asset_warranty_expire IS NOT NULL
AND asset_archived_at IS NULL
AND asset_warranty_expire < CURRENT_DATE
ORDER BY asset_warranty_expire ASC"
);
// Get Retired Assets
$sql_asset_retired = mysqli_query(
$mysqli,
"SELECT * FROM assets
WHERE asset_client_id = $session_client_id
AND asset_install_date IS NOT NULL
AND asset_archived_at IS NULL
AND asset_install_date + INTERVAL 7 YEAR < CURRENT_DATE -- Assets retired (installed more than 7 years ago)
ORDER BY asset_install_date ASC"
);
// Assigned Assets
$sql_assigned_assets = mysqli_query(
$mysqli,
"SELECT asset_name, asset_type, asset_uri_client FROM assets
WHERE asset_contact_id = $session_contact_id
AND asset_archived_at IS NULL
ORDER BY asset_name ASC"
);
?>
<div class="row">
<div class="col-md-2">
<a href="ticket_add.php" class="btn btn-primary w-100 mb-3">New ticket</a>
</div>
</div>
<?php
// Billing Cards
if (contactCan('accounting')) { ?>
<div class="row">
<?php if ($balance > 0) { ?>
<div class="col-sm-3">
<a href="unpaid_invoices.php" class="card">
<div class="card-header">
<h3 class="card-title text-bold text-dark">Account Balance</h3>
</div>
<div class="card-body">
<div class="h4 text-danger"><b><?= numfmt_format_currency($currency_format, $balance, $session_company_currency) ?></b></div>
</div>
</a>
</div>
<?php } ?>
<?php if ($recurring_monthly > 0) { ?>
<div class="col-sm-3">
<a href="recurring_invoices.php" class="card text-dark">
<div class="card-header">
<h3 class="card-title">Recurring Monthly</h3>
</div>
<div class="card-body">
<div class="h4"><b><?= numfmt_format_currency($currency_format, $recurring_monthly, $session_company_currency) ?></b></div>
</div>
</a>
</div>
<?php } ?>
</div>
<?php } //End Billing Cards ?>
<?php
// Technical Cards
if (contactCan('itdoc')) {
?>
<div class="row">
<?php if (mysqli_num_rows($sql_domains_expiring) > 0) { ?>
<div class="col-sm-3">
<a href="domains.php">
<div class="card text-dark">
<div class="card-header">
<h3 class="card-title text-bold"><i class="fas fa-fw fa-globe me-2"></i>Domains Expiring</h3>
</div>
<div class="card-body">
<?php
while ($row = mysqli_fetch_assoc($sql_domains_expiring)) {
$domain_id = intval($row['domain_id']);
$domain_name = escapeHtml($row['domain_name']);
$domain_expire = escapeHtml($row['domain_expire']);
$domain_expire_human = timeAgo($row['domain_expire']);
?>
<p>
<strong><?= $domain_name ?></strong>
<br>
<small class="text-secondary"><?= $domain_expire ?> (<?= $domain_expire_human ?>)</small>
</p>
<?php
}
?>
</div>
</div>
</a>
</div>
<?php } ?>
</div>
<?php } ?>
<?php
// Everone Cards
?>
<div class="row">
<?php if (mysqli_num_rows($sql_assigned_assets) > 0) { ?>
<div class="col-sm-3">
<a href="assets.php" class="card text-dark">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-fw fa-desktop me-2"></i>Your Assigned Assets</h3>
</div>
<div class="card-body">
<table>
<?php
while ($row = mysqli_fetch_assoc($sql_assigned_assets)) {
$asset_name = escapeHtml($row['asset_name']);
$asset_type = escapeHtml($row['asset_type']);
$asset_uri_client = escapeUrl($row['asset_uri_client']);
?>
<tr>
<td><i class=" text-secondary me-2"></i><?php if ($asset_uri_client) { ?><a href="<?= $asset_uri_client ?>" target="_blank"><i class='fas fa-external-link-alt me-2'></i></a><?php } ?><?= $asset_name ?></td>
<td class="text-secondary">(<?= $asset_type ?>)</td>
</tr>
<?php
}
?>
</table>
</div>
</a>
</div>
<?php } ?>
</div>
<?php require_once "includes/footer.php"; ?>