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,112 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Accounting;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class AccountingProfile : Profile
{
public AccountingProfile()
{
// ── Account ──────────────────────────────────────────────────────────
CreateMap<Account, AccountListDto>()
.ForMember(d => d.ParentAccountName, o => o.MapFrom(s =>
s.ParentAccount != null ? s.ParentAccount.Name : null));
CreateMap<Account, AccountDto>()
.ForMember(d => d.ParentAccountName, o => o.MapFrom(s =>
s.ParentAccount != null ? s.ParentAccount.Name : null))
.ForMember(d => d.SubAccounts, o => o.MapFrom(s => s.SubAccounts));
CreateMap<CreateAccountDto, Account>()
.ForMember(d => d.IsSystem, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
CreateMap<Account, EditAccountDto>();
CreateMap<EditAccountDto, Account>()
.ForMember(d => d.IsSystem, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
// ── Bill ─────────────────────────────────────────────────────────────
CreateMap<Bill, BillListDto>()
.ForMember(d => d.VendorName, o => o.MapFrom(s =>
s.Vendor != null ? s.Vendor.CompanyName : string.Empty));
CreateMap<Bill, BillDto>()
.ForMember(d => d.VendorName, o => o.MapFrom(s =>
s.Vendor != null ? s.Vendor.CompanyName : string.Empty))
.ForMember(d => d.VendorEmail, o => o.MapFrom(s =>
s.Vendor != null ? s.Vendor.Email : null))
.ForMember(d => d.VendorPhone, o => o.MapFrom(s =>
s.Vendor != null ? s.Vendor.Phone : null))
.ForMember(d => d.APAccountName, o => o.MapFrom(s =>
s.APAccount != null ? s.APAccount.Name : string.Empty))
.ForMember(d => d.LineItems, o => o.MapFrom(s => s.LineItems))
.ForMember(d => d.Payments, o => o.MapFrom(s => s.Payments));
CreateMap<BillLineItem, BillLineItemDto>()
.ForMember(d => d.AccountName, o => o.MapFrom(s =>
s.Account != null ? s.Account.Name : string.Empty))
.ForMember(d => d.AccountNumber, o => o.MapFrom(s =>
s.Account != null ? s.Account.AccountNumber : string.Empty))
.ForMember(d => d.JobNumber, o => o.MapFrom(s =>
s.Job != null ? s.Job.JobNumber : null));
CreateMap<BillPayment, BillPaymentDto>()
.ForMember(d => d.BankAccountName, o => o.MapFrom(s =>
s.BankAccount != null ? s.BankAccount.Name : string.Empty));
CreateMap<Bill, EditBillDto>();
CreateMap<CreateBillDto, Bill>()
.ForMember(d => d.BillNumber, o => o.Ignore())
.ForMember(d => d.Status, o => o.Ignore())
.ForMember(d => d.SubTotal, o => o.Ignore())
.ForMember(d => d.TaxAmount, o => o.Ignore())
.ForMember(d => d.Total, o => o.Ignore())
.ForMember(d => d.AmountPaid, o => o.Ignore())
.ForMember(d => d.BalanceDue, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
CreateMap<CreateBillLineItemDto, BillLineItem>()
.ForMember(d => d.Amount, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
CreateMap<RecordBillPaymentDto, BillPayment>()
.ForMember(d => d.PaymentNumber, o => o.Ignore())
.ForMember(d => d.VendorId, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
// ── Expense ──────────────────────────────────────────────────────────
CreateMap<Expense, ExpenseListDto>()
.ForMember(d => d.VendorName, o => o.MapFrom(s =>
s.Vendor != null ? s.Vendor.CompanyName : null))
.ForMember(d => d.ExpenseAccountName, o => o.MapFrom(s =>
s.ExpenseAccount != null ? s.ExpenseAccount.Name : string.Empty))
.ForMember(d => d.ExpenseAccountNumber, o => o.MapFrom(s =>
s.ExpenseAccount != null ? s.ExpenseAccount.AccountNumber : string.Empty))
.ForMember(d => d.PaymentAccountName, o => o.MapFrom(s =>
s.PaymentAccount != null ? s.PaymentAccount.Name : string.Empty))
.ForMember(d => d.JobNumber, o => o.MapFrom(s =>
s.Job != null ? s.Job.JobNumber : null))
.ForMember(d => d.HasReceipt, o => o.MapFrom(s =>
!string.IsNullOrEmpty(s.ReceiptFilePath)));
CreateMap<Expense, ExpenseDto>()
.IncludeBase<Expense, ExpenseListDto>();
CreateMap<CreateExpenseDto, Expense>()
.ForMember(d => d.ExpenseNumber, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
CreateMap<Expense, EditExpenseDto>();
CreateMap<EditExpenseDto, Expense>()
.ForMember(d => d.ExpenseNumber, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore());
}
}
@@ -0,0 +1,143 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Application.DTOs.Appointment;
namespace PowderCoating.Application.Mappings;
public class AppointmentProfile : Profile
{
public AppointmentProfile()
{
// Appointment to AppointmentDto - Map all lookup properties from navigation
CreateMap<Appointment, AppointmentDto>()
// Customer mappings (nullable for internal appointments)
.ForMember(dest => dest.CustomerName,
opt => opt.MapFrom(src => src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim())
: null))
.ForMember(dest => dest.CustomerPhone,
opt => opt.MapFrom(src => src.Customer != null ? src.Customer.Phone : null))
.ForMember(dest => dest.CustomerEmail,
opt => opt.MapFrom(src => src.Customer != null ? src.Customer.Email : null))
// Job mappings (optional)
.ForMember(dest => dest.JobNumber,
opt => opt.MapFrom(src => src.Job != null ? src.Job.JobNumber : null))
// Worker assignment
.ForMember(dest => dest.AssignedWorkerName,
opt => opt.MapFrom(src => src.AssignedUser != null ? src.AssignedUser.FullName : null))
// Appointment Type lookup mappings
.ForMember(dest => dest.AppointmentTypeId, opt => opt.MapFrom(src => src.AppointmentTypeId))
.ForMember(dest => dest.TypeCode, opt => opt.MapFrom(src => src.AppointmentType.TypeCode))
.ForMember(dest => dest.TypeDisplayName, opt => opt.MapFrom(src => src.AppointmentType.DisplayName))
.ForMember(dest => dest.TypeColorClass, opt => opt.MapFrom(src => src.AppointmentType.ColorClass))
.ForMember(dest => dest.TypeIconClass, opt => opt.MapFrom(src => src.AppointmentType.IconClass))
.ForMember(dest => dest.TypeRequiresJobLink, opt => opt.MapFrom(src => src.AppointmentType.RequiresJobLink))
// Appointment Status lookup mappings
.ForMember(dest => dest.AppointmentStatusId, opt => opt.MapFrom(src => src.AppointmentStatusId))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.AppointmentStatus.StatusCode))
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.AppointmentStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.AppointmentStatus.ColorClass))
.ForMember(dest => dest.StatusIconClass, opt => opt.MapFrom(src => src.AppointmentStatus.IconClass))
.ForMember(dest => dest.StatusIsTerminal, opt => opt.MapFrom(src => src.AppointmentStatus.IsTerminalStatus));
// Appointment to AppointmentListDto - Lightweight for grid views
CreateMap<Appointment, AppointmentListDto>()
.ForMember(dest => dest.CustomerName,
opt => opt.MapFrom(src => src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim())
: null))
.ForMember(dest => dest.AssignedWorkerName,
opt => opt.MapFrom(src => src.AssignedUser != null ? src.AssignedUser.FullName : null))
// Type lookup mappings
.ForMember(dest => dest.TypeDisplayName, opt => opt.MapFrom(src => src.AppointmentType.DisplayName))
.ForMember(dest => dest.TypeColorClass, opt => opt.MapFrom(src => src.AppointmentType.ColorClass))
// Status lookup mappings
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.AppointmentStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.AppointmentStatus.ColorClass));
// Appointment to CalendarEventDto - For calendar JSON API
CreateMap<Appointment, CalendarEventDto>()
.ForMember(dest => dest.Start,
opt => opt.MapFrom(src => src.ScheduledStartTime.ToString("yyyy-MM-ddTHH:mm:ss")))
.ForMember(dest => dest.End,
opt => opt.MapFrom(src => src.ScheduledEndTime.ToString("yyyy-MM-ddTHH:mm:ss")))
.ForMember(dest => dest.AllDay, opt => opt.MapFrom(src => src.IsAllDay))
.ForMember(dest => dest.CustomerName,
opt => opt.MapFrom(src => src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim())
: "Internal"))
.ForMember(dest => dest.TypeDisplayName, opt => opt.MapFrom(src => src.AppointmentType.DisplayName))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.AppointmentStatus.StatusCode))
// Calendar colors based on appointment type color class
.ForMember(dest => dest.BackgroundColor,
opt => opt.MapFrom(src => GetBootstrapColor(src.AppointmentType.ColorClass)))
.ForMember(dest => dest.BorderColor,
opt => opt.MapFrom(src => GetBootstrapColor(src.AppointmentType.ColorClass)))
.ForMember(dest => dest.TextColor, opt => opt.MapFrom(src => "#ffffff"));
// CreateAppointmentDto to Appointment
CreateMap<CreateAppointmentDto, Appointment>()
.ForMember(dest => dest.AppointmentNumber, opt => opt.Ignore()) // Generated by system
.ForMember(dest => dest.AppointmentStatusId, opt => opt.Ignore()) // Set to default "SCHEDULED" by controller
.ForMember(dest => dest.AppointmentStatus, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.AppointmentType, opt => opt.Ignore()); // Will be set by FK
// QuickCreateAppointmentDto to Appointment (for calendar quick create)
CreateMap<QuickCreateAppointmentDto, Appointment>()
.ForMember(dest => dest.AppointmentNumber, opt => opt.Ignore())
.ForMember(dest => dest.AppointmentStatusId, opt => opt.Ignore())
.ForMember(dest => dest.AppointmentStatus, opt => opt.Ignore())
.ForMember(dest => dest.AppointmentType, opt => opt.Ignore())
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => (string?)null))
.ForMember(dest => dest.Location, opt => opt.MapFrom(src => (string?)null))
.ForMember(dest => dest.Notes, opt => opt.MapFrom(src => (string?)null))
.ForMember(dest => dest.IsReminderEnabled, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.ReminderMinutesBefore, opt => opt.MapFrom(src => 30));
// UpdateAppointmentDto to Appointment
CreateMap<UpdateAppointmentDto, Appointment>()
.ForMember(dest => dest.AppointmentNumber, opt => opt.Ignore()) // Cannot be changed
.ForMember(dest => dest.AppointmentStatus, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.AppointmentType, opt => opt.Ignore()); // Will be set by FK
// Appointment to UpdateAppointmentDto (needed for Edit GET action)
CreateMap<Appointment, UpdateAppointmentDto>()
.ForMember(dest => dest.AppointmentStatusId, opt => opt.MapFrom(src => src.AppointmentStatusId))
.ForMember(dest => dest.AppointmentTypeId, opt => opt.MapFrom(src => src.AppointmentTypeId));
}
/// <summary>
/// Maps Bootstrap color class names to hex color codes for calendar events
/// </summary>
private static string GetBootstrapColor(string colorClass)
{
return colorClass.ToLower() switch
{
"primary" => "#0d6efd",
"secondary" => "#6c757d",
"success" => "#198754",
"danger" => "#dc3545",
"warning" => "#ffc107",
"info" => "#0dcaf0",
"light" => "#f8f9fa",
"dark" => "#212529",
// Custom color names for appointment types
"purple" => "#6f42c1",
"green" => "#198754",
"blue" => "#0d6efd",
"orange" => "#fd7e14",
"red" => "#dc3545",
"yellow" => "#ffc107",
"cyan" => "#0dcaf0",
"pink" => "#d63384",
"gray" => "#6c757d",
_ => "#0d6efd" // Default to primary blue
};
}
}
@@ -0,0 +1,23 @@
using AutoMapper;
using PowderCoating.Application.DTOs.BugReport;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class BugReportProfile : Profile
{
public BugReportProfile()
{
CreateMap<BugReport, BugReportDto>();
CreateMap<BugReport, EditBugReportDto>();
CreateMap<CreateBugReportDto, BugReport>();
CreateMap<EditBugReportDto, BugReport>()
.ForMember(dest => dest.SubmittedByUserId, opt => opt.Ignore())
.ForMember(dest => dest.SubmittedByUserName, opt => opt.Ignore())
.ForMember(dest => dest.CompanyName, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.Attachments, opt => opt.Ignore());
CreateMap<BugReportAttachment, BugReportAttachmentDto>();
}
}
@@ -0,0 +1,129 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Catalog;
using PowderCoating.Core.Entities;
using System.Collections.Generic;
using System.Linq;
namespace PowderCoating.Application.Mappings
{
/// <summary>
/// AutoMapper profile for product catalog entities.
/// </summary>
public class CatalogProfile : Profile
{
public CatalogProfile()
{
// ==================== Category Mappings ====================
// Category -> CategoryDto
CreateMap<CatalogCategory, CategoryDto>()
.ForMember(dest => dest.ParentCategoryName,
opt => opt.MapFrom(src => src.ParentCategory != null ? src.ParentCategory.Name : null))
.ForMember(dest => dest.ItemCount,
opt => opt.MapFrom(src => src.Items.Count(i => !i.IsDeleted)))
.ForMember(dest => dest.SubCategoryCount,
opt => opt.MapFrom(src => src.SubCategories.Count(c => !c.IsDeleted)));
// CreateCategoryDto -> CatalogCategory
CreateMap<CreateCategoryDto, CatalogCategory>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.ParentCategory, opt => opt.Ignore())
.ForMember(dest => dest.SubCategories, opt => opt.Ignore())
.ForMember(dest => dest.Items, opt => opt.Ignore());
// UpdateCategoryDto -> CatalogCategory
CreateMap<UpdateCategoryDto, CatalogCategory>()
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.ParentCategory, opt => opt.Ignore())
.ForMember(dest => dest.SubCategories, opt => opt.Ignore())
.ForMember(dest => dest.Items, opt => opt.Ignore());
// CatalogCategory -> CategoryTreeDto (recursive)
CreateMap<CatalogCategory, CategoryTreeDto>()
.ForMember(dest => dest.ItemCount,
opt => opt.MapFrom(src => src.Items.Count(i => !i.IsDeleted)))
.ForMember(dest => dest.HasItems,
opt => opt.MapFrom(src => src.Items.Any(i => !i.IsDeleted)))
.ForMember(dest => dest.Children,
opt => opt.MapFrom(src => src.SubCategories.Where(c => !c.IsDeleted).OrderBy(c => c.DisplayOrder)));
// ==================== Item Mappings ====================
// CatalogItem -> CatalogItemDto
CreateMap<CatalogItem, CatalogItemDto>()
.ForMember(dest => dest.CategoryName,
opt => opt.MapFrom(src => src.Category.Name))
.ForMember(dest => dest.FullCategoryPath,
opt => opt.MapFrom(src => GetCategoryPath(src.Category)))
.ForMember(dest => dest.RevenueAccountName,
opt => opt.MapFrom(src => src.RevenueAccount != null
? $"{src.RevenueAccount.AccountNumber} {src.RevenueAccount.Name}" : null))
.ForMember(dest => dest.CogsAccountName,
opt => opt.MapFrom(src => src.CogsAccount != null
? $"{src.CogsAccount.AccountNumber} {src.CogsAccount.Name}" : null));
// CatalogItem -> CatalogItemListDto
CreateMap<CatalogItem, CatalogItemListDto>()
.ForMember(dest => dest.CategoryName,
opt => opt.MapFrom(src => src.Category.Name));
// CreateCatalogItemDto -> CatalogItem
CreateMap<CreateCatalogItemDto, CatalogItem>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.Category, opt => opt.Ignore())
.ForMember(dest => dest.RevenueAccount, opt => opt.Ignore())
.ForMember(dest => dest.CogsAccount, opt => opt.Ignore());
// UpdateCatalogItemDto -> CatalogItem
CreateMap<UpdateCatalogItemDto, CatalogItem>()
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.Category, opt => opt.Ignore())
.ForMember(dest => dest.RevenueAccount, opt => opt.Ignore())
.ForMember(dest => dest.CogsAccount, opt => opt.Ignore());
// CatalogItem -> UpdateCatalogItemDto (reverse mapping for Edit)
CreateMap<CatalogItem, UpdateCatalogItemDto>();
}
/// <summary>
/// Builds the full category path by walking up the hierarchy.
/// Example: "Wheels > 18 inch"
/// </summary>
private string GetCategoryPath(CatalogCategory category)
{
var path = new List<string>();
var current = category;
// Walk up the hierarchy
while (current != null)
{
path.Insert(0, current.Name);
current = current.ParentCategory;
}
return string.Join(" > ", path);
}
}
}
@@ -0,0 +1,54 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Company;
using PowderCoating.Application.DTOs.User;
using PowderCoating.Application.Services;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class CompanyProfile : Profile
{
public CompanyProfile()
{
CreateMap<Company, CompanyDto>()
.ForMember(dest => dest.UserCount, opt => opt.MapFrom(src => src.Users.Count))
.ForMember(dest => dest.CustomerCount, opt => opt.MapFrom(src => src.Customers.Count))
.ForMember(dest => dest.JobCount, opt => opt.MapFrom(src => src.Jobs.Count))
.ForMember(dest => dest.Users, opt => opt.MapFrom(src => src.Users));
CreateMap<ApplicationUser, CompanyUserDto>();
CreateMap<Company, CompanyListDto>()
.ForMember(dest => dest.UserCount, opt => opt.MapFrom(src => src.Users.Count));
CreateMap<CreateCompanyDto, Company>();
CreateMap<UpdateCompanyDto, Company>();
CreateMap<Company, UpdateCompanyDto>();
// Company Settings mappings
CreateMap<Company, CompanySettingsDto>()
.ForMember(dest => dest.HasLogo, opt => opt.MapFrom(src =>
!string.IsNullOrEmpty(src.LogoFilePath) || (src.LogoData != null && src.LogoData.Length > 0)));
CreateMap<UpdateCompanySettingsDto, Company>();
// Company Operating Costs mappings
CreateMap<CompanyOperatingCosts, CompanyOperatingCostsDto>()
.ForMember(dest => dest.DerivedBlastRateSqFtPerHour,
opt => opt.MapFrom(src => ShopCapabilityCalculator.GetBlastRateSqFtPerHour(src)))
.ForMember(dest => dest.DerivedCoatingRateSqFtPerHour,
opt => opt.MapFrom(src => ShopCapabilityCalculator.GetCoatingRateSqFtPerHour(src)));
CreateMap<UpdateOperatingCostsDto, CompanyOperatingCosts>();
CreateMap<UpdateBlastProfileDto, CompanyOperatingCosts>();
// Company Preferences mappings
CreateMap<CompanyPreferences, CompanyPreferencesDto>().ReverseMap();
CreateMap<UpdateAppDefaultsDto, CompanyPreferences>();
CreateMap<UpdateJobDefaultsDto, CompanyPreferences>();
CreateMap<UpdateNotificationsDto, CompanyPreferences>();
CreateMap<UpdateDataRetentionDto, CompanyPreferences>();
CreateMap<UpdateQuoteTemplateDto, CompanyPreferences>();
CreateMap<UpdateInvoiceTemplateDto, CompanyPreferences>();
CreateMap<UpdateWorkOrderTemplateDto, CompanyPreferences>();
}
}
@@ -0,0 +1,45 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Application.DTOs.Customer;
namespace PowderCoating.Application.Mappings;
public class CustomerProfile : Profile
{
public CustomerProfile()
{
// Customer to CustomerDto
CreateMap<Customer, CustomerDto>()
.ForMember(dest => dest.PricingTierName,
opt => opt.MapFrom(src => src.PricingTier != null ? src.PricingTier.TierName : null))
.ForMember(dest => dest.HasTaxExemptCertificate,
opt => opt.MapFrom(src => src.TaxExemptCertificateData != null && src.TaxExemptCertificateData.Length > 0));
// CreateCustomerDto to Customer (SmsConsentGranted is a form-only flag — no entity field)
CreateMap<CreateCustomerDto, Customer>()
.ForMember(dest => dest.NotifyBySms, opt => opt.Ignore()) // controller sets this after consent check
.ForMember(dest => dest.SmsConsentedAt, opt => opt.Ignore())
.ForMember(dest => dest.SmsConsentMethod, opt => opt.Ignore());
// UpdateCustomerDto to Customer
CreateMap<UpdateCustomerDto, Customer>()
.ForMember(dest => dest.TaxExemptCertificateData, opt => opt.Ignore())
.ForMember(dest => dest.TaxExemptCertificateContentType, opt => opt.Ignore())
.ForMember(dest => dest.TaxExemptCertificateFileName, opt => opt.Ignore())
.ForMember(dest => dest.SmsConsentedAt, opt => opt.Ignore()) // controller manages
.ForMember(dest => dest.SmsConsentMethod, opt => opt.Ignore()); // controller manages
// Customer to UpdateCustomerDto (needed for Edit GET action)
CreateMap<Customer, UpdateCustomerDto>()
.ForMember(dest => dest.HasTaxExemptCertificate,
opt => opt.MapFrom(src => src.TaxExemptCertificateData != null && src.TaxExemptCertificateData.Length > 0))
.ForMember(dest => dest.SmsConsentGranted, opt => opt.Ignore()); // form-only, always starts unchecked
// Customer to CustomerListDto
CreateMap<Customer, CustomerListDto>()
.ForMember(dest => dest.ContactName,
opt => opt.MapFrom(src => !string.IsNullOrEmpty(src.ContactFirstName) || !string.IsNullOrEmpty(src.ContactLastName)
? $"{src.ContactFirstName} {src.ContactLastName}".Trim()
: string.Empty));
}
}
@@ -0,0 +1,51 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Application.DTOs.Equipment;
namespace PowderCoating.Application.Mappings;
public class EquipmentProfile : Profile
{
public EquipmentProfile()
{
// Equipment to EquipmentDto
CreateMap<Equipment, EquipmentDto>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()))
.ForMember(dest => dest.StatusDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.Status.ToString())))
.ForMember(dest => dest.DaysUntilMaintenance,
opt => opt.MapFrom(src => src.NextScheduledMaintenance.HasValue
? (int?)(src.NextScheduledMaintenance.Value - DateTime.Now).TotalDays
: null));
// CreateEquipmentDto to Equipment
CreateMap<CreateEquipmentDto, Equipment>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => Enum.Parse<EquipmentStatus>(src.Status)));
// UpdateEquipmentDto to Equipment
CreateMap<UpdateEquipmentDto, Equipment>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => Enum.Parse<EquipmentStatus>(src.Status)));
// Equipment to UpdateEquipmentDto (needed for Edit GET action)
CreateMap<Equipment, UpdateEquipmentDto>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()));
// Equipment to EquipmentListDto
CreateMap<Equipment, EquipmentListDto>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()))
.ForMember(dest => dest.StatusDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.Status.ToString())));
}
private static string FormatEnumName(string enumName)
{
// Convert "NeedsMaintenance" to "Needs Maintenance"
return System.Text.RegularExpressions.Regex.Replace(enumName, "([a-z])([A-Z])", "$1 $2");
}
}
@@ -0,0 +1,24 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Application.DTOs.GiftCertificate;
namespace PowderCoating.Application.Mappings;
public class GiftCertificateProfile : Profile
{
public GiftCertificateProfile()
{
// Used for the Index list — RemainingBalance is a computed property on the entity
// so it maps automatically, but we keep the explicit config for clarity.
CreateMap<GiftCertificate, GiftCertificateListDto>()
.ForMember(dest => dest.RemainingBalance,
opt => opt.MapFrom(src => src.OriginalAmount - src.RedeemedAmount))
.ForMember(dest => dest.RecipientName,
opt => opt.MapFrom(src => src.RecipientCustomer != null
? src.RecipientCustomer.CompanyName ?? $"{src.RecipientCustomer.ContactFirstName} {src.RecipientCustomer.ContactLastName}".Trim()
: src.RecipientName));
// GiftCertificateDto is constructed manually in the Details action to avoid
// AutoMapper inheritance resolution issues with GiftCertificateDto : GiftCertificateListDto.
}
}
@@ -0,0 +1,54 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Application.DTOs.Inventory;
namespace PowderCoating.Application.Mappings;
public class InventoryProfile : Profile
{
public InventoryProfile()
{
// InventoryItem to InventoryItemDto
CreateMap<InventoryItem, InventoryItemDto>()
.ForMember(dest => dest.CategoryName,
opt => opt.MapFrom(src => src.InventoryCategory != null ? src.InventoryCategory.DisplayName : null))
.ForMember(dest => dest.PrimaryVendorName,
opt => opt.MapFrom(src => src.PrimaryVendor != null ? src.PrimaryVendor.CompanyName : null))
.ForMember(dest => dest.IsLowStock,
opt => opt.MapFrom(src => src.QuantityOnHand > 0 && src.QuantityOnHand <= src.ReorderPoint))
.ForMember(dest => dest.IsOutOfStock,
opt => opt.MapFrom(src => src.QuantityOnHand <= 0))
.ForMember(dest => dest.InventoryAccountName,
opt => opt.MapFrom(src => src.InventoryAccount != null
? $"{src.InventoryAccount.AccountNumber} {src.InventoryAccount.Name}" : null))
.ForMember(dest => dest.CogsAccountName,
opt => opt.MapFrom(src => src.CogsAccount != null
? $"{src.CogsAccount.AccountNumber} {src.CogsAccount.Name}" : null));
// CreateInventoryItemDto to InventoryItem
CreateMap<CreateInventoryItemDto, InventoryItem>()
.ForMember(dest => dest.AverageCost, opt => opt.MapFrom(src => src.UnitCost))
.ForMember(dest => dest.LastPurchasePrice, opt => opt.MapFrom(src => src.UnitCost))
.ForMember(dest => dest.InventoryAccount, opt => opt.Ignore())
.ForMember(dest => dest.CogsAccount, opt => opt.Ignore());
// UpdateInventoryItemDto to InventoryItem
CreateMap<UpdateInventoryItemDto, InventoryItem>()
.ForMember(dest => dest.InventoryAccount, opt => opt.Ignore())
.ForMember(dest => dest.CogsAccount, opt => opt.Ignore());
// InventoryItem to UpdateInventoryItemDto (needed for Edit GET action)
CreateMap<InventoryItem, UpdateInventoryItemDto>();
// InventoryItem to InventoryListDto
CreateMap<InventoryItem, InventoryListDto>()
.ForMember(dest => dest.CategoryName,
opt => opt.MapFrom(src => src.InventoryCategory != null ? src.InventoryCategory.DisplayName : null))
.ForMember(dest => dest.PrimaryVendorName,
opt => opt.MapFrom(src => src.PrimaryVendor != null ? src.PrimaryVendor.CompanyName : null))
.ForMember(dest => dest.IsLowStock,
opt => opt.MapFrom(src => src.QuantityOnHand > 0 && src.QuantityOnHand <= src.ReorderPoint))
.ForMember(dest => dest.IsOutOfStock,
opt => opt.MapFrom(src => src.QuantityOnHand <= 0));
}
}
@@ -0,0 +1,75 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Invoice;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class InvoiceProfile : Profile
{
public InvoiceProfile()
{
CreateMap<Invoice, InvoiceListDto>()
.ForMember(d => d.JobNumber, o => o.MapFrom(s => s.Job != null ? s.Job.JobNumber : string.Empty))
.ForMember(d => d.CustomerName, o => o.MapFrom(s => s.Customer != null
? (s.Customer.IsCommercial
? s.Customer.CompanyName
: $"{s.Customer.ContactFirstName} {s.Customer.ContactLastName}".Trim())
: string.Empty))
.ForMember(d => d.BalanceDue, o => o.MapFrom(s => s.BalanceDue));
CreateMap<Invoice, InvoiceDto>()
.ForMember(d => d.JobNumber, o => o.MapFrom(s => s.Job != null ? s.Job.JobNumber : string.Empty))
.ForMember(d => d.CustomerName, o => o.MapFrom(s => s.Customer != null
? (s.Customer.IsCommercial
? s.Customer.CompanyName
: $"{s.Customer.ContactFirstName} {s.Customer.ContactLastName}".Trim())
: string.Empty))
.ForMember(d => d.CustomerEmail, o => o.MapFrom(s => s.Customer != null ? s.Customer.Email : null))
.ForMember(d => d.CustomerPhone, o => o.MapFrom(s => s.Customer != null ? s.Customer.Phone : null))
.ForMember(d => d.CustomerNotifyByEmail, o => o.MapFrom(s => s.Customer == null || s.Customer.NotifyByEmail))
.ForMember(d => d.PreparedByName, o => o.MapFrom(s => s.PreparedBy != null
? $"{s.PreparedBy.FirstName} {s.PreparedBy.LastName}".Trim()
: null))
.ForMember(d => d.BalanceDue, o => o.MapFrom(s => s.BalanceDue))
.ForMember(d => d.SalesTaxAccountName, o => o.MapFrom(s => s.SalesTaxAccount != null
? $"{s.SalesTaxAccount.AccountNumber} {s.SalesTaxAccount.Name}" : null))
.ForMember(d => d.GiftCertificateRedemptions, o => o.Ignore());
CreateMap<InvoiceItem, InvoiceItemDto>()
.ForMember(d => d.RevenueAccountName, o => o.MapFrom(s => s.RevenueAccount != null
? $"{s.RevenueAccount.AccountNumber} {s.RevenueAccount.Name}" : null));
CreateMap<InvoiceItemDto, InvoiceItem>()
.ForMember(d => d.RevenueAccount, o => o.Ignore())
.ForMember(d => d.Invoice, o => o.Ignore())
.ForMember(d => d.SourceJobItem, o => o.Ignore());
CreateMap<CreateInvoiceDto, Invoice>()
.ForMember(d => d.InvoiceNumber, o => o.Ignore())
.ForMember(d => d.Status, o => o.Ignore())
.ForMember(d => d.SubTotal, o => o.Ignore())
.ForMember(d => d.TaxAmount, o => o.Ignore())
.ForMember(d => d.Total, o => o.Ignore())
.ForMember(d => d.AmountPaid, o => o.Ignore());
CreateMap<CreateInvoiceItemDto, InvoiceItem>()
.ForMember(d => d.CompanyId, o => o.Ignore())
.ForMember(d => d.RevenueAccount, o => o.Ignore())
.ForMember(d => d.Invoice, o => o.Ignore())
.ForMember(d => d.SourceJobItem, o => o.Ignore());
CreateMap<Payment, PaymentDtos.PaymentDto>()
.ForMember(d => d.RecordedByName, o => o.MapFrom(s => s.RecordedBy != null
? $"{s.RecordedBy.FirstName} {s.RecordedBy.LastName}".Trim()
: null))
.ForMember(d => d.DepositAccountName, o => o.MapFrom(s => s.DepositAccount != null
? $"{s.DepositAccount.AccountNumber} {s.DepositAccount.Name}" : null));
CreateMap<PaymentDtos.RecordPaymentDto, Payment>()
.ForMember(d => d.RecordedById, o => o.Ignore())
.ForMember(d => d.CompanyId, o => o.Ignore())
.ForMember(d => d.DepositAccount, o => o.Ignore())
.ForMember(d => d.Invoice, o => o.Ignore())
.ForMember(d => d.RecordedBy, o => o.Ignore());
}
}
@@ -0,0 +1,232 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Application.DTOs.Job;
namespace PowderCoating.Application.Mappings;
public class JobProfile : Profile
{
public JobProfile()
{
// Job to JobDto - Map all lookup properties from navigation
CreateMap<Job, JobDto>()
.ForMember(dest => dest.CustomerName,
opt => opt.MapFrom(src => src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim())
: string.Empty))
.ForMember(dest => dest.CustomerCompanyName,
opt => opt.MapFrom(src => src.Customer != null ? src.Customer.CompanyName : null))
.ForMember(dest => dest.CustomerContactName,
opt => opt.MapFrom(src => src.Customer != null
? $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim()
: null))
.ForMember(dest => dest.AssignedWorkerName,
opt => opt.MapFrom(src => src.AssignedUser != null ? src.AssignedUser.FullName : null))
// Status lookup mappings
.ForMember(dest => dest.JobStatusId, opt => opt.MapFrom(src => src.JobStatusId))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.JobStatus.StatusCode))
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.JobStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.JobStatus.ColorClass))
.ForMember(dest => dest.StatusIconClass, opt => opt.MapFrom(src => src.JobStatus.IconClass))
.ForMember(dest => dest.StatusDisplayOrder, opt => opt.MapFrom(src => src.JobStatus.DisplayOrder))
.ForMember(dest => dest.StatusIsTerminal, opt => opt.MapFrom(src => src.JobStatus.IsTerminalStatus))
.ForMember(dest => dest.StatusIsWIP, opt => opt.MapFrom(src => src.JobStatus.IsWorkInProgressStatus))
// Priority lookup mappings
.ForMember(dest => dest.JobPriorityId, opt => opt.MapFrom(src => src.JobPriorityId))
.ForMember(dest => dest.PriorityCode, opt => opt.MapFrom(src => src.JobPriority.PriorityCode))
.ForMember(dest => dest.PriorityDisplayName, opt => opt.MapFrom(src => src.JobPriority.DisplayName))
.ForMember(dest => dest.PriorityColorClass, opt => opt.MapFrom(src => src.JobPriority.ColorClass))
.ForMember(dest => dest.PriorityIconClass, opt => opt.MapFrom(src => src.JobPriority.IconClass))
.ForMember(dest => dest.PriorityDisplayOrder, opt => opt.MapFrom(src => src.JobPriority.DisplayOrder))
// Oven selection
.ForMember(dest => dest.OvenLabel,
opt => opt.MapFrom(src => src.OvenCost != null ? src.OvenCost.Label : null))
.ForMember(dest => dest.Items,
opt => opt.MapFrom(src => src.JobItems))
// Prep services mappings
.ForMember(dest => dest.PrepServices, opt => opt.MapFrom(src =>
src.JobPrepServices.Select(jps => jps.PrepService).ToList()))
.ForMember(dest => dest.PrepServiceIds, opt => opt.MapFrom(src =>
src.JobPrepServices.Select(jps => jps.PrepServiceId).ToList()))
.ForMember(dest => dest.TimeEntries, opt => opt.MapFrom(src => src.TimeEntries))
.ForMember(dest => dest.IsReworkJob, opt => opt.MapFrom(src => src.IsReworkJob))
.ForMember(dest => dest.OriginalJobId, opt => opt.MapFrom(src => src.OriginalJobId))
.ForMember(dest => dest.OriginalJobNumber,
opt => opt.MapFrom(src => src.OriginalJob != null ? src.OriginalJob.JobNumber : null))
.ForMember(dest => dest.IntakeCheckedByName,
opt => opt.MapFrom(src => src.IntakeCheckedBy != null ? src.IntakeCheckedBy.FullName : null));
// JobTimeEntry → JobTimeEntryDto
CreateMap<JobTimeEntry, JobTimeEntryDto>()
.ForMember(dest => dest.WorkerName, opt => opt.MapFrom(src => src.Worker != null ? src.Worker.Name : string.Empty))
.ForMember(dest => dest.WorkerRole, opt => opt.MapFrom(src => src.Worker != null ? FormatEnumName(src.Worker.Role.ToString()) : string.Empty));
// CreateJobDto to Job
CreateMap<CreateJobDto, Job>()
.ForMember(dest => dest.JobNumber, opt => opt.Ignore()) // Generated by system
.ForMember(dest => dest.JobStatus, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.JobPriority, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.OvenCost, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.JobPrepServices, opt => opt.Ignore()) // Handled separately
.ForMember(dest => dest.JobItems, opt => opt.Ignore()); // Handled separately
// UpdateJobDto to Job
CreateMap<UpdateJobDto, Job>()
.ForMember(dest => dest.JobNumber, opt => opt.Ignore()) // Cannot be changed
.ForMember(dest => dest.JobStatus, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.JobPriority, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.OvenCost, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.JobPrepServices, opt => opt.Ignore()) // Handled separately
.ForMember(dest => dest.JobItems, opt => opt.Ignore()); // Handled separately
// Job to UpdateJobDto (needed for Edit GET action)
CreateMap<Job, UpdateJobDto>()
.ForMember(dest => dest.JobStatusId, opt => opt.MapFrom(src => src.JobStatusId))
.ForMember(dest => dest.JobPriorityId, opt => opt.MapFrom(src => src.JobPriorityId))
.ForMember(dest => dest.PrepServiceIds, opt => opt.MapFrom(src =>
src.JobPrepServices.Select(jps => jps.PrepServiceId).ToList()));
// Job to JobListDto
CreateMap<Job, JobListDto>()
.ForMember(dest => dest.CustomerName,
opt => opt.MapFrom(src => src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim())
: string.Empty))
.ForMember(dest => dest.AssignedWorkerName,
opt => opt.MapFrom(src => src.AssignedUser != null ? src.AssignedUser.FullName : null))
// Status lookup mappings
.ForMember(dest => dest.JobStatusId, opt => opt.MapFrom(src => src.JobStatusId))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.JobStatus.StatusCode))
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.JobStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.JobStatus.ColorClass))
.ForMember(dest => dest.StatusIsWIP, opt => opt.MapFrom(src => src.JobStatus.IsWorkInProgressStatus))
// Priority lookup mappings
.ForMember(dest => dest.JobPriorityId, opt => opt.MapFrom(src => src.JobPriorityId))
.ForMember(dest => dest.PriorityCode, opt => opt.MapFrom(src => src.JobPriority.PriorityCode))
.ForMember(dest => dest.PriorityDisplayName, opt => opt.MapFrom(src => src.JobPriority.DisplayName))
.ForMember(dest => dest.PriorityColorClass, opt => opt.MapFrom(src => src.JobPriority.ColorClass));
// JobItem mappings
CreateMap<JobItem, JobItemDto>()
.ForMember(dest => dest.Coats, opt => opt.MapFrom(src => src.Coats))
.ForMember(dest => dest.PrepServices, opt => opt.MapFrom(src => src.PrepServices));
CreateMap<JobItemPrepService, JobItemPrepServiceDto>()
.ForMember(dest => dest.PrepServiceName,
opt => opt.MapFrom(src => src.PrepService != null ? src.PrepService.ServiceName : null));
CreateMap<CreateJobItemDto, JobItem>()
.ForMember(dest => dest.TotalPrice,
opt => opt.MapFrom(src => src.UnitPrice * src.Quantity));
CreateMap<UpdateJobItemDto, JobItem>();
CreateMap<JobItem, UpdateJobItemDto>();
// JobItemCoat mappings
CreateMap<JobItemCoat, JobItemCoatDto>()
.ForMember(dest => dest.VendorName,
opt => opt.MapFrom(src => src.Vendor != null ? src.Vendor.CompanyName : null));
// Job to ShopFloorJobDto
CreateMap<Job, ShopFloorJobDto>()
.ForMember(dest => dest.CustomerName,
opt => opt.MapFrom(src => src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: $"{src.Customer.ContactFirstName} {src.Customer.ContactLastName}".Trim())
: string.Empty))
.ForMember(dest => dest.AssignedWorkerName,
opt => opt.MapFrom(src => src.AssignedUser != null ? src.AssignedUser.FullName : null))
// Status lookup mappings
.ForMember(dest => dest.JobStatusId, opt => opt.MapFrom(src => src.JobStatusId))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.JobStatus.StatusCode))
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.JobStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.JobStatus.ColorClass))
// Priority lookup mappings
.ForMember(dest => dest.JobPriorityId, opt => opt.MapFrom(src => src.JobPriorityId))
.ForMember(dest => dest.PriorityCode, opt => opt.MapFrom(src => src.JobPriority.PriorityCode))
.ForMember(dest => dest.PriorityDisplayName, opt => opt.MapFrom(src => src.JobPriority.DisplayName))
.ForMember(dest => dest.PriorityColorClass, opt => opt.MapFrom(src => src.JobPriority.ColorClass))
.ForMember(dest => dest.ItemCount,
opt => opt.MapFrom(src => src.JobItems.Count));
// Note: NextSteps will be removed - was workflow logic that will be reimplemented later
// JobPhoto mappings
CreateMap<JobPhoto, JobPhotoDto>()
.ForMember(dest => dest.PhotoTypeDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.PhotoType.ToString())))
.ForMember(dest => dest.UploadedByName,
opt => opt.MapFrom(src => src.UploadedBy != null ? src.UploadedBy.FullName : "Unknown"));
CreateMap<UploadJobPhotoDto, JobPhoto>();
CreateMap<UpdateJobPhotoDto, JobPhoto>();
// ReworkRecord → ReworkRecordDto
CreateMap<ReworkRecord, ReworkRecordDto>()
.ForMember(dest => dest.ReworkTypeDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.ReworkType.ToString())))
.ForMember(dest => dest.ReasonDisplay,
opt => opt.MapFrom(src => FormatReworkReason(src.Reason)))
.ForMember(dest => dest.DiscoveredByDisplay,
opt => opt.MapFrom(src => src.DiscoveredBy == ReworkDiscoveredBy.Internal ? "Internal (QC)" : "Customer"))
.ForMember(dest => dest.StatusDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.Status.ToString())))
.ForMember(dest => dest.StatusColorClass,
opt => opt.MapFrom(src => GetReworkStatusColor(src.Status)))
.ForMember(dest => dest.ResolutionDisplay,
opt => opt.MapFrom(src => src.Resolution.HasValue ? FormatReworkResolution(src.Resolution.Value) : null))
.ForMember(dest => dest.JobItemDescription,
opt => opt.MapFrom(src => src.JobItem != null ? src.JobItem.Description : null))
.ForMember(dest => dest.ReworkJobNumber,
opt => opt.MapFrom(src => src.ReworkJob != null ? src.ReworkJob.JobNumber : null));
// Job → JobDto (rework fields)
// (IsReworkJob and OriginalJobId map by convention; OriginalJobNumber needs explicit map — handled in controller)
// Job → JobListDto (rework fields map by convention)
// JobChangeHistory -> JobChangeHistoryDto
CreateMap<JobChangeHistory, JobChangeHistoryDto>()
.ForMember(dest => dest.ChangedByName, opt => opt.MapFrom(src =>
src.ChangedBy != null ? $"{src.ChangedBy.FirstName} {src.ChangedBy.LastName}" : "Unknown"));
}
private static string FormatEnumName(string enumName) =>
System.Text.RegularExpressions.Regex.Replace(enumName, "([a-z])([A-Z])", "$1 $2");
private static string FormatReworkReason(ReworkReason reason) => reason switch
{
ReworkReason.AdhesionFailure => "Adhesion Failure",
ReworkReason.Contamination => "Contamination",
ReworkReason.ColorMismatch => "Color Mismatch",
ReworkReason.RunsSags => "Runs / Sags",
ReworkReason.SurfacePrepFailure => "Surface Prep Failure",
ReworkReason.OvenIssue => "Oven Issue",
ReworkReason.InsufficientCoverage => "Insufficient Coverage",
ReworkReason.HandlingDamage => "Handling Damage",
_ => "Other"
};
private static string FormatReworkResolution(ReworkResolution resolution) => resolution switch
{
ReworkResolution.RecoatedNoCharge => "Recoated — No Charge",
ReworkResolution.RecoatedBilled => "Recoated — Billed to Customer",
ReworkResolution.CustomerCredited => "Customer Credited",
ReworkResolution.WrittenOff => "Written Off",
ReworkResolution.NoActionRequired => "No Action Required",
_ => resolution.ToString()
};
private static string GetReworkStatusColor(ReworkStatus status) => status switch
{
ReworkStatus.Open => "danger",
ReworkStatus.InProgress => "warning",
ReworkStatus.Resolved => "success",
ReworkStatus.WrittenOff => "secondary",
ReworkStatus.Disputed => "info",
_ => "secondary"
};
}
@@ -0,0 +1,195 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Lookup;
using PowderCoating.Application.DTOs.PrepService;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class LookupProfile : Profile
{
public LookupProfile()
{
// JobStatusLookup
CreateMap<JobStatusLookup, JobStatusLookupDto>()
.ForMember(dest => dest.JobCount, opt => opt.Ignore()); // Set manually in controller
CreateMap<CreateJobStatusLookupDto, JobStatusLookup>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.IsSystemDefined, opt => opt.MapFrom(src => false))
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Jobs, opt => opt.Ignore())
.ForMember(dest => dest.FromStatusHistory, opt => opt.Ignore())
.ForMember(dest => dest.ToStatusHistory, opt => opt.Ignore());
CreateMap<UpdateJobStatusLookupDto, JobStatusLookup>()
.ForMember(dest => dest.StatusCode, opt => opt.Ignore()) // Immutable
.ForMember(dest => dest.IsSystemDefined, opt => opt.Ignore()) // Cannot change
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Jobs, opt => opt.Ignore())
.ForMember(dest => dest.FromStatusHistory, opt => opt.Ignore())
.ForMember(dest => dest.ToStatusHistory, opt => opt.Ignore());
// JobPriorityLookup
CreateMap<JobPriorityLookup, JobPriorityLookupDto>()
.ForMember(dest => dest.JobCount, opt => opt.Ignore()); // Set manually in controller
CreateMap<CreateJobPriorityLookupDto, JobPriorityLookup>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.IsSystemDefined, opt => opt.MapFrom(src => false))
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Jobs, opt => opt.Ignore());
CreateMap<UpdateJobPriorityLookupDto, JobPriorityLookup>()
.ForMember(dest => dest.PriorityCode, opt => opt.Ignore()) // Immutable
.ForMember(dest => dest.IsSystemDefined, opt => opt.Ignore()) // Cannot change
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Jobs, opt => opt.Ignore());
// QuoteStatusLookup
CreateMap<QuoteStatusLookup, QuoteStatusLookupDto>()
.ForMember(dest => dest.QuoteCount, opt => opt.Ignore()); // Set manually in controller
CreateMap<CreateQuoteStatusLookupDto, QuoteStatusLookup>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.IsSystemDefined, opt => opt.MapFrom(src => false))
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Quotes, opt => opt.Ignore());
CreateMap<UpdateQuoteStatusLookupDto, QuoteStatusLookup>()
.ForMember(dest => dest.StatusCode, opt => opt.Ignore()) // Immutable
.ForMember(dest => dest.IsSystemDefined, opt => opt.Ignore()) // Cannot change
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Quotes, opt => opt.Ignore());
// InventoryCategoryLookup
CreateMap<InventoryCategoryLookup, InventoryCategoryLookupDto>()
.ForMember(dest => dest.ItemCount, opt => opt.Ignore()); // Set manually in controller
CreateMap<CreateInventoryCategoryLookupDto, InventoryCategoryLookup>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.IsSystemDefined, opt => opt.MapFrom(src => false))
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.InventoryItems, opt => opt.Ignore());
CreateMap<UpdateInventoryCategoryLookupDto, InventoryCategoryLookup>()
.ForMember(dest => dest.CategoryCode, opt => opt.Ignore()) // Immutable
.ForMember(dest => dest.IsSystemDefined, opt => opt.Ignore()) // Cannot change
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.InventoryItems, opt => opt.Ignore());
// AppointmentTypeLookup
CreateMap<AppointmentTypeLookup, AppointmentTypeLookupDto>()
.ForMember(dest => dest.AppointmentCount, opt => opt.Ignore()); // Set manually in controller
CreateMap<CreateAppointmentTypeLookupDto, AppointmentTypeLookup>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.IsSystemDefined, opt => opt.MapFrom(src => false))
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Appointments, opt => opt.Ignore());
CreateMap<UpdateAppointmentTypeLookupDto, AppointmentTypeLookup>()
.ForMember(dest => dest.TypeCode, opt => opt.Ignore()) // Immutable
.ForMember(dest => dest.IsSystemDefined, opt => opt.Ignore()) // Cannot change
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.Appointments, opt => opt.Ignore());
// PrepService
CreateMap<PrepService, PrepServiceDto>();
CreateMap<CreatePrepServiceDto, PrepService>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore());
CreateMap<UpdatePrepServiceDto, PrepService>()
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore());
}
}
@@ -0,0 +1,79 @@
using AutoMapper;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Application.DTOs.Maintenance;
namespace PowderCoating.Application.Mappings;
public class MaintenanceProfile : Profile
{
public MaintenanceProfile()
{
// MaintenanceRecord to MaintenanceRecordDto
CreateMap<MaintenanceRecord, MaintenanceRecordDto>()
.ForMember(dest => dest.EquipmentName,
opt => opt.MapFrom(src => src.Equipment.EquipmentName))
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()))
.ForMember(dest => dest.StatusDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.Status.ToString())))
.ForMember(dest => dest.Priority,
opt => opt.MapFrom(src => src.Priority.ToString()))
.ForMember(dest => dest.PriorityDisplay,
opt => opt.MapFrom(src => src.Priority.ToString()))
.ForMember(dest => dest.PerformedByName,
opt => opt.MapFrom(src => src.PerformedBy != null ? src.PerformedBy.FullName : null))
.ForMember(dest => dest.AssignedWorkerName,
opt => opt.MapFrom(src => src.AssignedUser != null ? src.AssignedUser.FullName : null))
.ForMember(dest => dest.RecurrenceFrequency,
opt => opt.MapFrom(src => src.RecurrenceFrequency.HasValue
? FormatEnumName(src.RecurrenceFrequency.Value.ToString())
: null));
// CreateMaintenanceDto to MaintenanceRecord
CreateMap<CreateMaintenanceDto, MaintenanceRecord>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => Enum.Parse<MaintenanceStatus>(src.Status)))
.ForMember(dest => dest.Priority,
opt => opt.MapFrom(src => Enum.Parse<MaintenancePriority>(src.Priority)))
.ForMember(dest => dest.TotalCost,
opt => opt.MapFrom(src => src.LaborCost + src.PartsCost));
// UpdateMaintenanceDto to MaintenanceRecord
CreateMap<UpdateMaintenanceDto, MaintenanceRecord>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => Enum.Parse<MaintenanceStatus>(src.Status)))
.ForMember(dest => dest.Priority,
opt => opt.MapFrom(src => Enum.Parse<MaintenancePriority>(src.Priority)))
.ForMember(dest => dest.TotalCost,
opt => opt.MapFrom(src => src.LaborCost + src.PartsCost));
// MaintenanceRecord to UpdateMaintenanceDto (needed for Edit GET action)
CreateMap<MaintenanceRecord, UpdateMaintenanceDto>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()))
.ForMember(dest => dest.Priority,
opt => opt.MapFrom(src => src.Priority.ToString()));
// RecurrenceFrequency, IsRecurring, RecurrenceEndDate map automatically (same type)
// MaintenanceRecord to MaintenanceListDto
CreateMap<MaintenanceRecord, MaintenanceListDto>()
.ForMember(dest => dest.EquipmentName,
opt => opt.MapFrom(src => src.Equipment.EquipmentName))
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()))
.ForMember(dest => dest.StatusDisplay,
opt => opt.MapFrom(src => FormatEnumName(src.Status.ToString())))
.ForMember(dest => dest.Priority,
opt => opt.MapFrom(src => src.Priority.ToString()))
.ForMember(dest => dest.PriorityDisplay,
opt => opt.MapFrom(src => src.Priority.ToString()));
// IsRecurring and RecurrenceGroupId map automatically
}
private static string FormatEnumName(string enumName)
{
// Convert "InProgress" to "In Progress"
return System.Text.RegularExpressions.Regex.Replace(enumName, "([a-z])([A-Z])", "$1 $2");
}
}
@@ -0,0 +1,19 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Customer;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class PricingTierProfile : Profile
{
public PricingTierProfile()
{
CreateMap<PricingTier, PricingTierDto>()
.ForMember(dest => dest.CustomerCount, opt => opt.Ignore());
CreateMap<CreatePricingTierDto, PricingTier>();
CreateMap<UpdatePricingTierDto, PricingTier>();
CreateMap<PricingTier, UpdatePricingTierDto>();
}
}
@@ -0,0 +1,63 @@
using AutoMapper;
using PowderCoating.Application.DTOs.PurchaseOrder;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class PurchaseOrderProfile : Profile
{
public PurchaseOrderProfile()
{
// PurchaseOrder → List DTO
CreateMap<PurchaseOrder, PurchaseOrderListDto>()
.ForMember(d => d.VendorName, o => o.MapFrom(s => s.Vendor != null ? s.Vendor.CompanyName : string.Empty))
.ForMember(d => d.ItemCount, o => o.MapFrom(s => s.Items.Count(i => !i.IsDeleted)));
// PurchaseOrder → Details DTO
CreateMap<PurchaseOrder, PurchaseOrderDto>()
.ForMember(d => d.VendorName, o => o.MapFrom(s => s.Vendor != null ? s.Vendor.CompanyName : string.Empty))
.ForMember(d => d.VendorEmail, o => o.MapFrom(s => s.Vendor != null ? s.Vendor.Email : null))
.ForMember(d => d.VendorPhone, o => o.MapFrom(s => s.Vendor != null ? s.Vendor.Phone : null))
.ForMember(d => d.BillNumber, o => o.MapFrom(s => s.Bill != null ? s.Bill.BillNumber : null));
// PurchaseOrderItem → DTO
// ItemName = inventory item name when linked, otherwise the custom Description
CreateMap<PurchaseOrderItem, PurchaseOrderItemDto>()
.ForMember(d => d.ItemName, o => o.MapFrom(s =>
s.InventoryItem != null ? s.InventoryItem.Name : (s.Description ?? string.Empty)))
.ForMember(d => d.ItemSKU, o => o.MapFrom(s =>
s.InventoryItem != null ? s.InventoryItem.SKU : string.Empty))
.ForMember(d => d.UnitOfMeasure, o => o.MapFrom(s =>
s.InventoryItem != null ? s.InventoryItem.UnitOfMeasure : (s.UnitOfMeasure ?? string.Empty)));
// Create DTO → PurchaseOrder entity
CreateMap<CreatePurchaseOrderDto, PurchaseOrder>()
.ForMember(d => d.PoNumber, o => o.Ignore())
.ForMember(d => d.Status, o => o.Ignore())
.ForMember(d => d.SubTotal, o => o.Ignore())
.ForMember(d => d.TotalAmount, o => o.Ignore())
.ForMember(d => d.ReceivedDate, o => o.Ignore())
.ForMember(d => d.BillId, o => o.Ignore())
.ForMember(d => d.Items, o => o.Ignore());
// Update DTO → PurchaseOrder entity
CreateMap<UpdatePurchaseOrderDto, PurchaseOrder>()
.ForMember(d => d.PoNumber, o => o.Ignore())
.ForMember(d => d.Status, o => o.Ignore())
.ForMember(d => d.SubTotal, o => o.Ignore())
.ForMember(d => d.TotalAmount, o => o.Ignore())
.ForMember(d => d.ReceivedDate, o => o.Ignore())
.ForMember(d => d.BillId, o => o.Ignore())
.ForMember(d => d.Items, o => o.Ignore());
// CreatePurchaseOrderItemDto → PurchaseOrderItem entity
CreateMap<CreatePurchaseOrderItemDto, PurchaseOrderItem>()
.ForMember(d => d.QuantityReceived, o => o.Ignore())
.ForMember(d => d.LineTotal, o => o.Ignore())
.ForMember(d => d.PurchaseOrder, o => o.Ignore())
.ForMember(d => d.InventoryItem, o => o.Ignore())
// For inventory-linked items, clear custom fields; for custom items, clear inventory FK
.ForMember(d => d.Description, o => o.MapFrom(s => s.InventoryItemId.HasValue ? null : s.Description))
.ForMember(d => d.UnitOfMeasure, o => o.MapFrom(s => s.InventoryItemId.HasValue ? null : s.UnitOfMeasure));
}
}
@@ -0,0 +1,310 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Quote;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class QuoteProfile : Profile
{
public QuoteProfile()
{
// ============================================================================
// Quote Entity <-> DTOs
// ============================================================================
// Quote -> QuoteListDto
CreateMap<Quote, QuoteListDto>()
.ForMember(dest => dest.CustomerOrProspectName, opt => opt.MapFrom(src =>
src.Customer != null
? (!string.IsNullOrWhiteSpace(src.Customer.CompanyName)
? src.Customer.CompanyName
: (src.Customer.ContactFirstName + " " + src.Customer.ContactLastName).Trim())
: src.ProspectCompanyName ?? src.ProspectContactName ?? "Unknown"))
.ForMember(dest => dest.IsProspect, opt => opt.MapFrom(src => !src.CustomerId.HasValue))
// Status lookup mappings
.ForMember(dest => dest.QuoteStatusId, opt => opt.MapFrom(src => src.QuoteStatusId))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.QuoteStatus.StatusCode))
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.QuoteStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.QuoteStatus.ColorClass))
.ForMember(dest => dest.StatusIsDraft, opt => opt.MapFrom(src => src.QuoteStatus.IsDraftStatus));
// Quote -> QuoteDto
CreateMap<Quote, QuoteDto>()
.ForMember(dest => dest.OvenLabel, opt => opt.MapFrom(src =>
src.OvenCost != null ? src.OvenCost.Label : null))
.ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src =>
src.Customer != null ? src.Customer.CompanyName : null))
.ForMember(dest => dest.PreparedByName, opt => opt.MapFrom(src =>
src.PreparedBy != null ? $"{src.PreparedBy.FirstName} {src.PreparedBy.LastName}" : null))
// Discount enum to string conversion
.ForMember(dest => dest.DiscountType, opt => opt.MapFrom(src => src.DiscountType.ToString()))
// Status lookup mappings
.ForMember(dest => dest.QuoteStatusId, opt => opt.MapFrom(src => src.QuoteStatusId))
.ForMember(dest => dest.StatusCode, opt => opt.MapFrom(src => src.QuoteStatus.StatusCode))
.ForMember(dest => dest.StatusDisplayName, opt => opt.MapFrom(src => src.QuoteStatus.DisplayName))
.ForMember(dest => dest.StatusColorClass, opt => opt.MapFrom(src => src.QuoteStatus.ColorClass))
.ForMember(dest => dest.StatusIconClass, opt => opt.MapFrom(src => src.QuoteStatus.IconClass))
.ForMember(dest => dest.StatusIsApproved, opt => opt.MapFrom(src => src.QuoteStatus.IsApprovedStatus))
.ForMember(dest => dest.StatusIsConverted, opt => opt.MapFrom(src => src.QuoteStatus.IsConvertedStatus))
.ForMember(dest => dest.StatusIsDraft, opt => opt.MapFrom(src => src.QuoteStatus.IsDraftStatus))
// Prep Services mappings
.ForMember(dest => dest.PrepServices, opt => opt.MapFrom(src =>
src.QuotePrepServices.Select(qps => qps.PrepService).ToList()))
.ForMember(dest => dest.PrepServiceIds, opt => opt.MapFrom(src =>
src.QuotePrepServices.Select(qps => qps.PrepServiceId).ToList()))
.ReverseMap()
.ForMember(dest => dest.Customer, opt => opt.Ignore())
.ForMember(dest => dest.OvenCost, opt => opt.Ignore())
.ForMember(dest => dest.PreparedBy, opt => opt.Ignore())
.ForMember(dest => dest.QuoteStatus, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.QuoteItems, opt => opt.Ignore())
.ForMember(dest => dest.QuotePrepServices, opt => opt.Ignore()) // Handled separately in controller
.ForMember(dest => dest.ConvertedToJob, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// CreateQuoteDto -> Quote
CreateMap<CreateQuoteDto, Quote>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.QuoteNumber, opt => opt.Ignore()) // Generated by controller
.ForMember(dest => dest.QuoteStatus, opt => opt.Ignore()) // Will be set by FK to Draft status
.ForMember(dest => dest.OvenCost, opt => opt.Ignore())
.ForMember(dest => dest.SubTotal, opt => opt.Ignore()) // Calculated
// Discount: map type and value from DTO, calculated fields ignored
.ForMember(dest => dest.DiscountType, opt => opt.MapFrom(src =>
Enum.Parse<PowderCoating.Core.Enums.DiscountType>(src.DiscountType)))
.ForMember(dest => dest.DiscountValue, opt => opt.MapFrom(src => src.DiscountValue))
.ForMember(dest => dest.DiscountReason, opt => opt.MapFrom(src => src.DiscountReason))
.ForMember(dest => dest.DiscountPercent, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.DiscountAmount, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.TaxAmount, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.Total, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.QuoteItems, opt => opt.Ignore()) // Mapped separately
.ForMember(dest => dest.QuotePrepServices, opt => opt.Ignore()) // Handled separately in controller
.ForMember(dest => dest.Customer, opt => opt.Ignore())
.ForMember(dest => dest.PreparedBy, opt => opt.Ignore())
.ForMember(dest => dest.ConvertedToJob, opt => opt.Ignore())
.ForMember(dest => dest.PreparedById, opt => opt.Ignore())
.ForMember(dest => dest.ConvertedToJobId, opt => opt.Ignore())
.ForMember(dest => dest.ConvertedDate, opt => opt.Ignore())
.ForMember(dest => dest.SentDate, opt => opt.Ignore())
.ForMember(dest => dest.ApprovedDate, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// UpdateQuoteDto -> Quote
CreateMap<UpdateQuoteDto, Quote>()
.ForMember(dest => dest.QuoteNumber, opt => opt.Ignore()) // Cannot change
.ForMember(dest => dest.CustomerId, opt => opt.Ignore()) // Cannot change after creation - preserved in controller
.ForMember(dest => dest.QuoteStatus, opt => opt.Ignore()) // Will be set by FK
.ForMember(dest => dest.OvenCost, opt => opt.Ignore())
.ForMember(dest => dest.SubTotal, opt => opt.Ignore()) // Calculated
// Discount: map type and value from DTO, calculated fields ignored
.ForMember(dest => dest.DiscountType, opt => opt.MapFrom(src =>
Enum.Parse<PowderCoating.Core.Enums.DiscountType>(src.DiscountType)))
.ForMember(dest => dest.DiscountValue, opt => opt.MapFrom(src => src.DiscountValue))
.ForMember(dest => dest.DiscountReason, opt => opt.MapFrom(src => src.DiscountReason))
.ForMember(dest => dest.DiscountPercent, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.DiscountAmount, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.TaxAmount, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.Total, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.QuoteItems, opt => opt.Ignore()) // Mapped separately
.ForMember(dest => dest.QuotePrepServices, opt => opt.Ignore()) // Handled separately in controller
.ForMember(dest => dest.Customer, opt => opt.Ignore())
.ForMember(dest => dest.PreparedBy, opt => opt.Ignore())
.ForMember(dest => dest.ConvertedToJob, opt => opt.Ignore())
.ForMember(dest => dest.PreparedById, opt => opt.Ignore())
.ForMember(dest => dest.ConvertedToJobId, opt => opt.Ignore())
.ForMember(dest => dest.ConvertedDate, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// Quote -> UpdateQuoteDto
CreateMap<Quote, UpdateQuoteDto>()
.ForMember(dest => dest.IsForProspect, opt => opt.MapFrom(src => !src.CustomerId.HasValue))
.ForMember(dest => dest.QuoteStatusId, opt => opt.MapFrom(src => src.QuoteStatusId));
// ============================================================================
// QuoteItem Entity <-> DTOs
// ============================================================================
// QuoteItem -> QuoteItemDto
CreateMap<QuoteItem, QuoteItemDto>()
.ForMember(dest => dest.CatalogItemName, opt => opt.MapFrom(src =>
src.CatalogItem != null ? src.CatalogItem.Name : null))
.ForMember(dest => dest.Coats, opt => opt.MapFrom(src => src.Coats))
.ForMember(dest => dest.PrepServices, opt => opt.MapFrom(src => src.PrepServices))
.ReverseMap()
.ForMember(dest => dest.Quote, opt => opt.Ignore())
.ForMember(dest => dest.CatalogItem, opt => opt.Ignore())
.ForMember(dest => dest.Coats, opt => opt.Ignore())
.ForMember(dest => dest.PrepServices, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// CreateQuoteItemDto -> QuoteItem
CreateMap<CreateQuoteItemDto, QuoteItem>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.QuoteId, opt => opt.Ignore())
.ForMember(dest => dest.UnitPrice, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.TotalPrice, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.Quote, opt => opt.Ignore())
.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.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// QuoteItem -> CreateQuoteItemDto (for Edit view)
CreateMap<QuoteItem, CreateQuoteItemDto>()
.ForMember(dest => dest.PrepServices, opt => opt.MapFrom(src => src.PrepServices));
// ============================================================================
// QuoteItemCoat Entity <-> DTOs
// ============================================================================
// QuoteItemCoat -> QuoteItemCoatDto
CreateMap<QuoteItemCoat, QuoteItemCoatDto>()
.ForMember(dest => dest.InventoryItemName, opt => opt.MapFrom(src =>
src.InventoryItem != null ? src.InventoryItem.Name : null))
.ForMember(dest => dest.VendorName, opt => opt.MapFrom(src =>
src.Vendor != null ? src.Vendor.CompanyName : null))
.ReverseMap()
.ForMember(dest => dest.QuoteItem, opt => opt.Ignore())
.ForMember(dest => dest.InventoryItem, opt => opt.Ignore())
.ForMember(dest => dest.Vendor, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// CreateQuoteItemCoatDto -> QuoteItemCoat
CreateMap<CreateQuoteItemCoatDto, QuoteItemCoat>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.QuoteItemId, opt => opt.Ignore())
.ForMember(dest => dest.CoatMaterialCost, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.CoatLaborCost, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.CoatTotalCost, opt => opt.Ignore()) // Calculated
.ForMember(dest => dest.QuoteItem, opt => opt.Ignore())
.ForMember(dest => dest.InventoryItem, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// QuoteItemCoat -> CreateQuoteItemCoatDto (for Edit view)
CreateMap<QuoteItemCoat, CreateQuoteItemCoatDto>();
// ============================================================================
// QuoteItemPrepService Entity <-> DTOs
// ============================================================================
// CreateQuoteItemPrepServiceDto -> QuoteItemPrepService
CreateMap<CreateQuoteItemPrepServiceDto, QuoteItemPrepService>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.QuoteItemId, opt => opt.Ignore())
.ForMember(dest => dest.QuoteItem, opt => opt.Ignore())
.ForMember(dest => dest.PrepService, opt => opt.Ignore())
.ForMember(dest => dest.BlastSetup, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.DeletedAt, opt => opt.Ignore())
.ForMember(dest => dest.DeletedBy, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// QuoteItemPrepService -> CreateQuoteItemPrepServiceDto (for Edit view)
CreateMap<QuoteItemPrepService, CreateQuoteItemPrepServiceDto>()
.ForMember(dest => dest.PrepServiceName, opt => opt.MapFrom(src =>
src.PrepService != null ? src.PrepService.ServiceName : null));
// ============================================================================
// Customer Conversion Mappings
// ============================================================================
// Quote -> ConvertQuoteToCustomerDto
CreateMap<Quote, ConvertQuoteToCustomerDto>()
.ForMember(dest => dest.QuoteId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.CompanyName, opt => opt.MapFrom(src => src.ProspectCompanyName ?? ""))
.ForMember(dest => dest.ContactName, opt => opt.MapFrom(src => src.ProspectContactName ?? ""))
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.ProspectEmail))
.ForMember(dest => dest.Phone, opt => opt.MapFrom(src => src.ProspectPhone))
.ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.ProspectAddress))
.ForMember(dest => dest.City, opt => opt.MapFrom(src => src.ProspectCity))
.ForMember(dest => dest.State, opt => opt.MapFrom(src => src.ProspectState))
.ForMember(dest => dest.ZipCode, opt => opt.MapFrom(src => src.ProspectZipCode))
.ForMember(dest => dest.IsCommercial, opt => opt.MapFrom(src => src.IsCommercial))
.ForMember(dest => dest.CreditLimit, opt => opt.MapFrom(src => 0m))
.ForMember(dest => dest.PricingTierId, opt => opt.Ignore())
.ForMember(dest => dest.TaxId, opt => opt.Ignore())
.ForMember(dest => dest.PaymentTerms, opt => opt.Ignore())
.ForMember(dest => dest.Notes, opt => opt.Ignore());
// ConvertQuoteToCustomerDto -> Customer
CreateMap<ConvertQuoteToCustomerDto, Customer>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.ContactFirstName, opt => opt.MapFrom(src => src.ContactName))
.ForMember(dest => dest.ContactLastName, opt => opt.Ignore())
.ForMember(dest => dest.MobilePhone, opt => opt.Ignore())
.ForMember(dest => dest.Country, opt => opt.MapFrom(src => "USA"))
.ForMember(dest => dest.CurrentBalance, opt => opt.MapFrom(src => 0m))
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.GeneralNotes, opt => opt.MapFrom(src => src.Notes))
.ForMember(dest => dest.LastContactDate, opt => opt.Ignore())
.ForMember(dest => dest.PricingTier, opt => opt.Ignore())
.ForMember(dest => dest.Jobs, opt => opt.Ignore())
.ForMember(dest => dest.Quotes, opt => opt.Ignore())
.ForMember(dest => dest.CustomerNotes, opt => opt.Ignore())
.ForMember(dest => dest.CompanyId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.IsDeleted, opt => opt.Ignore())
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedBy, opt => opt.Ignore());
// ============================================================================
// QuoteChangeHistory Entity <-> DTOs
// ============================================================================
// QuoteChangeHistory -> QuoteChangeHistoryDto
CreateMap<QuoteChangeHistory, QuoteChangeHistoryDto>()
.ForMember(dest => dest.ChangedByName, opt => opt.MapFrom(src =>
src.ChangedBy != null ? $"{src.ChangedBy.FirstName} {src.ChangedBy.LastName}"
: src.ChangedByUserId == null ? "Customer"
: "Unknown"));
}
}
@@ -0,0 +1,23 @@
using AutoMapper;
using PowderCoating.Application.DTOs.ShopWorker;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class ShopWorkerProfile : Profile
{
public ShopWorkerProfile()
{
// Entity to DTO
CreateMap<ShopWorker, ShopWorkerDto>();
// DTO to Entity
CreateMap<CreateShopWorkerDto, ShopWorker>();
CreateMap<UpdateShopWorkerDto, ShopWorker>();
// Reverse mappings
CreateMap<ShopWorkerDto, ShopWorker>();
CreateMap<ShopWorker, CreateShopWorkerDto>();
CreateMap<ShopWorker, UpdateShopWorkerDto>();
}
}
@@ -0,0 +1,25 @@
using AutoMapper;
using PowderCoating.Application.DTOs.Vendor;
using PowderCoating.Core.Entities;
namespace PowderCoating.Application.Mappings;
public class VendorProfile : Profile
{
public VendorProfile()
{
// Entity to DTO mappings
CreateMap<Vendor, VendorDto>();
CreateMap<Vendor, VendorListDto>()
.ForMember(dest => dest.InventoryItemCount,
opt => opt.MapFrom(src => src.InventoryItems.Count));
// DTO to Entity mappings
CreateMap<CreateVendorDto, Vendor>();
CreateMap<UpdateVendorDto, Vendor>();
// Reverse mappings
CreateMap<VendorDto, Vendor>();
CreateMap<Vendor, UpdateVendorDto>();
}
}