Nothing to see here

Go Back"; } else { $client_id = intval($contract['contract_client_id']); enforceClientAccess(); $can_edit = lookupUserPermission('module_sales') >= 2; $can_delete = lookupUserPermission('module_sales') >= 3; $name = escapeHtml($contract['contract_name']); $page_title = $contract['contract_name']; $state = contractState($contract); $type = CONTRACT_TYPES[$contract['contract_type']] ?? CONTRACT_TYPES['other']; $notice = contractNoticeDate($contract); $today = date('Y-m-d'); $status = (string) $contract['contract_status']; $slas = contractSlaChoices(array_map(function ($part) use ($contract) { return intval($contract["contract_sla_{$part}_id"]); }, CONTRACT_SLA_PRIORITIES)); $history = auditLogHistory('Contract', $contract_id); // Signing - the rounds, newest first, the latest's state, and the copy signed by both $signing = contractSignaturesReady(); $rounds = $signing ? contractSignatureRounds($contract_id) : []; $latest = $rounds[0] ?? null; $latest_state = contractSignatureState($latest); $signed_round = contractSignatureSigned($rounds); $outdated = $signed_round && (!$latest_state || in_array($latest_state['key'], ['signed', 'withdrawn'], true)) && contractSignatureOutdated($contract, $signed_round); $can_send = $signing && $can_edit && !in_array($status, ['Ended', 'Cancelled'], true); // Prepaid hours - the blocks, where they stand, and the tickets drawn from them $hours_on = contractHoursReady(); $blocks = $hours_on ? contractBlocks($contract_id) : []; $hours = contractHoursSummary($blocks); $show_hours = $hours_on && contractHasHours($contract, $blocks); $hours_uses = $show_hours ? contractHoursUses($contract_id, 20) : []; // Drawn from while active, and once cancelled, until its last day $hours_live = $hours_on && contractHoursLive($contract, $today); $can_hours = $hours_on && $can_edit && ($status === 'Draft' || $hours_live); $hours_alert = $hours_on ? floatval($contract['contract_hours_alert']) : 0; $hours_low = $show_hours && $hours_live && $hours['bought'] > 0 && ($hours['left'] <= 0 || ($hours_alert > 0 && $hours['left'] < $hours_alert)); // In force today - its SLAs, rates and net terms are the client's - and the recurring invoice that bills it $in_force = contractInForceOn($contract, $today); $billing_on = contractBillingReady() && intval($config_module_enable_accounting) === 1; $recurring = $billing_on ? contractRecurringRow($contract) : null; $can_link = $billing_on && $can_edit && !$recurring && (contractRecurringShouldRun($contract) || $status === 'Draft'); $currency = $session_company_currency ?? ''; $money = function ($amount) use ($currency) { return escapeHtml(function_exists('billingMoney') ? billingMoney($amount, $currency) : number_format((float) $amount, 2)); }; // How far through its term it is $progress = null; if ($contract['contract_start_date'] && $contract['contract_end_date'] && in_array($state['key'], ['active', 'renewing', 'ending'], true)) { // The current term began a term's length before it ends - or when the contract started, while it is in its first // term (a first term a few days longer or shorter than the rest is still the first) $months = intval($contract['contract_term_months']); $started = strtotime($contract['contract_start_date']); $term_start = strtotime("-$months months +1 day", strtotime($contract['contract_end_date'])); if ($term_start < strtotime("+$months months", $started)) { $term_start = $started; } $term_end = strtotime($contract['contract_end_date']); $progress = [ 'percent' => $term_end > $term_start ? max(0, min(100, round((time() - $term_start) / ($term_end - $term_start) * 100))) : 100, 'days_left' => max(0, intval(floor(($term_end - strtotime($today)) / 86400))), 'from' => date('M j, Y', $term_start), ]; } ?>

From
It renews itself on for another . = $today ? 'Notice to stop it is due by ' . escapeHtml(date('M j, Y', strtotime($notice))) . ' - a good time to review it with the client.' : 'The notice date has passed.' ?> It ends on and doesn't renew itself. = $today ? 'Notice is due by ' . escapeHtml(date('M j, Y', strtotime($notice))) . '.' : '' ?> Renew it to keep it going.

Signing

Not sent to the client to sign yet. They sign from a link in an email, or in the client portal; you countersign after.

The contract has changed since it was signed. Send it to sign again for the new version.
1) { ?>
Earlier requests ()

Term

Starts
Not set' ?>
Ends
No end date' ?>
Term
Renews
Notice
By
Renewed
Cancelled

Recurring invoice

Next
' . ($status === 'Draft' ? 'Starts when the contract is active' : 'Paused') . '' ?>
Scope
It runs while the contract is active, and is paused once it ends or its cancellation takes effect.

People

Client
Contact
' . escapeHtml($contract['contact_name']) . '' : 'None' ?>
Owner
No one - everyone is reminded' ?>
Added
$client_id]) ?>

What it covers

Support hours
Not set' ?>
Rates
(' . ($from === '' ? 'default' : ($from === "the client's" ? "client's" : escapeHtml($from))) . ')'; }; ?>
Settings > Defaults">' . $money($usual['rate'][0]) . '/hr' . $usual_from($usual['rate'][1]) . ''; } else { echo 'No rate'; } ?>
Net terms
SLA by priority
$part) { $sla = $slas[intval($contract["contract_sla_{$part}_id"])] ?? null; $sla_default = $sla ? null : $sla_rows[$priority]['sla']; ?>
Archived' : '' ?>
No SLA
0 ? max(0, min(100, round($hours['used'] / $hours['bought'] * 100))) : 0; $monthly = floatval($contract['contract_monthly_hours']); ?>

Prepaid hours

0) { ?>
Left
0) { ?> Of · used No hours to draw from 0 && $status === 'Draft' ? ' until it is active' : '' ?>
0) { ?>
    0) { ?>
  • included each month, for that month only
  • run out on
  • 0 ? 'The owner is told under ' . escapeHtml(contractHoursText($hours_alert)) . ' left' : 'No one is told when they run low' ?>
  • 0) { ?>
  • ran out unused
Time is drawn from them only while the contract is active.
Cancelled - time is drawn from them until its last day, .
0 ? 'Running low.' : 'All used.' ?> Time beyond them goes to Unbilled Time to bill as usual.
$block) { $what = $block['contract_block_note'] ?: ($block['contract_block_month'] ? 'Included hours' : 'Bought'); ?>
Hours Good for Used Left Paid
Ran out –
0 ? "This month's included hours are added once it is active." : 'No hours yet.' . ($can_hours ? ' Add the hours the client bought, and invoice them if you like.' : '') ?>
Drawn by tickets
  • Drawn part - the rest is billed' ?>

Terms

No terms written down. Add them

Notes