From 9e55d52432be5dee62e4bc097cd60a3bdcea993a Mon Sep 17 00:00:00 2001 From: johnnyq Date: Wed, 14 Oct 2026 22:49:05 -0400 Subject: [PATCH] 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 --- admin/database_updates/2.8.2.php | 36 + admin/modals/tag/tag_add.php | 2 + admin/modals/tag/tag_edit.php | 2 + admin/post/demo_data_model.php | 61 +- admin/post/starter_content_model.php | 16 +- admin/post/vendor_template.php | 7 +- admin/tags.php | 1 + admin/vendor_templates.php | 22 +- agent/ajax.php | 30 +- agent/asset.php | 2 +- agent/calendar.php | 2 +- agent/domains.php | 8 +- agent/expenses.php | 2 +- agent/includes/client_overview_side_nav.php | 16 + agent/includes/client_side_nav.php | 4 +- agent/includes/side_nav.php | 2 +- agent/js/tickets_add_modal.js | 10 +- agent/license.php | 10 +- agent/modals/asset/asset_add.php | 4 +- agent/modals/asset/asset_form.php | 6 +- agent/modals/credential/credential_add.php | 6 +- agent/modals/document/document_add.php | 6 +- agent/modals/domain/domain_add.php | 16 +- agent/modals/domain/domain_edit.php | 24 +- agent/modals/expense/expense_add.php | 6 +- agent/modals/expense/expense_copy.php | 4 +- agent/modals/expense/expense_edit.php | 4 +- agent/modals/file/file_upload.php | 8 +- agent/modals/link/link.php | 2 +- .../recurring_expense_add.php | 6 +- .../recurring_expense_edit.php | 4 +- agent/modals/service/service.php | 2 +- agent/modals/service/service_add.php | 4 +- agent/modals/service/service_edit.php | 6 +- agent/modals/software/software.php | 2 +- agent/modals/software/software_add.php | 3 +- agent/modals/software/software_form.php | 4 +- agent/modals/ticket/ticket_add.php | 3 +- agent/modals/ticket/ticket_edit.php | 8 +- agent/modals/ticket/ticket_edit_vendor.php | 7 +- agent/modals/vendor/vendor.php | 356 ++++++-- agent/modals/vendor/vendor_add.php | 163 +--- .../vendor/vendor_add_from_template.php | 52 +- agent/modals/vendor/vendor_edit.php | 213 +---- agent/modals/vendor/vendor_export.php | 59 +- agent/modals/vendor/vendor_form.php | 318 +++++++ .../modals/vendor/vendor_template_clients.php | 114 +++ agent/post/credential.php | 6 + agent/post/document.php | 10 +- agent/post/file.php | 6 +- agent/post/ticket.php | 32 +- agent/post/vendor.php | 505 ++++++----- agent/post/vendor_model.php | 16 - agent/recurring_expenses.php | 2 +- agent/software.php | 2 +- agent/ticket.php | 32 +- agent/vendor.php | 822 ++++++++++++++++++ agent/vendors.php | 495 +++++++---- api/v1/vendors/create.php | 2 +- api/v1/vendors/update.php | 2 +- api/v1/vendors/vendor_model.php | 24 + css/itflow_custom.css | 52 ++ db.sql | 20 + functions.php | 1 + functions/export.php | 14 +- functions/links.php | 24 +- functions/search.php | 13 +- functions/software.php | 2 +- functions/tags.php | 29 +- functions/vendor.php | 443 ++++++++++ js/app.js | 17 + 71 files changed, 3201 insertions(+), 1013 deletions(-) create mode 100644 admin/database_updates/2.8.2.php create mode 100644 agent/modals/vendor/vendor_form.php create mode 100644 agent/modals/vendor/vendor_template_clients.php delete mode 100644 agent/post/vendor_model.php create mode 100644 agent/vendor.php create mode 100644 functions/vendor.php diff --git a/admin/database_updates/2.8.2.php b/admin/database_updates/2.8.2.php new file mode 100644 index 000000000..7f4c02465 --- /dev/null +++ b/admin/database_updates/2.8.2.php @@ -0,0 +1,36 @@ + 0 AND `vendor_id` IS NULL"); + mysqli_query($mysqli, "UPDATE `software` LEFT JOIN `vendors` ON `vendor_id` = `software_vendor_id` SET `software_vendor_id` = 0 WHERE `software_vendor_id` > 0 AND `vendor_id` IS NULL"); + mysqli_query($mysqli, "UPDATE `tickets` LEFT JOIN `vendors` ON `vendor_id` = `ticket_vendor_id` SET `ticket_vendor_id` = 0 WHERE `ticket_vendor_id` > 0 AND `vendor_id` IS NULL"); + foreach (['domain_registrar', 'domain_webhost', 'domain_dnshost', 'domain_mailhost'] as $domain_vendor_column) { + mysqli_query($mysqli, "UPDATE `domains` LEFT JOIN `vendors` ON `vendor_id` = `$domain_vendor_column` SET `$domain_vendor_column` = 0 WHERE `$domain_vendor_column` > 0 AND `vendor_id` IS NULL"); + } diff --git a/admin/modals/tag/tag_add.php b/admin/modals/tag/tag_add.php index 4d8913bdb..473f61791 100644 --- a/admin/modals/tag/tag_add.php +++ b/admin/modals/tag/tag_add.php @@ -17,6 +17,8 @@ if (isset($_GET['type'])) { $type_display = "Credential"; } elseif ($type === 5) { $type_display = "Asset"; + } elseif ($type === 6) { + $type_display = "Vendor"; } } ob_start(); diff --git a/admin/modals/tag/tag_edit.php b/admin/modals/tag/tag_edit.php index 22c74ffac..f7cfc8a5d 100644 --- a/admin/modals/tag/tag_edit.php +++ b/admin/modals/tag/tag_edit.php @@ -22,6 +22,8 @@ if ($tag_type == 1) { $tag_type_display = "Credential"; } elseif ( $tag_type == 5) { $tag_type_display = "Asset"; +} elseif ($tag_type == 6) { + $tag_type_display = "Vendor"; } else { $tag_type_display = "Unknown"; } diff --git a/admin/post/demo_data_model.php b/admin/post/demo_data_model.php index d1361fbe5..dd116be30 100644 --- a/admin/post/demo_data_model.php +++ b/admin/post/demo_data_model.php @@ -410,6 +410,57 @@ function demoAttachTags($mysqli, $join_table, $join_column, $tag_column, $row_id } } +// ------------------------------ +// demoVendorTags / demoVendorManager +// What kind of vendor each demo vendor is, as Tags starter pack vendor tags, +// and the account manager who looks after the account - a name, a direct +// line and an email at the vendor's domain, the same every time it is seeded. +// ------------------------------ +function demoVendorTags($name, $description, $notes = '', $ours = false) { + if ($ours) { + $map = [ + 'Distribution' => ['Distributor', 'Hardware'], 'Netgate' => ['Hardware', 'Security'], 'Ubiquiti' => ['Hardware'], + 'Synology' => ['Hardware'], 'Proxmox' => ['Software'], 'Nextcloud' => ['Cloud', 'Hosting'], 'Licensing' => ['Cloud', 'Distributor'], + 'Colocation' => ['Hosting'], 'Bank' => ['Professional Services'], 'CPA' => ['Professional Services'], + 'Insurance' => ['Professional Services'], 'Property' => ['Professional Services'], + ]; + foreach ($map as $match => $tags) { + if (stripos($name, $match) !== false) { + return $tags; + } + } + return []; + } + $text = "$name $description"; + $tags = []; + if (preg_match('/internet|fibre|fiber|circuit|broadband|connect|wireless|cable/i', $text)) { + $tags[] = 'ISP'; + } + if (preg_match('/phone|telecom|voice/i', $text)) { + $tags[] = 'Telecom'; + } + if (!$tags) { + $tags[] = 'Line of Business'; + } + if (stripos($notes, 'authorised list') !== false) { + $tags[] = 'Authorised Contacts Only'; + } + return $tags; +} + +function demoVendorManager($name, $phone, $domain) { + $people = ['Dana Whitlock', 'Marcus Feld', 'Priya Raman', 'Owen Castell', 'Jolene Avery', 'Theo Marsh', 'Rosa Delgado', 'Ian Kowalski', 'Hannah Pryce', 'Victor Lund']; + $person = $people[crc32($name) % count($people)]; + $digits = preg_replace('/[^0-9]/', '', (string) $phone); + $direct = $digits !== '' ? substr($digits, 0, -2) . str_pad((string) ((intval(substr($digits, -2)) + 37) % 100), 2, '0', STR_PAD_LEFT) : ''; + return [ + 'vendor_contact_name' => $person, + 'vendor_contact_phone_country_code' => '1', + 'vendor_contact_phone' => $direct, + 'vendor_contact_email' => strtolower(str_replace(' ', '.', $person)) . '@' . $domain, + ]; +} + // ------------------------------ // demoLink // The relationship joins are all two integer columns. Linking assets to the @@ -475,10 +526,9 @@ function demoEnsureOrgVendors($mysqli) { $vendors[$vendor[0]] = intval($row['vendor_id']); continue; } - $vendors[$vendor[0]] = starterInsert($mysqli, 'vendors', [ + $vendors[$vendor[0]] = starterInsert($mysqli, 'vendors', demoVendorManager($vendor[0], $vendor[2], $vendor[3]) + [ 'vendor_name' => $vendor[0], 'vendor_description' => $vendor[1], - 'vendor_contact_name' => 'Account Manager', 'vendor_phone_country_code' => '1', 'vendor_phone' => $vendor[2], 'vendor_extension' => (string)(200 + (crc32($vendor[0]) % 90)), @@ -492,6 +542,7 @@ function demoEnsureOrgVendors($mysqli) { 'vendor_client_id' => 0, 'vendor_created_at' => demoMonthDateTime(24, 2, 9, 0), ]); + demoAttachTags($mysqli, 'vendor_tags', 'vendor_id', 'tag_id', $vendors[$vendor[0]], 6, demoVendorTags($vendor[0], $vendor[1], '', true)); } return $vendors; @@ -1170,10 +1221,9 @@ function demoBuildClient($mysqli, $profile, $index, $context, &$counts) { // The client's own vendors - who they buy their line of business kit from $vendor_ids = []; foreach ($profile['vendors'] as $vendor_index => $vendor) { - $vendor_ids[] = starterInsert($mysqli, 'vendors', [ + $vendor_id = starterInsert($mysqli, 'vendors', demoVendorManager($vendor[0], $vendor[2], $vendor[3]) + [ 'vendor_name' => $vendor[0], 'vendor_description' => $vendor[1], - 'vendor_contact_name' => 'Business Support', 'vendor_phone_country_code' => '1', 'vendor_phone' => $vendor[2], 'vendor_email' => 'support@' . $vendor[3], @@ -1183,9 +1233,12 @@ function demoBuildClient($mysqli, $profile, $index, $context, &$counts) { 'vendor_code' => demoSerial($profile['abbreviation'] . $vendor[0], 6), 'vendor_account_number' => demoSerial($profile['abbreviation'] . 'acct' . $vendor_index, 8), 'vendor_notes' => $vendor[5], + 'vendor_favorite' => $vendor_index === 0 ? 1 : 0, 'vendor_client_id' => $client_id, 'vendor_created_at' => demoMonthDateTime($months, 5, 13, 0), ]); + demoAttachTags($mysqli, 'vendor_tags', 'vendor_id', 'tag_id', $vendor_id, 6, demoVendorTags($vendor[0], $vendor[1], $vendor[5])); + $vendor_ids[] = $vendor_id; $counts['documentation']++; } $primary_vendor_id = $vendor_ids[0] ?? 0; diff --git a/admin/post/starter_content_model.php b/admin/post/starter_content_model.php index 96a406f7d..e578e67bb 100644 --- a/admin/post/starter_content_model.php +++ b/admin/post/starter_content_model.php @@ -30,7 +30,7 @@ function starterContentPacks() { 'tags' => [ 'label' => 'Tags', 'icon' => 'fa-tags', - 'description' => 'Client, location, contact, credential and asset tags.', + 'description' => 'Client, location, contact, credential, asset and vendor tags.', ], 'ticket_templates' => [ 'label' => 'Ticket Templates', @@ -661,6 +661,20 @@ function starterContentTags() { [5, 'Leased', '#6c757d', 'file-contract'], [5, 'Spare Stock', '#795548', 'boxes'], [5, 'Patch Excluded', '#ffc107', 'ban'], + + // Vendor + [6, 'ISP', '#007bff', 'wifi'], + [6, 'Telecom', '#28a745', 'phone-alt'], + [6, 'Line of Business', '#6f42c1', 'cubes'], + [6, 'Software', '#20c997', 'window-restore'], + [6, 'Hardware', '#001f3f', 'server'], + [6, 'Cloud', '#0078d4', 'cloud'], + [6, 'Security', '#dc3545', 'shield-alt'], + [6, 'Distributor', '#795548', 'truck'], + [6, 'Printing', '#6c757d', 'print'], + [6, 'Hosting', '#17a2b8', 'globe'], + [6, 'Professional Services', '#343a40', 'briefcase'], + [6, 'Authorised Contacts Only', '#d81b60', 'user-lock'], ]; return $tags; diff --git a/admin/post/vendor_template.php b/admin/post/vendor_template.php index fb4777d7d..edbca17b9 100644 --- a/admin/post/vendor_template.php +++ b/admin/post/vendor_template.php @@ -25,7 +25,7 @@ if (isset($_POST['add_vendor_template'])) { $code = escapeSql($_POST['code']); $notes = escapeSql($_POST['notes']); - mysqli_query($mysqli,"INSERT INTO vendor_templates SET vendor_template_name = '$name', vendor_template_description = '$description', vendor_template_contact_name = '$contact_name', vendor_template_phone = '$phone', vendor_template_extension = '$extension', vendor_template_email = '$email', vendor_template_website = '$website', vendor_template_hours = '$hours', vendor_template_sla = '$sla', vendor_template_code = '$code', vendor_template_account_number = '$account_number', vendor_template_notes = '$notes'"); + mysqli_query($mysqli,"INSERT INTO vendor_templates SET vendor_template_name = '$name', vendor_template_description = '$description', vendor_template_contact_name = '$contact_name', vendor_template_phone_country_code = '$phone_country_code', vendor_template_phone = '$phone', vendor_template_extension = '$extension', vendor_template_email = '$email', vendor_template_website = '$website', vendor_template_hours = '$hours', vendor_template_sla = '$sla', vendor_template_code = '$code', vendor_template_account_number = '$account_number', vendor_template_notes = '$notes'"); $vendor_template_id = mysqli_insert_id($mysqli); @@ -132,7 +132,10 @@ if (isset($_POST['edit_vendor_template'])) { // Remove the first comma to prevent MySQL error $sql = preg_replace('/,/', '', $sql, 1); - mysqli_query($mysqli,"UPDATE vendors SET $sql WHERE vendor_template_id = $vendor_template_id"); + // Nothing ticked to push - nothing to update, rather than an empty SET + if (trim($sql) !== '') { + mysqli_query($mysqli,"UPDATE vendors SET $sql WHERE vendor_template_id = $vendor_template_id"); + } } logAudit("Vendor Template", "Edit", "$session_name edited vendor template $name", 0, $vendor_template_id); diff --git a/admin/tags.php b/admin/tags.php index e3a8ce76c..8d2320d86 100644 --- a/admin/tags.php +++ b/admin/tags.php @@ -19,6 +19,7 @@ $tag_types = [ 3 => ['label' => 'Contact', 'icon' => 'fa-address-book'], 4 => ['label' => 'Credential', 'icon' => 'fa-key'], 5 => ['label' => 'Asset', 'icon' => 'fa-desktop'], + 6 => ['label' => 'Vendor', 'icon' => 'fa-building'], ]; // Label for the selected type diff --git a/admin/vendor_templates.php b/admin/vendor_templates.php index 0b44072d6..46a905c63 100644 --- a/admin/vendor_templates.php +++ b/admin/vendor_templates.php @@ -17,6 +17,13 @@ $sql = mysqli_query( $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); +// How many clients have a vendor made from each template +$template_use = []; +$sql_template_use = mysqli_query($mysqli, "SELECT vendor_template_id, COUNT(DISTINCT vendor_client_id) FROM vendors WHERE vendor_template_id > 0 AND vendor_client_id > 0 AND vendor_archived_at IS NULL GROUP BY vendor_template_id"); +while ($use_row = mysqli_fetch_row($sql_template_use)) { + $template_use[intval($use_row[0])] = intval($use_row[1]); +} + ?>
@@ -59,6 +66,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Contact + Used by Action @@ -95,7 +103,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + + + + + client + + - + + diff --git a/agent/modals/credential/credential_add.php b/agent/modals/credential/credential_add.php index b2d0fcf92..8ff05da0e 100644 --- a/agent/modals/credential/credential_add.php +++ b/agent/modals/credential/credential_add.php @@ -5,16 +5,20 @@ require_once '../../../includes/modal_header.php'; $client_id = intval($_GET['client_id'] ?? 0); $contact_id = intval($_GET['contact_id'] ?? 0); $asset_id = intval($_GET['asset_id'] ?? 0); +// Opened from a vendor's page - their portal login, linked to them +$vendor_id = $client_id ? assetClientOwnedId('vendor', $_GET['vendor_id'] ?? 0, $client_id) : 0; +$vendor_name = $vendor_id ? getFieldById('vendors', $vendor_id, 'vendor_name') : ''; ob_start(); ?>
+ @@ -104,12 +104,12 @@ ob_start(); - +
@@ -123,12 +123,12 @@ ob_start(); - + @@ -142,12 +142,12 @@ ob_start(); - + diff --git a/agent/modals/domain/domain_edit.php b/agent/modals/domain/domain_edit.php index d9f8780e6..5a6f034ba 100644 --- a/agent/modals/domain/domain_edit.php +++ b/agent/modals/domain/domain_edit.php @@ -92,12 +92,12 @@ ob_start(); - + @@ -132,12 +132,12 @@ ob_start(); - + diff --git a/agent/modals/expense/expense_add.php b/agent/modals/expense/expense_add.php index a7f2a5486..e0b5def43 100644 --- a/agent/modals/expense/expense_add.php +++ b/agent/modals/expense/expense_add.php @@ -3,6 +3,8 @@ require_once '../../../includes/modal_header.php'; $client_id = intval($_GET['client_id'] ?? 0); +// Opened from one of our vendors' pages - that vendor +$prefill_vendor_id = intval($_GET['vendor_id'] ?? 0); ob_start(); @@ -80,12 +82,12 @@ ob_start(); - + - + '$expense_created_at' OR vendor_archived_at IS NULL) ORDER BY vendor_name ASC"); + $sql_select = mysqli_query($mysqli, "SELECT vendor_id, vendor_name, vendor_description FROM vendors WHERE vendor_client_id = 0 AND (vendor_archived_at > '$expense_created_at' OR vendor_archived_at IS NULL OR vendor_id = $expense_vendor_id) ORDER BY vendor_favorite DESC, vendor_name ASC"); while ($row = mysqli_fetch_assoc($sql_select)) { $vendor_id_select = intval($row['vendor_id']); $vendor_name_select = escapeHtml($row['vendor_name']); ?> - + "> + diff --git a/agent/modals/ticket/ticket_add.php b/agent/modals/ticket/ticket_add.php index b27f68edb..7cd3063fb 100644 --- a/agent/modals/ticket/ticket_add.php +++ b/agent/modals/ticket/ticket_add.php @@ -7,6 +7,7 @@ $contact_id = intval($_GET['contact_id'] ?? 0); $project_id = intval($_GET['project_id'] ?? 0); $asset_id = intval($_GET['asset_id'] ?? 0); $location_id = intval($_GET['location_id'] ?? 0); +$vendor_id = intval($_GET['vendor_id'] ?? 0); // A ticket started from somewhere that knows what it is about (a license's // Renewal Ticket) - plain text in, escaped here @@ -247,7 +248,7 @@ ob_start();
-
diff --git a/agent/modals/ticket/ticket_edit.php b/agent/modals/ticket/ticket_edit.php index d47642e8d..4d41c77d0 100644 --- a/agent/modals/ticket/ticket_edit.php +++ b/agent/modals/ticket/ticket_edit.php @@ -306,12 +306,14 @@ ob_start(); - + No vendor - + 'That vendor no longer exists.']); + exit; +} +$client_id = intval($vendor['vendor_client_id']); if ($client_id) { + enforceUserPermission('module_client'); enforceClientAccess(); } -// Licenses bought through this vendor - soonest renewal first -$vendor_licenses = []; -if (lookupUserPermission('module_support') >= 1) { - $sql_vendor_licenses = mysqli_query($mysqli, "SELECT software_id AS id, software_name AS name, software_type, software_version, software_license_type, +$vendor_name = escapeHtml($vendor['vendor_name']); +$vendor_archived = !empty($vendor['vendor_archived_at']); +$vendor_phone = contactPhoneLink($vendor['vendor_phone'], $vendor['vendor_phone_country_code'], $vendor['vendor_extension']); +$vendor_dial = vendorDialNumber($vendor['vendor_phone'], $vendor['vendor_phone_country_code']); +$vendor_email = escapeHtml($vendor['vendor_email']); +$vendor_website_url = vendorWebsiteUrl($vendor['vendor_website']); +$vendor_account = escapeHtml($vendor['vendor_account_number']); +$vendor_pin = vendorSecretHtml($vendor['vendor_code']); +$vendor_notes = escapeHtml($vendor['vendor_notes']); +$manager_phone = contactPhoneLink($vendor['vendor_contact_phone'], $vendor['vendor_contact_phone_country_code']); +$manager_email = escapeHtml($vendor['vendor_contact_email']); + +$can_edit = vendorCan($client_id, 2) && !$vendor_archived; +$can_support = lookupUserPermission('module_support') >= 1; +$can_support_edit = lookupUserPermission('module_support') >= 2; +$can_credentials = lookupUserPermission('module_credential') >= 1; +$can_financial = lookupUserPermission('module_financial') >= 1; + +$cases = $licenses = $assets = $logins = []; +if ($can_support) { + $sql = mysqli_query($mysqli, "SELECT ticket_id, ticket_prefix, ticket_number, ticket_subject, ticket_vendor_ticket_number, ticket_status_name, ticket_status_color + FROM tickets + LEFT JOIN ticket_statuses ON ticket_status_id = ticket_status + WHERE ticket_client_id = $client_id AND ticket_vendor_id = $vendor_id AND ticket_resolved_at IS NULL AND ticket_closed_at IS NULL + ORDER BY ticket_number DESC"); + while ($row = mysqli_fetch_assoc($sql)) { + $cases[] = $row; + } + $sql = mysqli_query($mysqli, "SELECT software_id AS id, software_name AS name, software_type, software_version, software_license_type, software_seats, software_expire, " . softwareSeatsUsedSql('software.software_id') . " AS seats_used FROM software - WHERE software_vendor_id = $vendor_id AND software_archived_at IS NULL " . clientScopeSql('software_client_id') . " + WHERE software_client_id = $client_id AND software_vendor_id = $vendor_id AND software_archived_at IS NULL ORDER BY software_expire IS NULL, software_expire ASC, software_name ASC"); - while ($vendor_license = mysqli_fetch_assoc($sql_vendor_licenses)) { - $vendor_licenses[] = $vendor_license; + while ($row = mysqli_fetch_assoc($sql)) { + $licenses[] = $row; + } + $sql = mysqli_query($mysqli, "SELECT asset_id AS id, asset_name AS name, asset_type, asset_make, asset_model, asset_warranty_expire + FROM assets + WHERE asset_client_id = $client_id AND asset_vendor_id = $vendor_id AND asset_archived_at IS NULL + ORDER BY asset_type ASC, asset_name ASC"); + while ($row = mysqli_fetch_assoc($sql)) { + $assets[] = $row; } } +if ($can_credentials && $client_id) { + $logins = linkedRecords('vendor', $vendor_id, 'credential'); +} +$spend = !$client_id && $can_financial ? vendorSpend($vendor_id) : null; + +$quick_tabs = array_filter([ + 'cases' => ['label' => 'Open cases', 'count' => count($cases)], + 'licenses' => ['label' => 'Licenses', 'count' => count($licenses)], + 'assets' => ['label' => 'Assets', 'count' => count($assets)], + 'logins' => ['label' => 'Logins', 'count' => count($logins)], +], function ($tab) { + return $tab['count'] > 0; +}); +$first_tab = array_key_first($quick_tabs); +$quick_id = "vendor-quick-$vendor_id"; +$page_url = 'vendor.php?' . ($client_id ? "client_id=$client_id&" : '') . "vendor_id=$vendor_id"; ob_start(); ?> -