Add optional Project Name field to quotes, jobs, and printed documents
- Add ProjectName (nvarchar 100, nullable) to Quote and Job entities; migration AddProjectNameToQuotesAndJobs applied - Add ProjectName to all relevant DTOs: QuoteDto/Create/Update, JobDto/List/Create/Update, InvoiceDto (mapped from Job.ProjectName via AutoMapper so the invoice PDF picks it up without a separate column) - Form field added after Customer PO in Quote Create/Edit and Job Create/Edit - CreateJobFromQuote copies ProjectName from quote to job automatically - Details views (Quote and Job) display Project when set - Printable quote PDF: Project row in the quote details block - Work order: Project row in customer/job info section - Invoice PDF: Project shown in the Job Reference block alongside Job # and PO # Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,7 @@ public class InvoiceDto
|
||||
public string? InternalNotes { get; set; }
|
||||
public string? Terms { get; set; }
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
public string? ExternalReference { get; set; }
|
||||
public int? SalesTaxAccountId { get; set; }
|
||||
public string? SalesTaxAccountName { get; set; }
|
||||
|
||||
@@ -52,6 +52,7 @@ public class JobDto
|
||||
public decimal DiscountValue { get; set; }
|
||||
public string? DiscountReason { get; set; }
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
public string? SpecialInstructions { get; set; }
|
||||
public string? InternalNotes { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
@@ -114,6 +115,7 @@ public class JobListDto
|
||||
public string? CustomerEmail { get; set; }
|
||||
public bool CustomerNotifyByEmail { get; set; } = true;
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
public decimal FinalPrice { get; set; }
|
||||
@@ -167,6 +169,7 @@ public class CreateJobDto
|
||||
[StringLength(100, ErrorMessage = "Customer PO cannot exceed 100 characters")]
|
||||
[Display(Name = "Customer PO")]
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
|
||||
[StringLength(2000, ErrorMessage = "Special instructions cannot exceed 2000 characters")]
|
||||
[Display(Name = "Special Instructions")]
|
||||
@@ -252,6 +255,7 @@ public class UpdateJobDto
|
||||
[StringLength(100, ErrorMessage = "Customer PO cannot exceed 100 characters")]
|
||||
[Display(Name = "Customer PO")]
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
|
||||
[StringLength(2000, ErrorMessage = "Special instructions cannot exceed 2000 characters")]
|
||||
[Display(Name = "Special Instructions")]
|
||||
|
||||
@@ -107,6 +107,7 @@ public class QuoteDto
|
||||
public string? Terms { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
|
||||
// Items
|
||||
@@ -234,6 +235,7 @@ public class CreateQuoteDto
|
||||
[Display(Name = "Customer PO Number")]
|
||||
[StringLength(50)]
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
|
||||
[Display(Name = "Tags")]
|
||||
[StringLength(500)]
|
||||
@@ -376,6 +378,7 @@ public class UpdateQuoteDto
|
||||
[Display(Name = "Customer PO Number")]
|
||||
[StringLength(50)]
|
||||
public string? CustomerPO { get; set; }
|
||||
public string? ProjectName { get; set; }
|
||||
|
||||
[Display(Name = "Tags")]
|
||||
[StringLength(500)]
|
||||
|
||||
@@ -19,6 +19,7 @@ public class InvoiceProfile : Profile
|
||||
|
||||
CreateMap<Invoice, InvoiceDto>()
|
||||
.ForMember(d => d.JobNumber, o => o.MapFrom(s => s.Job != null ? s.Job.JobNumber : string.Empty))
|
||||
.ForMember(d => d.ProjectName, o => o.MapFrom(s => s.Job != null ? s.Job.ProjectName : null))
|
||||
.ForMember(d => d.CustomerName, o => o.MapFrom(s => s.Customer != null
|
||||
? (s.Customer.IsCommercial
|
||||
? s.Customer.CompanyName
|
||||
|
||||
@@ -217,6 +217,8 @@ public class PdfService : IPdfService
|
||||
c.Item().Text($"Job #: {invoice.JobNumber}");
|
||||
if (!string.IsNullOrWhiteSpace(invoice.CustomerPO))
|
||||
c.Item().Text($"PO #: {invoice.CustomerPO}");
|
||||
if (!string.IsNullOrWhiteSpace(invoice.ProjectName))
|
||||
c.Item().Text($"Project: {invoice.ProjectName}");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -609,6 +611,15 @@ public class PdfService : IPdfService
|
||||
row.RelativeItem().Text(quote.CustomerPO).FontSize(9);
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(quote.ProjectName))
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.ConstantItem(80).Text("Project:").FontSize(9);
|
||||
row.RelativeItem().Text(quote.ProjectName).FontSize(9);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user