From cd03a7765d6fffdee0cb357a538953ce3b98f404 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Tue, 20 Oct 2026 11:20:45 -0400 Subject: [PATCH] Vendor templates stage 2: keep client vendors in step with the template they were made from - saving a template updates the fields its vendors still shared, the template window gets the vendor window's tabs, a template page shows each client vendor as in step or the fields it differs in with Bring in line, Link and Unlink, lists hand-made vendors that look like it to link, and sets who should have it (no one, every client or clients with chosen tags) with coverage and Add to clients for those missing; the list gains Required and Out of step tabs, a client vendor shows its differences with Update from template, a client's Vendors list and overview show vendors missing, Add to clients no longer skips a client whose vendor from the template was archived, and database 3.3.5 adds vendor_template_required, vendor_template_required_tags and a vendor_template_id key on vendors --- CHANGELOG.md | 26 +- admin/database_updates/3.3.5.php | 37 ++ .../vendor_template/vendor_template_form.php | 24 +- .../vendor_template_required.php | 89 ++++ admin/post/vendor_template.php | 57 ++- admin/vendor_template.php | 244 ++++++++- admin/vendor_templates.php | 73 ++- .../modals/vendor/vendor_template_clients.php | 23 +- .../modals/vendor/vendor_template_update.php | 117 +++++ agent/post/vendor.php | 95 +++- agent/vendor.php | 38 ++ agent/vendors.php | 18 + db.sql | 21 +- functions/client_overview.php | 10 + functions/vendor_template.php | 471 ++++++++++++++++-- 15 files changed, 1242 insertions(+), 101 deletions(-) create mode 100644 admin/database_updates/3.3.5.php create mode 100644 admin/modals/vendor_template/vendor_template_required.php create mode 100644 agent/modals/vendor/vendor_template_update.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 7363703b7..f3b28ecbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,9 @@ This file documents all notable changes made to ITFlow. ### Upgrading to 26.11 -Update from Maintenance > Update — Queue Update hands the job to cron, which updates the files and applies this release's database changes (2.7.9 to 3.3.4) in the same pass. Nothing else is required. +Update from Maintenance > Update — Queue Update hands the job to cron, which updates the files and applies this release's database changes (2.7.9 to 3.3.5) in the same pass. Nothing else is required. -This is a big release, with fifty-five database steps in one go, so take a backup before you update. +This is a big release, with fifty-six database steps in one go, so take a backup before you update. The update also clears out data that deletes in older versions left behind, all of it pointing at something that no longer exists: @@ -51,7 +51,7 @@ Document templates keep their names, summaries, what they say and their images. License templates keep everything they hold. A license called exactly what one license template is called - as the New License form named it when started from one - counts as made from that template, so a template's use starts out counted; where two templates share a name, neither is given its licenses. Other licenses don't know a template, so they count from the update. No template sets seats, a renewal term, a vendor or a product until you give it one, and none is required of anyone until you set it up on its page. Maintenance > Starter Content has a new License Templates pack to add. -Vendor templates keep everything they hold, and the vendors made from them stay linked to them. Who made each and last changed it is taken from the audit log, where it has a record of it. +Vendor templates keep everything they hold, and the vendors made from them stay linked to them. Who made each and last changed it is taken from the audit log, where it has a record of it. A vendor made from one that has since been changed by hand shows as out of step with it, and nothing on it changes until you bring it in line. Vendors made by hand stay unlinked - a template's page lists the ones that look like it, to link. No template is required of anyone until you set it up on its page. The app logs are kept for as many days as the audit log, as before, and each can now be set on its own in Admin > Settings > Logs, which is where the log retention went from Settings > Security. A retention of 0 now keeps a log forever (see Breaking Changes, Logs). Nothing is sent to a syslog server or SIEM until you set one up there. @@ -583,7 +583,15 @@ If you are coming from 26.08 or earlier, the database update still has to be run - **Vendor templates** (rebuilt) - Admin > Vendor Templates lists what each is, its support line, hours and SLA, its support site and email, how many clients have a vendor made from it - and whether it is one of your own - when one was last added and when it last changed, and by whom. Searched by name, what they are, support line, email, site, hours, SLA, account manager or notes, sorted from the column headings, a page at a time, with Active and Archived tabs. - A template's page shows what a vendor made from it starts with, its support line a click to call, the clients with a vendor from it - each with its own account number, or marked when it has none yet - a click from their vendor, the license templates that take their vendor from it, who made it and last changed it, and its history. Add to clients is on the page and the list. - - The template window has the vendor window's tabs - Details, Support, Account and Notes - with their icons. Editing, any of its fields can be changed on the vendors made from it too, ticked one by one; what the window says replaces what each one had, and each vendor's history says so. The audit log says what changed on the template. + - The template window has the vendor window's tabs - Details, Support, Account and Notes - with their icons. The audit log says what changed on the template. + - Clients' vendors are kept in step with the template they were made from - its name, what they are, the support line, hours, SLA, support email, site and notes. Their account number, PIN and account manager are always each client's own. Case, spacing, and a site written with https:// or www. don't count as a difference, and a field the template says nothing for never does. + - Saving a template changes each field on the vendors made from it that still said what the template said - the one a client has its own for is left alone. A field taken off the template is taken off no vendor. Switch off Change the vendors made from it too to change only the template. Each vendor's history says what changed. + - A template's page shows each client's vendor as in step, or the fields it differs in, with All, Out of step and No account number views. Bring in line shows one vendor's fields beside the template's, or for several - ticked, or Bring these in line on Out of step - how many differ in each, and takes the fields ticked from the template. A vendor can be unlinked from its template there, or from its own page. + - Not linked yet on a template's page lists vendors made by hand with its name, or a support site on the same host, at clients without one from it, to link one by one or all at once - their details left as they are, or brought in line too. + - Who should have it, on a template's page and in the list's menu: no one in particular, every client, or the clients with one of the client tags picked. The page shows how many of them have it, with the clients missing it to add it to - one at a time or all at once, picked for you in Add to clients. Leads, archived clients and archived vendors don't count. + - The list has Required and Out of step tabs, and shows how many of the clients a required one is for have it, and how many of its vendors are out of step. + - A client's vendor says which fields differ from its template, with Update from template, and has Unlink from template in its menu. One made by hand that looks like a template - the same name or support site - offers Link to template. + - A client's Vendors list the vendors it should have and doesn't, each a click from New Vendor started from its template, and its overview counts them as Vendors missing. - Templates can be copied, archived - not offered on New Vendor or Add to Clients, while the vendors made from them stay - restored and deleted. Deleted, its vendors and the license templates that used it stay, no longer linked to it. - Make a template from it, in the menu of a client's vendor for an admin, starts a template from that vendor - leaving its account number, PIN and account manager, which are the client's own - and links the vendor to it, so other clients can have the same one. - A vendor made from a template links to it from its page, for an admin. @@ -1114,6 +1122,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Notifications: shared-link notifications reached agents without Support, with a link they couldn't open. - License templates: the type picker showed a stray "<" at the top. - Vendor templates: Delete was only offered to the built-in Administrator role, not to other admin roles. +- Vendor templates: Add to clients skipped a client whose vendor from the template had been archived, as if it still had it. - App Logs: the category filter did nothing - it read a misspelt field - and the date filter was slow on a large log. - App logs: an entry over 1,000 characters with an accented letter or a symbol where it was cut could fail to save, stopping what was logging it. - Admin > Settings > Defaults: Payment Method and Expense Payment Method listed the payment method categories that moved to Admin > Payment Methods in 2.3.1, so a new install had nothing to pick. They list Admin > Payment Methods now. @@ -1647,9 +1656,14 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Report: `agent/reports/audit_activity.php` (`client_id`, `agent` and the period; `export=csv` by person, `table=clients`, `kinds`, or for an admin `security`), `audit_activity` in `reportCatalog()` in a new Activity group, needing `module_client`; entries scoped by `auditLogRoleSql()` and `clientScopeSql()`. Out of hours uses the SLA business days and hours (`getSlaSettings()`), else 9 to 5 on weekdays. - Vendor templates: - Database 3.3.4: `vendor_templates.vendor_template_created_by` and `vendor_template_updated_by` (0), taken from the first Vendor Template Create and the latest Edit in the audit log with `log_entity_id` the template, where they are 0, without touching `vendor_template_updated_at`. - - New `functions/vendor_template.php`: `vendorTemplateFieldLabels()` (key => `label`, `icon`), `vendorTemplateColumns()` (key => [template column, vendor column] pairs - the support line is three), `vendorTemplateInfo()` (archived or not, with `created_by_name` and `updated_by_name`; `vendorTemplateRow()` in `functions/vendor.php` stays the current-only one used to make vendors), `vendorTemplateUsage($ids)` (`clients`, `vendors`, `ours`, `last` - current vendors, current clients), `vendorTemplateFields($post, &$error)` (as `vendorFormSet()` reads a vendor's, and the support email checked), `vendorTemplateRowFields()`, `vendorTemplateSave()`, `vendorTemplateChanged()`, `vendorTemplateCopy()`, `vendorTemplateDelete()` (vendors and license templates unlinked, their updated dates kept), `vendorTemplatePush($id, $keys)`, `vendorTemplateVendors()`, `vendorTemplateLicenseTemplates()`, `vendorTemplateTile()` and `vendorTemplatePhoneHtml()`. - - Admin: `admin/vendor_templates.php` is rebuilt (`q` - a support line searched by its digits - `sort` - `vendor_template_name`, `used_count`, `last_used` or `changed_at` - `order`, `page` and `show=archived`, `archived=1` still taken). New `admin/vendor_template.php` (`vendor_template_id`). `admin/modals/vendor_template/vendor_template_form.php` is shared by `vendor_template_add.php` (`from_vendor`) and `vendor_template_edit.php` (`id`); both post to `/admin/post.php`. `admin/post/vendor_template.php`: `add_vendor_template` (`from_vendor` links that vendor; redirects to its page) and `edit_vendor_template` (`push[]` - keys of `vendorTemplateFieldLabels()`, replacing the `global_update_vendor_*` and `update_base_vendors` fields), GET `copy_vendor_template`, `archive_vendor_template`, `restore_vendor_template` and `delete_vendor_template` (`from_page`). Audit entries are Vendor Template Create (with where a copy or one made from a vendor came from), Edit (with what changed), Archive, Restore and Delete, on the template, and Vendor Edit on each vendor a push changed. It no longer includes `agent/post/vendor.php`, so the agent vendor handlers don't run on `admin/post.php`. + - New `functions/vendor_template.php`: `vendorTemplateFieldLabels()` (key => `label`, `icon`), `vendorTemplateColumns()` (key => [template column, vendor column] pairs - the support line is three), `vendorTemplateInfo()` (archived or not, with `created_by_name` and `updated_by_name`; `vendorTemplateRow()` in `functions/vendor.php` stays the current-only one used to make vendors), `vendorTemplateUsage($ids)` (`clients`, `vendors`, `ours`, `last` - current vendors, current clients), `vendorTemplateFields($post, &$error)` (as `vendorFormSet()` reads a vendor's, and the support email checked), `vendorTemplateRowFields()`, `vendorTemplateSave()`, `vendorTemplateChanged()`, `vendorTemplateCopy()`, `vendorTemplateDelete()` (vendors and license templates unlinked, their updated dates kept), `vendorTemplateVendors()` (with each vendor's `differences` and raw `row`), `vendorTemplateLicenseTemplates()`, `vendorTemplateTile()` and `vendorTemplatePhoneHtml()`. + - Admin: `admin/vendor_templates.php` is rebuilt (`q` - a support line searched by its digits - `sort` - `vendor_template_name`, `used_count`, `last_used` or `changed_at` - `order`, `page` and `show=archived`, `archived=1` still taken). New `admin/vendor_template.php` (`vendor_template_id`). `admin/modals/vendor_template/vendor_template_form.php` is shared by `vendor_template_add.php` (`from_vendor`) and `vendor_template_edit.php` (`id`); both post to `/admin/post.php`. `admin/post/vendor_template.php`: `add_vendor_template` (`from_vendor` links that vendor; redirects to its page) and `edit_vendor_template` (`follow` - replacing the `global_update_vendor_*` and `update_base_vendors` fields), GET `copy_vendor_template`, `archive_vendor_template`, `restore_vendor_template` and `delete_vendor_template` (`from_page`). Audit entries are Vendor Template Create (with where a copy or one made from a vendor came from), Edit (with what changed), Archive, Restore and Delete, on the template, and Vendor Edit on each vendor that followed it. It no longer includes `agent/post/vendor.php`, so the agent vendor handlers don't run on `admin/post.php`. - `auditLogKinds()` links Vendor Template entries to the template's page. `agent/vendor.php` links its template for an admin, and has Make a template from it for a vendor with none. + - Database 3.3.5: `vendor_templates.vendor_template_required` (tinyint, 0 no one, 1 every client, 2 tagged), `vendor_template_required_tags` (`vendor_template_id`, `tag_id`, cascading from both) and a `vendor_template_id` (`vendor_template_id`, `vendor_client_id`) key on `vendors`. + - In step, in `functions/vendor_template.php`: `VENDOR_TEMPLATE_SHARED` (the keys kept in step - not `account_number`, `code` or `contact_name`), `vendorTemplateCompareValue($key, $row, $side)` (`template` or `vendor` columns), `vendorTemplateHasValue()`, `vendorTemplateDifferences($template, $vendor)` (key => label), `vendorTemplateLabelsText()`, `vendorTemplateValueHtml()`, `vendorTemplateFieldsRow()`, `vendorTemplateApply()`, `vendorTemplateFollow($id, $before, $after)` and `vendorTemplateBringInLine($id, $vendor_ids, $keys)` (both vendor id => `name`, `client_id`, `fields`), `vendorTemplateOutOfStep($ids)` (current vendors, current clients), `vendorTemplateHost()`, `vendorTemplateMatches($id)`, `vendorTemplateMatchFor($vendor)` and `vendorTemplateLink()`. Who should have one: `VENDOR_TEMPLATE_REQUIRED_NONE`, `_EVERY` and `_TAGGED`, `vendorTemplateRequiredTags($ids)`, `vendorTemplateSetRequired()` (client tags only), `vendorTemplateScopeSql()`, `vendorTemplateScopeText()`, `vendorTemplateCoverage()` (`clients`, `covered`, `missing`) and `vendorTemplatesMissingFor($client_id)`, as the license and document templates have. + - `admin/post/vendor_template.php`: `save_vendor_template_required` (`vendor_template_id`, `required`, `tags[]`), logged as Vendor Template Edit. New `admin/modals/vendor_template/vendor_template_required.php` (`id`). `admin/vendor_template.php` takes `view` (`out` or `account`) and loads `js/bulk_actions.js` (`vendor_ids[]`); `admin/vendor_templates.php` takes `show=required` and `show=out`. + - `agent/post/vendor.php`: `update_vendors_from_template` (`vendor_template_id`, `vendor_ids[]`, `fields[]` - keys of `VENDOR_TEMPLATE_SHARED`), `link_vendors_to_template` (`vendor_template_id`, `vendor_ids[]`, `bring`; not to an archived template) and GET `unlink_vendor_template`, each only for vendors the agent may edit (`vendorsForRequest()`), logged as Vendor Edit on each vendor with its client. `add_vendor_template_to_clients` counts only a current vendor from the template as having it. New `agent/modals/vendor/vendor_template_update.php` (`vendor_id`, or `template_id` and `vendor_ids[]`); `vendor_template_clients.php` takes `client_ids[]` and `missing=1` to start with clients picked. + - `agent/vendors.php` with `client_id` shows `vendorTemplatesMissingFor()` (`.itflow-vendors-missing`), and `clientOverviewAttention()` adds Vendors missing for agents with `module_client`. - Empty states: every list page sets `$empty_state` before including `includes/filter_footer.php`, and `includes/inc_empty_state.php` draws it with `emptyStateHtml()` (`functions/format.php`) when the list has nothing - `thing`, `icon`, `title`, `text`, `actions` (`label`, `modal` or `href`, `size`, `icon`; the first is the main one), `archived`, `view`, `filtered`, `views`, `match_title`, `match_text` and `clear_url`. `emptyStateFiltered($views)` reads the GET string for a search or filter, leaving out paging, sorting, `client_id`, `archived` and the page's views, and `emptyStateClearUrl($views)` is the page without them. `emptyStatePill($pills, $key, $default, $good)` builds a pill's own state from the page's pill array (`label`, `icon`, `title`, `alarm`), with a tick for `alarm` pills and the keys in `$good`. Files, notifications and racks call `emptyStateHtml()` in their own layout; files and notifications set `$empty_state_skip`. A page that sets nothing still gets one, worded from `$page_title`. The pages' own empty blocks (`.itflow-asset-empty` under a list) are gone. CSS: `.itflow-empty-state` with `-icon` (a grey round tile), `-title`, `-text` and `-actions`. - Removed unused helpers and files: - `credentialLink()`, `projectLink()`, `linkCounts()`, `assetContactAvatar()`, `clientOverviewPersonInitials()`, and `getMonthlyTax()` and `getQuarterlyTax()`, which looked a tax up by name diff --git a/admin/database_updates/3.3.5.php b/admin/database_updates/3.3.5.php new file mode 100644 index 000000000..160772437 --- /dev/null +++ b/admin/database_updates/3.3.5.php @@ -0,0 +1,37 @@ + - -
-
Change them on the vendor made from it too
-
Ticked fields are set to what is here on each one - what a client's vendor says now is replaced.
-
- $field) { ?> -
-
- - -
-
- + +
+
+ +
+
Each field you change here changes on the vendors that still say what it said - one a client has its own for is left as it is, and shows as out of step on the template's page. Account numbers, PINs and account managers are always each client's own.
diff --git a/admin/modals/vendor_template/vendor_template_required.php b/admin/modals/vendor_template/vendor_template_required.php new file mode 100644 index 000000000..f5fa95b82 --- /dev/null +++ b/admin/modals/vendor_template/vendor_template_required.php @@ -0,0 +1,89 @@ + + + + + + +
+ + + + + + + +
+ + + + Vendor Templates) - making, changing (and the - * fields ticked on the vendors made from it too), copying, archiving, - * restoring and deleting them. The work is in functions/vendor_template.php; - * a vendor is made from one by add_vendor, add_vendor_from_template and - * add_vendor_template_to_clients (agent/post/vendor.php). + * Vendor templates (Admin > Vendor Templates) - making, changing (the + * vendors made from it that still said what it did changing with it), + * copying, archiving, restoring and deleting them, and saying who should have + * one. The work is in functions/vendor_template.php; a vendor is made from + * one by add_vendor, add_vendor_from_template and + * add_vendor_template_to_clients, and brought in line with one, linked to one + * or unlinked by update_vendors_from_template, link_vendors_to_template and + * unlink_vendor_template (agent/post/vendor.php). */ defined('FROM_POST_HANDLER') || die("Direct file access is not allowed"); @@ -41,18 +44,15 @@ if (isset($_POST['add_vendor_template']) || isset($_POST['edit_vendor_template'] $renamed = $before['vendor_template_name'] !== $name ? ' (was ' . $before['vendor_template_name'] . ')' : ''; logAudit("Vendor Template", "Edit", "$session_name edited vendor template " . escapeSql($name . $renamed) . ($changed ? ' - ' . escapeSql(mb_strtolower(implode(', ', $changed))) : ''), 0, $vendor_template_id); - // The fields ticked, on every vendor made from it too - $labels = array_map(function ($field) { - return $field['label']; - }, vendorTemplateFieldLabels()); - $push = array_values(array_intersect(array_keys($labels), array_map('strval', array_filter((array) ($_POST['push'] ?? []), 'is_scalar')))); - $pushed = $push ? vendorTemplatePush($vendor_template_id, $push) : []; - $what = mb_strtolower(implode(', ', array_intersect_key($labels, array_flip($push)))); - foreach ($pushed as $vendor_id => $vendor) { - logAudit("Vendor", "Edit", "$session_name updated vendor " . escapeSql($vendor['name']) . " from template " . escapeSql($name) . " - " . escapeSql($what), $vendor['client_id'], $vendor_id); + // The vendors made from it that still said what it did follow it (vendorTemplateFollow()) + $followed = !empty($_POST['follow']) ? vendorTemplateFollow($vendor_template_id, vendorTemplateRowFields($before), $fields) : []; + foreach ($followed as $vendor_id => $vendor) { + logAudit("Vendor", "Edit", "$session_name updated vendor " . escapeSql($vendor['name']) . " from template " . escapeSql($name) . " - " . escapeSql(mb_strtolower(implode(', ', $vendor['fields']))), $vendor['client_id'], $vendor_id); } + $out_of_step = $followed || !empty($_POST['follow']) ? (vendorTemplateOutOfStep([$vendor_template_id])[$vendor_template_id] ?? 0) : 0; - flashAlert("Vendor template " . escapeHtml($name) . " saved" . ($pushed ? ", and " . escapeHtml($what) . " changed on " . count($pushed) . " vendor" . (count($pushed) === 1 ? '' : 's') . " made from it" : '')); + flashAlert("Vendor template " . escapeHtml($name) . " saved" . ($followed ? ", and " . count($followed) . " vendor" . (count($followed) === 1 ? '' : 's') . " made from it changed with it" : '') + . ($out_of_step ? " - $out_of_step out of step" : '')); redirect(); } @@ -125,3 +125,30 @@ if (isset($_GET['delete_vendor_template'])) { redirect(!empty($_GET['from_page']) ? 'vendor_templates.php' : null); } + +if (isset($_POST['save_vendor_template_required'])) { + + // Who should have a vendor from it + validateCSRFToken(); + + $row = vendorTemplateForRequest($_POST['vendor_template_id'] ?? 0); + $vendor_template_id = intval($row['vendor_template_id']); + $required = intval($_POST['required'] ?? 0); + $tag_ids = array_map('intval', array_filter((array) ($_POST['tags'] ?? []), 'is_scalar')); + if ($required === VENDOR_TEMPLATE_REQUIRED_TAGGED && !array_filter($tag_ids)) { + flashAlert('Pick the client tags - or Every client.', 'error'); + redirect(); + } + vendorTemplateSetRequired($vendor_template_id, $required, $tag_ids); + + $saved = vendorTemplateInfo($vendor_template_id); + $scope = vendorTemplateScopeText($saved['vendor_template_required'], vendorTemplateRequiredTags([$vendor_template_id])[$vendor_template_id]); + $said = $scope !== '' ? "required of " . lcfirst($scope) : 'not required of anyone'; + + logAudit("Vendor Template", "Edit", "$session_name set vendor template " . escapeSql($row['vendor_template_name']) . " " . escapeSql($said), 0, $vendor_template_id); + + flashAlert("" . escapeHtml($row['vendor_template_name']) . " is " . escapeHtml($said)); + + redirect(); + +} diff --git a/admin/vendor_template.php b/admin/vendor_template.php index e41511101..b40340039 100644 --- a/admin/vendor_template.php +++ b/admin/vendor_template.php @@ -5,9 +5,12 @@ * what they are, the support line, hours, SLA, support email and site, any * account number, PIN and account manager every client shares, and its * notes), who made it and last changed it, and its use: the clients with a - * vendor made from it - each with its own account number and PIN, or not yet - * - and the license templates that take their vendor from it. It can be - * added to clients from here. The work is in functions/vendor_template.php. + * vendor made from it - each with its own account number and PIN, or not yet, + * and in step with it or saying something else (views, and bringing one or + * several in line) - the vendors made by hand that look like it (linking + * them), who should have it with the clients missing it, and the license + * templates that take their vendor from it. It can be added to clients from + * here. The work is in functions/vendor_template.php. */ require_once "includes/inc_all_admin.php"; @@ -30,20 +33,59 @@ $csrf = $_SESSION['csrf_token']; // Its vendors - the current ones for current clients listed, the rest counted $all_vendors = vendorTemplateVendors($vendor_template_id); -$vendors = array_filter($all_vendors, function ($vendor) { +$current = array_filter($all_vendors, function ($vendor) { return !$vendor['archived'] && !$vendor['client_archived']; }); -$set_up = count(array_filter($vendors, function ($vendor) { +$set_up = count(array_filter($current, function ($vendor) { return $vendor['client_id'] && $vendor['account_number'] !== ''; })); -$client_vendors = count(array_filter($vendors, function ($vendor) { +$client_vendors = count(array_filter($current, function ($vendor) { return $vendor['client_id'] > 0; })); -$others = count($all_vendors) - count($vendors); +$out_of_step = count(array_filter($current, function ($vendor) { + return (bool) $vendor['differences']; +})); +$others = count($all_vendors) - count($current); $license_templates = vendorTemplateLicenseTemplates($vendor_template_id); +// ...seen all at once, the ones out of step, or the ones without the client's account number +$views = [ + '' => ['label' => 'All', 'icon' => 'fa-list', 'title' => 'Every current vendor made from it', 'test' => function ($vendor) { + return true; + }], + 'out' => ['label' => 'Out of step', 'icon' => 'fa-not-equal', 'title' => 'Saying something other than the template does', 'test' => function ($vendor) { + return (bool) $vendor['differences']; + }], + 'account' => ['label' => 'No account number', 'icon' => 'fa-fingerprint', 'title' => "Without the client's account number", 'test' => function ($vendor) { + return $vendor['client_id'] && $vendor['account_number'] === ''; + }], +]; +$view = is_string($_GET['view'] ?? null) && isset($views[$_GET['view']]) ? $_GET['view'] : ''; +$view_counts = []; +foreach ($views as $key => $definition) { + $view_counts[$key] = count(array_filter($current, $definition['test'])); +} +$vendors = array_filter($current, $views[$view]['test']); +$view_url = function ($key) use ($vendor_template_id) { + return '?' . http_build_query(array_filter(['vendor_template_id' => $vendor_template_id, 'view' => $key])) . '#vendorTemplateVendors'; +}; + +// Vendors made by hand that look like it - not offered while it is archived +$matches = $archived ? [] : vendorTemplateMatches($vendor_template_id); + +// Who should have one - not counted while it is archived +$required = intval($template['vendor_template_required']); +$required_tags = vendorTemplateRequiredTags([$vendor_template_id])[$vendor_template_id]; +$scope_text = vendorTemplateScopeText($required, $required_tags); +$coverage = $required && !$archived ? vendorTemplateCoverage($vendor_template_id, $required, array_keys($required_tags)) : null; +$list_limit = 8; +$missing_text = $coverage && $coverage['missing'] ? (count($coverage['missing']) === 1 ? 'the client missing it' : 'the ' . count($coverage['missing']) . ' clients missing it') : ''; + $edit_url = "modals/vendor_template/vendor_template_edit.php?id=$vendor_template_id"; $clients_url = "/agent/modals/vendor/vendor_template_clients.php?template_id=$vendor_template_id"; +$required_url = "modals/vendor_template/vendor_template_required.php?id=$vendor_template_id"; +$update_url = "/agent/modals/vendor/vendor_template_update.php"; +$in_line_url = "$update_url?template_id=$vendor_template_id"; // What a vendor from it starts with - the ones it has $t = $template; @@ -89,12 +131,23 @@ foreach (vendorTemplateFieldLabels() as $field) {
- + +
+ + + +
+ + +