diff --git a/src/PowderCoating.Application/DTOs/Company/CustomItemTemplateDtos.cs b/src/PowderCoating.Application/DTOs/Company/CustomItemTemplateDtos.cs
new file mode 100644
index 0000000..ed1f67b
--- /dev/null
+++ b/src/PowderCoating.Application/DTOs/Company/CustomItemTemplateDtos.cs
@@ -0,0 +1,174 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace PowderCoating.Application.DTOs.Company;
+
+// ============================================================================
+// LIST DTO - For Company Settings tab table
+// ============================================================================
+public class CustomItemTemplateListDto
+{
+ public int Id { get; set; }
+ public string Name { get; set; } = string.Empty;
+ public string? Description { get; set; }
+ public string OutputMode { get; set; } = "FixedRate";
+ public int FieldCount { get; set; }
+ public decimal? DefaultRate { get; set; }
+ public string? RateLabel { get; set; }
+ public bool IsActive { get; set; }
+ public int DisplayOrder { get; set; }
+ public string? DiagramImagePath { get; set; }
+}
+
+// ============================================================================
+// FULL DTO - For Edit modal and formula evaluation
+// ============================================================================
+public class CustomItemTemplateDto
+{
+ public int Id { get; set; }
+ public string Name { get; set; } = string.Empty;
+ public string? Description { get; set; }
+ public string OutputMode { get; set; } = "FixedRate";
+ public string FieldsJson { get; set; } = "[]";
+ public string Formula { get; set; } = string.Empty;
+ public decimal? DefaultRate { get; set; }
+ public string? RateLabel { get; set; }
+ public string? Notes { get; set; }
+ public int DisplayOrder { get; set; }
+ public bool IsActive { get; set; }
+ public string? DiagramImagePath { get; set; }
+}
+
+// ============================================================================
+// CREATE DTO
+// ============================================================================
+public class CreateCustomItemTemplateDto
+{
+ [Required]
+ [StringLength(100)]
+ public string Name { get; set; } = string.Empty;
+
+ [StringLength(500)]
+ public string? Description { get; set; }
+
+ /// "FixedRate" or "SurfaceAreaSqFt"
+ [Required]
+ public string OutputMode { get; set; } = "FixedRate";
+
+ /// JSON array of field definitions: [{name, label, unit, defaultValue}]
+ [Required]
+ public string FieldsJson { get; set; } = "[]";
+
+ [Required]
+ public string Formula { get; set; } = string.Empty;
+
+ public decimal? DefaultRate { get; set; }
+
+ [StringLength(50)]
+ public string? RateLabel { get; set; }
+
+ [StringLength(1000)]
+ public string? Notes { get; set; }
+
+ public int DisplayOrder { get; set; }
+ public bool IsActive { get; set; } = true;
+}
+
+// ============================================================================
+// UPDATE DTO
+// ============================================================================
+public class UpdateCustomItemTemplateDto
+{
+ public int Id { get; set; }
+
+ [Required]
+ [StringLength(100)]
+ public string Name { get; set; } = string.Empty;
+
+ [StringLength(500)]
+ public string? Description { get; set; }
+
+ [Required]
+ public string OutputMode { get; set; } = "FixedRate";
+
+ [Required]
+ public string FieldsJson { get; set; } = "[]";
+
+ [Required]
+ public string Formula { get; set; } = string.Empty;
+
+ public decimal? DefaultRate { get; set; }
+
+ [StringLength(50)]
+ public string? RateLabel { get; set; }
+
+ [StringLength(1000)]
+ public string? Notes { get; set; }
+
+ public int DisplayOrder { get; set; }
+ public bool IsActive { get; set; } = true;
+
+ /// Existing diagram path — kept if no new file is uploaded.
+ public string? DiagramImagePath { get; set; }
+}
+
+// ============================================================================
+// WIZARD PICKER DTO - Lean DTO for populating the quote wizard template list
+// ============================================================================
+public class CustomItemTemplatePickerDto
+{
+ public int Id { get; set; }
+ public string Name { get; set; } = string.Empty;
+ public string? Description { get; set; }
+ public string OutputMode { get; set; } = "FixedRate";
+ public string FieldsJson { get; set; } = "[]";
+ public string Formula { get; set; } = string.Empty;
+ public decimal? DefaultRate { get; set; }
+ public string? RateLabel { get; set; }
+ public string? DiagramImagePath { get; set; }
+}
+
+// ============================================================================
+// AI GENERATION DTOs
+// ============================================================================
+public class GenerateFormulaFromAiRequest
+{
+ [Required]
+ public string Description { get; set; } = string.Empty;
+}
+
+public class GenerateFormulaFromAiResponse
+{
+ public bool Success { get; set; }
+ public string? Error { get; set; }
+ public string? Name { get; set; }
+ public string? OutputMode { get; set; }
+ public string? FieldsJson { get; set; }
+ public string? Formula { get; set; }
+ public decimal? DefaultRate { get; set; }
+ public string? RateLabel { get; set; }
+ public string? Reasoning { get; set; }
+
+ /// Result of running the formula with any sample values found in the description.
+ public decimal? VerificationResult { get; set; }
+ public string? VerificationInputs { get; set; }
+}
+
+// ============================================================================
+// FORMULA EVALUATION DTOs
+// ============================================================================
+public class EvaluateFormulaRequest
+{
+ [Required]
+ public string Formula { get; set; } = string.Empty;
+
+ /// JSON object of variable name → value pairs, e.g. {"box_l": 43, "rate": 0.05}
+ [Required]
+ public string VariablesJson { get; set; } = "{}";
+}
+
+public class EvaluateFormulaResponse
+{
+ public bool Success { get; set; }
+ public decimal? Result { get; set; }
+ public string? Error { get; set; }
+}
diff --git a/src/PowderCoating.Application/DTOs/Job/JobDtos.cs b/src/PowderCoating.Application/DTOs/Job/JobDtos.cs
index da088ec..1eef12c 100644
--- a/src/PowderCoating.Application/DTOs/Job/JobDtos.cs
+++ b/src/PowderCoating.Application/DTOs/Job/JobDtos.cs
@@ -325,7 +325,11 @@ public class JobItemDto
public bool IsGenericItem { get; set; }
public bool IsLaborItem { get; set; }
public bool IsSalesItem { get; set; }
+ public bool IsAiItem { get; set; }
public string? Sku { get; set; }
+ public bool IsCustomFormulaItem { get; set; }
+ public int? CustomItemTemplateId { get; set; }
+ public string? FormulaFieldValuesJson { get; set; }
public List Coats { get; set; } = new();
public List PrepServices { get; set; } = new();
}
diff --git a/src/PowderCoating.Application/DTOs/Quote/QuoteDtos.cs b/src/PowderCoating.Application/DTOs/Quote/QuoteDtos.cs
index b754921..4d3827e 100644
--- a/src/PowderCoating.Application/DTOs/Quote/QuoteDtos.cs
+++ b/src/PowderCoating.Application/DTOs/Quote/QuoteDtos.cs
@@ -475,6 +475,11 @@ public class QuoteItemDto
public bool IsAiItem { get; set; }
+ // Custom formula item
+ public bool IsCustomFormulaItem { get; set; }
+ public int? CustomItemTemplateId { get; set; }
+ public string? FormulaFieldValuesJson { get; set; }
+
// Cost breakdown snapshot
public decimal ItemMaterialCost { get; set; }
public decimal ItemLaborCost { get; set; }
@@ -559,6 +564,11 @@ public class CreateQuoteItemDto
// ID of the AiItemPrediction record captured at analysis time (null for non-AI items)
public int? AiPredictionId { get; set; }
+
+ // Custom formula item routing — see IsCustomFormulaItem in PricingCalculationService
+ public bool IsCustomFormulaItem { get; set; }
+ public int? CustomItemTemplateId { get; set; }
+ public string? FormulaFieldValuesJson { get; set; }
}
// ============================================================================
diff --git a/src/PowderCoating.Application/Interfaces/ICustomFormulaAiService.cs b/src/PowderCoating.Application/Interfaces/ICustomFormulaAiService.cs
new file mode 100644
index 0000000..49a4db1
--- /dev/null
+++ b/src/PowderCoating.Application/Interfaces/ICustomFormulaAiService.cs
@@ -0,0 +1,22 @@
+using PowderCoating.Application.DTOs.Company;
+
+namespace PowderCoating.Application.Interfaces;
+
+public interface ICustomFormulaAiService
+{
+ ///
+ /// Generates a NCalc formula, field list, and notes from a natural-language description
+ /// and an optional diagram image. Returns a
+ /// ready to pre-fill the template editor.
+ ///
+ Task GenerateFormulaAsync(
+ GenerateFormulaFromAiRequest request,
+ byte[]? imageBytes = null,
+ string? imageContentType = null);
+
+ ///
+ /// Evaluates a NCalc formula with the supplied variable map and returns the numeric result.
+ /// Safe server-side only — no user-controlled code execution.
+ ///
+ EvaluateFormulaResponse EvaluateFormula(EvaluateFormulaRequest request);
+}
diff --git a/src/PowderCoating.Application/Mappings/CustomItemTemplateProfile.cs b/src/PowderCoating.Application/Mappings/CustomItemTemplateProfile.cs
new file mode 100644
index 0000000..d212035
--- /dev/null
+++ b/src/PowderCoating.Application/Mappings/CustomItemTemplateProfile.cs
@@ -0,0 +1,41 @@
+using AutoMapper;
+using PowderCoating.Core.Entities;
+using PowderCoating.Application.DTOs.Company;
+
+namespace PowderCoating.Application.Mappings;
+
+public class CustomItemTemplateProfile : Profile
+{
+ public CustomItemTemplateProfile()
+ {
+ CreateMap()
+ .ForMember(dest => dest.FieldCount,
+ opt => opt.MapFrom(src => CountFields(src.FieldsJson)));
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap()
+ .ForMember(dest => dest.DiagramImagePath, opt => opt.Ignore()); // set by controller after blob upload
+
+ CreateMap();
+ }
+
+ private static int CountFields(string fieldsJson)
+ {
+ try
+ {
+ var doc = System.Text.Json.JsonDocument.Parse(fieldsJson);
+ return doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Array
+ ? doc.RootElement.GetArrayLength()
+ : 0;
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+}
diff --git a/src/PowderCoating.Application/Mappings/QuoteProfile.cs b/src/PowderCoating.Application/Mappings/QuoteProfile.cs
index e4b845b..b0afae4 100644
--- a/src/PowderCoating.Application/Mappings/QuoteProfile.cs
+++ b/src/PowderCoating.Application/Mappings/QuoteProfile.cs
@@ -159,6 +159,7 @@ public class QuoteProfile : Profile
.ReverseMap()
.ForMember(dest => dest.Quote, opt => opt.Ignore())
.ForMember(dest => dest.CatalogItem, opt => opt.Ignore())
+ .ForMember(dest => dest.CustomItemTemplate, opt => opt.Ignore())
.ForMember(dest => dest.Coats, opt => opt.Ignore())
.ForMember(dest => dest.PrepServices, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
@@ -180,6 +181,7 @@ public class QuoteProfile : Profile
.ForMember(dest => dest.Coats, opt => opt.Ignore()) // Mapped separately
.ForMember(dest => dest.PrepServices, opt => opt.Ignore()) // Mapped separately
.ForMember(dest => dest.CatalogItem, opt => opt.Ignore())
+ .ForMember(dest => dest.CustomItemTemplate, opt => opt.Ignore()) // FK only; nav set by EF
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
diff --git a/src/PowderCoating.Application/PowderCoating.Application.csproj b/src/PowderCoating.Application/PowderCoating.Application.csproj
index 21bf084..394194b 100644
--- a/src/PowderCoating.Application/PowderCoating.Application.csproj
+++ b/src/PowderCoating.Application/PowderCoating.Application.csproj
@@ -16,6 +16,7 @@
+
diff --git a/src/PowderCoating.Application/Services/JobItemAssemblyService.cs b/src/PowderCoating.Application/Services/JobItemAssemblyService.cs
index bc2a240..2f301da 100644
--- a/src/PowderCoating.Application/Services/JobItemAssemblyService.cs
+++ b/src/PowderCoating.Application/Services/JobItemAssemblyService.cs
@@ -53,7 +53,10 @@ public class JobItemAssemblyService : IJobItemAssemblyService
IncludePrepCost = source.IncludePrepCost,
Complexity = source.Complexity,
AiTags = source.AiTags,
- AiPredictionId = source.AiPredictionId
+ AiPredictionId = source.AiPredictionId,
+ IsCustomFormulaItem = source.IsCustomFormulaItem,
+ CustomItemTemplateId = source.CustomItemTemplateId,
+ FormulaFieldValuesJson = source.FormulaFieldValuesJson
},
jobId,
companyId,
@@ -157,7 +160,10 @@ public class JobItemAssemblyService : IJobItemAssemblyService
IncludePrepCost = source.IncludePrepCost,
Complexity = source.Complexity,
AiTags = source.AiTags,
- AiPredictionId = source.AiPredictionId
+ AiPredictionId = source.AiPredictionId,
+ IsCustomFormulaItem = source.IsCustomFormulaItem,
+ CustomItemTemplateId = source.CustomItemTemplateId,
+ FormulaFieldValuesJson = source.FormulaFieldValuesJson
},
jobId,
companyId,
@@ -259,7 +265,10 @@ public class JobItemAssemblyService : IJobItemAssemblyService
IncludePrepCost = source.IncludePrepCost,
Complexity = source.Complexity,
AiTags = source.AiTags,
- AiPredictionId = source.AiPredictionId
+ AiPredictionId = source.AiPredictionId,
+ IsCustomFormulaItem = source.IsCustomFormulaItem,
+ CustomItemTemplateId = source.CustomItemTemplateId,
+ FormulaFieldValuesJson = source.FormulaFieldValuesJson
},
jobId,
companyId,
@@ -353,6 +362,9 @@ public class JobItemAssemblyService : IJobItemAssemblyService
Complexity = seed.Complexity,
AiTags = seed.AiTags,
AiPredictionId = seed.AiPredictionId,
+ IsCustomFormulaItem = seed.IsCustomFormulaItem,
+ CustomItemTemplateId = seed.CustomItemTemplateId,
+ FormulaFieldValuesJson = seed.FormulaFieldValuesJson,
CompanyId = companyId,
CreatedAt = createdAtUtc
};
@@ -480,6 +492,9 @@ public class JobItemAssemblyService : IJobItemAssemblyService
public string? Complexity { get; init; }
public string? AiTags { get; init; }
public int? AiPredictionId { get; init; }
+ public bool IsCustomFormulaItem { get; init; }
+ public int? CustomItemTemplateId { get; init; }
+ public string? FormulaFieldValuesJson { get; init; }
}
/// Intermediate value object for coat creation — see for rationale.
diff --git a/src/PowderCoating.Application/Services/PricingCalculationService.cs b/src/PowderCoating.Application/Services/PricingCalculationService.cs
index c56b501..c4a3434 100644
--- a/src/PowderCoating.Application/Services/PricingCalculationService.cs
+++ b/src/PowderCoating.Application/Services/PricingCalculationService.cs
@@ -288,6 +288,24 @@ public class PricingCalculationService : IPricingCalculationService
};
}
+ // Custom formula items (FixedRate mode): the wizard evaluated the NCalc formula server-side
+ // and stored the result as ManualUnitPrice. Use it directly — no coating math.
+ // SurfaceAreaSqFt mode: ManualUnitPrice is null; the formula produced sqft which was stored
+ // in SurfaceAreaSqFt, so the item falls through to the standard calculated path below.
+ if (item.IsCustomFormulaItem && item.ManualUnitPrice.HasValue)
+ {
+ var total = item.ManualUnitPrice.Value * item.Quantity;
+ return new QuoteItemPricingResult
+ {
+ MaterialCost = 0,
+ LaborCost = 0,
+ EquipmentCost = 0,
+ ItemSubtotal = total,
+ UnitPrice = item.ManualUnitPrice.Value,
+ TotalPrice = total
+ };
+ }
+
// Sales items (off-the-shelf merchandise) — manual unit price, no coating calculation.
if (item.IsSalesItem && item.ManualUnitPrice.HasValue)
{
diff --git a/src/PowderCoating.Application/Services/QuotePricingAssemblyService.cs b/src/PowderCoating.Application/Services/QuotePricingAssemblyService.cs
index db45f13..84ee1b8 100644
--- a/src/PowderCoating.Application/Services/QuotePricingAssemblyService.cs
+++ b/src/PowderCoating.Application/Services/QuotePricingAssemblyService.cs
@@ -130,6 +130,14 @@ public class QuotePricingAssemblyService : IQuotePricingAssemblyService
return;
}
+ if (itemDto.IsCustomFormulaItem && itemDto.ManualUnitPrice.HasValue)
+ {
+ item.UnitPrice = itemDto.ManualUnitPrice.Value;
+ item.TotalPrice = itemDto.ManualUnitPrice.Value * itemDto.Quantity;
+ _logger.LogInformation("Custom formula item (FixedRate) price: UnitPrice={Unit}, TotalPrice={Total}", item.UnitPrice, item.TotalPrice);
+ return;
+ }
+
if (itemDto.CatalogItemId.HasValue)
{
if (itemDto.Coats != null && itemDto.Coats.Any())
@@ -243,6 +251,9 @@ public class QuotePricingAssemblyService : IQuotePricingAssemblyService
IsAiItem = itemDto.IsAiItem,
AiTags = itemDto.AiTags,
AiPredictionId = itemDto.AiPredictionId,
+ IsCustomFormulaItem = itemDto.IsCustomFormulaItem,
+ CustomItemTemplateId = itemDto.CustomItemTemplateId,
+ FormulaFieldValuesJson = itemDto.FormulaFieldValuesJson,
CompanyId = companyId,
CreatedAt = createdAtUtc
};
diff --git a/src/PowderCoating.Core/Entities/CustomItemTemplate.cs b/src/PowderCoating.Core/Entities/CustomItemTemplate.cs
new file mode 100644
index 0000000..cb5830b
--- /dev/null
+++ b/src/PowderCoating.Core/Entities/CustomItemTemplate.cs
@@ -0,0 +1,38 @@
+namespace PowderCoating.Core.Entities;
+
+///
+/// A per-company reusable pricing formula template. Users define named input fields and an
+/// NCalc expression that produces either a fixed dollar amount (FixedRate) or a surface area
+/// in square feet (SurfaceAreaSqFt) that feeds the standard coatings pricing path.
+///
+public class CustomItemTemplate : BaseEntity
+{
+ public string Name { get; set; } = string.Empty;
+ public string? Description { get; set; }
+
+ /// "FixedRate" or "SurfaceAreaSqFt" — controls which pricing path is used after evaluation.
+ public string OutputMode { get; set; } = "FixedRate";
+
+ /// JSON array of field definitions: [{name, label, unit, defaultValue}]
+ public string FieldsJson { get; set; } = "[]";
+
+ /// NCalc expression using field name slugs and the reserved variable 'rate'.
+ public string Formula { get; set; } = string.Empty;
+
+ /// Default rate value populated into the quote wizard; user can override per quote.
+ public decimal? DefaultRate { get; set; }
+
+ /// Display label for the rate field, e.g. "$/sq in" or "$/lb".
+ public string? RateLabel { get; set; }
+
+ public string? Notes { get; set; }
+ public int DisplayOrder { get; set; }
+ public bool IsActive { get; set; } = true;
+
+ ///
+ /// Optional reference diagram (shop drawing, sketch) stored in blob storage.
+ /// Shown in the template editor and quote wizard so users know which measurements to take.
+ /// Path format: {companyId}/{templateId}/diagram.{ext}
+ ///
+ public string? DiagramImagePath { get; set; }
+}
diff --git a/src/PowderCoating.Core/Entities/JobItem.cs b/src/PowderCoating.Core/Entities/JobItem.cs
index 128ff19..7527733 100644
--- a/src/PowderCoating.Core/Entities/JobItem.cs
+++ b/src/PowderCoating.Core/Entities/JobItem.cs
@@ -52,6 +52,14 @@ public class JobItem : BaseEntity
public int? AiPredictionId { get; set; }
public virtual AiItemPrediction? AiPrediction { get; set; }
+ // Custom formula item — see IsCustomFormulaItem routing in PricingCalculationService
+ public bool IsCustomFormulaItem { get; set; }
+ public int? CustomItemTemplateId { get; set; }
+ public virtual CustomItemTemplate? CustomItemTemplate { get; set; }
+
+ /// Snapshot of field name/value pairs used in the formula, stored as JSON for display on details views.
+ public string? FormulaFieldValuesJson { get; set; }
+
// Relationships
public virtual Job Job { get; set; } = null!;
public virtual CatalogItem? CatalogItem { get; set; }
diff --git a/src/PowderCoating.Core/Entities/QuoteItem.cs b/src/PowderCoating.Core/Entities/QuoteItem.cs
index 5c96706..25fae48 100644
--- a/src/PowderCoating.Core/Entities/QuoteItem.cs
+++ b/src/PowderCoating.Core/Entities/QuoteItem.cs
@@ -56,6 +56,14 @@ public class QuoteItem : BaseEntity
public int? AiPredictionId { get; set; }
public virtual AiItemPrediction? AiPrediction { get; set; }
+ // Custom formula item — see IsCustomFormulaItem routing in PricingCalculationService
+ public bool IsCustomFormulaItem { get; set; }
+ public int? CustomItemTemplateId { get; set; }
+ public virtual CustomItemTemplate? CustomItemTemplate { get; set; }
+
+ /// Snapshot of field name/value pairs used in the formula, stored as JSON for display on details views.
+ public string? FormulaFieldValuesJson { get; set; }
+
// Relationships
public virtual Quote Quote { get; set; } = null!;
public virtual CatalogItem? CatalogItem { get; set; }
diff --git a/src/PowderCoating.Core/Interfaces/IUnitOfWork.cs b/src/PowderCoating.Core/Interfaces/IUnitOfWork.cs
index 8b417a9..4afd47b 100644
--- a/src/PowderCoating.Core/Interfaces/IUnitOfWork.cs
+++ b/src/PowderCoating.Core/Interfaces/IUnitOfWork.cs
@@ -155,6 +155,9 @@ IRepository ReworkRecords { get; }
// Customer Intake Kiosk
IRepository KioskSessions { get; }
+ // Custom Formula Templates
+ IRepository CustomItemTemplates { get; }
+
Task SaveChangesAsync();
Task CompleteAsync(); // Alias for SaveChangesAsync
diff --git a/src/PowderCoating.Infrastructure/Data/ApplicationDbContext.cs b/src/PowderCoating.Infrastructure/Data/ApplicationDbContext.cs
index 079d430..01a5f58 100644
--- a/src/PowderCoating.Infrastructure/Data/ApplicationDbContext.cs
+++ b/src/PowderCoating.Infrastructure/Data/ApplicationDbContext.cs
@@ -374,6 +374,10 @@ public class ApplicationDbContext : IdentityDbContext, IDataPro
/// Customer self-service intake sessions (walk-in tablet or remote email link); tenant-filtered with soft delete.
public DbSet KioskSessions { get; set; }
+ // Custom Formula Templates
+ /// Per-company reusable NCalc pricing formula templates; tenant-filtered with soft delete.
+ public DbSet CustomItemTemplates { get; set; }
+
///
/// Platform-wide audit log capturing who changed what and when, across all tenants.
/// No global query filter — SuperAdmin controllers query this directly.
diff --git a/src/PowderCoating.Infrastructure/Migrations/20260523153055_AddCustomItemTemplates.Designer.cs b/src/PowderCoating.Infrastructure/Migrations/20260523153055_AddCustomItemTemplates.Designer.cs
new file mode 100644
index 0000000..fd2916e
--- /dev/null
+++ b/src/PowderCoating.Infrastructure/Migrations/20260523153055_AddCustomItemTemplates.Designer.cs
@@ -0,0 +1,10780 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using PowderCoating.Infrastructure.Data;
+
+#nullable disable
+
+namespace PowderCoating.Infrastructure.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20260523153055_AddCustomItemTemplates")]
+ partial class AddCustomItemTemplates
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("FriendlyName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Xml")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DataProtectionKeys");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex")
+ .HasFilter("[NormalizedName] IS NOT NULL");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ProviderKey")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("RoleId")
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("LoginProvider")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Name")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Value")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.Account", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AccountNumber")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("AccountSubType")
+ .HasColumnType("int");
+
+ b.Property("AccountType")
+ .HasColumnType("int");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CurrentBalance")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsSystem")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("OpeningBalance")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("OpeningBalanceDate")
+ .HasColumnType("datetime2");
+
+ b.Property("ParentAccountId")
+ .HasColumnType("int");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentAccountId");
+
+ b.ToTable("Accounts");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AiItemPrediction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AiTags")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("Confidence")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ConversationRounds")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("PredictedComplexity")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PredictedMinutes")
+ .HasColumnType("int");
+
+ b.Property("PredictedSurfaceAreaSqFt")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("PredictedUnitPrice")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("Reasoning")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserOverrodeEstimate")
+ .HasColumnType("bit");
+
+ b.HasKey("Id");
+
+ b.ToTable("AiItemPredictions");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AiUsageLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CalledAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("Feature")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("InputLength")
+ .HasColumnType("int");
+
+ b.Property("Success")
+ .HasColumnType("bit");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompanyId", "CalledAt")
+ .HasDatabaseName("IX_AiUsageLogs_CompanyId_CalledAt");
+
+ b.ToTable("AiUsageLogs");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.Announcement", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedByUserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CreatedByUserName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("datetime2");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDismissible")
+ .HasColumnType("bit");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("StartsAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Target")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TargetCompanyId")
+ .HasColumnType("int");
+
+ b.Property("TargetPlan")
+ .HasColumnType("int");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.HasKey("Id");
+
+ b.ToTable("Announcements");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AnnouncementDismissal", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AnnouncementId")
+ .HasColumnType("int");
+
+ b.Property("DismissedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AnnouncementId", "UserId")
+ .IsUnique();
+
+ b.ToTable("AnnouncementDismissals");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.ApplicationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("Address")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("BanReason")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("BannedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("BannedByUserId")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CanApproveQuotes")
+ .HasColumnType("bit");
+
+ b.Property("CanCreateQuotes")
+ .HasColumnType("bit");
+
+ b.Property("CanManageAccounting")
+ .HasColumnType("bit");
+
+ b.Property("CanManageBills")
+ .HasColumnType("bit");
+
+ b.Property("CanManageCalendar")
+ .HasColumnType("bit");
+
+ b.Property("CanManageCustomers")
+ .HasColumnType("bit");
+
+ b.Property("CanManageEquipment")
+ .HasColumnType("bit");
+
+ b.Property("CanManageInventory")
+ .HasColumnType("bit");
+
+ b.Property("CanManageInvoices")
+ .HasColumnType("bit");
+
+ b.Property("CanManageJobs")
+ .HasColumnType("bit");
+
+ b.Property("CanManageMaintenance")
+ .HasColumnType("bit");
+
+ b.Property("CanManageProducts")
+ .HasColumnType("bit");
+
+ b.Property("CanManageVendors")
+ .HasColumnType("bit");
+
+ b.Property("CanViewCalendar")
+ .HasColumnType("bit");
+
+ b.Property("CanViewProducts")
+ .HasColumnType("bit");
+
+ b.Property("CanViewReports")
+ .HasColumnType("bit");
+
+ b.Property("CanViewShopFloor")
+ .HasColumnType("bit");
+
+ b.Property("City")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CompanyRole")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DashboardLayout")
+ .HasColumnType("int");
+
+ b.Property("DateFormat")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Department")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("bit");
+
+ b.Property("EmployeeNumber")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("HireDate")
+ .HasColumnType("datetime2");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsBanned")
+ .HasColumnType("bit");
+
+ b.Property("LaborCostPerHour")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("LastLoginDate")
+ .HasColumnType("datetime2");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("bit");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("Notes")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PasskeyPromptDismissed")
+ .HasColumnType("bit");
+
+ b.Property("PasswordHash")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("bit");
+
+ b.Property("Position")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProfilePictureFilePath")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SidebarColor")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("State")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TerminationDate")
+ .HasColumnType("datetime2");
+
+ b.Property("Theme")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TimeZone")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("bit");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("ZipCode")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompanyId");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex")
+ .HasFilter("[NormalizedUserName] IS NOT NULL");
+
+ b.ToTable("AspNetUsers", (string)null);
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.Appointment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ActualEndTime")
+ .HasColumnType("datetime2");
+
+ b.Property("ActualStartTime")
+ .HasColumnType("datetime2");
+
+ b.Property("AppointmentNumber")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("AppointmentStatusId")
+ .HasColumnType("int");
+
+ b.Property("AppointmentTypeId")
+ .HasColumnType("int");
+
+ b.Property("AssignedUserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CustomerId")
+ .HasColumnType("int");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsAllDay")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsReminderEnabled")
+ .HasColumnType("bit");
+
+ b.Property("JobId")
+ .HasColumnType("int");
+
+ b.Property("Location")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Notes")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ReminderMinutesBefore")
+ .HasColumnType("int");
+
+ b.Property("ReminderSentAt")
+ .HasColumnType("datetime2");
+
+ b.Property("ScheduledEndTime")
+ .HasColumnType("datetime2");
+
+ b.Property("ScheduledStartTime")
+ .HasColumnType("datetime2");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AppointmentStatusId");
+
+ b.HasIndex("AppointmentTypeId");
+
+ b.HasIndex("AssignedUserId");
+
+ b.HasIndex("CustomerId");
+
+ b.HasIndex("JobId");
+
+ b.HasIndex("ScheduledStartTime");
+
+ b.HasIndex("CompanyId", "AppointmentStatusId")
+ .HasDatabaseName("IX_Appointments_CompanyId_AppointmentStatusId");
+
+ b.HasIndex("CompanyId", "ScheduledStartTime")
+ .HasDatabaseName("IX_Appointments_CompanyId_ScheduledStartTime");
+
+ b.ToTable("Appointments");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AppointmentStatusLookup", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ColorClass")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayOrder")
+ .HasColumnType("int");
+
+ b.Property("IconClass")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsSystemDefined")
+ .HasColumnType("bit");
+
+ b.Property("IsTerminalStatus")
+ .HasColumnType("bit");
+
+ b.Property("StatusCode")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("AppointmentStatusLookups");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AppointmentTypeLookup", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ColorClass")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayOrder")
+ .HasColumnType("int");
+
+ b.Property("IconClass")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsSystemDefined")
+ .HasColumnType("bit");
+
+ b.Property("RequiresJobLink")
+ .HasColumnType("bit");
+
+ b.Property("TypeCode")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("AppointmentTypeLookups");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AuditLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Action")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CompanyName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("EntityDescription")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property