Initial commit

This commit is contained in:
2026-04-23 21:38:24 -04:00
commit 63e12a9636
1762 changed files with 1672620 additions and 0 deletions
@@ -0,0 +1,354 @@
using System.ComponentModel.DataAnnotations;
using PowderCoating.Core.Enums;
namespace PowderCoating.Application.DTOs.Wizard;
/// <summary>
/// Tracks the current user's wizard progress (read from CompanyPreferences)
/// </summary>
public class WizardProgressDto
{
public bool Started { get; set; }
public bool Completed { get; set; }
public List<int> DoneSteps { get; set; } = new();
public List<int> SkippedSteps { get; set; } = new();
public const int TotalSteps = 10;
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;
}
// ─── 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
{
/// <summary>JSON array of WizardInventoryItemDto submitted as a hidden field</summary>
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
{
/// <summary>JSON array of WizardTeamMemberDto submitted as a hidden field</summary>
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
{
/// <summary>JSON array of WizardVendorDto submitted as a hidden field</summary>
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
{
/// <summary>JSON array of WizardOvenDto submitted as a hidden field</summary>
public string? OvensJson { get; set; }
/// <summary>JSON array of WizardBlastSetupDto submitted as a hidden field</summary>
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;
/// <summary>Maps to BlastSetupType enum: 0=SiphonCabinet, 1=SiphonPot, 2=PressurePot, 3=WetBlasting</summary>
public int SetupType { get; set; } = 2;
public decimal CompressorCfm { get; set; }
/// <summary>Nozzle orifice number 28.</summary>
public int BlastNozzleSize { get; set; } = 5;
/// <summary>Maps to BlastSubstrateType enum: 0=Paint, 1=PowderCoat, 2=RustAndScale, 3=Mixed</summary>
public int PrimarySubstrate { get; set; } = 3;
public decimal? BlastRateSqFtPerHourOverride { get; set; }
public bool IsDefault { get; set; }
}
// ─── Step 15: Pricing Tiers ──────────────────────────────────────────────────
public class WizardPricingTiersStepDto
{
/// <summary>JSON array of WizardPricingTierDto submitted as a hidden field</summary>
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
{
/// <summary>JSON array of WizardCatalogItemDto submitted as a hidden field</summary>
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; }
}