Initial commit
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
using System.Linq.Expressions;
|
||||
using PowderCoating.Core.Entities;
|
||||
|
||||
namespace PowderCoating.Core.Interfaces;
|
||||
|
||||
public interface IRepository<T> where T : BaseEntity
|
||||
{
|
||||
Task<T?> GetByIdAsync(int id, bool ignoreQueryFilters = false, params Expression<Func<T, object>>[] includes);
|
||||
Task<IEnumerable<T>> GetAllAsync(bool ignoreQueryFilters = false, params Expression<Func<T, object>>[] includes);
|
||||
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate, bool ignoreQueryFilters = false, params Expression<Func<T, object>>[] includes);
|
||||
Task<T?> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate, bool ignoreQueryFilters = false, params Expression<Func<T, object>>[] includes);
|
||||
Task<bool> AnyAsync(Expression<Func<T, bool>> predicate, bool ignoreQueryFilters = false);
|
||||
Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null, bool ignoreQueryFilters = false);
|
||||
|
||||
Task<T> AddAsync(T entity);
|
||||
Task<IEnumerable<T>> AddRangeAsync(IEnumerable<T> entities);
|
||||
|
||||
Task UpdateAsync(T entity);
|
||||
Task UpdateRangeAsync(IEnumerable<T> entities);
|
||||
|
||||
Task DeleteAsync(T entity);
|
||||
Task DeleteAsync(int id);
|
||||
Task DeleteRangeAsync(IEnumerable<T> entities);
|
||||
|
||||
// Soft delete
|
||||
Task SoftDeleteAsync(T entity);
|
||||
Task SoftDeleteAsync(int id);
|
||||
|
||||
// Pagination
|
||||
Task<(IEnumerable<T> Items, int TotalCount)> GetPagedAsync(
|
||||
int pageNumber,
|
||||
int pageSize,
|
||||
Expression<Func<T, bool>>? filter = null,
|
||||
Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null,
|
||||
params Expression<Func<T, object>>[] includes);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Core.Enums;
|
||||
|
||||
namespace PowderCoating.Core.Interfaces;
|
||||
|
||||
public interface ISubscriptionService
|
||||
{
|
||||
Task<bool> CanAddUserAsync(int companyId);
|
||||
Task<bool> CanAddJobAsync(int companyId);
|
||||
Task<bool> CanAddCustomerAsync(int companyId);
|
||||
Task<bool> CanAddQuoteAsync(int companyId);
|
||||
Task<bool> CanAddCatalogItemAsync(int companyId);
|
||||
Task<bool> CanAddJobPhotoAsync(int companyId, int jobId);
|
||||
Task<bool> CanAddQuotePhotoAsync(int companyId, int quoteId);
|
||||
Task<(int Used, int Max)> GetUserCountAsync(int companyId);
|
||||
Task<(int Used, int Max)> GetJobCountAsync(int companyId);
|
||||
Task<(int Used, int Max)> GetCustomerCountAsync(int companyId);
|
||||
Task<(int Used, int Max)> GetQuoteCountAsync(int companyId);
|
||||
Task<(int Used, int Max)> GetCatalogItemCountAsync(int companyId);
|
||||
Task<(int Used, int Max)> GetJobPhotoCountAsync(int companyId, int jobId);
|
||||
Task<(int Used, int Max)> GetQuotePhotoCountAsync(int companyId, int quoteId);
|
||||
Task<SubscriptionStatus> GetStatusAsync(int companyId);
|
||||
|
||||
// AI feature gating
|
||||
/// <summary>Returns true if the AI Inventory Assist lookup is enabled for this company.</summary>
|
||||
Task<bool> IsAiInventoryAssistEnabledAsync(int companyId);
|
||||
/// <summary>Returns true if the AI Photo Quote feature is enabled for this company (flag + quota check).</summary>
|
||||
Task<bool> CanUseAiPhotoQuoteAsync(int companyId);
|
||||
/// <summary>Returns (used this month, monthly max). Max = -1 means unlimited.</summary>
|
||||
Task<(int Used, int Max)> GetAiPhotoQuoteUsageAsync(int companyId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns days until expiry (negative = days past expiry). Returns null if no end date set.
|
||||
/// </summary>
|
||||
int? DaysUntilExpiry(Company company);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using PowderCoating.Core.Entities;
|
||||
|
||||
namespace PowderCoating.Core.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Provides context about the current tenant (company) in the multi-tenant system
|
||||
/// </summary>
|
||||
public interface ITenantContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current company ID from the authenticated user's claims
|
||||
/// </summary>
|
||||
/// <returns>The company ID if user is authenticated, null otherwise</returns>
|
||||
int? GetCurrentCompanyId();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full company entity for the current user
|
||||
/// </summary>
|
||||
/// <returns>The company entity if user is authenticated, null otherwise</returns>
|
||||
Task<Company?> GetCurrentCompanyAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current user is a SuperAdmin (platform administrator)
|
||||
/// </summary>
|
||||
/// <returns>True if user is SuperAdmin, false otherwise</returns>
|
||||
bool IsSuperAdmin();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true only for SuperAdmins tied to the platform demo company (ID 1).
|
||||
/// Platform admins have unrestricted cross-company data access.
|
||||
/// Company SuperAdmins (CompanyId != 1) are scoped to their own company's data.
|
||||
/// </summary>
|
||||
bool IsPlatformAdmin();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metric system preference for the current company
|
||||
/// </summary>
|
||||
/// <returns>True if metric system is enabled, false for imperial system</returns>
|
||||
Task<bool> UseMetricSystemAsync();
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using PowderCoating.Core.Entities;
|
||||
|
||||
namespace PowderCoating.Core.Interfaces;
|
||||
|
||||
public interface IUnitOfWork : IDisposable
|
||||
{
|
||||
// Multi-tenancy
|
||||
IRepository<Company> Companies { get; }
|
||||
IRepository<CompanyOperatingCosts> CompanyOperatingCosts { get; }
|
||||
IRepository<CompanyPreferences> CompanyPreferences { get; }
|
||||
|
||||
// AI Predictions
|
||||
IRepository<AiItemPrediction> AiItemPredictions { get; }
|
||||
|
||||
// Powder Insights
|
||||
IRepository<PowderUsageLog> PowderUsageLogs { get; }
|
||||
|
||||
// Core entities
|
||||
IRepository<Customer> Customers { get; }
|
||||
IRepository<Job> Jobs { get; }
|
||||
IRepository<JobDailyPriority> JobDailyPriorities { get; }
|
||||
IRepository<JobItem> JobItems { get; }
|
||||
IRepository<JobItemCoat> JobItemCoats { get; }
|
||||
IRepository<JobChangeHistory> JobChangeHistories { get; }
|
||||
IRepository<Quote> 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; }
|
||||
IRepository<InventoryTransaction> InventoryTransactions { get; }
|
||||
IRepository<Equipment> Equipment { get; }
|
||||
IRepository<OvenCost> OvenCosts { get; }
|
||||
IRepository<CompanyBlastSetup> BlastSetups { get; }
|
||||
IRepository<MaintenanceRecord> MaintenanceRecords { get; }
|
||||
IRepository<Vendor> Vendors { get; }
|
||||
IRepository<JobPhoto> JobPhotos { get; }
|
||||
IRepository<JobNote> JobNotes { get; }
|
||||
IRepository<CustomerNote> CustomerNotes { 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<ShopWorker> ShopWorkers { get; }
|
||||
IRepository<ShopWorkerRoleCost> ShopWorkerRoleCosts { 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; }
|
||||
|
||||
// Oven Scheduling
|
||||
IRepository<OvenBatch> OvenBatches { get; }
|
||||
IRepository<OvenBatchItem> OvenBatchItems { get; }
|
||||
|
||||
// Invoices, Payments & Deposits
|
||||
IRepository<Invoice> Invoices { get; }
|
||||
IRepository<InvoiceItem> InvoiceItems { get; }
|
||||
IRepository<Payment> Payments { get; }
|
||||
IRepository<Deposit> Deposits { get; }
|
||||
|
||||
// Purchase Orders
|
||||
IRepository<PurchaseOrder> PurchaseOrders { get; }
|
||||
IRepository<PurchaseOrderItem> PurchaseOrderItems { get; }
|
||||
|
||||
// Expense Tracking / Accounts Payable
|
||||
IRepository<Account> Accounts { get; }
|
||||
IRepository<Bill> Bills { get; }
|
||||
IRepository<BillLineItem> BillLineItems { get; }
|
||||
IRepository<BillPayment> BillPayments { get; }
|
||||
IRepository<Expense> Expenses { get; }
|
||||
|
||||
// Notifications
|
||||
IRepository<NotificationLog> NotificationLogs { get; }
|
||||
IRepository<NotificationTemplate> NotificationTemplates { get; }
|
||||
|
||||
// Subscription
|
||||
IRepository<SubscriptionPlanConfig> SubscriptionPlanConfigs { get; }
|
||||
|
||||
// Job Templates
|
||||
IRepository<JobTemplate> JobTemplates { get; }
|
||||
IRepository<JobTemplateItem> JobTemplateItems { get; }
|
||||
IRepository<JobTemplateItemCoat> JobTemplateItemCoats { get; }
|
||||
IRepository<JobTemplateItemPrepService> JobTemplateItemPrepServices { get; }
|
||||
|
||||
// Bug Reports
|
||||
IRepository<BugReport> BugReports { 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; }
|
||||
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user