dfb1d34af3
- Inventory: location filter dropdown + Print Bin page (line #, name, color, SKU) - Fix: Prismatic Powders QR scan now extracts manufacturer/SKU/color from URL path and uses full LookupAsync pipeline instead of relying on page fetch alone - Fix: iOS Safari 'Login / data Zero KB' download -- add OnRejected HTML response to rate limiter - Fix: mobile session logout -- ConfigureApplicationCookie with 30-day MaxAge persistent cookie - Help: new 'Location Filtering & Bin Print' section in Inventory help article - Help: HelpKnowledgeBase updated with bin filter and print bin details Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
291 lines
11 KiB
C#
291 lines
11 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
|
|
namespace PowderCoating.Application.DTOs.Inventory;
|
|
|
|
public class InventoryItemDto
|
|
{
|
|
public int Id { get; set; }
|
|
public string SKU { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public int? InventoryCategoryId { get; set; }
|
|
public string? CategoryName { get; set; }
|
|
public string Category { get; set; } = string.Empty; // Legacy field
|
|
public string? ColorName { get; set; }
|
|
public string? ColorCode { get; set; }
|
|
public string? Finish { get; set; }
|
|
public string? Manufacturer { get; set; }
|
|
public string? ManufacturerPartNumber { get; set; }
|
|
public decimal? CoverageSqFtPerLb { get; set; }
|
|
public decimal? SpecificGravity { get; set; }
|
|
public decimal? TransferEfficiency { get; set; }
|
|
public int? CureTemperatureF { get; set; }
|
|
public int? CureTimeMinutes { get; set; }
|
|
public string? ColorFamilies { get; set; }
|
|
public bool RequiresClearCoat { get; set; }
|
|
public string? SpecPageUrl { get; set; }
|
|
public string? ImageUrl { get; set; }
|
|
public string? SdsUrl { get; set; }
|
|
public string? TdsUrl { get; set; }
|
|
public decimal QuantityOnHand { get; set; }
|
|
public string UnitOfMeasure { get; set; } = "lbs";
|
|
public decimal ReorderPoint { get; set; }
|
|
public decimal ReorderQuantity { get; set; }
|
|
public decimal MinimumStock { get; set; }
|
|
public decimal MaximumStock { get; set; }
|
|
public decimal UnitCost { get; set; }
|
|
public decimal AverageCost { get; set; }
|
|
public decimal LastPurchasePrice { get; set; }
|
|
public DateTime? LastPurchaseDate { get; set; }
|
|
public int? PrimaryVendorId { get; set; }
|
|
public string? PrimaryVendorName { get; set; }
|
|
public string? VendorPartNumber { get; set; }
|
|
public string? Location { get; set; }
|
|
public string? Notes { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public bool IsIncoming { get; set; }
|
|
public DateTime? DiscontinuedDate { get; set; }
|
|
public DateTime CreatedAt { get; set; }
|
|
public bool IsLowStock { get; set; }
|
|
public bool IsOutOfStock { get; set; }
|
|
public bool HasSamplePanel { get; set; }
|
|
|
|
[Display(Name = "Inventory Asset Account")]
|
|
public int? InventoryAccountId { get; set; }
|
|
public string? InventoryAccountName { get; set; }
|
|
|
|
[Display(Name = "COGS Account")]
|
|
public int? CogsAccountId { get; set; }
|
|
public string? CogsAccountName { get; set; }
|
|
}
|
|
|
|
public class InventoryListDto
|
|
{
|
|
public int Id { get; set; }
|
|
public string SKU { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public int? InventoryCategoryId { get; set; }
|
|
public string? CategoryName { get; set; }
|
|
public string Category { get; set; } = string.Empty; // Legacy field
|
|
public string? ColorName { get; set; }
|
|
public string? Location { get; set; }
|
|
public decimal QuantityOnHand { get; set; }
|
|
public string UnitOfMeasure { get; set; } = "lbs";
|
|
public decimal ReorderPoint { get; set; }
|
|
public decimal UnitCost { get; set; }
|
|
public int? PrimaryVendorId { get; set; }
|
|
public string? PrimaryVendorName { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public bool IsIncoming { get; set; }
|
|
public bool IsLowStock { get; set; }
|
|
public bool IsOutOfStock { get; set; }
|
|
public bool HasSamplePanel { get; set; }
|
|
}
|
|
|
|
public class CreateInventoryItemDto
|
|
{
|
|
[Required(ErrorMessage = "SKU is required")]
|
|
[StringLength(50, ErrorMessage = "SKU cannot exceed 50 characters")]
|
|
[Display(Name = "SKU")]
|
|
public string SKU { get; set; } = string.Empty;
|
|
|
|
[Required(ErrorMessage = "Name is required")]
|
|
[StringLength(200, ErrorMessage = "Name cannot exceed 200 characters")]
|
|
[Display(Name = "Item Name")]
|
|
public string Name { get; set; } = string.Empty;
|
|
|
|
[StringLength(2000, ErrorMessage = "Description cannot exceed 2000 characters")]
|
|
[Display(Name = "Description")]
|
|
public string? Description { get; set; }
|
|
|
|
[Display(Name = "Category")]
|
|
public int? InventoryCategoryId { get; set; }
|
|
|
|
[StringLength(100, ErrorMessage = "Category cannot exceed 100 characters")]
|
|
[Display(Name = "Legacy Category")]
|
|
public string Category { get; set; } = string.Empty; // Legacy field - kept for backward compatibility
|
|
|
|
[StringLength(100, ErrorMessage = "Color name cannot exceed 100 characters")]
|
|
[Display(Name = "Color Name")]
|
|
public string? ColorName { get; set; }
|
|
|
|
[StringLength(50, ErrorMessage = "Color code cannot exceed 50 characters")]
|
|
[Display(Name = "Color Code")]
|
|
public string? ColorCode { get; set; }
|
|
|
|
[StringLength(50, ErrorMessage = "Finish cannot exceed 50 characters")]
|
|
[Display(Name = "Finish")]
|
|
public string? Finish { get; set; }
|
|
|
|
[StringLength(100, ErrorMessage = "Manufacturer cannot exceed 100 characters")]
|
|
[Display(Name = "Manufacturer")]
|
|
public string? Manufacturer { get; set; }
|
|
|
|
[StringLength(100, ErrorMessage = "Manufacturer part number cannot exceed 100 characters")]
|
|
[Display(Name = "Manufacturer Part Number")]
|
|
public string? ManufacturerPartNumber { get; set; }
|
|
|
|
[Range(0, 10000, ErrorMessage = "Coverage must be between 0 and 10,000 sq ft/lb")]
|
|
[Display(Name = "Coverage (Sq Ft/Lb)")]
|
|
public decimal? CoverageSqFtPerLb { get; set; }
|
|
|
|
[Range(0, 100, ErrorMessage = "Specific gravity must be between 0 and 100")]
|
|
[Display(Name = "Specific Gravity")]
|
|
public decimal? SpecificGravity { get; set; }
|
|
|
|
[Range(0, 100, ErrorMessage = "Transfer efficiency must be between 0 and 100%")]
|
|
[Display(Name = "Transfer Efficiency (%)")]
|
|
public decimal? TransferEfficiency { get; set; }
|
|
|
|
[Range(200, 500, ErrorMessage = "Cure temperature must be between 200°F and 500°F")]
|
|
[Display(Name = "Cure Temperature (°F)")]
|
|
public int? CureTemperatureF { get; set; }
|
|
|
|
[Range(1, 120, ErrorMessage = "Cure time must be between 1 and 120 minutes")]
|
|
[Display(Name = "Cure Time (minutes)")]
|
|
public int? CureTimeMinutes { get; set; }
|
|
|
|
[Display(Name = "Color Families")]
|
|
public string? ColorFamilies { get; set; }
|
|
|
|
[Display(Name = "Requires Clear Coat")]
|
|
public bool RequiresClearCoat { get; set; }
|
|
|
|
[StringLength(500, ErrorMessage = "URL cannot exceed 500 characters")]
|
|
[Display(Name = "Product URL")]
|
|
public string? SpecPageUrl { get; set; }
|
|
|
|
[StringLength(1000, ErrorMessage = "Image URL cannot exceed 1000 characters")]
|
|
[Display(Name = "Product Image URL")]
|
|
public string? ImageUrl { get; set; }
|
|
|
|
[StringLength(1000, ErrorMessage = "URL cannot exceed 1000 characters")]
|
|
[Display(Name = "Safety Data Sheet URL")]
|
|
public string? SdsUrl { get; set; }
|
|
|
|
[StringLength(1000, ErrorMessage = "URL cannot exceed 1000 characters")]
|
|
[Display(Name = "Technical Data Sheet URL")]
|
|
public string? TdsUrl { get; set; }
|
|
|
|
[Range(0, 999999999, ErrorMessage = "Quantity on hand must be 0 or greater")]
|
|
[Display(Name = "Quantity on Hand")]
|
|
public decimal QuantityOnHand { get; set; }
|
|
|
|
[Required(ErrorMessage = "Unit of measure is required")]
|
|
[StringLength(20, ErrorMessage = "Unit of measure cannot exceed 20 characters")]
|
|
[Display(Name = "Unit of Measure")]
|
|
public string UnitOfMeasure { get; set; } = "lbs";
|
|
|
|
[Range(0, 999999999, ErrorMessage = "Reorder point must be 0 or greater")]
|
|
[Display(Name = "Reorder Point")]
|
|
public decimal ReorderPoint { get; set; }
|
|
|
|
[Range(0, 999999999, ErrorMessage = "Reorder quantity must be 0 or greater")]
|
|
[Display(Name = "Reorder Quantity")]
|
|
public decimal ReorderQuantity { get; set; }
|
|
|
|
[Range(0, 999999999, ErrorMessage = "Minimum stock must be 0 or greater")]
|
|
[Display(Name = "Minimum Stock")]
|
|
public decimal MinimumStock { get; set; }
|
|
|
|
[Range(0, 999999999, ErrorMessage = "Maximum stock must be 0 or greater")]
|
|
[Display(Name = "Maximum Stock")]
|
|
public decimal MaximumStock { get; set; }
|
|
|
|
[Range(0, 9999999.99, ErrorMessage = "Unit cost must be between 0 and 9,999,999.99")]
|
|
[Display(Name = "Unit Cost")]
|
|
public decimal UnitCost { get; set; }
|
|
|
|
[Display(Name = "Primary Vendor")]
|
|
public int? PrimaryVendorId { get; set; }
|
|
|
|
[StringLength(100, ErrorMessage = "Vendor part number cannot exceed 100 characters")]
|
|
[Display(Name = "Vendor Part Number")]
|
|
public string? VendorPartNumber { get; set; }
|
|
|
|
[StringLength(200, ErrorMessage = "Location cannot exceed 200 characters")]
|
|
[Display(Name = "Location")]
|
|
public string? Location { get; set; }
|
|
|
|
[StringLength(2000, ErrorMessage = "Notes cannot exceed 2000 characters")]
|
|
[Display(Name = "Notes")]
|
|
public string? Notes { get; set; }
|
|
|
|
[Display(Name = "Inventory Asset Account")]
|
|
public int? InventoryAccountId { get; set; }
|
|
|
|
[Display(Name = "COGS Account")]
|
|
public int? CogsAccountId { get; set; }
|
|
|
|
[Display(Name = "Sample Panel on Wall")]
|
|
public bool HasSamplePanel { get; set; }
|
|
|
|
[Display(Name = "Incoming / On Order")]
|
|
public bool IsIncoming { get; set; }
|
|
|
|
}
|
|
|
|
public class UpdateInventoryItemDto : CreateInventoryItemDto
|
|
{
|
|
[Required]
|
|
public int Id { get; set; }
|
|
|
|
[Display(Name = "Active")]
|
|
public bool IsActive { get; set; }
|
|
}
|
|
|
|
public class InventoryTransactionDto
|
|
{
|
|
public int Id { get; set; }
|
|
public int InventoryItemId { get; set; }
|
|
public string ItemName { get; set; } = string.Empty;
|
|
public string SKU { get; set; } = string.Empty;
|
|
public string TransactionType { get; set; } = string.Empty;
|
|
public decimal Quantity { get; set; }
|
|
public decimal UnitCost { get; set; }
|
|
public decimal TotalCost { get; set; }
|
|
public DateTime TransactionDate { get; set; }
|
|
public string? Reference { get; set; }
|
|
public string? Notes { get; set; }
|
|
public decimal BalanceAfter { get; set; }
|
|
public int? PurchaseOrderId { get; set; }
|
|
public string? PurchaseOrderNumber { get; set; }
|
|
public int? JobId { get; set; }
|
|
public string? JobNumber { get; set; }
|
|
}
|
|
|
|
public class PowderUsageLogDto
|
|
{
|
|
public int Id { get; set; }
|
|
public int JobId { get; set; }
|
|
public string JobNumber { get; set; } = string.Empty;
|
|
public string? CustomerName { get; set; }
|
|
public int? InventoryItemId { get; set; }
|
|
public string? ItemName { get; set; }
|
|
public string? SKU { get; set; }
|
|
public string? CoatColor { get; set; }
|
|
public decimal ActualLbsUsed { get; set; }
|
|
public decimal EstimatedLbs { get; set; }
|
|
public decimal VarianceLbs { get; set; }
|
|
public DateTime RecordedAt { get; set; }
|
|
public string? Notes { get; set; }
|
|
/// <summary>Set only for rows synthesized from a scan-based InventoryTransaction (no PowderUsageLog record).</summary>
|
|
public int? SourceTransactionId { get; set; }
|
|
}
|
|
|
|
public class InventoryLedgerViewModel
|
|
{
|
|
public int? InventoryItemId { get; set; }
|
|
public string? SelectedItemName { get; set; }
|
|
public string? SelectedItemSku { get; set; }
|
|
public DateTime? DateFrom { get; set; }
|
|
public DateTime? DateTo { get; set; }
|
|
public string? TypeFilter { get; set; }
|
|
public List<InventoryTransactionDto> Transactions { get; set; } = new();
|
|
public List<PowderUsageLogDto> PowderUsageLogs { get; set; } = new();
|
|
public List<InventoryListDto> AllItems { get; set; } = new();
|
|
public decimal TotalPurchased { get; set; }
|
|
public decimal TotalUsed { get; set; }
|
|
public decimal TotalAdjusted { get; set; }
|
|
}
|