fde24b09c9
- Invoice Write-Off: WriteOff POST action in InvoicesController posts bad-debt JE (DR bad debt expense / CR AR), reduces customer balance, marks invoice WrittenOff; write-off modal added to Invoice Details view with expense account selector - Fixed Assets: FixedAsset + FixedAssetDepreciationEntry entities with straight-line depreciation; FixedAssetsController (Index/Create/Edit/Details/PostDepreciation/Delete); PostDepreciation auto-generates one JE per asset per period, skips already-posted, fully-depreciated, and disposed assets; full CRUD views + nav link - Period Locking: Company.BookLockedThrough field; AccountingPeriodValidator static helper; lock check added to JE Post and Bill Create (blocks backdating into closed periods); SetPeriodLock action + date picker UI in Company Settings Accounting section - 1099 Tracking: Is1099Vendor flag on Vendor entity + DTOs; checkbox in Create/Edit views; TaxReporting1099 report action + view lists payments by year, flags vendors >= $600; report card added to Reports Landing - Migration AddFixedAssetsLockAnd1099 applied Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
6.0 KiB
C#
180 lines
6.0 KiB
C#
using PowderCoating.Core.Enums;
|
|
|
|
namespace PowderCoating.Core.Entities;
|
|
|
|
public class Vendor : BaseEntity
|
|
{
|
|
public string CompanyName { get; set; } = string.Empty;
|
|
public string? ContactName { get; set; }
|
|
public string? Email { get; set; }
|
|
public string? Phone { get; set; }
|
|
public string? Address { get; set; }
|
|
public string? City { get; set; }
|
|
public string? State { get; set; }
|
|
public string? ZipCode { get; set; }
|
|
public string? Country { get; set; } = "USA";
|
|
|
|
public string? Website { get; set; }
|
|
|
|
// Business Information
|
|
public string? AccountNumber { get; set; }
|
|
public string? TaxId { get; set; }
|
|
public string? PaymentTerms { get; set; }
|
|
public decimal? CreditLimit { get; set; }
|
|
|
|
public string? Notes { get; set; }
|
|
public bool IsActive { get; set; } = true;
|
|
public bool IsPreferred { get; set; } = false;
|
|
|
|
// Accounts Payable tracking
|
|
/// <summary>Running AP balance: increases with new bills, decreases with payments.</summary>
|
|
public decimal CurrentBalance { get; set; } = 0;
|
|
/// <summary>Balance owed at go-live (for migrating existing vendors).</summary>
|
|
public decimal OpeningBalance { get; set; } = 0;
|
|
public DateTime? OpeningBalanceDate { get; set; }
|
|
/// <summary>Default expense account pre-filled on new bill line items for this vendor.</summary>
|
|
public int? DefaultExpenseAccountId { get; set; }
|
|
|
|
// 1099 Contractor tracking
|
|
/// <summary>When true, this vendor is an independent contractor subject to 1099-NEC reporting.</summary>
|
|
public bool Is1099Vendor { get; set; } = false;
|
|
|
|
// Navigation
|
|
public virtual ICollection<InventoryItem> InventoryItems { get; set; } = new List<InventoryItem>();
|
|
public virtual ICollection<Bill> Bills { get; set; } = new List<Bill>();
|
|
public virtual ICollection<BillPayment> BillPayments { get; set; } = new List<BillPayment>();
|
|
public virtual ICollection<Expense> Expenses { get; set; } = new List<Expense>();
|
|
public virtual Account? DefaultExpenseAccount { get; set; }
|
|
}
|
|
|
|
public class InventoryTransaction : BaseEntity
|
|
{
|
|
public int InventoryItemId { get; set; }
|
|
public InventoryTransactionType TransactionType { get; set; }
|
|
public decimal Quantity { get; set; }
|
|
public decimal UnitCost { get; set; }
|
|
public decimal TotalCost { get; set; }
|
|
|
|
public DateTime TransactionDate { get; set; } = DateTime.UtcNow;
|
|
|
|
public string? Reference { get; set; } // PO number, Job number, etc.
|
|
public string? Notes { get; set; }
|
|
|
|
public decimal BalanceAfter { get; set; }
|
|
|
|
// Optional FK to the PO that generated this purchase transaction
|
|
public int? PurchaseOrderId { get; set; }
|
|
public virtual PurchaseOrder? PurchaseOrder { get; set; }
|
|
|
|
// Optional FK to the job this material was used on (set by QR scan / manual log)
|
|
public int? JobId { get; set; }
|
|
public virtual Job? Job { get; set; }
|
|
|
|
public virtual InventoryItem InventoryItem { get; set; } = null!;
|
|
}
|
|
|
|
public class JobPhoto : BaseEntity
|
|
{
|
|
public int JobId { get; set; }
|
|
public virtual Job Job { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Relative path from media folder (e.g., "1/job-photos/5/1.jpg")
|
|
/// </summary>
|
|
public string FilePath { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Original filename when uploaded
|
|
/// </summary>
|
|
public string FileName { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// File size in bytes
|
|
/// </summary>
|
|
public long FileSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// Content type (e.g., "image/jpeg")
|
|
/// </summary>
|
|
public string ContentType { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// User-provided caption/note for the photo
|
|
/// </summary>
|
|
public string? Caption { get; set; }
|
|
|
|
/// <summary>
|
|
/// Type of photo (Before, After, Progress, QualityCheck, Issue, Completed)
|
|
/// </summary>
|
|
public JobPhotoType PhotoType { get; set; } = JobPhotoType.Progress;
|
|
|
|
/// <summary>
|
|
/// Display order for sorting photos
|
|
/// </summary>
|
|
public int DisplayOrder { get; set; }
|
|
|
|
/// <summary>
|
|
/// User who uploaded the photo
|
|
/// </summary>
|
|
public string UploadedById { get; set; } = string.Empty;
|
|
public virtual ApplicationUser? UploadedBy { get; set; }
|
|
|
|
public DateTime UploadedDate { get; set; } = DateTime.UtcNow;
|
|
|
|
/// <summary>
|
|
/// Comma-separated tags for this photo (e.g., colors used, finish type)
|
|
/// </summary>
|
|
public string? Tags { get; set; }
|
|
|
|
/// <summary>
|
|
/// True for photos copied from an AI quote analysis — excluded from subscription photo limits.
|
|
/// </summary>
|
|
public bool IsAiAnalysisPhoto { get; set; }
|
|
}
|
|
|
|
public class JobNote : BaseEntity
|
|
{
|
|
public int JobId { get; set; }
|
|
public string Note { get; set; } = string.Empty;
|
|
public bool IsImportant { get; set; }
|
|
public bool IsInternal { get; set; } = true; // Don't show to customer
|
|
|
|
public virtual Job Job { get; set; } = null!;
|
|
}
|
|
|
|
public class CustomerNote : BaseEntity
|
|
{
|
|
public int CustomerId { get; set; }
|
|
public string Note { get; set; } = string.Empty;
|
|
public bool IsImportant { get; set; }
|
|
|
|
public virtual Customer Customer { get; set; } = null!;
|
|
}
|
|
|
|
public class JobStatusHistory : BaseEntity
|
|
{
|
|
public int JobId { get; set; }
|
|
|
|
// Lookup foreign keys (replacing enums)
|
|
public int FromStatusId { get; set; }
|
|
public int ToStatusId { get; set; }
|
|
|
|
public DateTime ChangedDate { get; set; } = DateTime.UtcNow;
|
|
public string? Notes { get; set; }
|
|
|
|
// Navigation properties
|
|
public virtual Job Job { get; set; } = null!;
|
|
public virtual JobStatusLookup FromStatus { get; set; } = null!;
|
|
public virtual JobStatusLookup ToStatus { get; set; } = null!;
|
|
}
|
|
|
|
public class PricingTier : BaseEntity
|
|
{
|
|
public string TierName { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public decimal DiscountPercent { get; set; }
|
|
public bool IsActive { get; set; } = true;
|
|
|
|
public virtual ICollection<Customer> Customers { get; set; } = new List<Customer>();
|
|
}
|