using System.ComponentModel.DataAnnotations; using PowderCoating.Core.Enums; namespace PowderCoating.Core.Entities { public class CompanyOperatingCosts : BaseEntity { // Navigation public new int CompanyId { get; set; } public virtual Company Company { get; set; } = null!; // Labor Rates (per hour, in currency) [Range(0, 10000)] public decimal StandardLaborRate { get; set; } /// /// Actual labor cost per hour (wages + burden) used exclusively for internal job costing and profit/margin display. /// This is NOT the billing rate — it should reflect what you actually pay workers. /// When null, the costing engine defaults to 20% of StandardLaborRate. /// [Range(0, 10000)] public decimal? LaborCostPerHour { get; set; } // Additional Coat Labor Percentage (percentage of base labor for each additional coat beyond the first) [Range(0, 100)] public decimal AdditionalCoatLaborPercent { get; set; } = 30m; // Equipment Operating Costs (per hour) [Range(0, 10000)] public decimal OvenOperatingCostPerHour { get; set; } [Range(0, 10000)] public decimal SandblasterCostPerHour { get; set; } [Range(0, 10000)] public decimal CoatingBoothCostPerHour { get; set; } // Material Costs [Range(0, 1000)] public decimal PowderCoatingCostPerSqFt { get; set; } // per square foot // Markup / Margin /// Whether markup is applied to material only, or margin is applied to total item cost. public PricingMode PricingMode { get; set; } = PricingMode.MarkupOnMaterial; /// Markup % added on top of material costs (used when PricingMode = MarkupOnMaterial). [Range(0, 100)] public decimal GeneralMarkupPercentage { get; set; } /// Target gross margin % applied to total item cost (used when PricingMode = MarginOnTotalCost). [Range(0, 99)] public decimal TargetMarginPercent { get; set; } // Tax Percentage [Range(0, 100)] public decimal TaxPercent { get; set; } // Shop Supplies Rate (percentage applied to materials/labor) [Range(0, 100)] public decimal ShopSuppliesRate { get; set; } // Oven batch defaults [Range(1, 1440)] public int DefaultOvenCycleMinutes { get; set; } = 45; // Rush Charge public string RushChargeType { get; set; } = "Percentage"; // "Percentage" or "FixedAmount" [Range(0, 100)] public decimal RushChargePercentage { get; set; } [Range(0, 100000)] public decimal RushChargeFixedAmount { get; set; } // Shop Minimum [Range(0, 100000)] public decimal ShopMinimumCharge { get; set; } // Part Complexity Multipliers (% added to calculated item price) [Range(0, 500)] public decimal ComplexitySimplePercent { get; set; } = 0m; [Range(0, 500)] public decimal ComplexityModeratePercent { get; set; } = 5m; [Range(0, 500)] public decimal ComplexityComplexPercent { get; set; } = 15m; [Range(0, 500)] public decimal ComplexityExtremePercent { get; set; } = 25m; /// /// Free-text description of this shop's specialties, typical item types, and pricing style. /// Injected into the AI system prompt so the model can calibrate estimates for this company. /// Example: "We specialize in automotive restoration parts — wheels, frames, brackets. /// We charge premium rates and rarely work on items over 20 sqft." /// [StringLength(2000)] public string? AiContextProfile { get; set; } // ── Shop Capability / Quoting Calibration ───────────────────────────────────── // These fields drive the derived BlastRateSqFtPerHour used in AI photo quoting // and calculated item time suggestions. The Override field lets a shop bypass the // formula and enter their real-world number directly. /// Broad capability tier chosen in Setup Wizard; sets sensible defaults for all fields below. public ShopCapabilityTier ShopCapabilityTier { get; set; } = ShopCapabilityTier.Small; /// Type of blasting setup — biggest single factor in blast throughput. public BlastSetupType BlastSetupType { get; set; } = BlastSetupType.SiphonCabinet; /// Compressor CFM available for the blast setup. [Range(0, 2000)] public decimal CompressorCfm { get; set; } = 0m; /// Blast nozzle size (#3 – #8). Larger nozzle = more media flow. [Range(3, 8)] public int BlastNozzleSize { get; set; } = 4; /// Primary substrate being removed; affects passes required per sqft. public BlastSubstrateType PrimaryBlastSubstrate { get; set; } = BlastSubstrateType.Mixed; /// /// Manual blast rate override (sqft/hr). When set, the formula is bypassed entirely. /// Useful for dialed-in shops who know their exact throughput. /// [Range(0, 5000)] public decimal? BlastRateSqFtPerHourOverride { get; set; } /// Coating gun technology — affects application speed on complex parts. public CoatingGunType CoatingGunType { get; set; } = CoatingGunType.Corona; /// /// Manual coating application rate override (sqft/hr). When set, bypasses the formula. /// [Range(0, 5000)] public decimal? CoatingRateSqFtPerHourOverride { get; set; } // ── Facility Overhead ───────────────────────────────────────────────────── // Monthly fixed costs divided by billable hours to derive a per-hour overhead // rate, which is then applied to each quote based on estimated labor time. /// Monthly shop lease / rent payment. [Range(0, 1000000)] public decimal MonthlyRent { get; set; } = 0m; /// Monthly utilities combined (electricity, gas, water, internet). [Range(0, 1000000)] public decimal MonthlyUtilities { get; set; } = 0m; /// /// Estimated billable shop hours per month used to amortise facility overhead. /// Defaults to 160 (4 weeks × 40 hrs). Must be at least 1 to avoid division by zero. /// [Range(1, 10000)] public int MonthlyBillableHours { get; set; } = 160; } }