Files
PowderCoatingLogix/src/PowderCoating.Application/DTOs/AI/AccountingAiDtos.cs
T
spouliot 959e323f3a Add 4 AI bookkeeping features
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>
2026-05-10 19:22:49 -04:00

536 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.01.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; }
}