mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-04 09:02:10 +00:00
Add vendor dispatch workflow backend
- Create Vendor model with company info, trade specialties, active flag - Create Dispatch model (doubles as Vendor PO) with PO number, NTE, status, reply-to address - VendorController: CRUD, paginated list, dropdown endpoint with trade filtering - DispatchToVendor endpoint: multi-vendor dispatch, auto-generated PO numbers (VPO-00001), HTML email with full WO details via SendGrid, reply-to wo-{number}@int.seahaven.com - GetDispatches endpoint for listing dispatches by WO - Include dispatches in GetWorkorderById response - SendMessage.SendDispatchEmail with reply-to support - Audit log entry for each dispatch
This commit is contained in:
parent
05b36ef7dd
commit
d24c4643f5
12 changed files with 3144 additions and 8 deletions
176
Api.SeaHavenIndustries/Controllers/VendorController.cs
Normal file
176
Api.SeaHavenIndustries/Controllers/VendorController.cs
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
using Api.SeaHavenIndustries.DTOs;
|
||||||
|
using Data.SeaHavenIndustries;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Api.SeaHavenIndustries.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[Route("api/vendors")]
|
||||||
|
public class VendorController : Controller
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _db;
|
||||||
|
|
||||||
|
public VendorController(ApplicationDbContext db)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetVendorList")]
|
||||||
|
public IActionResult GetVendorList(string? search = "", int page = 1, int pageSize = 10)
|
||||||
|
{
|
||||||
|
var query = _db.Vendors.Where(v => v.IsActive);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
|
{
|
||||||
|
var s = search.ToLower();
|
||||||
|
query = query.Where(v =>
|
||||||
|
(v.CompanyName ?? "").ToLower().Contains(s) ||
|
||||||
|
(v.ContactName ?? "").ToLower().Contains(s) ||
|
||||||
|
(v.Email ?? "").ToLower().Contains(s) ||
|
||||||
|
(v.TradeSpecialties ?? "").ToLower().Contains(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount = query.Count();
|
||||||
|
|
||||||
|
var data = query.OrderByDescending(v => v.Id)
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.Select(v => new
|
||||||
|
{
|
||||||
|
v.Id,
|
||||||
|
v.CompanyName,
|
||||||
|
v.ContactName,
|
||||||
|
v.Email,
|
||||||
|
v.Phone,
|
||||||
|
v.TradeSpecialties,
|
||||||
|
v.IsActive
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Ok(new Pagination_DTO
|
||||||
|
{
|
||||||
|
Data = data,
|
||||||
|
PageNumber = page,
|
||||||
|
PageSize = pageSize,
|
||||||
|
TotalCount = totalCount,
|
||||||
|
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> GetVendorById(int id)
|
||||||
|
{
|
||||||
|
var vendor = await _db.Vendors.Where(v => v.Id == id).Select(v => new
|
||||||
|
{
|
||||||
|
v.Id,
|
||||||
|
v.CompanyName,
|
||||||
|
v.ContactName,
|
||||||
|
v.Email,
|
||||||
|
v.Phone,
|
||||||
|
v.Address,
|
||||||
|
v.City,
|
||||||
|
v.State,
|
||||||
|
v.Zip,
|
||||||
|
v.TradeSpecialties,
|
||||||
|
v.IsActive
|
||||||
|
}).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (vendor == null)
|
||||||
|
return NotFound(new Response { Status = "Error", Message = "Vendor not found" });
|
||||||
|
|
||||||
|
return Ok(vendor);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create([FromBody] Vendor_DTO model)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var vendor = new Vendor
|
||||||
|
{
|
||||||
|
CompanyName = model.CompanyName,
|
||||||
|
ContactName = model.ContactName,
|
||||||
|
Email = model.Email,
|
||||||
|
Phone = model.Phone,
|
||||||
|
Address = model.Address,
|
||||||
|
City = model.City,
|
||||||
|
State = model.State,
|
||||||
|
Zip = model.Zip,
|
||||||
|
TradeSpecialties = model.TradeSpecialties,
|
||||||
|
IsActive = model.IsActive
|
||||||
|
};
|
||||||
|
_db.Vendors.Add(vendor);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return Ok(new DataResponse { Message = "Vendor Created", Status = "200" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> Update(int id, [FromBody] EditVendor_DTO model)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var exist = await _db.Vendors.FindAsync(id);
|
||||||
|
if (exist == null)
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = "Vendor not found" });
|
||||||
|
|
||||||
|
exist.CompanyName = model.CompanyName;
|
||||||
|
exist.ContactName = model.ContactName;
|
||||||
|
exist.Email = model.Email;
|
||||||
|
exist.Phone = model.Phone;
|
||||||
|
exist.Address = model.Address;
|
||||||
|
exist.City = model.City;
|
||||||
|
exist.State = model.State;
|
||||||
|
exist.Zip = model.Zip;
|
||||||
|
exist.TradeSpecialties = model.TradeSpecialties;
|
||||||
|
exist.IsActive = model.IsActive;
|
||||||
|
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return Ok(new DataResponse { Message = "Vendor Updated", Status = "200" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Delete(int id)
|
||||||
|
{
|
||||||
|
var exist = await _db.Vendors.FindAsync(id);
|
||||||
|
if (exist == null)
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = "Vendor not found" });
|
||||||
|
|
||||||
|
_db.Vendors.Remove(exist);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return Ok(new DataResponse { Message = "Vendor Deleted", Status = "200" });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("Dropdown")]
|
||||||
|
public async Task<IActionResult> GetDropdown([FromQuery] string? trade = null)
|
||||||
|
{
|
||||||
|
var query = _db.Vendors.Where(v => v.IsActive);
|
||||||
|
|
||||||
|
var vendors = await query.OrderBy(v => v.CompanyName)
|
||||||
|
.Select(v => new { v.Id, v.CompanyName, v.TradeSpecialties })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(trade))
|
||||||
|
{
|
||||||
|
var tradeMatched = vendors.Where(v => (v.TradeSpecialties ?? "").Contains(trade)).ToList();
|
||||||
|
var rest = vendors.Where(v => !(v.TradeSpecialties ?? "").Contains(trade)).ToList();
|
||||||
|
vendors = tradeMatched.Concat(rest).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(vendors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,12 +26,14 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
private readonly ApplicationDbContext _db;
|
private readonly ApplicationDbContext _db;
|
||||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||||
IHttpContextAccessor _httpContext;
|
IHttpContextAccessor _httpContext;
|
||||||
public WorkOrderController(UserManager<ApplicationUser> userManager, ApplicationDbContext db, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContext)
|
private readonly Helper.SendMessage _sendMessage;
|
||||||
|
public WorkOrderController(UserManager<ApplicationUser> userManager, ApplicationDbContext db, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContext, Helper.SendMessage sendMessage)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_db = db;
|
_db = db;
|
||||||
_webHostEnvironment = webHostEnvironment;
|
_webHostEnvironment = webHostEnvironment;
|
||||||
_httpContext = httpContext;
|
_httpContext = httpContext;
|
||||||
|
_sendMessage = sendMessage;
|
||||||
}
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("AddWorkorder")]
|
[Route("AddWorkorder")]
|
||||||
|
|
@ -593,7 +595,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
[HttpGet("GetWorkorderById")]
|
[HttpGet("GetWorkorderById")]
|
||||||
public async Task<IActionResult> GetWorkorderById(int id)
|
public async Task<IActionResult> GetWorkorderById(int id)
|
||||||
{
|
{
|
||||||
var exist = await _db.workOrders.Include(w => w.Locations).Include(w => w.Comments).Include(w => w.Quotes).Where(w => w.Id == id).Select(s => new
|
var exist = await _db.workOrders.Include(w => w.Locations).Include(w => w.Comments).Include(w => w.Quotes).Include(w => w.Dispatches).ThenInclude(d => d.Vendor).Where(w => w.Id == id).Select(s => new
|
||||||
{
|
{
|
||||||
s.Id,
|
s.Id,
|
||||||
s.InternalWONumber,
|
s.InternalWONumber,
|
||||||
|
|
@ -639,7 +641,18 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
a.Action,
|
a.Action,
|
||||||
a.CreatedAt,
|
a.CreatedAt,
|
||||||
userName = a.User != null ? a.User.FirstName + " " + a.User.LastName : ""
|
userName = a.User != null ? a.User.FirstName + " " + a.User.LastName : ""
|
||||||
}).ToList()
|
}).ToList(),
|
||||||
|
dispatches = s.Dispatches.Select(d => new {
|
||||||
|
d.Id,
|
||||||
|
d.PONumber,
|
||||||
|
d.NTEAmount,
|
||||||
|
d.Description,
|
||||||
|
d.Status,
|
||||||
|
d.DispatchedAt,
|
||||||
|
d.EmailSent,
|
||||||
|
vendorName = d.Vendor != null ? d.Vendor.CompanyName : "",
|
||||||
|
vendorEmail = d.Vendor != null ? d.Vendor.Email : ""
|
||||||
|
}).ToList()
|
||||||
}).FirstOrDefaultAsync();
|
}).FirstOrDefaultAsync();
|
||||||
if (exist == null)
|
if (exist == null)
|
||||||
{
|
{
|
||||||
|
|
@ -980,6 +993,125 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
return Ok(data);
|
return Ok(data);
|
||||||
}
|
}
|
||||||
|
[HttpPost]
|
||||||
|
[Route("DispatchToVendor")]
|
||||||
|
public async Task<IActionResult> DispatchToVendor([FromBody] Dispatch_DTO model)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var workOrder = await _db.workOrders.Include(w => w.Locations)
|
||||||
|
.FirstOrDefaultAsync(w => w.Id == model.WorkOrderId);
|
||||||
|
if (workOrder == null)
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
|
||||||
|
|
||||||
|
var lastPO = await _db.Dispatches
|
||||||
|
.Where(d => d.PONumber != null)
|
||||||
|
.OrderByDescending(d => d.PONumber)
|
||||||
|
.Select(d => d.PONumber)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
int nextPO = 1;
|
||||||
|
if (lastPO != null && lastPO.StartsWith("VPO-") && int.TryParse(lastPO.Substring(4), out var lastNum))
|
||||||
|
nextPO = lastNum + 1;
|
||||||
|
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var dispatches = new List<object>();
|
||||||
|
|
||||||
|
foreach (var vendorId in model.VendorIds)
|
||||||
|
{
|
||||||
|
var vendor = await _db.Vendors.FindAsync(vendorId);
|
||||||
|
if (vendor == null) continue;
|
||||||
|
|
||||||
|
var poNumber = $"VPO-{nextPO:D5}";
|
||||||
|
nextPO++;
|
||||||
|
|
||||||
|
var replyTo = $"wo-{workOrder.InternalWONumber}@int.seahaven.com";
|
||||||
|
|
||||||
|
var dispatch = new Dispatch
|
||||||
|
{
|
||||||
|
WorkOrderId = model.WorkOrderId,
|
||||||
|
VendorId = vendorId,
|
||||||
|
PONumber = poNumber,
|
||||||
|
NTEAmount = model.NTEAmount,
|
||||||
|
Description = model.Description,
|
||||||
|
Status = "Sent",
|
||||||
|
DispatchedAt = DateTime.UtcNow,
|
||||||
|
ReplyToAddress = replyTo
|
||||||
|
};
|
||||||
|
|
||||||
|
var subject = $"[WO-{workOrder.InternalWONumber}] {workOrder.WorkerOrderTitle}";
|
||||||
|
var locationName = workOrder.Locations?.Name ?? "";
|
||||||
|
var locationAddr = workOrder.Locations?.Address ?? "";
|
||||||
|
|
||||||
|
var html = $@"
|
||||||
|
<h2>Work Order Dispatch</h2>
|
||||||
|
<table style='border-collapse:collapse;width:100%;font-family:Arial,sans-serif;'>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>WO Number</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.InternalWONumber}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Title</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.WorkerOrderTitle}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Location</td><td style='padding:8px;border:1px solid #ddd;'>{locationName} {locationAddr}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Priority</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.Priority}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Due Date</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.DueDate?.ToString("yyyy-MM-dd") ?? "N/A"}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Trade</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.Trade}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Sub-Trade</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.SubTrade}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Problem</td><td style='padding:8px;border:1px solid #ddd;'>{workOrder.Problem}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>NTE</td><td style='padding:8px;border:1px solid #ddd;'>${model.NTEAmount?.ToString("F2") ?? "N/A"}</td></tr>
|
||||||
|
<tr><td style='padding:8px;font-weight:bold;border:1px solid #ddd;'>Vendor PO</td><td style='padding:8px;border:1px solid #ddd;'>{poNumber}</td></tr>
|
||||||
|
</table>
|
||||||
|
<h3>Description</h3>
|
||||||
|
<p>{model.Description ?? workOrder.Description}</p>
|
||||||
|
<hr/>
|
||||||
|
<p style='color:#999;font-size:12px;'>Reply to this email to communicate about this work order.</p>";
|
||||||
|
|
||||||
|
var emailSent = await _sendMessage.SendDispatchEmail(vendor.Email, subject, html, replyTo);
|
||||||
|
dispatch.EmailSent = emailSent;
|
||||||
|
|
||||||
|
_db.Dispatches.Add(dispatch);
|
||||||
|
|
||||||
|
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||||
|
{
|
||||||
|
WorkOrderId = model.WorkOrderId,
|
||||||
|
UserId = userId,
|
||||||
|
FieldName = "Dispatch",
|
||||||
|
OldValue = "",
|
||||||
|
NewValue = $"{vendor.CompanyName} ({poNumber})",
|
||||||
|
Action = "dispatch",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatches.Add(new { dispatch.Id, poNumber, vendorName = vendor.CompanyName, dispatch.Status });
|
||||||
|
}
|
||||||
|
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return Ok(new { message = "Dispatched", dispatches });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetDispatches/{workOrderId}")]
|
||||||
|
public async Task<IActionResult> GetDispatches(int workOrderId)
|
||||||
|
{
|
||||||
|
var data = await _db.Dispatches
|
||||||
|
.Where(d => d.WorkOrderId == workOrderId)
|
||||||
|
.Include(d => d.Vendor)
|
||||||
|
.OrderByDescending(d => d.DispatchedAt)
|
||||||
|
.Select(d => new
|
||||||
|
{
|
||||||
|
d.Id,
|
||||||
|
d.PONumber,
|
||||||
|
d.NTEAmount,
|
||||||
|
d.Description,
|
||||||
|
d.Status,
|
||||||
|
d.DispatchedAt,
|
||||||
|
d.EmailSent,
|
||||||
|
vendorName = d.Vendor != null ? d.Vendor.CompanyName : "",
|
||||||
|
vendorEmail = d.Vendor != null ? d.Vendor.Email : ""
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
return Ok(data);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> GenerateInternalWONumber()
|
private async Task<string> GenerateInternalWONumber()
|
||||||
{
|
{
|
||||||
var lastWO = await _db.workOrders
|
var lastWO = await _db.workOrders
|
||||||
|
|
|
||||||
10
Api.SeaHavenIndustries/DTOs/Dispatch_DTO.cs
Normal file
10
Api.SeaHavenIndustries/DTOs/Dispatch_DTO.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Api.SeaHavenIndustries.DTOs
|
||||||
|
{
|
||||||
|
public class Dispatch_DTO
|
||||||
|
{
|
||||||
|
public int WorkOrderId { get; set; }
|
||||||
|
public List<int> VendorIds { get; set; } = new();
|
||||||
|
public decimal? NTEAmount { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Api.SeaHavenIndustries/DTOs/Vendor_DTO.cs
Normal file
21
Api.SeaHavenIndustries/DTOs/Vendor_DTO.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Api.SeaHavenIndustries.DTOs
|
||||||
|
{
|
||||||
|
public class Vendor_DTO
|
||||||
|
{
|
||||||
|
public string? CompanyName { get; set; }
|
||||||
|
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? Zip { get; set; }
|
||||||
|
public string? TradeSpecialties { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EditVendor_DTO : Vendor_DTO
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -98,8 +98,27 @@ namespace Api.SeaHavenIndustries.Helper
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task<bool> SendDispatchEmail(string emailTo, string subject, string htmlBody, string? replyTo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var apiKey = _configuration.GetValue<string>("SendGrid:ApiKey");
|
||||||
|
var client = new SendGridClient(apiKey);
|
||||||
|
var from = new EmailAddress("tech@seahavenind.com", "Sea Haven Industries");
|
||||||
|
var to = new EmailAddress(emailTo, "");
|
||||||
|
|
||||||
|
var msg = MailHelper.CreateSingleEmail(from, to, subject, "", htmlBody);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(replyTo))
|
||||||
|
msg.SetReplyTo(new EmailAddress(replyTo));
|
||||||
|
|
||||||
|
var response = await client.SendEmailAsync(msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ namespace Data.SeaHavenIndustries
|
||||||
public DbSet<WorkOrderAttachments> workOrderAttachments { get; set; }
|
public DbSet<WorkOrderAttachments> workOrderAttachments { get; set; }
|
||||||
public DbSet<WorkOrderAuditLog> WorkOrderAuditLogs { get; set; }
|
public DbSet<WorkOrderAuditLog> WorkOrderAuditLogs { get; set; }
|
||||||
public DbSet<DropdownOption> DropdownOptions { get; set; }
|
public DbSet<DropdownOption> DropdownOptions { get; set; }
|
||||||
|
public DbSet<Vendor> Vendors { get; set; }
|
||||||
|
public DbSet<Dispatch> Dispatches { get; set; }
|
||||||
|
|
||||||
// New DbSets for added entities
|
// New DbSets for added entities
|
||||||
public DbSet<Accounts> Accounts { get; set; }
|
public DbSet<Accounts> Accounts { get; set; }
|
||||||
|
|
|
||||||
2478
Data.SeaHavenIndustries/Migrations/20260417153405_AddVendorAndDispatch.Designer.cs
generated
Normal file
2478
Data.SeaHavenIndustries/Migrations/20260417153405_AddVendorAndDispatch.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,104 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.SeaHavenIndustries.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddVendorAndDispatch : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Vendors",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
CompanyName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
ContactName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Email = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Phone = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Address = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
City = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
State = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Zip = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
TradeSpecialties = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||||
|
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Vendors", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Dispatches",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
WorkOrderId = table.Column<int>(type: "int", nullable: false),
|
||||||
|
VendorId = table.Column<int>(type: "int", nullable: false),
|
||||||
|
PONumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
NTEAmount = table.Column<decimal>(type: "decimal(18,2)", nullable: true),
|
||||||
|
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Status = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DispatchedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
EmailSent = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
ReplyToAddress = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||||
|
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Dispatches", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Dispatches_Vendors_VendorId",
|
||||||
|
column: x => x.VendorId,
|
||||||
|
principalTable: "Vendors",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Dispatches_workOrders_WorkOrderId",
|
||||||
|
column: x => x.WorkOrderId,
|
||||||
|
principalTable: "workOrders",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Dispatches_VendorId",
|
||||||
|
table: "Dispatches",
|
||||||
|
column: "VendorId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Dispatches_WorkOrderId",
|
||||||
|
table: "Dispatches",
|
||||||
|
column: "WorkOrderId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Dispatches");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Vendors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -627,6 +627,71 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
b.ToTable("Departments");
|
b.ToTable("Departments");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Data.SeaHavenIndustries.Dispatch", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("CreatedDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("DeleterUserId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletionTime")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DispatchedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailSent")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool?>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int?>("LastModifierUserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal?>("NTEAmount")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("PONumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ReplyToAddress")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("VendorId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("WorkOrderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("createdby")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("VendorId");
|
||||||
|
|
||||||
|
b.HasIndex("WorkOrderId");
|
||||||
|
|
||||||
|
b.ToTable("Dispatches");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Data.SeaHavenIndustries.DropdownOption", b =>
|
modelBuilder.Entity("Data.SeaHavenIndustries.DropdownOption", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
|
@ -1409,6 +1474,70 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
b.ToTable("Templates");
|
b.ToTable("Templates");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Data.SeaHavenIndustries.Vendor", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Address")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("City")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("CompanyName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("CreatedDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("DeleterUserId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletionTime")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool?>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int?>("LastModifierUserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("State")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("TradeSpecialties")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Zip")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("createdby")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Vendors");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Data.SeaHavenIndustries.WorkOrder", b =>
|
modelBuilder.Entity("Data.SeaHavenIndustries.WorkOrder", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
|
@ -1932,6 +2061,25 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
b.Navigation("Location");
|
b.Navigation("Location");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Data.SeaHavenIndustries.Dispatch", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Data.SeaHavenIndustries.Vendor", "Vendor")
|
||||||
|
.WithMany("Dispatches")
|
||||||
|
.HasForeignKey("VendorId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Data.SeaHavenIndustries.WorkOrder", "WorkOrder")
|
||||||
|
.WithMany("Dispatches")
|
||||||
|
.HasForeignKey("WorkOrderId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Vendor");
|
||||||
|
|
||||||
|
b.Navigation("WorkOrder");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Data.SeaHavenIndustries.Employee", b =>
|
modelBuilder.Entity("Data.SeaHavenIndustries.Employee", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Data.SeaHavenIndustries.Department", "Department")
|
b.HasOne("Data.SeaHavenIndustries.Department", "Department")
|
||||||
|
|
@ -2302,10 +2450,17 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
b.Navigation("Employees");
|
b.Navigation("Employees");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Data.SeaHavenIndustries.Vendor", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Dispatches");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Data.SeaHavenIndustries.WorkOrder", b =>
|
modelBuilder.Entity("Data.SeaHavenIndustries.WorkOrder", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Comments");
|
b.Navigation("Comments");
|
||||||
|
|
||||||
|
b.Navigation("Dispatches");
|
||||||
|
|
||||||
b.Navigation("Quotes");
|
b.Navigation("Quotes");
|
||||||
|
|
||||||
b.Navigation("WorkOrderCategories");
|
b.Navigation("WorkOrderCategories");
|
||||||
|
|
|
||||||
23
Data.SeaHavenIndustries/Models/Dispatch.cs
Normal file
23
Data.SeaHavenIndustries/Models/Dispatch.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Data.SeaHavenIndustries
|
||||||
|
{
|
||||||
|
public class Dispatch : FullAuditEntity
|
||||||
|
{
|
||||||
|
public int WorkOrderId { get; set; }
|
||||||
|
[ForeignKey(nameof(WorkOrderId))]
|
||||||
|
public virtual WorkOrder? WorkOrder { get; set; }
|
||||||
|
|
||||||
|
public int VendorId { get; set; }
|
||||||
|
[ForeignKey(nameof(VendorId))]
|
||||||
|
public virtual Vendor? Vendor { get; set; }
|
||||||
|
|
||||||
|
public string? PONumber { get; set; }
|
||||||
|
public decimal? NTEAmount { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Status { get; set; }
|
||||||
|
public DateTime? DispatchedAt { get; set; }
|
||||||
|
public bool EmailSent { get; set; }
|
||||||
|
public string? ReplyToAddress { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Data.SeaHavenIndustries/Models/Vendor.cs
Normal file
17
Data.SeaHavenIndustries/Models/Vendor.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Data.SeaHavenIndustries
|
||||||
|
{
|
||||||
|
public class Vendor : FullAuditEntity
|
||||||
|
{
|
||||||
|
public string? CompanyName { get; set; }
|
||||||
|
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? Zip { get; set; }
|
||||||
|
public string? TradeSpecialties { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public ICollection<Dispatch>? Dispatches { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -56,7 +56,6 @@ namespace Data.SeaHavenIndustries
|
||||||
public ICollection<WorkOrderContacts>? WorkOrderContacts { get; set; }
|
public ICollection<WorkOrderContacts>? WorkOrderContacts { get; set; }
|
||||||
public ICollection<WorkOrderAttachments>? workOrderAttachments { get; set; }
|
public ICollection<WorkOrderAttachments>? workOrderAttachments { get; set; }
|
||||||
public ICollection<WorkOrderCategories>? WorkOrderCategories { get; set; }
|
public ICollection<WorkOrderCategories>? WorkOrderCategories { get; set; }
|
||||||
|
public ICollection<Dispatch>? Dispatches { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue