using System.ComponentModel.DataAnnotations; using PowderCoating.Core.Enums; namespace PowderCoating.Application.DTOs.Wizard; /// /// Tracks the current user's wizard progress (read from CompanyPreferences) /// public class WizardProgressDto { public bool Started { get; set; } public bool Completed { get; set; } public List DoneSteps { get; set; } = new(); public List SkippedSteps { get; set; } = new(); public const int TotalSteps = 5; public bool IsStepDone(int step) => DoneSteps.Contains(step); public bool IsStepSkipped(int step) => SkippedSteps.Contains(step); public bool IsStepTouched(int step) => IsStepDone(step) || IsStepSkipped(step); public int CompletedCount => DoneSteps.Count + SkippedSteps.Count; public int ProgressPercent => TotalSteps == 0 ? 0 : (int)Math.Round((double)CompletedCount / TotalSteps * 100); } // ─── Step 1: Company Profile ──────────────────────────────────────────────── public class WizardStep1Dto { [Required, MaxLength(200)] [Display(Name = "Company Name")] public string CompanyName { get; set; } = string.Empty; [MaxLength(200)] [Display(Name = "Primary Contact Name")] public string? PrimaryContactName { get; set; } [EmailAddress, MaxLength(200)] [Display(Name = "Primary Contact Email")] public string? PrimaryContactEmail { get; set; } [Phone, MaxLength(50)] [Display(Name = "Phone")] public string? Phone { get; set; } [MaxLength(300)] [Display(Name = "Address")] public string? Address { get; set; } [MaxLength(100)] [Display(Name = "City")] public string? City { get; set; } [MaxLength(100)] [Display(Name = "State")] public string? State { get; set; } [MaxLength(20)] [Display(Name = "ZIP Code")] public string? ZipCode { get; set; } [MaxLength(100)] [Display(Name = "Time Zone")] public string? TimeZone { get; set; } [MaxLength(10)] [Display(Name = "Default Currency")] public string DefaultCurrency { get; set; } = "USD"; [Display(Name = "Use Metric System (metres / kilograms)")] public bool UseMetricSystem { get; set; } = false; } // ─── Step 2: QuickBooks Migration ─────────────────────────────────────────── public class WizardStep2QbDto { public bool MigratingFromQuickBooks { get; set; } = false; } // ─── Step 3: Operating Costs (was Step 2) ─────────────────────────────────── public class WizardStep2Dto { [Range(0, 10000)] [Display(Name = "Standard Labor Rate ($/hr)")] public decimal StandardLaborRate { get; set; } [Range(0, 10000)] [Display(Name = "Sandblaster Cost ($/hr)")] public decimal SandblasterCostPerHour { get; set; } [Range(0, 10000)] [Display(Name = "Coating Booth Cost ($/hr)")] public decimal CoatingBoothCostPerHour { get; set; } [Range(0, 10000)] [Display(Name = "Oven Operating Cost ($/hr)")] public decimal OvenOperatingCostPerHour { get; set; } [Range(0, 1000)] [Display(Name = "Powder Coating Cost ($/sq ft)")] public decimal PowderCoatingCostPerSqFt { get; set; } [Range(0, 100)] [Display(Name = "General Markup (%)")] public decimal GeneralMarkupPercentage { get; set; } [Range(0, 100)] [Display(Name = "Tax Rate (%)")] public decimal TaxPercent { get; set; } [Range(0, 100000)] [Display(Name = "Shop Minimum Charge ($)")] public decimal ShopMinimumCharge { get; set; } [Display(Name = "Shop Capability Tier")] public ShopCapabilityTier ShopCapabilityTier { get; set; } = ShopCapabilityTier.Small; // Facility Overhead [Range(0, 1000000)] [Display(Name = "Monthly Rent ($)")] public decimal MonthlyRent { get; set; } = 0m; [Range(0, 1000000)] [Display(Name = "Monthly Utilities ($)")] public decimal MonthlyUtilities { get; set; } = 0m; [Range(1, 10000)] [Display(Name = "Billable Hours/Month")] public int MonthlyBillableHours { get; set; } = 160; } // ─── Step 3: Branding & Numbering ─────────────────────────────────────────── public class WizardStep3Dto { [MaxLength(10)] [Display(Name = "Quote Number Prefix")] public string QuoteNumberPrefix { get; set; } = "QT"; [MaxLength(10)] [Display(Name = "Job Number Prefix")] public string JobNumberPrefix { get; set; } = "JOB"; [MaxLength(10)] [Display(Name = "Invoice Number Prefix")] public string InvoiceNumberPrefix { get; set; } = "INV"; [MaxLength(7)] [Display(Name = "Quote Accent Color")] public string QtAccentColor { get; set; } = "#374151"; [MaxLength(7)] [Display(Name = "Invoice Accent Color")] public string InAccentColor { get; set; } = "#374151"; [MaxLength(7)] [Display(Name = "Work Order Accent Color")] public string WoAccentColor { get; set; } = "#374151"; } // ─── Step 4: Invoice & Quote Defaults ─────────────────────────────────────── public class WizardStep4Dto { [MaxLength(100)] [Display(Name = "Default Payment Terms")] public string DefaultPaymentTerms { get; set; } = "Net 30"; [Range(1, 365)] [Display(Name = "Quote Validity (days)")] public int DefaultQuoteValidityDays { get; set; } = 30; [Range(1, 365)] [Display(Name = "Default Turnaround (days)")] public int DefaultTurnaroundDays { get; set; } = 7; [MaxLength(2000)] [Display(Name = "Default Quote Terms & Conditions")] public string? QtDefaultTerms { get; set; } [MaxLength(500)] [Display(Name = "Quote Footer Note")] public string? QtFooterNote { get; set; } } // ─── Step 5: Job & Workflow ────────────────────────────────────────────────── public class WizardStep5Dto { [Display(Name = "Default Job Priority")] public string DefaultJobPriority { get; set; } = "Normal"; [Display(Name = "Require Customer PO Number")] public bool RequireCustomerPO { get; set; } = false; [Display(Name = "Allow Customer Approval via Link")] public bool AllowCustomerApproval { get; set; } = true; } // ─── Step 6: Chart of Accounts (review only) ──────────────────────────────── public class WizardStep6Dto { public int AccountCount { get; set; } public int RevenueAccounts { get; set; } public int ExpenseAccounts { get; set; } public int AssetAccounts { get; set; } } // ─── Step 7: Notification Preferences ─────────────────────────────────────── public class WizardStep7Dto { [Display(Name = "Enable Email Notifications")] public bool EmailNotificationsEnabled { get; set; } = true; [EmailAddress, MaxLength(200)] [Display(Name = "Send Emails From Address")] public string? EmailFromAddress { get; set; } [MaxLength(200)] [Display(Name = "From Display Name")] public string? EmailFromName { get; set; } [Display(Name = "Notify on New Job Created")] public bool NotifyOnNewJob { get; set; } = true; [Display(Name = "Notify on New Quote Created")] public bool NotifyOnNewQuote { get; set; } = true; [Display(Name = "Notify on Job Status Change")] public bool NotifyOnJobStatusChange { get; set; } = true; [Display(Name = "Notify on Quote Approval")] public bool NotifyOnQuoteApproval { get; set; } = true; [Display(Name = "Notify on Payment Received")] public bool NotifyOnPaymentReceived { get; set; } = true; [Display(Name = "Enable Automated Payment Reminders")] public bool PaymentRemindersEnabled { get; set; } = false; [MaxLength(50)] [Display(Name = "Reminder Days (comma-separated, days past due)")] public string PaymentReminderDays { get; set; } = "7,14,30"; // Alert Thresholds [Range(0, 90)] [Display(Name = "Quote Expiry Warning (days before expiry)")] public int QuoteExpiryWarningDays { get; set; } = 3; [Range(0, 90)] [Display(Name = "Due Date Warning (days before due)")] public int DueDateWarningDays { get; set; } = 2; [Range(0, 90)] [Display(Name = "Maintenance Alert (days before scheduled date)")] public int MaintenanceAlertDays { get; set; } = 7; } // ─── Step 8: Inventory / Powder Colors ────────────────────────────────────── public class WizardStep8Dto { /// JSON array of WizardInventoryItemDto submitted as a hidden field public string? ItemsJson { get; set; } } public class WizardInventoryItemDto { public string Name { get; set; } = string.Empty; public string? ColorName { get; set; } public string? ColorCode { get; set; } public string? Manufacturer { get; set; } public string? Finish { get; set; } public decimal UnitCost { get; set; } public decimal QuantityOnHand { get; set; } } // ─── Step 9: Team Members ──────────────────────────────────────────────────── public class WizardStep9Dto { /// JSON array of WizardTeamMemberDto submitted as a hidden field public string? MembersJson { get; set; } } public class WizardTeamMemberDto { public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; public string CompanyRole { get; set; } = "Worker"; } // ─── Step 10: Vendors & Suppliers ──────────────────────────────────────────── public class WizardStep10Dto { /// JSON array of WizardVendorDto submitted as a hidden field public string? VendorsJson { get; set; } } public class WizardVendorDto { public string CompanyName { get; set; } = string.Empty; public string? ContactName { get; set; } public string? Email { get; set; } public string? Phone { get; set; } public string? Website { get; set; } } // ─── Step 14: Equipment / Named Ovens + Blast Setups ──────────────────────── public class WizardOvensStepDto { /// JSON array of WizardOvenDto submitted as a hidden field public string? OvensJson { get; set; } /// JSON array of WizardBlastSetupDto submitted as a hidden field public string? BlastSetupsJson { get; set; } } public class WizardOvenDto { public int Id { get; set; } public string Label { get; set; } = string.Empty; public decimal CostPerHour { get; set; } public decimal? MaxLoadSqFt { get; set; } public int? DefaultCycleMinutes { get; set; } } public class WizardBlastSetupDto { public int Id { get; set; } public string Name { get; set; } = string.Empty; /// Maps to BlastSetupType enum: 0=SiphonCabinet, 1=SiphonPot, 2=PressurePot, 3=WetBlasting public int SetupType { get; set; } = 2; public decimal CompressorCfm { get; set; } /// Nozzle orifice number 2–8. public int BlastNozzleSize { get; set; } = 5; /// Maps to BlastSubstrateType enum: 0=Paint, 1=PowderCoat, 2=RustAndScale, 3=Mixed public int PrimarySubstrate { get; set; } = 3; public decimal? BlastRateSqFtPerHourOverride { get; set; } public bool IsDefault { get; set; } } // ─── Step 15: Pricing Tiers ────────────────────────────────────────────────── public class WizardPricingTiersStepDto { /// JSON array of WizardPricingTierDto submitted as a hidden field public string? TiersJson { get; set; } } public class WizardPricingTierDto { public int Id { get; set; } public string TierName { get; set; } = string.Empty; public string? Description { get; set; } public decimal DiscountPercent { get; set; } } // ─── Step 16: Service Catalog ──────────────────────────────────────────────── public class WizardCatalogStepDto { /// JSON array of WizardCatalogItemDto submitted as a hidden field public string? ItemsJson { get; set; } } public class WizardCatalogItemDto { public string Name { get; set; } = string.Empty; public string? Description { get; set; } public decimal DefaultPrice { get; set; } public decimal? ApproximateArea { get; set; } }