using System.ComponentModel.DataAnnotations; namespace PowderCoating.Application.DTOs.Customer; public class CustomerDto { public int Id { get; set; } public string? CompanyName { get; set; } public string? ContactFirstName { get; set; } public string? ContactLastName { get; set; } public string? Email { get; set; } public string? BillingEmail { get; set; } public string? Phone { get; set; } public string? MobilePhone { get; set; } public string? Address { get; set; } public string? City { get; set; } public string? State { get; set; } public string? ZipCode { get; set; } public string? Country { get; set; } public bool IsCommercial { get; set; } public string? TaxId { get; set; } public decimal CreditLimit { get; set; } public decimal CurrentBalance { get; set; } public decimal CreditBalance { get; set; } public string? PaymentTerms { get; set; } public int? PricingTierId { get; set; } public string? PricingTierName { get; set; } public bool IsTaxExempt { get; set; } public bool HasTaxExemptCertificate { get; set; } public string? TaxExemptCertificateFileName { get; set; } public string? GeneralNotes { get; set; } public bool IsActive { get; set; } public DateTime? LastContactDate { get; set; } public DateTime CreatedAt { get; set; } public bool NotifyByEmail { get; set; } public bool NotifyBySms { get; set; } public DateTime? SmsConsentedAt { get; set; } public string? SmsConsentMethod { get; set; } } public class CreateCustomerDto : IValidatableObject { [Display(Name = "Company Name")] [StringLength(200)] public string? CompanyName { get; set; } [Display(Name = "First Name")] [StringLength(100)] public string? ContactFirstName { get; set; } [Display(Name = "Last Name")] [StringLength(100)] public string? ContactLastName { get; set; } [Display(Name = "Email")] [StringLength(1000)] public string? Email { get; set; } [Display(Name = "Billing / Accounting Email")] [StringLength(1000)] public string? BillingEmail { get; set; } [Display(Name = "Phone")] [Phone(ErrorMessage = "Please enter a valid phone number")] [StringLength(20)] public string? Phone { get; set; } [Display(Name = "Mobile Phone")] [Phone(ErrorMessage = "Please enter a valid mobile phone number")] [StringLength(20)] public string? MobilePhone { get; set; } [Display(Name = "Street Address")] [StringLength(500)] public string? Address { get; set; } [Display(Name = "City")] [StringLength(100)] public string? City { get; set; } [Display(Name = "State")] [StringLength(50)] public string? State { get; set; } [Display(Name = "Zip Code")] [StringLength(20)] public string? ZipCode { get; set; } [Display(Name = "Country")] [StringLength(100)] public string? Country { get; set; } = "USA"; [Display(Name = "Customer Type")] public bool IsCommercial { get; set; } [Display(Name = "Tax ID / EIN")] [StringLength(50)] public string? TaxId { get; set; } [Display(Name = "Credit Limit")] [Range(0, 9999999.99)] public decimal CreditLimit { get; set; } [Display(Name = "Payment Terms")] [StringLength(50)] public string? PaymentTerms { get; set; } [Display(Name = "Pricing Tier")] public int? PricingTierId { get; set; } [Display(Name = "Tax Exempt")] public bool IsTaxExempt { get; set; } [Display(Name = "General Notes")] [StringLength(2000)] public string? GeneralNotes { get; set; } [Display(Name = "Notify by Email")] public bool NotifyByEmail { get; set; } = true; [Display(Name = "Notify by SMS")] public bool NotifyBySms { get; set; } = false; /// /// Form-only flag: staff checks this to record that the customer gave verbal consent to receive SMS. /// Not mapped to any entity field — the controller handles setting SmsConsentedAt and SmsConsentMethod. /// [Display(Name = "Customer has verbally consented to receive SMS notifications")] public bool SmsConsentGranted { get; set; } = false; public IEnumerable Validate(ValidationContext validationContext) { // At least one name field is required (Company Name OR Contact First/Last Name) if (string.IsNullOrWhiteSpace(CompanyName) && string.IsNullOrWhiteSpace(ContactFirstName) && string.IsNullOrWhiteSpace(ContactLastName)) { yield return new ValidationResult( "Please provide either a Company Name or a Contact Name (First and/or Last Name)", new[] { nameof(CompanyName), nameof(ContactFirstName), nameof(ContactLastName) }); } // At least one contact method is required (Email OR Phone) if (string.IsNullOrWhiteSpace(Email) && string.IsNullOrWhiteSpace(Phone)) { yield return new ValidationResult( "Please provide at least one contact method (Email or Phone)", new[] { nameof(Email), nameof(Phone) }); } // Validate each address in comma-separated email fields foreach (var addr in SplitEmails(Email)) { if (!IsValidEmail(addr)) yield return new ValidationResult( $"'{addr}' is not a valid email address.", new[] { nameof(Email) }); } foreach (var addr in SplitEmails(BillingEmail)) { if (!IsValidEmail(addr)) yield return new ValidationResult( $"'{addr}' is not a valid email address.", new[] { nameof(BillingEmail) }); } } private static IEnumerable SplitEmails(string? value) => string.IsNullOrWhiteSpace(value) ? [] : value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); private static bool IsValidEmail(string email) { try { _ = new System.Net.Mail.MailAddress(email); return true; } catch { return false; } } } public class UpdateCustomerDto : CreateCustomerDto { public int Id { get; set; } public bool IsActive { get; set; } public bool HasTaxExemptCertificate { get; set; } public string? TaxExemptCertificateFileName { get; set; } // Read from entity so the Edit view can display consent status public DateTime? SmsConsentedAt { get; set; } public string? SmsConsentMethod { get; set; } } public class CustomerListDto { public int Id { get; set; } public string? CompanyName { get; set; } public string ContactName { get; set; } = string.Empty; public string? Phone { get; set; } public string? Email { get; set; } public bool IsCommercial { get; set; } public decimal CurrentBalance { get; set; } public bool IsActive { get; set; } public DateTime? LastContactDate { get; set; } public bool NotifyByEmail { get; set; } public bool NotifyBySms { get; set; } } public class AddCreditDto { [Required] [Range(0.01, 99999.99, ErrorMessage = "Amount must be between $0.01 and $99,999.99")] public decimal Amount { get; set; } [Required] [StringLength(200, ErrorMessage = "Reason cannot exceed 200 characters")] [Display(Name = "Reason")] public string Reason { get; set; } = string.Empty; [StringLength(1000)] [Display(Name = "Notes")] public string? Notes { get; set; } [Display(Name = "Expiry Date (optional)")] public DateTime? ExpiryDate { get; set; } }