Seed data fixes:
- Fix EF interceptor: no longer overwrites explicitly-set CreatedAt on Added
entities — root cause of all "same month" chart issues
- Customer seeder: generates 15 customers/month from Jan → current month;
keeps 10 commercial anchors in deterministic order for job seeder index map
- Invoice seeder: historical range bumped from 2→8 paid invoices/month so
P&L shows consistent profit (~$5,200 collected vs ~$4,200 monthly expenses)
- Month -1 bumped to 7 paid invoices to stay above expenses
- Jobs: set UpdatedAt to historical event date so analytics don't need null fallback
- Analytics (ReportsController): use CompletedDate ?? UpdatedAt ?? CreatedAt for
revenue chart grouping; fixes empty Revenue Trend charts on Overview/Revenue tabs
- SeedDataService: inject IAccountBalanceService; auto-recalculate account balances
after seeding; patch checking/savings opening balances unconditionally on reset
- Customer list: sort by CompanyName ?? ContactLastName so individuals and
commercial accounts interleave instead of appearing as two blocks
Invoice resend:
- ResendInvoice action now accepts sendEmail + sendSms parameters; SMS-only
resend no longer requires an email address on file
- Ensures PublicViewToken exists before SMS so the view link is always valid
- canResend in Details view now allows Paid invoices (removed != Paid guard)
- Resend button shows channel-choice modal when customer has both email + SMS,
direct SMS button when SMS only, or email button when email only
- New #resendChannelModal mirrors the Send channel modal but posts to ResendInvoice
- resendInvoice() JS updated to pass sendEmail/sendSms query params
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Appointments: add ~25 past appointments (last 90 days) with Completed,
Cancelled, No Show, and Rescheduled statuses; completed records carry
ActualStartTime/ActualEndTime with realistic variance; cancel/no-show
notes explain why; customer label falls back to ContactFirst/LastName
for residential customers
- Fix future appointment title for residential customers (was always using
CompanyName which is null for individuals)
- New SeedDataService.AiPredictions.cs: seeds 8 AiItemPrediction records
(varied complexity/confidence/tags/reasoning) and attaches them to the
first 8 eligible QuoteItems, marking those items IsAiItem=true; 3 of 8
have UserOverrodeEstimate=true for AI Accuracy report demo
- SeedDataService.cs: wire SeedAiPredictionsAsync after Invoices
- Remove.cs: collect QuoteItem.AiPredictionId FKs before deleting items,
then delete orphaned AiItemPrediction records after quotes are removed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bills.cs: replace aceHardware/fastenal lookups with grainger/harbor/localSupply
to match Phase 1 vendor renames; update all vendor invoice number prefixes
- Bills.cs: add 3 AP aging-bucket bills (30-60, 61-90, 90+ days overdue) so all
four AP aging buckets are populated for report demos
- Invoices.cs: add 3 more overdue invoices (31-60, 61-90, 90+ day AR buckets)
alongside the existing 21-day overdue; total now 29 invoices
- New SeedDataService.PurchaseOrders.cs: 7 POs — 3 Received (historical), 2
Submitted (in-flight), 2 Draft (pending approval); links to inventory items
where available
- SeedDataService.cs: wire SeedPurchaseOrdersAsync after Vendors seeder
- Remove.cs: add PO + POItem cleanup inside Bills removal block (two-step ID
fetch to avoid nested LINQ translation issues)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 5 named shop workers seeded as ApplicationUser (Employee role):
Mike Sanders (Coater), Jake Wilson (Sandblaster), Sarah Brooks (Inspector),
Tyler Green (General), Chris Mason (Lead) — @pcldemo.com fingerprint domain
- Job time entries seeded for all in-progress and completed jobs;
Worker Productivity report will have data from day one
- Maintenance history seeded per equipment: 2 completed records + 1 upcoming
scheduled + 1 overdue record on Pressure Pot for overdue alert demo
- Equipment renamed to spec names: Main Batch Oven, Small Batch Oven, Powder
Coating Booth, Blast Cabinet, Pressure Pot Blaster, Air Compressor, Wash
Station, Forklift (replaced Overhead Conveyor which wasn't in spec)
- RemoveSeedDataOptions.Workers added; Remove.cs cleans up workers + time
entries on Demo Reset; SeedDataController resets workers in ResetDemoCompany
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stripe payments/refunds/chargebacks now post DR/CR entries (PaymentController)
- Vendor credit void now reverses the posted GL lines (VendorCreditsController)
- Gift certificate issue/redeem/void post GL to account 2500 GC Liability;
FinancialReportService Trial Balance + Balance Sheet include GC liability and
breakage income; P&L shows deferred revenue deduction and breakage income line
- Customer deposits now post DR Checking / CR 2300 on record, reverse on delete;
invoice auto-apply uses DR 2300 / CR AR (not a second bank debit); draft
invoice delete reverses deposit-apply GL before the AR reversal
- Deposit.DepositAccountId column added; account 2300 seeded via migration
- InvoicesController.ApplyCredit now posts DR Sales Discounts / CR AR,
consistent with CreditMemosController.Apply
- IssueRefund (cash/card) posts DR AR / CR Bank and sets Refund.DepositAccountId;
refund modal gains a bank account selector hidden for store-credit path
- CancelRefund (cash/card) reverses the IssueRefund GL entries
- LedgerService GetAccountLedgerAsync + ComputePriorBalanceAsync now include
Refunds, CreditMemoApplications, VendorCreditApplications, GC Liability (2500),
and Customer Deposits (2300) so account ledger view and RecalculateAllAsync
produce correct balances
- Three EF migrations applied: SeedSalesDiscountsAccount, AccountingGapsPhase2,
AccountingDepositsGL
- Unit tests updated for new IAccountBalanceService constructor params (200/200)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>