Files
itflow/admin/getting_started/client.php
johnnyq 1e5bffff3c 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
2026-10-27 22:22:34 -04:00

80 lines
4.9 KiB
PHP

<?php
/*
* Get started - Your first client: a name, a website and who to talk to,
* made by the same New Client handler as the Clients page (add_client,
* agent/post/client.php - every field it reads is sent, the ones not asked
* here empty); or the demo data, to look around first (load_demo_data, as on
* Maintenance > Starter Content). Both come back here.
*/
defined('FROM_GETTING_STARTED') || die("Direct file access is not allowed");
defined('FROM_STARTER_CONTENT') || define('FROM_STARTER_CONTENT', true);
require_once dirname(__DIR__) . '/post/demo_data_model.php';
$gs_demo = demoDataStatus($mysqli);
$gs_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_created_at DESC, client_id DESC LIMIT 3");
$gs_client_count = intval(mysqli_fetch_row(mysqli_query($mysqli, "SELECT COUNT(*) FROM clients WHERE client_archived_at IS NULL"))[0] ?? 0);
?>
<?php if ($gs_client_count) { ?>
<ul class="list-unstyled itflow-gs-people mb-4" data-gs-clients="<?= $gs_client_count ?>">
<?php while ($gs_client = mysqli_fetch_assoc($gs_clients)) { ?>
<li>
<span class="itflow-gs-person-initials" aria-hidden="true"><?= escapeHtml(personInitials($gs_client['client_name'])) ?></span>
<a href="/agent/client_overview.php?client_id=<?= intval($gs_client['client_id']) ?>" class="fw-semibold text-truncate min-w-0 flex-grow-1"><?= escapeHtml($gs_client['client_name']) ?></a>
</li>
<?php } ?>
<?php if ($gs_client_count > 3) { ?>
<li class="small text-body-secondary ps-1"><a href="/agent/clients.php">and <?= $gs_client_count - 3 ?> more</a></li>
<?php } ?>
</ul>
<h2 class="h6 fw-semibold">Add another</h2>
<?php } ?>
<form action="/agent/post.php" method="post" autocomplete="off" data-gs-form data-gs-client-form>
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<?php foreach (['type', 'address', 'city', 'state', 'zip', 'country', 'location_phone_country_code', 'location_phone', 'location_extension', 'location_fax_country_code', 'location_fax',
'title', 'contact_phone_country_code', 'contact_phone', 'contact_extension', 'contact_mobile_country_code', 'contact_mobile'] as $gs_field) { ?>
<input type="hidden" name="<?= $gs_field ?>" value="">
<?php } ?>
<div class="row g-3 mb-3">
<div class="col-sm-7">
<?= settingsInput('name', '', ['id' => 'gsClientName', 'icon' => 'fa-user-friends', 'label' => 'Client', 'required' => true, 'maxlength' => 200, 'placeholder' => 'Name or company']) ?>
</div>
<div class="col-sm-5">
<?= settingsInput('website', '', ['id' => 'gsClientWebsite', 'icon' => 'fa-globe', 'label' => 'Website', 'maxlength' => 200, 'placeholder' => 'example.com']) ?>
</div>
<div class="col-sm-6">
<?= settingsInput('contact', '', ['id' => 'gsClientContact', 'icon' => 'fa-user', 'label' => 'Who you talk to', 'maxlength' => 200, 'placeholder' => 'Their name']) ?>
</div>
<div class="col-sm-6">
<?= settingsInput('contact_email', '', ['id' => 'gsClientEmail', 'icon' => 'fa-envelope', 'type' => 'email', 'label' => 'Their email', 'maxlength' => 200, 'placeholder' => 'name@example.com']) ?>
</div>
</div>
<p class="small text-body-secondary">The website's domain and certificate are added too, to watch for renewals. The rest - their address, phones, rate and SLAs - is on the client once it's made, and Clients &gt; Import brings in a whole list from a CSV.</p>
<button type="submit" name="add_client" value="1" class="btn btn-primary" data-gs-save><i class="fas fa-plus me-2"></i>Add the client</button>
</form>
<div class="itflow-gs-or" aria-hidden="true"><span>or</span></div>
<div class="d-flex flex-wrap align-items-center gap-3" data-gs-demo="<?= count($gs_demo['loaded']) ?>">
<div class="min-w-0 flex-grow-1">
<div class="fw-semibold">Look around with the demo data first</div>
<div class="small text-body-secondary">
<?php if ($gs_demo['loaded']) { ?>
<?= count($gs_demo['loaded']) ?> of the <?= $gs_demo['total'] ?> demo clients are here. Remove them all in Maintenance &gt; Starter Content once you're done.
<?php } else { ?>
<?= $gs_demo['total'] ?> made-up clients, each with contacts, assets, tickets and a year of invoices. They come out again in one go from Maintenance &gt; Starter Content.
<?php } ?>
</div>
</div>
<?php if (count($gs_demo['loaded']) < $gs_demo['total']) { ?>
<form action="post.php" method="post" class="m-0">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button type="submit" name="load_demo_data" value="1" class="btn btn-outline-secondary" data-gs-demo-load><i class="fas fa-flask me-2"></i>Load the demo data</button>
</form>
<?php } ?>
</div>