using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Interfaces;
using PowderCoating.Core.Interfaces.Repositories;
using PowderCoating.Infrastructure.Data;
namespace PowderCoating.Infrastructure.Repositories;
///
/// Concrete implementation of that coordinates all entity repositories
/// and exposes transaction management for the application layer.
///
/// All entity repositories are lazily instantiated — a is only
/// allocated the first time the corresponding property is accessed. This keeps the per-request
/// memory footprint small when only a handful of entities are needed.
///
///
/// Persistence: call or
/// (identical behaviour) to flush pending changes to the database.
///
///
/// Explicit transactions: use /
/// / when multiple
/// SaveChanges calls must succeed or fail atomically. For a simpler fire-and-forget pattern,
/// prefer which wraps the operation in an
/// EF Core execution-strategy-aware transaction automatically.
///
///
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
private IDbContextTransaction? _transaction;
// Multi-tenancy
private IRepository? _companies;
private IRepository? _companyOperatingCosts;
private IRepository? _companyPreferences;
private IRepository? _companySmsAgreements;
// AI Predictions
private IRepository? _aiItemPredictions;
// Powder Insights
private IPowderUsageLogRepository? _powderUsageLogs;
// Core repositories
private ICustomerRepository? _customers;
private IJobRepository? _jobs;
private IRepository? _jobDailyPriorities;
private IRepository? _jobItems;
private IJobItemCoatRepository? _jobItemCoats;
private IRepository? _jobItemPrepServices;
private IRepository? _jobChangeHistories;
private IRepository? _jobPrepServices;
private IQuoteRepository? _quotes;
private IRepository? _quotePhotos;
private IRepository? _quoteItems;
private IRepository? _quoteItemCoats;
private IRepository? _quoteItemPrepServices;
private IRepository? _quoteChangeHistories;
private IRepository? _inventoryItems;
private IPlainRepository? _powderCatalog;
private IInventoryTransactionRepository? _inventoryTransactions;
private IRepository? _equipment;
private IRepository? _ovenCosts;
private IRepository? _blastSetups;
private IRepository? _maintenanceRecords;
private IRepository? _vendors;
private IJobPhotoRepository? _jobPhotos;
private IRepository? _jobNotes;
private IRepository? _customerNotes;
private IRepository? _jobStatusHistory;
private IRepository? _pricingTiers;
// Lookup tables (replacing enums)
private IRepository? _jobStatusLookups;
private IRepository? _jobPriorityLookups;
private IRepository? _quoteStatusLookups;
private IRepository? _inventoryCategoryLookups;
private IRepository? _appointmentStatusLookups;
private IRepository? _appointmentTypeLookups;
private IRepository? _prepServices;
private IRepository? _shopWorkers;
// Appointments
private IRepository? _appointments;
// Product Catalog
private IRepository? _catalogCategories;
private IRepository? _catalogItems;
private IRepository? _catalogPriceCheckReports;
// Notifications
private INotificationLogRepository? _notificationLogs;
private IRepository? _notificationTemplates;
// Subscription
private IRepository? _subscriptionPlanConfigs;
// Job Templates
private IJobTemplateRepository? _jobTemplates;
private IRepository? _jobTemplateItems;
private IRepository? _jobTemplateItemCoats;
private IRepository? _jobTemplateItemPrepServices;
// Platform content
private IPlainRepository? _announcements;
private IPlainRepository? _bannedIps;
private IPlainRepository? _dashboardTips;
private IRepository? _inAppNotifications;
private IPlainRepository? _releaseNotes;
// Bug Reports
private IRepository? _bugReports;
private IRepository? _bugReportAttachments;
private IRepository? _contactSubmissions;
private IRepository? _manufacturerLookupPatterns;
// Gift Certificates
private IRepository? _giftCertificates;
private IRepository? _giftCertificateRedemptions;
// Customer Intake Kiosk
private IRepository? _kioskSessions;
// Purchase Orders
private IPurchaseOrderRepository? _purchaseOrders;
private IRepository? _purchaseOrderItems;
// Oven Scheduling
private IRepository? _ovenBatches;
private IRepository? _ovenBatchItems;
// Invoices, Payments & Deposits
private IInvoiceRepository? _invoices;
private IRepository? _invoiceItems;
private IRepository? _payments;
private IRepository? _deposits;
// Expense Tracking / Accounts Payable
private IRepository? _accounts;
private IBillRepository? _bills;
private IRepository? _billLineItems;
private IRepository? _billPayments;
private IRepository? _expenses;
// Manual Journal Entries
private IRepository? _journalEntries;
private IRepository? _journalEntryLines;
// Vendor Credits
private IRepository? _vendorCredits;
private IRepository? _vendorCreditLineItems;
private IRepository? _vendorCreditApplications;
// Bank Reconciliation
private IRepository? _bankReconciliations;
// Tax Rates
private IRepository? _taxRates;
// Recurring Transactions
private IRepository? _recurringTemplates;
private IRepository? _fixedAssets;
private IRepository? _fixedAssetDepreciationEntries;
private IRepository? _budgets;
private IRepository? _budgetLines;
private IRepository? _yearEndCloses;
///
/// Initialises the unit of work with the scoped .
/// The context is shared across all repositories created by this instance so that
/// all reads and writes within a single request participate in the same EF Core identity map
/// and change-tracker, enabling zero-copy entity sharing between repositories.
///
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
}
// -------------------------------------------------------------------------
// Repository properties — lazy-initialised via the null-coalescing assignment
// operator (??=). Each property creates one Repository instance at most
// per UnitOfWork lifetime (= per HTTP request when registered as Scoped).
// Repositories share the underlying DbContext so they share the change tracker.
// -------------------------------------------------------------------------
/// Repository for tenant records. Soft-delete filter only (no tenant filter — SuperAdmin manages all companies).
public IRepository Companies =>
_companies ??= new Repository(_context);
/// Repository for (one-to-one with Company).
public IRepository CompanyOperatingCosts =>
_companyOperatingCosts ??= new Repository(_context);
/// Repository for (one-to-one with Company).
public IRepository CompanyPreferences =>
_companyPreferences ??= new Repository(_context);
/// Repository for audit records. Tenant-filtered; never soft-deleted — legal audit trail.
public IRepository CompanySmsAgreements =>
_companySmsAgreements ??= new Repository(_context);
// AI Predictions
/// Repository for records; tenant-filtered. Shared between QuoteItem and JobItem via a single nullable FK — no duplication on quote→job conversion.
public IRepository AiItemPredictions =>
_aiItemPredictions ??= new Repository(_context);
// Powder Insights
/// Repository for records capturing per-coat powder consumption; used by powder-usage analytics.
public IPowderUsageLogRepository PowderUsageLogs =>
_powderUsageLogs ??= new PowderUsageLogRepository(_context);
// Core repositories
/// Repository for records (commercial and non-commercial); tenant-filtered with soft delete.
public ICustomerRepository Customers =>
_customers ??= new CustomerRepository(_context);
/// Repository for records progressing through the 16-status lifecycle; tenant-filtered with soft delete.
public IJobRepository Jobs =>
_jobs ??= new JobRepository(_context);
/// Repository for overrides that let supervisors re-order the shop floor queue.
public IRepository JobDailyPriorities =>
_jobDailyPriorities ??= new Repository(_context);
/// Repository for line-items; tenant-filtered with soft delete.
public IRepository JobItems =>
_jobItems ??= new Repository(_context);
/// Repository for powder coat passes; tenant-filtered with soft delete.
public IJobItemCoatRepository JobItemCoats =>
_jobItemCoats ??= new JobItemCoatRepository(_context);
public IRepository JobItemPrepServices =>
_jobItemPrepServices ??= new Repository(_context);
/// Repository for audit entries; tenant-filtered with soft delete.
public IRepository JobChangeHistories =>
_jobChangeHistories ??= new Repository(_context);
/// Repository for job-level prep service assignments; tenant-filtered with soft delete.
public IRepository JobPrepServices =>
_jobPrepServices ??= new Repository(_context);
/// Repository for records with multi-item pricing; tenant-filtered with soft delete.
public IQuoteRepository Quotes =>
_quotes ??= new QuoteRepository(_context);
/// Repository for AI photo uploads; tenant-filtered with soft delete.
public IRepository QuotePhotos =>
_quotePhotos ??= new Repository(_context);
/// Repository for line-items; tenant-filtered with soft delete.
public IRepository QuoteItems =>
_quoteItems ??= new Repository(_context);
/// Repository for powder coat passes on a quote item; tenant-filtered with soft delete.
public IRepository QuoteItemCoats =>
_quoteItemCoats ??= new Repository(_context);
/// Repository for prep-service assignments on a quote item; tenant-filtered with soft delete.
public IRepository QuoteItemPrepServices =>
_quoteItemPrepServices ??= new Repository(_context);
/// Repository for audit entries; tenant-filtered with soft delete.
public IRepository QuoteChangeHistories =>
_quoteChangeHistories ??= new Repository(_context);
/// Repository for powder and material stock; tenant-filtered with soft delete.
public IRepository InventoryItems =>
_inventoryItems ??= new Repository(_context);
/// Platform-level powder catalog — no tenant filter, no soft delete.
public IPlainRepository PowderCatalog =>
_powderCatalog ??= new PlainRepository(_context);
/// Repository for stock movements; tenant-filtered with soft delete.
public IInventoryTransactionRepository InventoryTransactions =>
_inventoryTransactions ??= new InventoryTransactionRepository(_context);
/// Repository for records (ovens, sandblasters, booths); tenant-filtered with soft delete.
public IRepository Equipment =>
_equipment ??= new Repository(_context);
/// Repository for named oven configurations used by the Oven Scheduler; tenant-filtered with soft delete.
public IRepository OvenCosts =>
_ovenCosts ??= new Repository(_context);
/// Repository for named blast setups; tenant-filtered with soft delete.
public IRepository BlastSetups =>
_blastSetups ??= new Repository(_context);
/// Repository for equipment maintenance records; tenant-filtered with soft delete.
public IRepository MaintenanceRecords =>
_maintenanceRecords ??= new Repository(_context);
/// Repository for supplier records; tenant-filtered with soft delete.
public IRepository Vendors =>
_vendors ??= new Repository(_context);
/// Repository for attachments; tenant-filtered with soft delete.
public IJobPhotoRepository JobPhotos =>
_jobPhotos ??= new JobPhotoRepository(_context);
/// Repository for free-text staff notes on jobs; tenant-filtered with soft delete.
public IRepository JobNotes =>
_jobNotes ??= new Repository(_context);
/// Repository for free-text staff notes on customer records; tenant-filtered with soft delete.
public IRepository CustomerNotes =>
_customerNotes ??= new Repository(_context);
/// Repository for status-transition audit records; tenant-filtered with soft delete.
public IRepository JobStatusHistory =>
_jobStatusHistory ??= new Repository(_context);
/// Repository for customer discount tiers; tenant-filtered with soft delete.
public IRepository PricingTiers =>
_pricingTiers ??= new Repository(_context);
// Lookup tables (replacing enums)
// These are stored in the database instead of C# enums so that tenants can
// customise display names, colours, and display order without a code deployment.
/// Repository for DB-backed job status definitions.
public IRepository JobStatusLookups =>
_jobStatusLookups ??= new Repository(_context);
/// Repository for DB-backed job priority definitions.
public IRepository JobPriorityLookups =>
_jobPriorityLookups ??= new Repository(_context);
/// Repository for DB-backed quote status definitions.
public IRepository QuoteStatusLookups =>
_quoteStatusLookups ??= new Repository(_context);
/// Repository for user-defined inventory categories.
public IRepository InventoryCategoryLookups =>
_inventoryCategoryLookups ??= new Repository(_context);
/// Repository for appointment status definitions.
public IRepository AppointmentStatusLookups =>
_appointmentStatusLookups ??= new Repository(_context);
/// Repository for appointment type definitions.
public IRepository AppointmentTypeLookups =>
_appointmentTypeLookups ??= new Repository(_context);
/// Repository for prep-service catalog entries shared across quotes and jobs.
public IRepository PrepServices =>
_prepServices ??= new Repository(_context);
/// Repository for profiles with role assignments; tenant-filtered with soft delete.
public IRepository ShopWorkers =>
_shopWorkers ??= new Repository(_context);
/// Repository for per-role labour cost rates; unique on (CompanyId, Role).
private IRepository? _shopWorkerRoleCosts;
public IRepository ShopWorkerRoleCosts =>
_shopWorkerRoleCosts ??= new Repository(_context);
/// Repository for quality-failure and remediation records; tenant-filtered with soft delete.
private IRepository? _reworkRecords;
public IRepository ReworkRecords =>
_reworkRecords ??= new Repository(_context);
/// Repository for customer refund records; tenant-filtered with soft delete.
private IRepository? _refunds;
public IRepository Refunds =>
_refunds ??= new Repository(_context);
/// Repository for records issued to customers; unique memo number per company.
private IRepository? _creditMemos;
public IRepository CreditMemos =>
_creditMemos ??= new Repository(_context);
/// Repository for records linking credit memos to specific invoices.
private IRepository? _creditMemoApplications;
public IRepository CreditMemoApplications =>
_creditMemoApplications ??= new Repository(_context);
/// Repository for clock-in/clock-out worker time records; used for labour-cost calculations.
private IRepository? _jobTimeEntries;
public IRepository JobTimeEntries =>
_jobTimeEntries ??= new Repository(_context);
// Appointments
/// Repository for customer appointment records; tenant-filtered with soft delete.
public IRepository Appointments =>
_appointments ??= new Repository(_context);
// Product Catalog
/// Repository for hierarchical service catalog categories; tenant-filtered with soft delete.
public IRepository CatalogCategories =>
_catalogCategories ??= new Repository(_context);
/// Repository for pre-priced service catalog items; tenant-filtered with soft delete.
public IRepository CatalogItems =>
_catalogItems ??= new Repository(_context);
/// Repository for AI price-check results archived per company.
public IRepository CatalogPriceCheckReports =>
_catalogPriceCheckReports ??= new Repository(_context);
// Notifications
/// Repository for outbound notification audit records; provides IgnoreQueryFilters lookups by InvoiceId, QuoteId, and JobId for notification history panels.
public INotificationLogRepository NotificationLogs =>
_notificationLogs ??= new NotificationLogRepository(_context);
/// Repository for per-company channel template overrides; unique on (CompanyId, Type, Channel).
public IRepository NotificationTemplates =>
_notificationTemplates ??= new Repository(_context);
// Subscription
/// Repository for Stripe plan definitions; global (no tenant filter).
public IRepository SubscriptionPlanConfigs =>
_subscriptionPlanConfigs ??= new Repository(_context);
// Platform content
/// Repository for platform-wide announcements; no tenant filter, no soft delete.
public IPlainRepository Announcements =>
_announcements ??= new PlainRepository(_context);
/// Repository for IP ban records; no tenant filter, no soft delete.
public IPlainRepository BannedIps =>
_bannedIps ??= new PlainRepository(_context);
/// Repository for rotating tip-of-the-day entries; no tenant filter, no soft delete.
public IPlainRepository DashboardTips =>
_dashboardTips ??= new PlainRepository(_context);
/// Repository for bell-notification records; tenant-filtered with soft delete.
public IRepository InAppNotifications =>
_inAppNotifications ??= new Repository(_context);
/// Repository for platform changelog entries; no tenant filter, no soft delete.
public IPlainRepository ReleaseNotes =>
_releaseNotes ??= new PlainRepository(_context);
// Bug Reports
/// Repository for user-submitted bug reports; tenant-filtered with soft delete.
public IRepository BugReports =>
_bugReports ??= new Repository(_context);
public IRepository BugReportAttachments =>
_bugReportAttachments ??= new Repository(_context);
// Contact Us
/// Repository for contact form submissions; platform admins see all, company users see their own.
public IRepository ContactSubmissions =>
_contactSubmissions ??= new Repository(_context);
/// Repository for global AI URL patterns; soft-delete only (CompanyId = 0 by convention).
public IRepository ManufacturerLookupPatterns =>
_manufacturerLookupPatterns ??= new Repository(_context);
// Gift Certificates
/// Repository for records; certificate code unique per company.
public IRepository GiftCertificates =>
_giftCertificates ??= new Repository(_context);
/// Repository for usage events against a gift certificate.
public IRepository GiftCertificateRedemptions =>
_giftCertificateRedemptions ??= new Repository(_context);
/// Repository for customer self-service intake sessions; tenant-filtered with soft delete.
public IRepository KioskSessions =>
_kioskSessions ??= new Repository(_context);
// Job Templates
/// Repository for reusable job blueprints; tenant-filtered with soft delete.
public IJobTemplateRepository JobTemplates =>
_jobTemplates ??= new JobTemplateRepository(_context);
/// Repository for item definitions within a job template.
public IRepository JobTemplateItems =>
_jobTemplateItems ??= new Repository(_context);
/// Repository for coat definitions within a job template item.
public IRepository JobTemplateItemCoats =>
_jobTemplateItemCoats ??= new Repository(_context);
/// Repository for prep-service definitions within a job template item.
public IRepository JobTemplateItemPrepServices =>
_jobTemplateItemPrepServices ??= new Repository(_context);
// Purchase Orders
/// Repository for vendor purchase orders; tenant-filtered with soft delete.
public IPurchaseOrderRepository PurchaseOrders =>
_purchaseOrders ??= new PurchaseOrderRepository(_context);
/// Repository for line-items on a purchase order; cascade-deleted with the PO.
public IRepository PurchaseOrderItems =>
_purchaseOrderItems ??= new Repository(_context);
// Oven Scheduling
/// Repository for scheduled oven cure batches; tenant-filtered with soft delete.
public IRepository OvenBatches =>
_ovenBatches ??= new Repository(_context);
/// Repository for job/item assignments within an oven batch.
public IRepository OvenBatchItems =>
_ovenBatchItems ??= new Repository(_context);
// Invoices, Payments & Deposits
/// Repository for customer invoices (1:1 with Job); tenant-filtered with soft delete.
public IInvoiceRepository Invoices =>
_invoices ??= new InvoiceRepository(_context);
/// Repository for line-items on an invoice; tenant-filtered with soft delete.
public IRepository InvoiceItems =>
_invoiceItems ??= new Repository(_context);
/// Repository for customer payment records against invoices; tenant-filtered with soft delete.
public IRepository Payments =>
_payments ??= new Repository(_context);
///
/// Repository for pre-invoice deposit records.
/// Unapplied deposits are auto-swept into records when an invoice is created.
///
public IRepository Deposits =>
_deposits ??= new Repository(_context);
// Expense Tracking / Accounts Payable
/// Repository for chart-of-accounts entries; supports self-referencing parent/child hierarchy.
public IRepository Accounts =>
_accounts ??= new Repository(_context);
/// Repository for vendor bills (accounts payable); tenant-filtered with soft delete.
public IBillRepository Bills =>
_bills ??= new BillRepository(_context);
/// Repository for expense line-items on a vendor bill; each assigned to a chart-of-accounts entry.
public IRepository BillLineItems =>
_billLineItems ??= new Repository(_context);
/// Repository for cash disbursement records against a vendor bill.
public IRepository BillPayments =>
_billPayments ??= new Repository(_context);
/// Repository for ad-hoc non-bill expense records; tenant-filtered with soft delete.
public IRepository Expenses =>
_expenses ??= new Repository(_context);
// Manual Journal Entries
/// Repository for double-entry manual journal entries; tenant-filtered with soft delete.
public IRepository JournalEntries =>
_journalEntries ??= new Repository(_context);
/// Repository for individual debit/credit lines within a journal entry.
public IRepository JournalEntryLines =>
_journalEntryLines ??= new Repository(_context);
// Vendor Credits
/// Repository for credit notes received from vendors; tenant-filtered with soft delete.
public IRepository VendorCredits =>
_vendorCredits ??= new Repository(_context);
/// Repository for expense-reversal lines on a vendor credit.
public IRepository VendorCreditLineItems =>
_vendorCreditLineItems ??= new Repository(_context);
/// Repository for records linking a vendor credit to a specific bill.
public IRepository VendorCreditApplications =>
_vendorCreditApplications ??= new Repository(_context);
// Bank Reconciliation
/// Repository for sessions reconciling a bank account against a statement.
public IRepository BankReconciliations =>
_bankReconciliations ??= new Repository(_context);
// Tax Rates
/// Repository for named tax rates used to pre-fill invoice tax percent by jurisdiction.
public IRepository TaxRates =>
_taxRates ??= new Repository(_context);
// Recurring Transactions
/// Repository for — saved recipes that auto-generate bills or expenses on a schedule.
public IRepository RecurringTemplates =>
_recurringTemplates ??= new Repository(_context);
public IRepository FixedAssets =>
_fixedAssets ??= new Repository(_context);
public IRepository FixedAssetDepreciationEntries =>
_fixedAssetDepreciationEntries ??= new Repository(_context);
public IRepository Budgets =>
_budgets ??= new Repository(_context);
public IRepository BudgetLines =>
_budgetLines ??= new Repository(_context);
public IRepository YearEndCloses =>
_yearEndCloses ??= new Repository(_context);
///
/// Flushes all pending changes in the EF Core change tracker to the database.
/// Returns the number of state entries written.
///