Files
PowderCoatingLogix/src/PowderCoating.Core/Interfaces/IUnitOfWork.cs
T
spouliot f671f7e62e Add WisePOS E in-person card payments (Stripe Terminal)
Server-driven Stripe Terminal integration for taking in-person card payments
against an invoice, running on the same Stripe Connect connected account used
for online payments. No native app or Terminal SDK — the WisePOS E is driven
from the web backend via Stripe's REST API.

- Domain: TerminalReader entity + status enum, PaymentMethod.CardReader,
  Company.StripeTerminalLocationId / TerminalSurchargeEnabled, DbSet + tenant
  filter + indexes, IUnitOfWork repo, migration AddTerminalReaders (additive).
- StripeConnectService: location/reader registration, list, delete, process
  payment on reader, status poll, cancel, and a test-mode simulated tap. All
  routed to the connected account like the existing online-payment methods.
- TerminalController: admin reader management + per-invoice ProcessPayment,
  PaymentStatus (poll), CancelPayment, SimulateTap (test mode only). Stores the
  PaymentIntent id on the invoice; the webhook remains the authoritative writer.
- PaymentController webhook: HandlePaymentSucceededAsync records source=terminal
  payments as CardReader (online path unchanged — no source key means no change);
  new terminal.reader.action_failed handler for declines/timeouts (notification
  only, no ledger mutation). Refund path reused unchanged.
- UI: Card Readers settings tab (register/list/deactivate + in-person surcharge
  toggle, default off with a compliance warning) and an invoice "Take Card
  Payment" modal with live status polling. External JS per project convention.
- Feature bundled with the existing online-payments entitlement (no new plan
  flag); additionally requires StripeConnectStatus == Active.
- Help: HelpKnowledgeBase + Invoices help article updated.
- Tests: TerminalController validation + surcharge-routing tests (241 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:57:58 -04:00

199 lines
8.1 KiB
C#

using PowderCoating.Core.Entities;
using PowderCoating.Core.Interfaces.Repositories;
namespace PowderCoating.Core.Interfaces;
public interface IUnitOfWork : IDisposable
{
// Multi-tenancy
IRepository<Company> Companies { get; }
IRepository<CompanyOperatingCosts> CompanyOperatingCosts { get; }
IRepository<CompanyPreferences> CompanyPreferences { get; }
IRepository<CompanySmsAgreement> CompanySmsAgreements { get; }
// AI Predictions
IRepository<AiItemPrediction> AiItemPredictions { get; }
// Powder Insights
IPowderUsageLogRepository PowderUsageLogs { get; }
// Core entities — typed repositories for complex domains
ICustomerRepository Customers { get; }
IJobRepository Jobs { get; }
IRepository<JobDailyPriority> JobDailyPriorities { get; }
IRepository<JobItem> JobItems { get; }
IJobItemCoatRepository JobItemCoats { get; }
IRepository<JobItemPrepService> JobItemPrepServices { get; }
IRepository<JobChangeHistory> JobChangeHistories { get; }
IRepository<JobPrepService> JobPrepServices { get; }
IQuoteRepository Quotes { get; }
IRepository<QuotePhoto> QuotePhotos { get; }
IRepository<QuoteItem> QuoteItems { get; }
IRepository<QuoteItemCoat> QuoteItemCoats { get; }
IRepository<QuoteItemPrepService> QuoteItemPrepServices { get; }
IRepository<QuoteChangeHistory> QuoteChangeHistories { get; }
IRepository<InventoryItem> InventoryItems { get; }
IPlainRepository<PowderCatalogItem> PowderCatalog { get; }
IInventoryTransactionRepository InventoryTransactions { get; }
IRepository<Equipment> Equipment { get; }
IRepository<OvenCost> OvenCosts { get; }
IRepository<CompanyBlastSetup> BlastSetups { get; }
IRepository<MaintenanceRecord> MaintenanceRecords { get; }
IRepository<Vendor> Vendors { get; }
IJobPhotoRepository JobPhotos { get; }
IRepository<JobNote> JobNotes { get; }
IRepository<CustomerNote> CustomerNotes { get; }
IRepository<CustomerContact> CustomerContacts { get; }
IRepository<CustomerPreferredPowder> CustomerPreferredPowders { get; }
IRepository<JobStatusHistory> JobStatusHistory { get; }
IRepository<PricingTier> PricingTiers { get; }
// Lookup tables (replacing enums)
IRepository<JobStatusLookup> JobStatusLookups { get; }
IRepository<JobPriorityLookup> JobPriorityLookups { get; }
IRepository<QuoteStatusLookup> QuoteStatusLookups { get; }
IRepository<InventoryCategoryLookup> InventoryCategoryLookups { get; }
IRepository<AppointmentStatusLookup> AppointmentStatusLookups { get; }
IRepository<AppointmentTypeLookup> AppointmentTypeLookups { get; }
IRepository<PrepService> PrepServices { get; }
IRepository<ReworkRecord> ReworkRecords { get; }
IRepository<Refund> Refunds { get; }
IRepository<CreditMemo> CreditMemos { get; }
IRepository<CreditMemoApplication> CreditMemoApplications { get; }
IRepository<JobTimeEntry> JobTimeEntries { get; }
// Appointments
IRepository<Appointment> Appointments { get; }
// Product Catalog
IRepository<CatalogCategory> CatalogCategories { get; }
IRepository<CatalogItem> CatalogItems { get; }
IRepository<CatalogPriceCheckReport> CatalogPriceCheckReports { get; }
// Oven Scheduling
IRepository<OvenBatch> OvenBatches { get; }
IRepository<OvenBatchItem> OvenBatchItems { get; }
// Invoices, Payments & Deposits — typed repository for complex include chains
IInvoiceRepository Invoices { get; }
IRepository<InvoiceItem> InvoiceItems { get; }
IRepository<Payment> Payments { get; }
IRepository<Deposit> Deposits { get; }
IRepository<TerminalReader> TerminalReaders { get; }
// Purchase Orders — typed repository for paged/filtered list and detail load
IPurchaseOrderRepository PurchaseOrders { get; }
IRepository<PurchaseOrderItem> PurchaseOrderItems { get; }
// Expense Tracking / Accounts Payable — typed repository for Bills
IRepository<Account> Accounts { get; }
IBillRepository Bills { get; }
IRepository<BillLineItem> BillLineItems { get; }
IRepository<BillPayment> BillPayments { get; }
IRepository<Expense> Expenses { get; }
// Manual Journal Entries
IRepository<JournalEntry> JournalEntries { get; }
IRepository<JournalEntryLine> JournalEntryLines { get; }
// Vendor Credits
IRepository<VendorCredit> VendorCredits { get; }
IRepository<VendorCreditLineItem> VendorCreditLineItems { get; }
IRepository<VendorCreditApplication> VendorCreditApplications { get; }
// Bank Reconciliation
IRepository<BankReconciliation> BankReconciliations { get; }
// Tax Rates
IRepository<TaxRate> TaxRates { get; }
// Recurring Transactions
IRepository<RecurringTemplate> RecurringTemplates { get; }
// Fixed Assets
IRepository<FixedAsset> FixedAssets { get; }
IRepository<FixedAssetDepreciationEntry> FixedAssetDepreciationEntries { get; }
// Budgeting
IRepository<Budget> Budgets { get; }
IRepository<BudgetLine> BudgetLines { get; }
// Year-End Close
IRepository<YearEndClose> YearEndCloses { get; }
// Notifications — typed repository for IgnoreQueryFilters-based history lookups
INotificationLogRepository NotificationLogs { get; }
IRepository<NotificationTemplate> NotificationTemplates { get; }
// Subscription
IRepository<SubscriptionPlanConfig> SubscriptionPlanConfigs { get; }
// Job Templates
IJobTemplateRepository JobTemplates { get; }
IRepository<JobTemplateItem> JobTemplateItems { get; }
IRepository<JobTemplateItemCoat> JobTemplateItemCoats { get; }
IRepository<JobTemplateItemPrepService> JobTemplateItemPrepServices { get; }
// Platform content (SuperAdmin-managed, no tenant filter, no soft delete)
IPlainRepository<Announcement> Announcements { get; }
IPlainRepository<BannedIp> BannedIps { get; }
IPlainRepository<DashboardTip> DashboardTips { get; }
IInAppNotificationRepository InAppNotifications { get; }
IPlainRepository<ReleaseNote> ReleaseNotes { get; }
// Bug Reports
IRepository<BugReport> BugReports { get; }
IRepository<BugReportAttachment> BugReportAttachments { get; }
// Contact Us
IRepository<ContactSubmission> ContactSubmissions { get; }
// AI lookup: per-manufacturer URL patterns
IRepository<ManufacturerLookupPattern> ManufacturerLookupPatterns { get; }
// Gift Certificates
IRepository<GiftCertificate> GiftCertificates { get; }
IRepository<GiftCertificateRedemption> GiftCertificateRedemptions { get; }
// Customer Intake Kiosk
IRepository<KioskSession> KioskSessions { get; }
// Custom Formula Templates
IRepository<CustomItemTemplate> CustomItemTemplates { get; }
// Formula Community Library
IPlainRepository<FormulaLibraryItem> FormulaLibrary { get; }
IRepository<FormulaLibraryImport> FormulaLibraryImports { get; }
IPlainRepository<FormulaLibraryRating> FormulaLibraryRatings { get; }
// Employee Timeclock
IRepository<EmployeeClockEntry> EmployeeClockEntries { get; }
IRepository<TimeclockKioskDevice> TimeclockKioskDevices { get; }
Task<int> SaveChangesAsync();
Task<int> CompleteAsync(); // Alias for SaveChangesAsync
/// <summary>
/// Executes <paramref name="operation"/> inside a database transaction using EF Core's
/// execution strategy, enabling compatibility with SqlServerRetryingExecutionStrategy.
/// Commits on success and rolls back on any exception (which is re-thrown).
/// </summary>
Task ExecuteInTransactionAsync(Func<Task> operation);
/// <summary>
/// Same as <see cref="ExecuteInTransactionAsync(Func{Task})"/> but returns a value.
/// </summary>
Task<T> ExecuteInTransactionAsync<T>(Func<Task<T>> operation);
/// <summary>
/// Detaches all tracked entities from the change tracker.
/// Use after a failed save to prevent contaminating subsequent operations.
/// </summary>
void ClearChangeTracker();
// Kept for backwards-compatibility — prefer ExecuteInTransactionAsync for new code.
Task BeginTransactionAsync();
Task CommitTransactionAsync();
Task RollbackTransactionAsync();
}