mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 07:01:16 +00:00
Rates, net terms and SLAs come from the client's contract, else the instance defaults: database 3.5.0 adds a default after-hours rate (Settings > Defaults, now Rates and terms), gives each client whose own rate, net terms or SLAs differ from the defaults an active Standard terms contract setting just those (12-month term renewing itself, starting the day the client was added or the day before its earliest contract, archived with an archived client), deletes per-client SLA assignments and drops clients.client_rate and client_net_terms; time bills at the contract's rate, its after-hours rate or the default after-hours rate, else the default rate, new invoices are due on the contract's net terms else the default, and new tickets get the contract's SLA else the default; a client's own terms still apply until the update runs; the client add and edit windows lose the rate, terms and SLA fields for a note saying what the client is billed on and where it comes from, the Clients list loses Set Hourly Rate and Set Net Terms and shows the contract's rate, the overview's Terms shows the after-hours rate and whether terms are a contract's or the defaults, a contract's page and document show what it leaves empty from another contract in force or the default so signed documents stay unchanged, contract windows show the defaults as placeholders, contract names take a proper possessive; the client import and API create make a Standard terms contract for a rate or terms that aren't the defaults (returning contract_id), API update reports them in not_kept; the client export gives the rates, terms and SLAs as billed; demo data puts each demo client's terms on a contract; changelog updated
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
/*
|
||||
* Contract - one agreement with a client: where it stands and when it renews
|
||||
* or ends (with the notice date), what it covers (support hours, an SLA for
|
||||
* each ticket priority, rates and net terms - the client's own where the
|
||||
* contract sets none), its terms, the internal notes, and its history from
|
||||
* each ticket priority, rates and net terms - where the contract sets none,
|
||||
* another in force for the client, else the defaults), its terms, the internal notes, and its history from
|
||||
* the audit log. Make active, Renew, Cancel and Edit from the top. Signing:
|
||||
* send it to the client to sign, where each request stands - sent, opened,
|
||||
* signed, countersigned - the link to copy, the copies signed before, and
|
||||
@@ -361,15 +361,39 @@ if (!$contract || ($client_url && intval($contract['contract_client_id']) !== in
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="small text-body-secondary text-uppercase fw-semibold mb-1">Rates</div>
|
||||
<div><?= $contract['contract_rate_standard'] !== null ? $money($contract['contract_rate_standard']) . '/hr' : '<span title="The client\'s own rate">' . $money($contract['client_rate']) . '/hr <span class="text-body-secondary small">(client\'s)</span></span>' ?></div>
|
||||
<div class="small text-body-secondary"<?= $contract['contract_rate_after_hours'] !== null ? ' title="For tickets opened outside the business days and hours in Admin > Settings"' : '' ?>><?= $contract['contract_rate_after_hours'] !== null ? $money($contract['contract_rate_after_hours']) . '/hr after hours' : 'No after-hours rate' ?></div>
|
||||
<?php
|
||||
// What it leaves empty: another contract in force for the client, else the default
|
||||
$usual = contractUsualTerms($contract);
|
||||
$usual_from = function ($from) {
|
||||
// A contract's name, the client's own (before the 3.5.0 update), or the default
|
||||
return ' <span class="text-body-secondary small">(' . ($from === '' ? 'default' : ($from === "the client's" ? "client's" : escapeHtml($from))) . ')</span>';
|
||||
};
|
||||
?>
|
||||
<div data-contract-rate><?php
|
||||
if ($contract['contract_rate_standard'] !== null) {
|
||||
echo $money($contract['contract_rate_standard']) . '/hr';
|
||||
} elseif ($usual['rate'][0] !== null) {
|
||||
echo '<span title="What it leaves empty - another contract in force for the client, else the default in Admin > Settings > Defaults">' . $money($usual['rate'][0]) . '/hr' . $usual_from($usual['rate'][1]) . '</span>';
|
||||
} else {
|
||||
echo '<span class="text-body-secondary">No rate</span>';
|
||||
}
|
||||
?></div>
|
||||
<div class="small text-body-secondary" title="For tickets opened outside the business days and hours in Admin > SLAs" data-contract-after-hours><?php
|
||||
if ($contract['contract_rate_after_hours'] !== null) {
|
||||
echo $money($contract['contract_rate_after_hours']) . '/hr after hours';
|
||||
} elseif ($usual['after_hours'][0] !== null) {
|
||||
echo $money($usual['after_hours'][0]) . '/hr after hours' . $usual_from($usual['after_hours'][1]);
|
||||
} else {
|
||||
echo 'No after-hours rate';
|
||||
}
|
||||
?></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="small text-body-secondary text-uppercase fw-semibold mb-1">Net terms</div>
|
||||
<div><?php
|
||||
$terms = $contract['contract_net_terms'] !== null ? intval($contract['contract_net_terms']) : intval($contract['client_net_terms']);
|
||||
$terms = $contract['contract_net_terms'] !== null ? intval($contract['contract_net_terms']) : $usual['net_terms'][0];
|
||||
echo $terms ? "Net $terms" : 'Due on receipt';
|
||||
echo $contract['contract_net_terms'] === null ? ' <span class="text-body-secondary small">(client\'s)</span>' : '';
|
||||
echo $contract['contract_net_terms'] === null ? $usual_from($usual['net_terms'][1]) : '';
|
||||
?></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -377,16 +401,21 @@ if (!$contract || ($client_url && intval($contract['contract_client_id']) !== in
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-borderless mb-0" data-contract-slas>
|
||||
<tbody>
|
||||
<?php foreach (CONTRACT_SLA_PRIORITIES as $priority => $part) {
|
||||
$sla = $slas[intval($contract["contract_sla_{$part}_id"])] ?? null; ?>
|
||||
<?php $sla_rows = contractSlaRows($contract);
|
||||
foreach (CONTRACT_SLA_PRIORITIES as $priority => $part) {
|
||||
$sla = $slas[intval($contract["contract_sla_{$part}_id"])] ?? null;
|
||||
$sla_default = $sla ? null : $sla_rows[$priority]['sla']; ?>
|
||||
<tr>
|
||||
<td class="ps-0" style="width: 7rem;"><?= ticketPriorityHtml($priority, 'detail') ?></td>
|
||||
<td>
|
||||
<?php if ($sla) { ?>
|
||||
<span class="fw-semibold"><?= escapeHtml($sla['sla_name']) ?></span><?= $sla['sla_archived_at'] ? ' <span class="badge text-bg-secondary">Archived</span>' : '' ?>
|
||||
<div class="small text-body-secondary"><?= escapeHtml(contractSlaTargets($sla)) ?></div>
|
||||
<?php } elseif ($sla_default) { ?>
|
||||
<span class="fw-semibold"><?= escapeHtml($sla_default['sla_name']) ?></span><?= $usual_from($sla_rows[$priority]['from']) ?>
|
||||
<div class="small text-body-secondary"><?= escapeHtml(contractSlaTargets($sla_default)) ?></div>
|
||||
<?php } else { ?>
|
||||
<span class="text-body-secondary">The client's usual SLA</span>
|
||||
<span class="text-body-secondary">No SLA</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -397,7 +426,7 @@ if (!$contract || ($client_url && intval($contract['contract_client_id']) !== in
|
||||
</div>
|
||||
<div class="card-footer small text-body-secondary" data-contract-in-force>
|
||||
<i class="fas fa-fw <?= $in_force ? 'fa-check-circle text-success' : 'fa-info-circle' ?> me-1"></i>
|
||||
<?= $in_force ? 'In force now:' : 'While it is in force,' ?> new tickets get these SLAs, time bills at these rates - the after-hours one for tickets opened outside business hours - and new invoices are due on these terms. What it leaves empty, the client's own applies.
|
||||
<?= $in_force ? 'In force now:' : 'While it is in force,' ?> new tickets get these SLAs, time bills at these rates - the after-hours one for tickets opened outside business hours - and new invoices are due on these terms. What it leaves empty comes from another contract in force for the client, else the default.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user