Add Quote Sent SMS template and fix consent confirmation wording

Adds a customizable QuoteSent SMS template to seed data and
DefaultTemplates so companies can edit the quote approval message
from Notification Templates. Wires NotifyQuoteSentSmsAsync to use
the template system instead of a hardcoded string. Updates
SmsConsentConfirmation wording to mention quote approvals alongside
job updates. Help docs and AI knowledge base updated to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-02 21:19:43 -04:00
parent a9048dea2e
commit 0b6a7a14c4
4 changed files with 41 additions and 9 deletions
@@ -860,7 +860,7 @@ New accounts walk through an 18-step setup wizard to configure company informati
}
/// <summary>
/// Returns the 8 canonical default notification templates for a company.
/// Returns the canonical default notification templates for a company.
/// Called by both SeedData and CompanySettingsController for auto-seeding.
/// </summary>
public static List<NotificationTemplate> BuildDefaultNotificationTemplates(int companyId)
@@ -934,12 +934,23 @@ New accounts walk through an 18-step setup wizard to configure company informati
CreatedAt = DateTime.UtcNow
},
new NotificationTemplate
{
NotificationType = NotificationType.QuoteSent,
Channel = NotificationChannel.Sms,
DisplayName = "Quote Sent (SMS)",
Subject = null,
Body = "{{companyName}}: Quote {{quoteNumber}} for {{quoteTotal}} is ready for your review. Approve or decline: {{approvalUrl}} Reply STOP to opt out.",
IsActive = true,
CompanyId = companyId,
CreatedAt = DateTime.UtcNow
},
new NotificationTemplate
{
NotificationType = NotificationType.SmsConsentConfirmation,
Channel = NotificationChannel.Sms,
DisplayName = "SMS Enrollment Confirmation",
Subject = null,
Body = "{{companyName}}: You're now enrolled for SMS job & pickup updates. Reply STOP at any time to opt out. Msg & data rates may apply.",
Body = "{{companyName}}: You're now enrolled for SMS job updates and quote approvals. Reply STOP at any time to opt out. Msg & data rates may apply.",
IsActive = true,
CompanyId = companyId,
CreatedAt = DateTime.UtcNow
@@ -224,7 +224,17 @@ public class NotificationService : INotificationService
}
}
var message = $"{companyName}: Quote {quote.QuoteNumber} for {quote.Total:C} is ready for your review. Approve or decline: {approvalUrl} Reply STOP to opt out.";
var smsValues = new Dictionary<string, string>
{
["companyName"] = companyName,
["quoteNumber"] = quote.QuoteNumber ?? string.Empty,
["quoteTotal"] = quote.Total.ToString("C"),
["approvalUrl"] = approvalUrl
};
var message = await GetRenderedSmsAsync(
quote.CompanyId, NotificationType.QuoteSent, smsValues,
$"{companyName}: Quote {quote.QuoteNumber} for {quote.Total:C} is ready for your review. Approve or decline: {approvalUrl} Reply STOP to opt out.");
var (success, error) = await _smsService.SendSmsAsync(smsPhone, message);
await WriteLog(new NotificationLog
@@ -945,7 +955,7 @@ public class NotificationService : INotificationService
var smsMessage = await GetRenderedSmsAsync(
customer.CompanyId, NotificationType.SmsConsentConfirmation, values,
$"{companyName}: You're now enrolled for SMS job & pickup updates. Reply STOP at any time to opt out. Msg & data rates may apply.");
$"{companyName}: You're now enrolled for SMS job updates and quote approvals. Reply STOP at any time to opt out. Msg & data rates may apply.");
var (success, error) = await _smsService.SendSmsAsync(smsPhone, smsMessage);
@@ -1103,13 +1113,17 @@ public class NotificationService : INotificationService
"Job {{jobNumber}} Complete — {{companyName}}",
"<p>Dear {{customerName}},</p><p>Your job <strong>{{jobNumber}}</strong> is complete. Final price: <strong>{{finalPrice}}</strong>. It is now ready for pickup.</p><p>Thank you for choosing {{companyName}}.</p>"
),
[(NotificationType.QuoteSent, NotificationChannel.Sms)] = (
null,
"{{companyName}}: Quote {{quoteNumber}} for {{quoteTotal}} is ready for your review. Approve or decline: {{approvalUrl}} Reply STOP to opt out."
),
[(NotificationType.JobCompleted, NotificationChannel.Sms)] = (
null,
"{{companyName}}: Job {{jobNumber}} is done and ready for pickup! Reply STOP to opt out."
),
[(NotificationType.SmsConsentConfirmation, NotificationChannel.Sms)] = (
null,
"{{companyName}}: You're now enrolled for SMS job & pickup updates. Reply STOP at any time to opt out. Msg & data rates may apply."
"{{companyName}}: You're now enrolled for SMS job updates and quote approvals. Reply STOP at any time to opt out. Msg & data rates may apply."
),
[(NotificationType.InvoiceSent, NotificationChannel.Email)] = (
"Invoice {{invoiceNumber}} from {{companyName}}",