@model List @using PowderCoating.Core.Enums @{ ViewData["Title"] = "Recurring Transactions"; }

Recurring Transactions

Templates that auto-generate bills or expenses on a schedule.

New Template
@if (TempData["Success"] != null) {
@TempData["Success"]
} @if (TempData["Error"] != null) {
@TempData["Error"]
} @if (!Model.Any()) {
No recurring templates yet

Create a template to automatically generate bills or expenses on a schedule.

Create Template
} else {
@foreach (var t in Model) { var isOverdueRT = t.IsActive && t.NextFireDate.Date < DateTime.Today;
@t.Name
@if (t.TemplateType == RecurringTemplateType.Bill) { Bill } else { Expense } — @(t.IntervalCount == 1 ? t.Frequency.ToString() : $"Every {t.IntervalCount} × {t.Frequency}")
Status @if (t.IsActive) { Active } else { Paused }
@if (t.IsActive) {
Next Fire @t.NextFireDate.ToString("MM/dd/yyyy") @if (isOverdueRT) { }
}
Occurrences @t.OccurrenceCount @if (t.MaxOccurrences.HasValue) { / @t.MaxOccurrences }
@if (!string.IsNullOrWhiteSpace(t.LastError)) {
Error @t.LastError
}
}
@foreach (var t in Model) { }
Name Type Frequency Next Fire Occurrences Status Last Error Actions
@t.Name @if (t.TemplateType == RecurringTemplateType.Bill) { Bill } else { Expense } @{ var freqLabel = t.IntervalCount == 1 ? t.Frequency.ToString() : $"Every {t.IntervalCount} × {t.Frequency}"; } @freqLabel @if (t.IsActive) { var isOverdue = t.NextFireDate.Date < DateTime.Today; @t.NextFireDate.ToString("MM/dd/yyyy") @if (isOverdue) { } } else { } @t.OccurrenceCount @if (t.MaxOccurrences.HasValue) { / @t.MaxOccurrences } @if (t.IsActive) { Active } else { Paused } @if (!string.IsNullOrWhiteSpace(t.LastError)) { Error }
@Html.AntiForgeryToken()
@if (t.IsActive) {
@Html.AntiForgeryToken()
}
@Html.AntiForgeryToken()

The background service checks hourly and auto-generates due templates. Bills are created as Draft; Expenses are recorded immediately. Generate Now fires one occurrence immediately.

}