Document Templates - documents ready to start a client's from: a * runbook, a build sheet, a plan. Each row shows what it is for, how many * documents have been made from it and for how many clients, when it was * last used and last changed. A template can be changed, copied as the start * of another, archived - not offered for new documents, while the documents * made from it stay - or deleted. One that every client, or the clients with * a tag, should have shows how many of them do, and one whose documents are * reviewed shows how many are due; the Required tab lists those it is * required of. They can be exported to a file and imported from one, and * the report shows how they are used. The work is in * functions/document_template.php; the handlers are * admin/post/document_template.php. */ // Default Column Sortby Filter $sort = "document_template_name"; $order = "ASC"; require_once "includes/inc_all_admin.php"; // '' current, 'required' the current ones someone should have, 'archived' $show = (string) ($_GET['show'] ?? ''); $show = in_array($show, ['required', 'archived'], true) ? $show : ''; $show_archived = $show === 'archived'; $show_where = [ '' => 'document_template_archived_at IS NULL', 'required' => 'document_template_archived_at IS NULL AND document_template_required > 0', 'archived' => 'document_template_archived_at IS NOT NULL', ][$show]; // Only these sort - an unknown column would take the page down $sortable_columns = [ 'document_template_name' => "document_template_name $order", 'used_count' => "used_count $order, document_template_name ASC", 'last_used' => "last_used IS NULL, last_used $order, document_template_name ASC", 'changed_at' => "changed_at $order, document_template_name ASC", ]; if (!isset($sortable_columns[$sort])) { $sort = 'document_template_name'; } $search = "(document_template_name LIKE '%$q%' OR document_template_description LIKE '%$q%' OR document_template_content LIKE '%$q%')"; // Its use is the documents made from it that are not archived - as documentTemplateUsage() counts them $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS document_templates.document_template_id, document_template_name, document_template_description, document_template_content, document_template_required, document_template_review_months, document_template_created_at, document_template_updated_at, document_template_archived_at, COALESCE(document_template_updated_at, document_template_created_at) AS changed_at, COALESCE(updater.user_name, creator.user_name) AS changed_by, COALESCE(used.used_count, 0) AS used_count, COALESCE(used.client_count, 0) AS client_count, used.last_used FROM document_templates LEFT JOIN users creator ON creator.user_id = document_template_created_by AND document_template_created_by > 0 LEFT JOIN users updater ON updater.user_id = document_template_updated_by AND document_template_updated_by > 0 LEFT JOIN ( SELECT document_template_id AS used_template_id, COUNT(*) AS used_count, COUNT(DISTINCT document_client_id) AS client_count, MAX(document_created_at) AS last_used FROM documents WHERE document_template_id > 0 AND document_archived_at IS NULL GROUP BY document_template_id ) used ON used.used_template_id = document_templates.document_template_id WHERE $search AND $show_where ORDER BY {$sortable_columns[$sort]}, document_templates.document_template_id ASC LIMIT $record_from, $record_to"); $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $rows = []; while ($row = mysqli_fetch_assoc($sql)) { $rows[intval($row['document_template_id'])] = $row; } // Who should have them and how many do, and the documents due a review - for the ones on show $required_tags = documentTemplateRequiredTags(array_keys($rows)); $due_counts = documentTemplateDueCounts(array_keys($rows)); $coverage = []; foreach ($rows as $template_id => $row) { if (intval($row['document_template_required']) && !$row['document_template_archived_at']) { $coverage[$template_id] = documentTemplateCoverage($template_id, $row['document_template_required'], array_keys($required_tags[$template_id])); } } $counts = mysqli_fetch_row(mysqli_query($mysqli, "SELECT COALESCE(SUM(document_template_archived_at IS NULL), 0), COALESCE(SUM(document_template_archived_at IS NOT NULL), 0), COALESCE(SUM(document_template_archived_at IS NULL AND document_template_required > 0), 0) FROM document_templates WHERE $search")); $sort_link = function ($column, $label) use ($url_query_strings_sort, $sort, $disp, $order_icon) { return '' . $label . ($sort === $column ? " $order_icon" : '') . ''; }; $pill_url = function ($view) { return '?' . http_build_query(array_filter(['q' => $_GET['q'] ?? '', 'show' => $view])); }; $add_url = 'modals/document_template/document_template_add.php'; $csrf = $_SESSION['csrf_token']; ?>

Document Templates

"> $row) { $name = escapeHtml($row['document_template_name']); $used_count = intval($row['used_count']); $client_count = intval($row['client_count']); $summary = trim((string) $row['document_template_description']) !== '' ? $row['document_template_description'] : documentTemplatePreview($row['document_template_content'], 140); $used_text = $used_count . ' document' . ($used_count === 1 ? '' : 's') . ($client_count > 1 ? ' · ' . $client_count . ' clients' : ''); $edit_url = "modals/document_template/document_template_edit.php?id=$template_id"; ?>
Not yet' ?> >
'document templates', 'icon' => 'fa-file-alt', 'filtered' => $q !== '', 'archived' => $show_archived, 'views' => ['show'], 'view' => $show === 'required' ? ['title' => 'No required templates', 'text' => 'Set one up from its page - Who should have one - to see which clients are missing a document from it.'] : null, 'text' => 'A document ready to start a client\'s from - a runbook, a build sheet, a plan. Agents start one from Document from template in a client\'s Files. Maintenance > Starter Content has a set for an MSP.', 'actions' => [['label' => 'New Document Template', 'modal' => $add_url, 'size' => 'xl']], ]; require_once "../includes/filter_footer.php"; ?>