Commit remaining unstaged changes from this session

- Platform settings service: IPlatformSettingsService, PlatformSettingKeys,
  PlatformSettingsService, SubscriptionService, AppConstants,
  SubscriptionExpiryBackgroundService, SubscriptionMiddleware
- JobTimeEntry entity, DTOs, AutoMapper profile (ShopWorker → UserId migration)
- InventoryDtos: SourceTransactionId on PowderUsageLogDto
- InventoryTransactionRepository: include Job.Customer in ledger query
- InventoryAiLookupService: @graph unwrap + HTML price fallback
- ApplicationDbContextModelSnapshot: reflect migration changes
- launchSettings.json, publish profile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 21:20:30 -04:00
parent 4c070e7487
commit 20ae11be03
16 changed files with 177 additions and 35 deletions
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using PowderCoating.Application.Interfaces;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Core.Interfaces;
@@ -27,11 +28,13 @@ public class SubscriptionService : ISubscriptionService
{
private readonly IUnitOfWork _unitOfWork;
private readonly ApplicationDbContext _context;
private readonly IPlatformSettingsService _platformSettings;
public SubscriptionService(IUnitOfWork unitOfWork, ApplicationDbContext context)
public SubscriptionService(IUnitOfWork unitOfWork, ApplicationDbContext context, IPlatformSettingsService platformSettings)
{
_unitOfWork = unitOfWork;
_context = context;
_platformSettings = platformSettings;
}
/// <summary>
@@ -203,7 +206,8 @@ public class SubscriptionService : ISubscriptionService
if (daysUntil == null || daysUntil > 0)
return SubscriptionStatus.Active;
if (daysUntil >= -AppConstants.SubscriptionConstants.GracePeriodDays)
var graceDays = await GetEffectiveGracePeriodDaysAsync(company);
if (daysUntil >= -graceDays)
return SubscriptionStatus.GracePeriod;
return SubscriptionStatus.Expired;
@@ -226,6 +230,25 @@ public class SubscriptionService : ISubscriptionService
return (int)(expiry - today).TotalDays;
}
/// <summary>
/// Returns the effective grace period in days for a company. Trial companies (no Stripe subscription)
/// get 0 days unless the <c>GracePeriodAppliesToTrials</c> platform setting is explicitly enabled.
/// Paid companies always use the configured <c>GracePeriodDays</c> value.
/// </summary>
private async Task<int> GetEffectiveGracePeriodDaysAsync(Company company)
{
var isTrial = string.IsNullOrEmpty(company.StripeSubscriptionId);
if (isTrial)
{
var appliesToTrials = await _platformSettings.GetBoolAsync(
PlatformSettingKeys.GracePeriodAppliesToTrials, defaultValue: false);
if (!appliesToTrials) return 0;
}
return await _platformSettings.GetIntAsync(
PlatformSettingKeys.GracePeriodDays,
AppConstants.SubscriptionConstants.GracePeriodDays);
}
/// <summary>
/// Returns the total catalog item count (including soft-deleted) and the plan maximum.
/// Returns a max of -1 when the plan config is absent (unlimited by default).