= 2; $can_delete = lookupUserPermission('module_sales') >= 3; // Client Filter (all clients) $client = ''; if (!$client_url && !empty($_GET['client'])) { $client = intval($_GET['client']); $client_query = "AND recurring_invoice_client_id = $client"; } // Category Filter $category_filter = intval($_GET['category'] ?? 0); $category_query = $category_filter ? "AND recurring_invoice_category_id = $category_filter" : ''; $statuses = [ 'active' => ['label' => 'Active', 'icon' => 'fa-play', 'where' => 'recurring_invoice_status = 1'], 'soon' => ['label' => 'Next 7 days', 'icon' => 'fa-calendar-day', 'where' => 'recurring_invoice_status = 1 AND recurring_invoice_next_date BETWEEN CURDATE() AND CURDATE() + INTERVAL 7 DAY', 'title' => 'Running, and billing in the next week'], 'late' => ['label' => 'Late', 'icon' => 'fa-exclamation-triangle', 'where' => 'recurring_invoice_status = 1 AND recurring_invoice_next_date < CURDATE()', 'title' => "Running, but the next date has passed - they won't bill until it moves on"], 'inactive' => ['label' => 'Paused', 'icon' => 'fa-pause', 'where' => 'recurring_invoice_status = 0'], 'all' => ['label' => 'All', 'where' => ''], ]; $status = isset($statuses[$_GET['status'] ?? '']) ? ($_GET['status'] ?? '') : 'active'; $status_query = $statuses[$status]['where'] ? 'AND ' . $statuses[$status]['where'] : ''; $sortable_columns = ['recurring_invoice_number' => 'recurring_invoice_number', 'recurring_invoice_scope' => 'recurring_invoice_scope', 'client_name' => 'client_name', 'recurring_invoice_frequency' => 'recurring_invoice_frequency', 'recurring_invoice_amount' => 'recurring_invoice_amount', 'monthly' => 'monthly', 'recurring_invoice_next_date' => 'recurring_invoice_next_date', 'recurring_invoice_last_sent' => 'recurring_invoice_last_sent', 'category_name' => 'category_name']; if (!isset($sortable_columns[$sort])) { $sort = 'recurring_invoice_next_date'; } $monthly_sql = recurringInvoiceMonthlySql(); $scope_query = clientScopeSql('recurring_invoice_client_id'); $search_query = "AND (CONCAT(recurring_invoice_prefix, recurring_invoice_number) LIKE '%$q%' OR recurring_invoice_frequency LIKE '%$q%' OR recurring_invoice_scope LIKE '%$q%' OR client_name LIKE '%$q%' OR category_name LIKE '%$q%')"; $filters = "AND recurring_invoice_archived_at IS NULL $scope_query $client_query $category_query $search_query"; $joins = "LEFT JOIN clients ON recurring_invoice_client_id = client_id LEFT JOIN categories ON recurring_invoice_category_id = category_id"; // The contract each bills, when one is linked (functions/contract_billing.php) $contract_sql = contractBillingReady() ? "(SELECT contract_name FROM contracts WHERE contract_recurring_invoice_id = recurring_invoice_id AND contract_archived_at IS NULL ORDER BY contract_id DESC LIMIT 1)" : 'NULL'; $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS recurring_invoices.*, client_name, category_name, recurring_payment_id, $monthly_sql AS monthly, $contract_sql AS contract_name FROM recurring_invoices $joins LEFT JOIN recurring_payments ON recurring_payment_recurring_invoice_id = recurring_invoice_id WHERE 1 $filters $status_query ORDER BY {$sortable_columns[$sort]} $order, recurring_invoice_id ASC LIMIT $record_from, $record_to"); $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $recurring_invoices = []; while ($row = mysqli_fetch_assoc($sql)) { $recurring_invoices[] = $row; } $count_sql = []; foreach ($statuses as $key => $s) { $count_sql[] = $s['where'] === '' ? 'COUNT(*)' : "COALESCE(SUM({$s['where']}), 0)"; } $row = mysqli_fetch_row(mysqli_query($mysqli, "SELECT " . implode(', ', $count_sql) . " FROM recurring_invoices $joins WHERE 1 $filters")); $counts = []; foreach (array_keys($statuses) as $i => $key) { $counts[$key] = intval($row[$i]); } // The recurring revenue along the top - everything in this scope $revenue = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COALESCE(SUM(CASE WHEN recurring_invoice_status = 1 THEN $monthly_sql END), 0) AS mrr, COALESCE(SUM(CASE WHEN recurring_invoice_status = 1 AND recurring_invoice_next_date BETWEEN CURDATE() AND CURDATE() + INTERVAL 30 DAY THEN recurring_invoice_amount END), 0) AS next30, COALESCE(SUM(CASE WHEN recurring_invoice_status = 1 AND recurring_invoice_next_date BETWEEN CURDATE() AND CURDATE() + INTERVAL 30 DAY THEN 1 END), 0) AS next30_count, COALESCE(SUM(recurring_invoice_status = 1), 0) AS active_count, COALESCE(SUM(recurring_invoice_status = 0), 0) AS paused_count, COALESCE(SUM(recurring_invoice_status = 1 AND recurring_invoice_next_date < CURDATE()), 0) AS late_count FROM recurring_invoices WHERE recurring_invoice_archived_at IS NULL $scope_query $client_query")); $filter_url = function ($changes) { $params = array_merge($_GET, $changes); unset($params['page']); return '?' . http_build_query(array_filter($params, function ($v) { return $v !== '' && $v !== null; })); }; $stat_money = function ($amount) use ($session_company_currency) { return escapeHtml(billingMoney($amount, $session_company_currency)); }; ?>
Monthly recurring
a year
Bills in 30 days
invoice to be made
Running
paused
Need a look

Recurring Invoices

0) { ?>
Recurring invoice Client Amount A month Next Last
·
Not yet' ?>
'recurring invoices', 'icon' => 'fa-redo-alt', 'filtered' => (bool) $q, 'views' => ['status'], 'view' => emptyStatePill($statuses, $status, 'active', ['late']), 'text' => 'Monthly agreements and subscriptions - each bills itself when it comes round.', 'actions' => $can_edit ? [['label' => 'New Recurring Invoice', 'modal' => "modals/recurring_invoice/recurring_invoice_add.php?$client_url"]] : [], ]; require_once "../includes/filter_footer.php"; ?>