Settings > Invoice, ready * to change before it goes on, and every expense with its price. Tick the * ones to bill and make an invoice, or add them to one of the client's * drafts - or mark them not billable. A client with prepaid hours on a * contract has them drawn from each night (functions/contract_hours.php): * what is left shows here, with Draw now, and a ticket they covered part of * is here with the rest. * * functions/billing.php (Billing time) counts and bills the time, * functions/expenses.php (Billing expenses on) the expenses and * functions/trips.php (Mileage money) the trips; the handlers are in * agent/post/billing.php. */ if (isset($_GET['client_id'])) { require_once "includes/inc_all_client.php"; $client_view = true; } else { require_once "includes/inc_all.php"; $client_view = false; $client_id = 0; } enforceUserPermission('module_sales'); $can_edit = lookupUserPermission('module_sales') >= 2; $with_time = intval($config_module_enable_ticketing) === 1; $with_expenses = lookupUserPermission('module_financial') >= 1; $ready = intval($config_module_enable_accounting) === 1 && ($with_time || $with_expenses); $can_bill_expenses = $can_edit && lookupUserPermission('module_financial') >= 2; $rules = billingTimeRules(); $clients = $ready && $with_time ? billingUnbilledByClient($client_view ? $client_id : 0) : []; $clients = $ready && $with_expenses ? tripUnbilledMerge(expenseUnbilledMerge($clients, $client_view ? $client_id : 0), $client_view ? $client_id : 0) : $clients; $currency = (string) $session_company_currency; $money = function ($amount) use ($currency) { return escapeHtml(billingMoney($amount, $currency)); }; $total = ['tickets' => 0, 'seconds' => 0, 'hours' => 0.0, 'prepaid' => 0.0, 'value' => 0.0, 'oldest' => 0, 'no_time' => 0, 'no_rate' => 0, 'expenses' => 0, 'expense_value' => 0.0, 'expense_cost' => 0.0, 'trips' => 0, 'trip_value' => 0.0]; foreach ($clients as $c) { $total['tickets'] += count($c['tickets']); $total['seconds'] += $c['seconds']; $total['hours'] += $c['hours']; $total['prepaid'] += $c['prepaid'] ?? 0; $total['value'] += $c['value']; $total['oldest'] = max($total['oldest'], $c['oldest']); $total['no_time'] += $c['no_time']; $total['no_rate'] += $c['tickets'] && $c['rate'] <= 0 ? 1 : 0; $total['expenses'] += count($c['expenses'] ?? []); $total['expense_value'] += $c['expense_value'] ?? 0; $total['expense_cost'] += $c['expense_cost'] ?? 0; $total['trips'] += count($c['trips'] ?? []); $total['trip_value'] += $c['trip_value'] ?? 0; } // Expenses and trips together, where they share a column $total['extras'] = $total['expenses'] + $total['trips']; $total['extra_value'] = $total['expense_value'] + $total['trip_value']; $extras_text = function ($expenses, $trips) { $parts = []; if ($expenses) { $parts[] = $expenses . ' expense' . ($expenses === 1 ? '' : 's'); } if ($trips) { $parts[] = $trips . ' trip' . ($trips === 1 ? '' : 's'); } return implode(', ', $parts); }; $this_client = $client_view ? ($clients[$client_id] ?? null) : null; // One rate for all the client's tickets, or each its own (a contract's after-hours rate, or a change of contract) $one_rate = $this_client && count($this_client['rates'] ?? []) <= 1; // The client's prepaid hours - drawn from each night, or now $prepaid = $client_view && $with_time && $this_client && $this_client['tickets'] ? contractClientHours($client_id) : []; $prepaid_left = round(array_sum(array_map(function ($c) { return $c['hours']['left']; }, $prepaid)), 2); $show_time = $with_time && (!$client_view || ($this_client && $this_client['tickets'])); $markup_text = function ($markup) { return rtrim(rtrim(number_format(floatval($markup), 2, '.', ''), '0'), '.') . '%'; }; $taxes = billingTaxes(); $scope_options = ['Support - ' . date('F Y'), 'Support - ' . date('F Y', strtotime('first day of last month'))]; $waiting_pill = function ($days) { $tone = $days > 30 ? 'danger' : ($days > 7 ? 'warning' : 'secondary'); return '' . ($days <= 0 ? 'Today' : $days . ' day' . ($days === 1 ? '' : 's')) . ''; }; $hours_text = function ($hours) { return billingQuantity($hours) . ' h'; }; ?>