9221fcc783
When a source quote is edited after a job was created from it, the job details page now shows a warning banner with the date of the change and a link to the quote. Two actions are offered: - Re-sync from Quote: replaces all job items, coats, prep services, and pricing from the current quote. Only available while the job is still in a pre-production status (Pending, Quoted, Approved); hidden once shop work has started (InPreparation or beyond). - Dismiss: acknowledges the change without altering the job, clearing the banner by advancing the stored snapshot timestamp. Implemented via Job.QuoteSnapshotUpdatedAt (new nullable column), set at quote→job conversion time. The banner fires when quote.UpdatedAt exceeds this baseline. Migration: AddJobQuoteSnapshotUpdatedAt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
85 lines
3.7 KiB
C#
85 lines
3.7 KiB
C#
using System.ComponentModel.DataAnnotations.Schema;
|
|
using PowderCoating.Core.Enums;
|
|
|
|
namespace PowderCoating.Core.Entities;
|
|
|
|
public class Job : BaseEntity
|
|
{
|
|
public string JobNumber { get; set; } = string.Empty;
|
|
public int CustomerId { get; set; }
|
|
public int? QuoteId { get; set; }
|
|
public string? AssignedUserId { get; set; } // Assigned user
|
|
|
|
public string Description { get; set; } = string.Empty;
|
|
|
|
// Lookup foreign keys (replacing enums)
|
|
public int JobStatusId { get; set; }
|
|
public int JobPriorityId { get; set; }
|
|
|
|
// Dates
|
|
public DateTime? ScheduledDate { get; set; }
|
|
public DateTime? StartedDate { get; set; }
|
|
public DateTime? CompletedDate { get; set; }
|
|
public DateTime? DueDate { get; set; }
|
|
|
|
// Selected oven (carried over from quote; null = company default rate)
|
|
public int? OvenCostId { get; set; }
|
|
|
|
// Pricing
|
|
public decimal QuotedPrice { get; set; }
|
|
public decimal FinalPrice { get; set; }
|
|
|
|
// Discount & rush (mirrors quote fields; preserved through quote→job conversion and job edits)
|
|
public bool IsRushJob { get; set; }
|
|
public DiscountType DiscountType { get; set; } = DiscountType.None;
|
|
public decimal DiscountValue { get; set; }
|
|
public string? DiscountReason { get; set; }
|
|
|
|
// Job Completion Details
|
|
public decimal? ActualTimeSpentHours { get; set; }
|
|
|
|
// Additional Information
|
|
public string? CustomerPO { get; set; }
|
|
public string? SpecialInstructions { get; set; }
|
|
public string? InternalNotes { get; set; } // Internal notes from quote
|
|
public string? Tags { get; set; }
|
|
public bool RequiresCustomerApproval { get; set; }
|
|
public bool IsCustomerApproved { get; set; }
|
|
|
|
// Shop floor QR access token (no login required; scoped to this job only)
|
|
public Guid ShopAccessCode { get; set; } = Guid.NewGuid();
|
|
|
|
// Part intake / receiving
|
|
public DateTime? IntakeDate { get; set; }
|
|
public string? IntakeConditionNotes { get; set; }
|
|
public int? IntakePartCount { get; set; }
|
|
public string? IntakeCheckedByUserId { get; set; }
|
|
|
|
// Quote snapshot — UpdatedAt of the source quote at the moment this job was created from it.
|
|
// Used to detect when the quote was subsequently edited so the job details page can warn the user.
|
|
public DateTime? QuoteSnapshotUpdatedAt { get; set; }
|
|
|
|
// Rework tracking
|
|
public bool IsReworkJob { get; set; }
|
|
public int? OriginalJobId { get; set; } // Set when this job was created as a rework
|
|
|
|
// Relationships
|
|
[ForeignKey("IntakeCheckedByUserId")]
|
|
public virtual ApplicationUser? IntakeCheckedBy { get; set; }
|
|
public virtual OvenCost? OvenCost { get; set; }
|
|
public virtual Customer Customer { get; set; } = null!;
|
|
public virtual Quote? Quote { get; set; }
|
|
public virtual ApplicationUser? AssignedUser { get; set; }
|
|
public virtual JobStatusLookup JobStatus { get; set; } = null!;
|
|
public virtual JobPriorityLookup JobPriority { get; set; } = null!;
|
|
public virtual ICollection<JobItem> JobItems { get; set; } = new List<JobItem>();
|
|
public virtual ICollection<JobPhoto> Photos { get; set; } = new List<JobPhoto>();
|
|
public virtual ICollection<JobNote> Notes { get; set; } = new List<JobNote>();
|
|
public virtual ICollection<JobStatusHistory> StatusHistory { get; set; } = new List<JobStatusHistory>();
|
|
public virtual ICollection<JobPrepService> JobPrepServices { get; set; } = new List<JobPrepService>();
|
|
public virtual Invoice? Invoice { get; set; }
|
|
public virtual ICollection<JobTimeEntry> TimeEntries { get; set; } = new List<JobTimeEntry>();
|
|
public virtual ICollection<ReworkRecord> ReworkRecords { get; set; } = new List<ReworkRecord>();
|
|
public virtual Job? OriginalJob { get; set; }
|
|
}
|