959e323f3a
Feature 7: Bank Rec Auto-Match — AiSuggestMatches endpoint scores uncleared transactions vs statement ending balance; AI Auto-Match panel in Reconcile.cshtml with confidence highlights and Apply All button. Feature 8: Late Payment Prediction — PredictLatePayments endpoint scores open AR customers by risk (high/medium/low) using historical avg-days-to-pay + late rate; rendered as badge table in AR Aging view via ar-aging-ai.js. Feature 9: Natural Language Financial Queries — FinancialQuery GET page + RunFinancialQuery POST; 12-month context snapshot pre-loaded; answers grounded in real data with supporting facts, follow-up suggestions, session history, and example chips. Feature 10: Recurring Bill Detection — RunRecurringDetection scans 12 months of bills for vendor payment patterns (monthly/quarterly/annual); card grid view in Bills/RecurringDetection.cshtml with confidence badges, next-expected-date, and suggested actions. Supporting: 4 new DTO groups in AccountingAiDtos.cs, 4 method signatures in IAccountingAiService.cs, 4 implementations in AccountingAiService.cs, 4 new AiFeatures constants, 2 new Landing page AI report cards. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
536 lines
19 KiB
C#
536 lines
19 KiB
C#
namespace PowderCoating.Application.DTOs.AI;
|
||
|
||
// ── Shared ────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>Lightweight account summary passed to AI for account matching.</summary>
|
||
public class AccountSummary
|
||
{
|
||
public int Id { get; set; }
|
||
public string AccountNumber { get; set; } = string.Empty;
|
||
public string Name { get; set; } = string.Empty;
|
||
public string AccountType { get; set; } = string.Empty; // "Expense", "CostOfGoods", "Asset"
|
||
public string? AccountSubType { get; set; }
|
||
}
|
||
|
||
// ── Feature 1: Receipt / Bill Scanning ───────────────────────────────────────
|
||
|
||
public class ScannedLineItem
|
||
{
|
||
public string Description { get; set; } = string.Empty;
|
||
public decimal Amount { get; set; }
|
||
public int? SuggestedAccountId { get; set; }
|
||
public string? SuggestedAccountName { get; set; }
|
||
}
|
||
|
||
public class ReceiptScanResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
|
||
public string? VendorName { get; set; }
|
||
public string? Date { get; set; } // ISO 8601 date string
|
||
public decimal? Total { get; set; }
|
||
public string? InvoiceNumber { get; set; }
|
||
public List<ScannedLineItem> LineItems { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for receipt scans.</summary>
|
||
public class ClaudeReceiptResponse
|
||
{
|
||
public string? VendorName { get; set; }
|
||
public string? Date { get; set; }
|
||
public decimal? Total { get; set; }
|
||
public string? InvoiceNumber { get; set; }
|
||
public List<ClaudeReceiptLineItem> LineItems { get; set; } = new();
|
||
}
|
||
|
||
public class ClaudeReceiptLineItem
|
||
{
|
||
public string Description { get; set; } = string.Empty;
|
||
public decimal Amount { get; set; }
|
||
public int? SuggestedAccountId { get; set; }
|
||
public string? SuggestedAccountName { get; set; }
|
||
}
|
||
|
||
// ── Feature 2: AR Follow-up Email Drafts ─────────────────────────────────────
|
||
|
||
public class OverdueInvoice
|
||
{
|
||
public string InvoiceNumber { get; set; } = string.Empty;
|
||
public decimal Amount { get; set; }
|
||
public int DaysOverdue { get; set; }
|
||
}
|
||
|
||
public class ArFollowUpRequest
|
||
{
|
||
public string CustomerName { get; set; } = string.Empty;
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public decimal AmountOwed { get; set; }
|
||
public int DaysOverdue { get; set; }
|
||
public List<OverdueInvoice> Invoices { get; set; } = new();
|
||
}
|
||
|
||
public class ArFollowUpResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
|
||
public string Subject { get; set; } = string.Empty;
|
||
public string Body { get; set; } = string.Empty;
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for AR email drafts.</summary>
|
||
public class ClaudeArEmailResponse
|
||
{
|
||
public string Subject { get; set; } = string.Empty;
|
||
public string Body { get; set; } = string.Empty;
|
||
}
|
||
|
||
// ── Feature 3: Smart Account Categorization ──────────────────────────────────
|
||
|
||
public class AccountSuggestionRequest
|
||
{
|
||
public string? VendorName { get; set; }
|
||
public string? Description { get; set; }
|
||
public decimal Amount { get; set; }
|
||
public List<AccountSummary> AvailableAccounts { get; set; } = new();
|
||
}
|
||
|
||
public class AccountSuggestion
|
||
{
|
||
public int AccountId { get; set; }
|
||
public string AccountName { get; set; } = string.Empty;
|
||
public double Confidence { get; set; }
|
||
public string? Reasoning { get; set; }
|
||
}
|
||
|
||
public class AccountSuggestionResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
|
||
public int? SuggestedAccountId { get; set; }
|
||
public string? SuggestedAccountName { get; set; }
|
||
public string? Reasoning { get; set; }
|
||
public List<AccountSuggestion> Alternatives { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for account suggestions.</summary>
|
||
public class ClaudeAccountSuggestionResponse
|
||
{
|
||
public int? SuggestedAccountId { get; set; }
|
||
public string? SuggestedAccountName { get; set; }
|
||
public string? Reasoning { get; set; }
|
||
public List<ClaudeAccountAlternative> Alternatives { get; set; } = new();
|
||
}
|
||
|
||
public class ClaudeAccountAlternative
|
||
{
|
||
public int AccountId { get; set; }
|
||
public string AccountName { get; set; } = string.Empty;
|
||
public double Confidence { get; set; }
|
||
public string? Reasoning { get; set; }
|
||
}
|
||
|
||
// ── Feature 4: Plain-English Financial Summary ────────────────────────────────
|
||
|
||
public class ExpenseByCategory
|
||
{
|
||
public string Category { get; set; } = string.Empty;
|
||
public decimal Amount { get; set; }
|
||
}
|
||
|
||
public class FinancialSummaryRequest
|
||
{
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public string Period { get; set; } = string.Empty; // e.g. "Last 6 months"
|
||
public decimal TotalRevenue { get; set; }
|
||
public decimal TotalExpenses { get; set; }
|
||
public decimal NetIncome { get; set; }
|
||
public decimal PriorMonthRevenue { get; set; }
|
||
public decimal PriorMonthExpenses { get; set; }
|
||
public decimal TotalArOutstanding { get; set; }
|
||
public decimal ArOverdue30Days { get; set; }
|
||
public int OverdueInvoiceCount { get; set; }
|
||
public List<ExpenseByCategory> ExpensesByCategory { get; set; } = new();
|
||
}
|
||
|
||
public class FinancialSummaryResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
|
||
/// <summary>Markdown bullet lines (plain English, no jargon).</summary>
|
||
public List<string> Bullets { get; set; } = new();
|
||
|
||
/// <summary>"positive", "neutral", or "concerning".</summary>
|
||
public string Sentiment { get; set; } = "neutral";
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for financial summaries.</summary>
|
||
public class ClaudeFinancialSummaryResponse
|
||
{
|
||
public List<string> Bullets { get; set; } = new();
|
||
public string Sentiment { get; set; } = "neutral";
|
||
}
|
||
|
||
// ── Feature 5: Cash Flow Forecast ─────────────────────────────────────────────
|
||
|
||
public class CashFlowArItem
|
||
{
|
||
public string CustomerName { get; set; } = string.Empty;
|
||
public string InvoiceNumber { get; set; } = string.Empty;
|
||
public decimal BalanceDue { get; set; }
|
||
public string? DueDateIso { get; set; }
|
||
public int DaysOverdue { get; set; }
|
||
public int AvgDaysToPay { get; set; }
|
||
}
|
||
|
||
public class CashFlowApItem
|
||
{
|
||
public string VendorName { get; set; } = string.Empty;
|
||
public string BillNumber { get; set; } = string.Empty;
|
||
public decimal BalanceDue { get; set; }
|
||
public string? DueDateIso { get; set; }
|
||
}
|
||
|
||
public class CashFlowJobItem
|
||
{
|
||
public string JobNumber { get; set; } = string.Empty;
|
||
public string CustomerName { get; set; } = string.Empty;
|
||
public string Status { get; set; } = string.Empty;
|
||
public decimal EstimatedValue { get; set; }
|
||
}
|
||
|
||
public class CashFlowForecastRequest
|
||
{
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public string AsOfDate { get; set; } = string.Empty;
|
||
public List<CashFlowArItem> OpenInvoices { get; set; } = new();
|
||
public List<CashFlowApItem> OpenBills { get; set; } = new();
|
||
public List<CashFlowJobItem> ActiveJobs { get; set; } = new();
|
||
}
|
||
|
||
public class CashFlowPeriod
|
||
{
|
||
public decimal ExpectedInflows { get; set; }
|
||
public decimal ExpectedOutflows { get; set; }
|
||
public decimal NetCashFlow { get; set; }
|
||
public List<string> KeyItems { get; set; } = new();
|
||
}
|
||
|
||
public class CashFlowForecastResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
public CashFlowPeriod Next30Days { get; set; } = new();
|
||
public CashFlowPeriod Next60Days { get; set; } = new();
|
||
public CashFlowPeriod Next90Days { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
/// <summary>"strong", "moderate", "tight", or "concerning"</summary>
|
||
public string Outlook { get; set; } = "moderate";
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for cash flow forecasts.</summary>
|
||
public class ClaudeCashFlowResponse
|
||
{
|
||
public ClaudeCashFlowPeriod Next30Days { get; set; } = new();
|
||
public ClaudeCashFlowPeriod Next60Days { get; set; } = new();
|
||
public ClaudeCashFlowPeriod Next90Days { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
public string Outlook { get; set; } = "moderate";
|
||
}
|
||
|
||
public class ClaudeCashFlowPeriod
|
||
{
|
||
public decimal ExpectedInflows { get; set; }
|
||
public decimal ExpectedOutflows { get; set; }
|
||
public decimal NetCashFlow { get; set; }
|
||
public List<string> KeyItems { get; set; } = new();
|
||
}
|
||
|
||
// ── Feature 6: Anomaly / Duplicate Detection ──────────────────────────────────
|
||
|
||
public class AnomalyBillSummary
|
||
{
|
||
public int Id { get; set; }
|
||
public string BillNumber { get; set; } = string.Empty;
|
||
public string VendorName { get; set; } = string.Empty;
|
||
public decimal Total { get; set; }
|
||
public string BillDateIso { get; set; } = string.Empty;
|
||
public string? VendorInvoiceNumber { get; set; }
|
||
}
|
||
|
||
public class AnomalyVendorHistory
|
||
{
|
||
public string VendorName { get; set; } = string.Empty;
|
||
public decimal AverageInvoiceAmount { get; set; }
|
||
public decimal AverageMonthlySpend { get; set; }
|
||
public int InvoiceCount { get; set; }
|
||
}
|
||
|
||
public class AnomalyAccountTrend
|
||
{
|
||
public string AccountName { get; set; } = string.Empty;
|
||
public decimal ThisMonthAmount { get; set; }
|
||
public decimal LastMonthAmount { get; set; }
|
||
public decimal AverageMonthlyAmount { get; set; }
|
||
}
|
||
|
||
public class AnomalyDetectionRequest
|
||
{
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public List<AnomalyBillSummary> RecentBills { get; set; } = new();
|
||
public List<AnomalyVendorHistory> VendorHistory { get; set; } = new();
|
||
public List<AnomalyAccountTrend> AccountTrends { get; set; } = new();
|
||
}
|
||
|
||
public class AnomalyFlag
|
||
{
|
||
/// <summary>"duplicate", "amount_spike", "unusual_vendor", "account_overrun"</summary>
|
||
public string Type { get; set; } = string.Empty;
|
||
/// <summary>"critical", "warning", "info"</summary>
|
||
public string Severity { get; set; } = "warning";
|
||
public string Title { get; set; } = string.Empty;
|
||
public string Description { get; set; } = string.Empty;
|
||
public string? RecommendedAction { get; set; }
|
||
public string? BillNumber { get; set; }
|
||
}
|
||
|
||
public class AnomalyDetectionResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
public List<AnomalyFlag> Flags { get; set; } = new();
|
||
public int CriticalCount { get; set; }
|
||
public int WarningCount { get; set; }
|
||
public int InfoCount { get; set; }
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for anomaly detection.</summary>
|
||
public class ClaudeAnomalyResponse
|
||
{
|
||
public List<ClaudeAnomalyFlag> Flags { get; set; } = new();
|
||
}
|
||
|
||
public class ClaudeAnomalyFlag
|
||
{
|
||
public string Type { get; set; } = string.Empty;
|
||
public string Severity { get; set; } = "warning";
|
||
public string Title { get; set; } = string.Empty;
|
||
public string Description { get; set; } = string.Empty;
|
||
public string? RecommendedAction { get; set; }
|
||
public string? BillNumber { get; set; }
|
||
}
|
||
|
||
// ── Feature 7: Bank Rec Auto-Match ───────────────────────────────────────────
|
||
|
||
public class BankRecMatchItem
|
||
{
|
||
public string EntityType { get; set; } = string.Empty; // "Payment", "BillPayment", "Expense"
|
||
public int EntityId { get; set; }
|
||
public string Date { get; set; } = string.Empty; // ISO 8601
|
||
public string Reference { get; set; } = string.Empty;
|
||
public string Description { get; set; } = string.Empty;
|
||
public decimal Amount { get; set; }
|
||
public string Direction { get; set; } = string.Empty; // "deposit" or "payment"
|
||
}
|
||
|
||
public class AutoMatchRequest
|
||
{
|
||
public List<BankRecMatchItem> UnclearedItems { get; set; } = new();
|
||
public decimal BeginningBalance { get; set; }
|
||
public decimal StatementEndingBalance { get; set; }
|
||
}
|
||
|
||
public class AutoMatchSuggestion
|
||
{
|
||
public string EntityType { get; set; } = string.Empty;
|
||
public int EntityId { get; set; }
|
||
public double Confidence { get; set; } // 0.0–1.0
|
||
public string Reason { get; set; } = string.Empty;
|
||
}
|
||
|
||
public class AutoMatchResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
public List<AutoMatchSuggestion> SuggestedCleared { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for bank rec auto-match.</summary>
|
||
public class ClaudeAutoMatchResponse
|
||
{
|
||
public List<ClaudeAutoMatchSuggestion> SuggestedCleared { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
}
|
||
|
||
public class ClaudeAutoMatchSuggestion
|
||
{
|
||
public string EntityType { get; set; } = string.Empty;
|
||
public int EntityId { get; set; }
|
||
public double Confidence { get; set; }
|
||
public string Reason { get; set; } = string.Empty;
|
||
}
|
||
|
||
// ── Feature 8: Late Payment Prediction ───────────────────────────────────────
|
||
|
||
public class OpenInvoiceSummary
|
||
{
|
||
public string InvoiceNumber { get; set; } = string.Empty;
|
||
public decimal BalanceDue { get; set; }
|
||
public string? DueDateIso { get; set; }
|
||
public int DaysOverdue { get; set; }
|
||
}
|
||
|
||
public class LatePaymentCustomerData
|
||
{
|
||
public string CustomerName { get; set; } = string.Empty;
|
||
public decimal TotalOwed { get; set; }
|
||
public double AvgDaysToPay { get; set; } // historical average
|
||
public int TotalInvoicesAllTime { get; set; }
|
||
public int LateInvoicesAllTime { get; set; }
|
||
public List<OpenInvoiceSummary> OpenInvoices { get; set; } = new();
|
||
}
|
||
|
||
public class LatePaymentPredictionRequest
|
||
{
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public List<LatePaymentCustomerData> Customers { get; set; } = new();
|
||
}
|
||
|
||
public class LatePaymentPrediction
|
||
{
|
||
public string CustomerName { get; set; } = string.Empty;
|
||
/// <summary>"high", "medium", or "low"</summary>
|
||
public string RiskLevel { get; set; } = "medium";
|
||
public int EstimatedDaysToPayment { get; set; }
|
||
public string Reasoning { get; set; } = string.Empty;
|
||
}
|
||
|
||
public class LatePaymentPredictionResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
public List<LatePaymentPrediction> Predictions { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for late payment predictions.</summary>
|
||
public class ClaudeLatePaymentResponse
|
||
{
|
||
public List<ClaudeLatePaymentPrediction> Predictions { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
}
|
||
|
||
public class ClaudeLatePaymentPrediction
|
||
{
|
||
public string CustomerName { get; set; } = string.Empty;
|
||
public string RiskLevel { get; set; } = "medium";
|
||
public int EstimatedDaysToPayment { get; set; }
|
||
public string Reasoning { get; set; } = string.Empty;
|
||
}
|
||
|
||
// ── Feature 9: Natural Language Financial Queries ─────────────────────────────
|
||
|
||
public class MonthlyFinancialSummary
|
||
{
|
||
public string Month { get; set; } = string.Empty; // "YYYY-MM"
|
||
public decimal Revenue { get; set; }
|
||
public decimal Expenses { get; set; }
|
||
public decimal NetIncome { get; set; }
|
||
}
|
||
|
||
public class FinancialQueryContext
|
||
{
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public string AsOfDate { get; set; } = string.Empty;
|
||
public decimal TotalRevenueYtd { get; set; }
|
||
public decimal TotalExpensesYtd { get; set; }
|
||
public decimal NetIncomeYtd { get; set; }
|
||
public decimal ArOutstanding { get; set; }
|
||
public decimal ApOutstanding { get; set; }
|
||
public List<MonthlyFinancialSummary> Last12Months { get; set; } = new();
|
||
public List<ExpenseByCategory> ExpensesByCategory { get; set; } = new();
|
||
}
|
||
|
||
public class FinancialQueryRequest
|
||
{
|
||
public string Question { get; set; } = string.Empty;
|
||
public FinancialQueryContext Context { get; set; } = new();
|
||
}
|
||
|
||
public class FinancialQueryResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
public string Answer { get; set; } = string.Empty;
|
||
public string? FollowUpSuggestion { get; set; }
|
||
public List<string> RelevantFacts { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for financial queries.</summary>
|
||
public class ClaudeFinancialQueryResponse
|
||
{
|
||
public string Answer { get; set; } = string.Empty;
|
||
public string? FollowUpSuggestion { get; set; }
|
||
public List<string> RelevantFacts { get; set; } = new();
|
||
}
|
||
|
||
// ── Feature 10: Recurring Bill Detection ─────────────────────────────────────
|
||
|
||
public class RecurringBillHistoryItem
|
||
{
|
||
public string VendorName { get; set; } = string.Empty;
|
||
public string BillNumber { get; set; } = string.Empty;
|
||
public decimal Amount { get; set; }
|
||
public string DateIso { get; set; } = string.Empty;
|
||
public string? Memo { get; set; }
|
||
}
|
||
|
||
public class RecurringBillDetectionRequest
|
||
{
|
||
public string CompanyName { get; set; } = string.Empty;
|
||
public List<RecurringBillHistoryItem> Bills { get; set; } = new();
|
||
}
|
||
|
||
public class RecurringBillPattern
|
||
{
|
||
public string VendorName { get; set; } = string.Empty;
|
||
/// <summary>"monthly", "quarterly", "biannual", "annual"</summary>
|
||
public string Frequency { get; set; } = string.Empty;
|
||
public decimal TypicalAmount { get; set; }
|
||
public string? NextExpectedDateIso { get; set; }
|
||
/// <summary>"high", "medium", or "low"</summary>
|
||
public string Confidence { get; set; } = "medium";
|
||
public string Description { get; set; } = string.Empty;
|
||
public string? SuggestedAction { get; set; }
|
||
}
|
||
|
||
public class RecurringBillDetectionResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
public List<RecurringBillPattern> Patterns { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>Internal JSON schema that Claude returns for recurring bill detection.</summary>
|
||
public class ClaudeRecurringBillResponse
|
||
{
|
||
public List<ClaudeRecurringPattern> Patterns { get; set; } = new();
|
||
public List<string> Insights { get; set; } = new();
|
||
}
|
||
|
||
public class ClaudeRecurringPattern
|
||
{
|
||
public string VendorName { get; set; } = string.Empty;
|
||
public string Frequency { get; set; } = string.Empty;
|
||
public decimal TypicalAmount { get; set; }
|
||
public string? NextExpectedDateIso { get; set; }
|
||
public string Confidence { get; set; } = "medium";
|
||
public string Description { get; set; } = string.Empty;
|
||
public string? SuggestedAction { get; set; }
|
||
}
|