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,39 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing appointments from CSV files.
/// </summary>
public class AppointmentImportDto
{
[Name("AppointmentNumber")]
public string? AppointmentNumber { get; set; }
[Name("CustomerEmail")]
public string? CustomerEmail { get; set; }
[Name("AppointmentType")]
public string AppointmentType { get; set; } = string.Empty;
[Name("Status")]
public string Status { get; set; } = "Scheduled";
[Name("ScheduledStart")]
public DateTime ScheduledStart { get; set; }
[Name("ScheduledEnd")]
public DateTime ScheduledEnd { get; set; }
[Name("Title")]
public string Title { get; set; } = string.Empty;
[Name("Description")]
public string? Description { get; set; }
[Name("Location")]
public string? Location { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,39 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing catalog items from CSV files.
/// </summary>
public class CatalogItemImportDto
{
[Name("CategoryPath")]
public string CategoryPath { get; set; } = string.Empty;
[Name("ItemName")]
public string ItemName { get; set; } = string.Empty;
[Name("SKU")]
public string? SKU { get; set; }
[Name("Description")]
public string? Description { get; set; }
[Name("BasePrice")]
public decimal? BasePrice { get; set; }
[Name("ApproximateArea")]
public decimal? ApproximateArea { get; set; }
[Name("EstimatedMinutes")]
public int? EstimatedMinutes { get; set; }
[Name("RequiresSandblasting")]
public bool? RequiresSandblasting { get; set; }
[Name("RequiresMasking")]
public bool? RequiresMasking { get; set; }
[Name("IsActive")]
public bool? IsActive { get; set; }
}
@@ -0,0 +1,37 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing Chart of Accounts entries from CSV.
/// AccountNumber must be unique per company. System accounts (IsSystem=true) are
/// skipped on import — they are managed by the platform seed data only.
/// </summary>
public class ChartOfAccountsImportDto
{
[Name("AccountNumber")]
public string AccountNumber { get; set; } = string.Empty;
[Name("Name")]
public string Name { get; set; } = string.Empty;
/// <summary>Asset, Liability, Equity, Revenue, CostOfGoods, Expense</summary>
[Name("AccountType")]
public string AccountType { get; set; } = string.Empty;
/// <summary>AccountSubType enum name (e.g. Checking, AccountsReceivable, Sales).</summary>
[Name("AccountSubType")]
public string AccountSubType { get; set; } = string.Empty;
[Name("Description")]
public string? Description { get; set; }
[Name("OpeningBalance")]
public decimal? OpeningBalance { get; set; }
[Name("OpeningBalanceDate")]
public string? OpeningBalanceDate { get; set; }
[Name("IsActive")]
public bool? IsActive { get; set; }
}
@@ -0,0 +1,27 @@
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// Result of a CSV bulk import operation.
/// </summary>
public class CsvImportResultDto
{
public bool Success { get; set; }
public int SuccessCount { get; set; }
public int ErrorCount { get; set; }
public int SkippedCount { get; set; }
public int TotalRows { get; set; }
public List<string> Errors { get; set; } = new();
public List<string> Warnings { get; set; } = new();
public string Summary
{
get
{
var parts = new List<string> { $"{SuccessCount} imported" };
if (SkippedCount > 0) parts.Add($"{SkippedCount} skipped (already exist)");
if (ErrorCount > 0) parts.Add($"{ErrorCount} failed");
parts.Add($"{TotalRows} total rows");
return string.Join(", ", parts) + ".";
}
}
}
@@ -0,0 +1,66 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing customers from CSV files.
/// </summary>
public class CustomerImportDto
{
[Name("CompanyName")]
public string CompanyName { get; set; } = string.Empty;
[Name("ContactFirstName")]
public string? ContactFirstName { get; set; }
[Name("ContactLastName")]
public string? ContactLastName { get; set; }
[Name("Email")]
public string? Email { get; set; }
[Name("Phone")]
public string? Phone { get; set; }
[Name("MobilePhone")]
public string? MobilePhone { get; set; }
[Name("Address")]
public string? Address { get; set; }
[Name("City")]
public string? City { get; set; }
[Name("State")]
public string? State { get; set; }
[Name("ZipCode")]
public string? ZipCode { get; set; }
[Name("Country")]
public string? Country { get; set; }
[Name("CustomerType")]
public string? CustomerType { get; set; }
[Name("PricingTierCode")]
public string? PricingTierCode { get; set; }
[Name("CreditLimit")]
public decimal? CreditLimit { get; set; }
[Name("PaymentTerms")]
public string? PaymentTerms { get; set; }
[Name("TaxExempt")]
public bool? TaxExempt { get; set; }
[Name("TaxId")]
public string? TaxId { get; set; }
[Name("IsActive")]
public bool? IsActive { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,51 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing equipment from CSV files.
/// </summary>
public class EquipmentImportDto
{
[Name("EquipmentName")]
public string EquipmentName { get; set; } = string.Empty;
[Name("EquipmentNumber")]
public string? EquipmentNumber { get; set; }
[Name("EquipmentType")]
public string EquipmentType { get; set; } = string.Empty;
[Name("Manufacturer")]
public string? Manufacturer { get; set; }
[Name("Model")]
public string? Model { get; set; }
[Name("SerialNumber")]
public string? SerialNumber { get; set; }
[Name("PurchaseDate")]
public DateTime? PurchaseDate { get; set; }
[Name("PurchasePrice")]
public decimal? PurchasePrice { get; set; }
[Name("WarrantyExpiration")]
public DateTime? WarrantyExpiration { get; set; }
[Name("Location")]
public string? Location { get; set; }
[Name("RecommendedMaintenanceIntervalDays")]
public int? RecommendedMaintenanceIntervalDays { get; set; }
[Name("Status")]
public string Status { get; set; } = "Operational";
[Name("IsActive")]
public bool? IsActive { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,46 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing expenses from CSV files.
/// ExpenseNumber is optional — a new EXP-YYMM-#### number is generated when left blank.
/// ExpenseAccountNumber and PaymentAccountNumber are matched against Account.AccountNumber.
/// VendorName is matched against Vendor.CompanyName (optional).
/// JobNumber is matched against Job.JobNumber (optional).
/// PaymentMethod accepts: Cash, Check, CreditDebitCard, BankTransferACH, DigitalPayment.
/// </summary>
public class ExpenseImportDto
{
// Optional — auto-generated if blank
[Name("ExpenseNumber")]
public string? ExpenseNumber { get; set; }
[Name("Date")]
public DateTime Date { get; set; }
// Optional — matched to Vendor by CompanyName
[Name("VendorName")]
public string? VendorName { get; set; }
// Required — matched to Account by AccountNumber (e.g. "6200")
[Name("ExpenseAccountNumber")]
public string ExpenseAccountNumber { get; set; } = string.Empty;
// Required — matched to Account by AccountNumber (e.g. "1000")
[Name("PaymentAccountNumber")]
public string PaymentAccountNumber { get; set; } = string.Empty;
// Optional — matched to Job by JobNumber
[Name("JobNumber")]
public string? JobNumber { get; set; }
[Name("PaymentMethod")]
public string PaymentMethod { get; set; } = "Cash";
[Name("Amount")]
public decimal Amount { get; set; }
[Name("Memo")]
public string? Memo { get; set; }
}
@@ -0,0 +1,91 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing inventory items from CSV files.
/// </summary>
public class InventoryItemImportDto
{
[Name("SKU")]
public string SKU { get; set; } = string.Empty;
// "Name" = old export header; "ItemName" = new export header
[Name("ItemName", "Name")]
public string ItemName { get; set; } = string.Empty;
[Name("Description")]
public string? Description { get; set; }
// "Category" = old export header; "CategoryName" = new export header
[Name("CategoryName", "Category")]
public string? CategoryName { get; set; }
[Name("Manufacturer")]
public string? Manufacturer { get; set; }
[Name("ManufacturerPartNumber")]
public string? ManufacturerPartNumber { get; set; }
// "Color" = old export header; "ColorName" = new export header
[Name("ColorName", "Color")]
public string? ColorName { get; set; }
[Name("ColorCode")]
public string? ColorCode { get; set; }
[Name("Finish")]
public string? Finish { get; set; }
// Vendor name is used to look up PrimaryVendorId
[Name("VendorName")]
public string? VendorName { get; set; }
[Name("VendorPartNumber")]
public string? VendorPartNumber { get; set; }
// "QuantityOnHand" / "Qty on Hand" = old export headers; "QuantityInStock" = new export header
[Name("QuantityInStock", "QuantityOnHand", "Qty on Hand")]
public decimal? QuantityInStock { get; set; }
// "Unit" = old export header; "UnitOfMeasure" = new export header
[Name("UnitOfMeasure", "Unit")]
public string? UnitOfMeasure { get; set; }
// "Unit Cost" = old export header; "UnitCost" = new export header
[Name("UnitCost", "Unit Cost")]
public decimal? UnitCost { get; set; }
[Name("LastPurchasePrice")]
public decimal? LastPurchasePrice { get; set; }
// "Reorder Point" = old export header; "ReorderPoint" = new export header
[Name("ReorderPoint", "Reorder Point")]
public decimal? ReorderPoint { get; set; }
[Name("ReorderQuantity")]
public decimal? ReorderQuantity { get; set; }
[Name("MinimumStock")]
public decimal? MinimumStock { get; set; }
[Name("MaximumStock")]
public decimal? MaximumStock { get; set; }
// Powder-specific coverage fields
[Name("CoverageSqFtPerLb")]
public decimal? CoverageSqFtPerLb { get; set; }
[Name("TransferEfficiencyPct")]
public decimal? TransferEfficiencyPct { get; set; }
[Name("Location")]
public string? Location { get; set; }
// "Active" = old export header (Yes/No); "IsActive" = new export header (true/false)
[Name("IsActive", "Active")]
public bool? IsActive { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,66 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing invoice headers from CSV files.
/// Column names match the native CSV export (ExportInvoicesCsv) for round-trip compatibility.
/// CustomerEmail is an additional optional column for more reliable customer matching when
/// the file did not originate from the native export.
/// </summary>
public class InvoiceImportDto
{
[Name("InvoiceNumber")]
public string? InvoiceNumber { get; set; }
// Customer resolution: email takes priority, name is the fallback
[Name("CustomerEmail")]
public string? CustomerEmail { get; set; }
// "Customer" matches the column written by ExportInvoicesCsv
[Name("Customer")]
public string? CustomerName { get; set; }
[Name("JobNumber")]
public string? JobNumber { get; set; }
[Name("Status")]
public string Status { get; set; } = "Draft";
[Name("InvoiceDate")]
public DateTime InvoiceDate { get; set; }
[Name("DueDate")]
public DateTime? DueDate { get; set; }
[Name("SubTotal")]
public decimal SubTotal { get; set; }
[Name("TaxPercent")]
public decimal TaxPercent { get; set; }
[Name("TaxAmount")]
public decimal TaxAmount { get; set; }
[Name("DiscountAmount")]
public decimal DiscountAmount { get; set; }
[Name("Total")]
public decimal Total { get; set; }
[Name("AmountPaid")]
public decimal AmountPaid { get; set; }
// BalanceDue is computed (Total - AmountPaid); ignored on import
[Ignore]
public decimal BalanceDue { get; set; }
[Name("CustomerPO")]
public string? CustomerPO { get; set; }
[Name("Terms")]
public string? Terms { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,49 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing jobs from CSV files.
/// CustomerEmail is the preferred customer lookup key; CustomerName is used as a fallback
/// when the customer has no email address on file (the importer tries email first, then name).
/// </summary>
public class JobImportDto
{
// "Job Number" = old export header (spaced); "JobNumber" = current header
[Name("JobNumber", "Job Number")]
public string? JobNumber { get; set; }
// Optional — importer falls back to CustomerName when blank
[Name("CustomerEmail")]
public string? CustomerEmail { get; set; }
// Fallback identifier when CustomerEmail is absent; matched against CompanyName
[Name("CustomerName")]
public string? CustomerName { get; set; }
[Name("Status")]
public string Status { get; set; } = "Pending";
[Name("Priority")]
public string Priority { get; set; } = "Normal";
[Name("ScheduledDate")]
public DateTime? ScheduledDate { get; set; }
// "Due Date" = old export header (spaced); "DueDate" = current header
[Name("DueDate", "Due Date")]
public DateTime? DueDate { get; set; }
// "Final Price" = old export header (spaced); "FinalPrice" = current header
[Name("FinalPrice", "Final Price")]
public decimal? FinalPrice { get; set; }
[Name("CustomerPO")]
public string? CustomerPO { get; set; }
[Name("SpecialInstructions")]
public string? SpecialInstructions { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,45 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing maintenance records from CSV files.
/// </summary>
public class MaintenanceImportDto
{
[Name("EquipmentName")]
public string EquipmentName { get; set; } = string.Empty;
[Name("MaintenanceType")]
public string MaintenanceType { get; set; } = string.Empty;
[Name("ScheduledDate")]
public DateTime ScheduledDate { get; set; }
[Name("CompletedDate")]
public DateTime? CompletedDate { get; set; }
[Name("Status")]
public string Status { get; set; } = "Scheduled";
[Name("Priority")]
public string Priority { get; set; } = "Normal";
[Name("LaborCost")]
public decimal? LaborCost { get; set; }
[Name("PartsCost")]
public decimal? PartsCost { get; set; }
[Name("TotalCost")]
public decimal? TotalCost { get; set; }
[Name("Description")]
public string Description { get; set; } = string.Empty;
[Name("WorkPerformed")]
public string? WorkPerformed { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,32 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing payment records from CSV. Column names match the native
/// ExportPaymentsCsv output for round-trip compatibility. Payments are matched
/// to invoices by InvoiceNumber; duplicates are detected by InvoiceNumber + PaymentDate + Amount.
/// </summary>
public class PaymentImportDto
{
[Name("InvoiceNumber")]
public string? InvoiceNumber { get; set; }
[Name("Amount")]
public decimal Amount { get; set; }
[Name("PaymentDate")]
public DateTime PaymentDate { get; set; }
/// <summary>
/// Valid values: Cash, Check, CreditDebitCard, BankTransferACH, DigitalPayment
/// </summary>
[Name("PaymentMethod")]
public string PaymentMethod { get; set; } = "Cash";
[Name("Reference")]
public string? Reference { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,21 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing prep services from CSV files.
/// </summary>
public class PrepServiceImportDto
{
[Name("ServiceName")]
public string ServiceName { get; set; } = string.Empty;
[Name("Description")]
public string? Description { get; set; }
[Name("DisplayOrder")]
public int? DisplayOrder { get; set; }
[Name("IsActive")]
public bool? IsActive { get; set; }
}
@@ -0,0 +1,44 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing purchase order headers from CSV. Column names match the native
/// ExportPurchaseOrdersCsv output for round-trip compatibility. Upsert key is PoNumber.
/// </summary>
public class PurchaseOrderImportDto
{
[Name("PoNumber")]
public string? PoNumber { get; set; }
/// <summary>Vendor company name — must match an existing vendor record.</summary>
[Name("Vendor")]
public string? Vendor { get; set; }
/// <summary>
/// Valid values: Draft, Submitted, PartiallyReceived, Received, Cancelled
/// </summary>
[Name("Status")]
public string Status { get; set; } = "Draft";
[Name("OrderDate")]
public DateTime OrderDate { get; set; }
[Name("ExpectedDeliveryDate")]
public DateTime? ExpectedDeliveryDate { get; set; }
[Name("ReceivedDate")]
public DateTime? ReceivedDate { get; set; }
[Name("SubTotal")]
public decimal SubTotal { get; set; }
[Name("ShippingCost")]
public decimal ShippingCost { get; set; }
[Name("TotalAmount")]
public decimal TotalAmount { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,55 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing quotes from CSV files.
/// </summary>
public class QuoteImportDto
{
[Name("QuoteNumber")]
public string? QuoteNumber { get; set; }
[Name("CustomerEmail")]
public string? CustomerEmail { get; set; }
// Fallback when CustomerEmail is absent; matched against CompanyName then ContactName
[Name("CustomerName")]
public string? CustomerName { get; set; }
[Name("ProspectCompany")]
public string? ProspectCompany { get; set; }
[Name("ProspectContact")]
public string? ProspectContact { get; set; }
[Name("ProspectEmail")]
public string? ProspectEmail { get; set; }
[Name("ProspectPhone")]
public string? ProspectPhone { get; set; }
[Name("Status")]
public string Status { get; set; } = "Draft";
[Name("QuoteDate")]
public DateTime QuoteDate { get; set; }
[Name("ExpirationDate")]
public DateTime? ExpirationDate { get; set; }
[Name("Subtotal")]
public decimal Subtotal { get; set; }
[Name("TaxAmount")]
public decimal TaxAmount { get; set; }
[Name("Total")]
public decimal Total { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
[Name("TermsAndConditions")]
public string? TermsAndConditions { get; set; }
}
@@ -0,0 +1,28 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing shop workers from CSV files.
/// Valid Role values: GeneralLabor, Sandblaster, Coater, Masker, QualityControl, OvenOperator, Supervisor, Maintenance
/// </summary>
public class ShopWorkerImportDto
{
[Name("Name")]
public string Name { get; set; } = string.Empty;
[Name("Role")]
public string Role { get; set; } = "GeneralLabor";
[Name("Phone")]
public string? Phone { get; set; }
[Name("Email")]
public string? Email { get; set; }
[Name("IsActive")]
public bool? IsActive { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}
@@ -0,0 +1,60 @@
using CsvHelper.Configuration.Attributes;
namespace PowderCoating.Application.DTOs.Import;
/// <summary>
/// DTO for importing vendors from CSV files.
/// </summary>
public class VendorImportDto
{
[Name("CompanyName")]
public string CompanyName { get; set; } = string.Empty;
[Name("ContactName")]
public string? ContactName { get; set; }
[Name("Email")]
public string? Email { get; set; }
[Name("Phone")]
public string? Phone { get; set; }
[Name("Address")]
public string? Address { get; set; }
[Name("City")]
public string? City { get; set; }
[Name("State")]
public string? State { get; set; }
[Name("ZipCode")]
public string? ZipCode { get; set; }
[Name("Country")]
public string? Country { get; set; }
[Name("Website")]
public string? Website { get; set; }
[Name("AccountNumber")]
public string? AccountNumber { get; set; }
[Name("TaxId")]
public string? TaxId { get; set; }
[Name("PaymentTerms")]
public string? PaymentTerms { get; set; }
[Name("CreditLimit")]
public decimal? CreditLimit { get; set; }
[Name("IsPreferred")]
public bool? IsPreferred { get; set; }
[Name("IsActive")]
public bool? IsActive { get; set; }
[Name("Notes")]
public string? Notes { get; set; }
}