0 ? rtrim(rtrim(number_format(floatval($value), 2, '.', ''), '0'), '.') : ''; }; // The clients to pick from, and the contacts of the client it is for. With no client yet (New Contract // from the contracts page) there are none here - they are fetched when one is picked (js/contract_form.js) $c_clients = []; $c_contacts = []; $client_where = $c_client_id ? "client_id = $c_client_id" : "client_archived_at IS NULL " . clientScopeSql('clients.client_id'); $sql = mysqli_query($mysqli, "SELECT client_id, client_name, client_abbreviation FROM clients WHERE $client_where ORDER BY client_name ASC"); while ($row = mysqli_fetch_assoc($sql)) { $c_clients[intval($row['client_id'])] = $row; } if ($c_client_id && $c_clients) { $sql = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_title, contact_photo, contact_client_id, contact_primary, contact_technical FROM contacts WHERE contact_client_id = $c_client_id AND (contact_archived_at IS NULL OR contact_id = " . intval($c_value('contact_id', 0)) . ") ORDER BY contact_primary DESC, contact_technical DESC, contact_important DESC, contact_name ASC"); while ($row = mysqli_fetch_assoc($sql)) { $c_contacts[] = $row; } } $c_contact_id = intval($c_value('contact_id', 0)); if ($c_new && !$c_contact_id && $c_client_id) { foreach ($c_contacts as $contact) { if (intval($contact['contact_primary']) === 1 && intval($contact['contact_client_id']) === $c_client_id) { $c_contact_id = intval($contact['contact_id']); break; } } } $c_owner_id = intval($c_value('owner_id', $c_new ? ($session_user_id ?? 0) : 0)); $c_owners = []; $sql = mysqli_query($mysqli, "SELECT user_id, user_name, user_avatar FROM users WHERE user_type = 1 AND (user_archived_at IS NULL OR user_id = $c_owner_id) ORDER BY user_name ASC"); while ($row = mysqli_fetch_assoc($sql)) { $c_owners[intval($row['user_id'])] = $row; } // The templates, for filling the fields from one $c_template_data = []; foreach ($c_templates as $template_id => $template) { $c_template_data[$template_id] = [ 'name' => $template['contract_template_name'], 'type' => $template['contract_template_type'], 'term_months' => intval($template['contract_template_term_months']), 'auto_renew' => intval($template['contract_template_auto_renew']), 'notice_days' => intval($template['contract_template_notice_days']), 'rate_standard' => $c_money($template['contract_template_rate_standard']), 'rate_after_hours' => $c_money($template['contract_template_rate_after_hours']), 'net_terms' => $template['contract_template_net_terms'] === null ? '' : intval($template['contract_template_net_terms']), 'support_hours' => (string) $template['contract_template_support_hours'], 'details' => documentHtmlPurify((string) $template['contract_template_details']), 'monthly_hours' => $c_hours($template['contract_template_monthly_hours'] ?? 0), 'hours_alert' => $c_hours($template['contract_template_hours_alert'] ?? 0), ]; foreach (CONTRACT_SLA_PRIORITIES as $part) { $c_template_data[$template_id]["sla_$part"] = intval($template["contract_template_sla_{$part}_id"]); } } $tab = 'contractTab' . bin2hex(random_bytes(3)); ?>
Fills in the type, term, cover and terms from it - change any of them after.

No contract templates yet - a template fills in the type, term, cover and terms of a new contract. Make one in Admin > Contract Templates

1 || !$c_client_id) { ?>
data-contract-field="name">
Reminded before it renews or ends.
>
data-contract-field="auto_renew">
days
hours
hours left
Tickets' time is drawn from the hours included each month - for that month only - and from any hours bought on the contract, before it is billed.
days
While the contract is in force, time bills at these rates - the after-hours one for tickets opened outside business hours - and new invoices are due on these terms. Leave one empty and the default in Admin > Settings > Defaults applies. $part) { ?>
From Admin > SLAs. While the contract is in force, the client's new tickets get them, and the default for a priority left on the default.
Only agents see these.