Files
itflow/agent/modals/ticket/ticket_edit.php
johnnyq 9e55d52432 Vendors overhaul for daily MSP work: new vendor page built around calling them, with Call support (dials the extension), Email, Support site, a star, and a New menu (case/ticket with them, license from them, asset bought from them, portal login, document, upload, or expense and recurring expense for our own vendors), Support card (phone, hours, SLA, email, site), Account card (account number, PIN shown as dots with show and copy, account manager name, direct line and email), blur-to-save notes, an Also used by list of the agent's other clients with the same vendor by name or template, and tabs for Cases (open first, with their ticket number), Licenses (soonest renewal first), Assets (serial, order ref, warranty status), Domains (registrar, web host, DNS, mail), Services, Logins, Documents, Files and History; our own vendors show spend this year, last year and in all, recent expenses and recurring expenses; new vendor quick look; rebuilt vendors list with starred first, initials tiles coloured by vendor name, support line, hours and SLA, account number and PIN, chips for open cases, licenses, assets, domains, services and logins, an Ours / Clients' switch with an all-clients view in All Client Docs, wider search (account manager, emails, tags), tag filter and bulk Tags/Archive/Restore/Delete; shared tabbed vendor form (Details, Support, Account, Notes) with star, tags and start from template that fills every field; Add a Template to Clients to roll one vendor out to many clients, skipping those that have it; Admin Vendor Templates shows how many clients use each template and has Add to clients; vendor tags (tag type 6) with vendor tags in the Tags starter pack; ticket vendor block shows their ticket number, support line, hours, SLA, account number and PIN with copy buttons; vendor tiles in every vendor picker and quick look links across tickets, licenses, assets, domains, services and expense lists; search opens the vendor page; CSV export adds tags, open cases, account manager and PIN; logins, documents and files can be linked to a vendor and ones made from a vendor's page are linked to it; demo vendors get tags, a star and an account manager; database update 2.8.2 adds vendor_tags and vendor_contact_phone_country_code, vendor_contact_phone, vendor_contact_email (db.sql updated) and clears references to vendors that no longer exist; ticket vendor now validated against the ticket's client and removing a vendor from a ticket is logged as such; deleting a vendor from a template no longer unlinks every other vendor from that template; deleting a vendor clears it from assets, licenses, tickets and domains, and vendors with expenses, recurring expenses or payment provider fees are archived rather than deleted; vendor websites without a scheme open correctly; domain form vendor names escaped; new vendor templates keep the phone country code and pushing a template update with nothing ticked no longer errors; vendors API accepts the account manager fields; new functions/vendor.php (VENDOR_USES, vendorCan/vendorEnforce with client vendors on module_client and ours on module_financial, tile, link, website, PIN, form, tags, counts, delete check, reference clearing, also used by, spend, history), credential|vendor and file|vendor link relations, tag module_ours for our own vendors, handlers add_vendor_template_to_clients, favorite_vendor, unfavorite_vendor and ajax vendor_set_notes, vendor_model.php removed
2026-10-14 22:49:05 -04:00

376 lines
21 KiB
PHP

<?php
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support', 2);
// Tickets with no client stay visible to restricted agents - clientScopeSql() includes 0
$access_permission_query_overide = clientScopeSql('ticket_client_id');
$ticket_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT client_id, client_name, ticket_asset_id, ticket_assigned_to, ticket_billable,
ticket_category, ticket_contact_id, ticket_created_at, ticket_details, ticket_due_at,
ticket_location_id, ticket_number, ticket_prefix, ticket_priority, ticket_project_id,
ticket_subject, ticket_vendor_id, ticket_vendor_ticket_number FROM tickets LEFT JOIN clients ON client_id = ticket_client_id WHERE ticket_id = $ticket_id $access_permission_query_overide LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$client_id = intval($row['client_id']);
$client_name = escapeHtml($row['client_name']);
$ticket_prefix = escapeHtml($row['ticket_prefix']);
$ticket_number = intval($row['ticket_number']);
$ticket_category = intval($row['ticket_category']);
$ticket_subject = escapeHtml($row['ticket_subject']);
$ticket_details = escapeHtml($row['ticket_details']);
$ticket_priority = escapeHtml($row['ticket_priority']);
$ticket_billable = intval($row['ticket_billable']);
$ticket_vendor_ticket_number = escapeHtml($row['ticket_vendor_ticket_number']);
$ticket_created_at = escapeHtml($row['ticket_created_at']);
$ticket_due_at = escapeHtml($row['ticket_due_at']);
$ticket_assigned_to = intval($row['ticket_assigned_to']);
$contact_id = intval($row['ticket_contact_id']);
$asset_id = intval($row['ticket_asset_id']);
$location_id = intval($row['ticket_location_id']);
$vendor_id = intval($row['ticket_vendor_id']);
$project_id = intval($row['ticket_project_id']);
if ($client_id) {
enforceClientAccess();
}
// Additional Assets Selected
$additional_assets_array = array();
$sql_additional_assets = mysqli_query($mysqli, "SELECT asset_id FROM ticket_assets WHERE ticket_id = $ticket_id");
while ($row = mysqli_fetch_assoc($sql_additional_assets)) {
$additional_asset_id = intval($row['asset_id']);
$additional_assets_array[] = $additional_asset_id;
}
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring me-2"></i>Edit ticket <small class="ms-1 opacity-75"><?= "$ticket_prefix$ticket_number" ?></small></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="ticket_id" value="<?= $ticket_id ?>">
<div class="modal-body">
<?php if ($client_id) { ?>
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details"><i class="fa fa-fw fa-life-ring me-2"></i>Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-contacts"><i class="fa fa-fw fa-user me-2"></i>Contact</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-assignment"><i class="fa fa-fw fa-link me-2"></i>Related</a>
</li>
</ul>
<hr>
<?php } ?>
<div class="tab-content" <?php if (lookupUserPermission('module_support') <= 1) { echo 'inert'; } ?>>
<div class="tab-pane fade show active" id="pills-details">
<div class="mb-3">
<label>Subject <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<input type="text" class="form-control" name="subject" maxlength="500" value="<?= $ticket_subject ?>" placeholder="Subject" required>
</div>
</div>
<div class="mb-3">
<textarea class="form-control tinymceTicket" rows="8" name="details"><?= $ticket_details ?></textarea>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label>Priority <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
<select class="form-select select2" name="priority" required>
<option <?php if ($ticket_priority == 'Low') { echo "selected"; } ?> data-color="var(--bs-info)">Low</option>
<option <?php if ($ticket_priority == 'Medium') { echo "selected"; } ?> data-color="var(--bs-warning)">Medium</option>
<option <?php if ($ticket_priority == 'High') { echo "selected"; } ?> data-color="var(--bs-danger)">High</option>
<option <?php if ($ticket_priority == 'Urgent') { echo "selected"; } ?> data-color="var(--bs-emphasis-color)">Urgent</option>
</select>
</div>
</div>
</div>
<div class="col">
<div class="mb-3">
<label>Category</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-layer-group"></i></span>
<select class="form-select select2" name="category_id">
<option value="0">- Uncategorized -</option>
<?php
$sql_categories = mysqli_query($mysqli, "SELECT category_id, category_name, category_color, category_icon FROM categories WHERE category_type = 'Ticket' AND category_archived_at IS NULL ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_categories)) {
$category_id = intval($row['category_id']);
$category_name = escapeHtml($row['category_name']);
?>
<option <?php if ($ticket_category == $category_id) {echo "selected";} ?> value="<?= $category_id ?>"<?= selectOptionColor($row['category_color'] ?? '', $row['category_icon'] ?? '') ?>><?= $category_name ?></option>
<?php } ?>
</select>
<button class="btn btn-secondary ajax-modal" title="New category" type="button"
data-modal-url="../admin/modals/category/category_add.php?category=Ticket">
<i class="fas fa-fw fa-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label>Assigned to</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-user-check"></i></span>
<select class="form-select select2" name="assigned_to">
<option value="0">Unassigned</option>
<?php
$sql = mysqli_query(
$mysqli,
"SELECT user_id, user_name, user_avatar FROM users
WHERE user_role_id > 1
AND user_type = 1
AND user_status = 1
AND user_archived_at IS NULL
ORDER BY user_name ASC"
);
while ($row = mysqli_fetch_assoc($sql)) {
$user_id = intval($row['user_id']);
$user_name = escapeHtml($row['user_name']); ?>
<option <?php if ($ticket_assigned_to === $user_id) { echo "selected"; } ?> value="<?= $user_id ?>"<?= selectOptionUser($row['user_id'], $row['user_name'], $row['user_avatar'] ?? '') ?>><?= $user_name ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="col">
<div class="mb-3">
<label>Due date</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-calendar-check"></i></span>
<input type="datetime-local" class="form-control" name="due" value="<?= $ticket_due_at ?>">
</div>
</div>
</div>
</div>
<?php if ($config_module_enable_accounting && lookupUserPermission("module_sales") >= 2) { ?>
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" name="billable" <?php if ($ticket_billable == 1) { echo "checked"; } ?> value="1" id="billableSwitch<?= $ticket_id ?>">
<label class="form-check-label" for="billableSwitch<?= $ticket_id ?>">Billable</label>
</div>
</div>
<?php } else { ?>
<!-- No switch for this user, so carry the current value through untouched -->
<input type="hidden" name="billable" value="<?= $ticket_billable ?>">
<?php } ?>
</div>
<?php if ($client_id) { ?>
<div class="tab-pane fade" id="pills-contacts">
<div class="mb-3">
<label>Contact</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
<select class="form-select select2" name="contact_id">
<option value="0">No contact</option>
<?php
$sql_client_contacts_select = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_client_id, contact_photo, contact_title, contact_primary, contact_technical FROM contacts WHERE contact_client_id = $client_id AND contact_archived_at IS NULL ORDER BY contact_primary DESC, contact_technical DESC, contact_name ASC");
while ($row = mysqli_fetch_assoc($sql_client_contacts_select)) {
$contact_id_select = intval($row['contact_id']);
$contact_name_select = escapeHtml($row['contact_name']);
?>
<option value="<?= $contact_id_select ?>" <?php if ($contact_id_select == $contact_id) { echo "selected"; } ?><?= selectOptionContact($row['contact_client_id'] ?? 0, $row['contact_name'], $row['contact_photo'] ?? '', contactOptionDescription($row), $row['contact_id'] ?? 0) ?>><?= $contact_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
<?php if (!empty($config_smtp_provider)) { ?>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="contact_notify" value="1" id="checkNotifyContact">
<label class="form-check-label" for="checkNotifyContact">
Email the contact a link to this ticket
</label>
</div>
</div>
<?php } ?>
</div>
<div class="tab-pane fade" id="pills-assignment">
<div class="mb-3">
<label>Primary asset</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
<select class="form-select select2" name="asset_id">
<option value="0">- None -</option>
<?php
$sql_assets = mysqli_query($mysqli, "SELECT asset_id, asset_name, asset_type, asset_make, asset_model, contact_name FROM assets LEFT JOIN contacts ON contact_id = asset_contact_id WHERE asset_client_id = $client_id AND asset_archived_at IS NULL ORDER BY asset_name ASC");
while ($row = mysqli_fetch_assoc($sql_assets)) {
$asset_id_select = intval($row['asset_id']);
$asset_name_select = escapeHtml($row['asset_name']);
$asset_contact_name_select = escapeHtml($row['contact_name']);
?>
<option <?php if ($asset_id == $asset_id_select) { echo "selected"; } ?> value="<?= $asset_id_select ?>"<?= selectOptionAsset($row['asset_type'] ?? '', trim(($row['asset_make'] ?? '') . ' ' . ($row['asset_model'] ?? ''))) ?>><?= $asset_contact_name_select ? "$asset_name_select ($asset_contact_name_select)" : $asset_name_select ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="mb-3">
<label>Other assets</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
<select class="form-select select2" name="additional_assets[]" data-placeholder="- Any other assets involved -" multiple>
<option value=""></option>
<?php
$sql_assets = mysqli_query($mysqli, "SELECT asset_id, asset_name, asset_type, asset_make, asset_model, contact_name FROM assets LEFT JOIN contacts ON contact_id = asset_contact_id WHERE asset_client_id = $client_id AND asset_id != $asset_id AND asset_archived_at IS NULL ORDER BY asset_name ASC");
while ($row = mysqli_fetch_assoc($sql_assets)) {
$asset_id_select = intval($row['asset_id']);
$asset_name_select = escapeHtml($row['asset_name']);
$asset_contact_name_select = escapeHtml($row['contact_name']);
?>
<option value="<?= $asset_id_select ?>"
<?php if (in_array($asset_id_select, $additional_assets_array)) { echo "selected"; } ?>
<?= selectOptionAsset($row['asset_type'] ?? '', trim(($row['asset_make'] ?? '') . ' ' . ($row['asset_model'] ?? ''))) ?>><?= $asset_contact_name_select ? "$asset_name_select ($asset_contact_name_select)" : $asset_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="mb-3">
<label>Location</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
<select class="form-select select2" name="location_id">
<option value="0">- None -</option>
<?php
$sql_locations = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC");
while ($row = mysqli_fetch_assoc($sql_locations)) {
$location_id_select = intval($row['location_id']);
$location_name_select = escapeHtml($row['location_name']);
?>
<option <?php if ($location_id == $location_id_select) { echo "selected"; } ?> value="<?= $location_id_select ?>"><?= $location_name_select ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label>Vendor</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
<select class="form-select select2" name="vendor_id">
<option value="0">- None -</option>
<?php
$sql_vendors = mysqli_query($mysqli, "SELECT vendor_id, vendor_name, vendor_description, vendor_archived_at FROM vendors
WHERE vendor_client_id = $client_id AND (vendor_archived_at IS NULL OR vendor_id = $vendor_id)
ORDER BY vendor_favorite DESC, vendor_name ASC");
while ($row = mysqli_fetch_assoc($sql_vendors)) {
$vendor_id_select = intval($row['vendor_id']);
$vendor_name_select = escapeHtml($row['vendor_name']) . ($row['vendor_archived_at'] ? ' (archived)' : '');
?>
<option <?php if ($vendor_id == $vendor_id_select) { echo "selected"; } ?> value="<?= $vendor_id_select ?>"<?= selectOptionVendor($row['vendor_name'], $row['vendor_description']) ?>><?= $vendor_name_select ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="col">
<div class="mb-3">
<label>Vendor's ticket number</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<input type="text" class="form-control" name="vendor_ticket_number" placeholder="Their reference for this issue" maxlength="255" value="<?= $ticket_vendor_ticket_number ?>">
</div>
</div>
</div>
</div>
<div class="mb-3">
<label>Project</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
<select class="form-select select2" name="project_id">
<option value="0">- None -</option>
<?php
$sql_projects = mysqli_query($mysqli, "SELECT project_id, project_name FROM projects WHERE (project_client_id = $client_id OR project_client_id = 0) AND project_completed_at IS NULL AND project_archived_at IS NULL ORDER BY project_name ASC");
while ($row = mysqli_fetch_assoc($sql_projects)) {
$project_id_select = intval($row['project_id']);
$project_name_select = escapeHtml($row['project_name']); ?>
<option <?php if ($project_id == $project_id_select) { echo "selected"; } ?> value="<?= $project_id_select ?>"><?= $project_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<?php } // End client_id check ?>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save changes</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';