Files
PowderCoatingLogix/src/PowderCoating.Application/DTOs/Subscription/SubscriptionPlanConfigDto.cs
T
spouliot 6569d9c4ea Add SMS gating, TCPA terms agreement, and compose-before-send modal
- Three-tier SMS gate: platform kill-switch → admin force-disable → plan AllowSms → company opt-in
- CompanySmsAgreement entity records admin acceptance of TCPA terms with IP, user agent, and terms version
- SMS terms of service modal on Company Settings with versioned re-agreement (AppConstants.SmsTermsVersion)
- Dev redirect: non-production SMS routed to Twilio:DevRedirectPhone to protect real customer numbers
- Removed redundant Ready for Pickup SMS (Job Completed covers it)
- Role-based compose modal on job completion: Admin/Manager reviews and edits before send; ShopFloor auto-sends
- Send SMS button on job details for ad-hoc messages (Admin/Manager only)
- SendJobSmsAsync auto-appends STOP opt-out language if missing
- Migrations: AddSmsGating, AddCompanySmsAgreement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 22:29:39 -04:00

80 lines
3.1 KiB
C#

using System.ComponentModel.DataAnnotations;
namespace PowderCoating.Application.DTOs.Subscription;
public class SubscriptionPlanConfigDto
{
public int Id { get; set; }
public int Plan { get; set; }
public string DisplayName { get; set; } = string.Empty;
public string? Description { get; set; }
public int MaxUsers { get; set; }
public int MaxActiveJobs { get; set; }
public int MaxCustomers { get; set; }
public int MaxQuotes { get; set; }
public int MaxCatalogItems { get; set; }
public int MaxJobPhotos { get; set; }
public int MaxQuotePhotos { get; set; }
public int MaxAiPhotoQuotesPerMonth { get; set; }
public decimal MonthlyPrice { get; set; }
public decimal AnnualPrice { get; set; }
public string? StripePriceIdMonthly { get; set; }
public string? StripePriceIdAnnual { get; set; }
public bool AllowOnlinePayments { get; set; }
public bool AllowAccounting { get; set; }
public bool AllowAiPhotoQuotes { get; set; }
public bool AllowAiInventoryAssist { get; set; }
public bool AllowAiCatalogPriceCheck { get; set; }
public bool AllowSms { get; set; }
public bool IsActive { get; set; }
public int SortOrder { get; set; }
}
public class UpdateSubscriptionPlanConfigDto
{
public int Id { get; set; }
public string? Description { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxUsers { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxActiveJobs { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxCustomers { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxQuotes { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxCatalogItems { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxJobPhotos { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxQuotePhotos { get; set; }
[Range(-1, int.MaxValue, ErrorMessage = "Value must be -1 (unlimited) or a positive number")]
public int MaxAiPhotoQuotesPerMonth { get; set; }
[Range(0, 99999, ErrorMessage = "Price must be between 0 and 99999")]
public decimal MonthlyPrice { get; set; }
[Range(0, 99999, ErrorMessage = "Price must be between 0 and 99999")]
public decimal AnnualPrice { get; set; }
public string? StripePriceIdMonthly { get; set; }
public string? StripePriceIdAnnual { get; set; }
public bool AllowOnlinePayments { get; set; }
public bool AllowAccounting { get; set; }
public bool AllowAiPhotoQuotes { get; set; }
public bool AllowAiInventoryAssist { get; set; }
public bool AllowAiCatalogPriceCheck { get; set; }
public bool AllowSms { get; set; }
public bool IsActive { get; set; }
}