AI stage 4: Billing use case falling back to General; Write it with AI on quotes and invoices whose lines can still change - say what it is for and AI picks lines from the product catalog at catalog prices and frequency, labor in quarter hours at the client's rate, and anything else unpriced for the agent to set (AI never sets a price), with a suggested scope, client note and pre-send checks, optionally using the quote's ticket and the client's people, sites and devices (ticket and documentation only with Support); ticked lines added through billing_ai_lines, taxed by kind, scope and note kept on request, lines that can't go on listed; Write it on the ticket Invoice window describes the work done from replies and notes for the client under the usual ticket line, passwords never sent nor written back; new functions/ai_billing.php, ai_billing_draft and ai_ticket_invoice_description endpoints, billing_ai_writer modal, billing_draft and invoice_description features; callAiApi remove mode also strips made-up secret placeholders; changelog

This commit is contained in:
johnnyq
2026-10-20 21:23:52 -04:00
parent e76fb69928
commit 26437daa44
14 changed files with 994 additions and 8 deletions

View File

@@ -22,7 +22,7 @@ $page_title = 'AI Models';
$sortable_columns = [
'ai_model_name' => "ai_model_name $order",
'ai_provider_name' => "ai_provider_name $order, ai_model_name ASC",
'ai_model_use_case' => "FIELD(ai_model_use_case, 'General', 'Tickets', 'Documentation') " . ($order === 'ASC' ? 'ASC' : 'DESC') . ", ai_model_id ASC",
'ai_model_use_case' => "FIELD(ai_model_use_case, 'General', 'Tickets', 'Documentation', 'Billing') " . ($order === 'ASC' ? 'ASC' : 'DESC') . ", ai_model_id ASC",
'requests' => "requests $order, ai_model_name ASC",
];
if (!isset($sortable_columns[$sort])) {

View File

@@ -51,7 +51,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
// Each provider's models, with what they are for - and those nothing uses
$models_by_provider = [];
$model_sql = mysqli_query($mysqli, "SELECT ai_model_id, ai_model_name, ai_model_use_case, ai_model_ai_provider_id FROM ai_models ORDER BY FIELD(ai_model_use_case, 'General', 'Tickets', 'Documentation'), ai_model_name");
$model_sql = mysqli_query($mysqli, "SELECT ai_model_id, ai_model_name, ai_model_use_case, ai_model_ai_provider_id FROM ai_models ORDER BY FIELD(ai_model_use_case, 'General', 'Tickets', 'Documentation', 'Billing'), ai_model_name");
while ($model = mysqli_fetch_assoc($model_sql)) {
$models_by_provider[intval($model['ai_model_ai_provider_id'])][] = $model;
}