mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
Add dispatcher verification as final dispatch gate
- VerifiedBy and VerifiedAt fields on Dispatch model - VerifyDispatch endpoint validates all checklist items complete + both signoffs present - Returns missing items list if validation fails - Sets status to Verified, logs to audit trail - GetDispatchById includes verifiedBy and verifiedAt - Migration for new fields
This commit is contained in:
parent
81472bf729
commit
575e8ab6fc
5 changed files with 2919 additions and 0 deletions
|
|
@ -1235,6 +1235,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
x.EmailSent,
|
x.EmailSent,
|
||||||
x.WorkOrderId,
|
x.WorkOrderId,
|
||||||
x.AcknowledgedAt,
|
x.AcknowledgedAt,
|
||||||
|
x.VerifiedBy,
|
||||||
|
x.VerifiedAt,
|
||||||
woNumber = x.WorkOrder != null ? x.WorkOrder.InternalWONumber : "",
|
woNumber = x.WorkOrder != null ? x.WorkOrder.InternalWONumber : "",
|
||||||
woTitle = x.WorkOrder != null ? x.WorkOrder.WorkerOrderTitle : "",
|
woTitle = x.WorkOrder != null ? x.WorkOrder.WorkerOrderTitle : "",
|
||||||
vendorName = x.Vendor != null ? x.Vendor.CompanyName : "",
|
vendorName = x.Vendor != null ? x.Vendor.CompanyName : "",
|
||||||
|
|
@ -1386,6 +1388,67 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
return match.Success ? match.Groups[1].Value : null;
|
return match.Success ? match.Groups[1].Value : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("VerifyDispatch")]
|
||||||
|
public async Task<IActionResult> VerifyDispatch(int dispatchId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dispatch = await _db.Dispatches.FindAsync(dispatchId);
|
||||||
|
if (dispatch == null)
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||||
|
|
||||||
|
var incompleteItems = await _db.DispatchChecklistItems
|
||||||
|
.Where(c => c.DispatchId == dispatchId && !c.IsCompleted)
|
||||||
|
.CountAsync();
|
||||||
|
|
||||||
|
var signoffCount = await _db.DispatchSignoffs
|
||||||
|
.Where(s => s.DispatchId == dispatchId)
|
||||||
|
.CountAsync();
|
||||||
|
|
||||||
|
var missing = new List<string>();
|
||||||
|
if (incompleteItems > 0)
|
||||||
|
missing.Add($"{incompleteItems} checklist item(s) incomplete");
|
||||||
|
if (signoffCount < 2)
|
||||||
|
{
|
||||||
|
var existingTypes = await _db.DispatchSignoffs
|
||||||
|
.Where(s => s.DispatchId == dispatchId)
|
||||||
|
.Select(s => s.SignoffType)
|
||||||
|
.ToListAsync();
|
||||||
|
if (!existingTypes.Contains("Customer")) missing.Add("Missing Customer sign-off");
|
||||||
|
if (!existingTypes.Contains("Vendor")) missing.Add("Missing Vendor sign-off");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missing.Count > 0)
|
||||||
|
return BadRequest(new { status = "Error", message = "Cannot verify", missing });
|
||||||
|
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var user = userId != null ? await _db.Users.FindAsync(userId) : null;
|
||||||
|
var userName = user != null ? (user.FirstName + " " + user.LastName).Trim() : "";
|
||||||
|
|
||||||
|
dispatch.Status = "Verified";
|
||||||
|
dispatch.VerifiedBy = userName;
|
||||||
|
dispatch.VerifiedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
|
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||||
|
{
|
||||||
|
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||||
|
UserId = userId,
|
||||||
|
FieldName = $"Dispatch {dispatch.DispatchNumber}",
|
||||||
|
OldValue = dispatch.Status ?? "",
|
||||||
|
NewValue = "Verified",
|
||||||
|
Action = "dispatch_verified",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return Ok(new { message = "Dispatch verified", verifiedBy = userName, verifiedAt = dispatch.VerifiedAt });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("AddDispatchSignoff")]
|
[HttpPost("AddDispatchSignoff")]
|
||||||
public async Task<IActionResult> AddDispatchSignoff([FromBody] DispatchSignoff_DTO model)
|
public async Task<IActionResult> AddDispatchSignoff([FromBody] DispatchSignoff_DTO model)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
2809
Data.SeaHavenIndustries/Migrations/20260417195316_AddDispatchVerification.Designer.cs
generated
Normal file
2809
Data.SeaHavenIndustries/Migrations/20260417195316_AddDispatchVerification.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.SeaHavenIndustries.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddDispatchVerification : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "VerifiedAt",
|
||||||
|
table: "Dispatches",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "VerifiedBy",
|
||||||
|
table: "Dispatches",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "VerifiedAt",
|
||||||
|
table: "Dispatches");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "VerifiedBy",
|
||||||
|
table: "Dispatches");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -697,6 +697,12 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
b.Property<int>("VendorId")
|
b.Property<int>("VendorId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("VerifiedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("VerifiedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<int?>("WorkOrderId")
|
b.Property<int?>("WorkOrderId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ namespace Data.SeaHavenIndustries
|
||||||
public bool EmailSent { get; set; }
|
public bool EmailSent { get; set; }
|
||||||
public string? ReplyToAddress { get; set; }
|
public string? ReplyToAddress { get; set; }
|
||||||
public string? AcceptToken { get; set; }
|
public string? AcceptToken { get; set; }
|
||||||
|
public string? VerifiedBy { get; set; }
|
||||||
|
public DateTime? VerifiedAt { get; set; }
|
||||||
public ICollection<Comments>? Comments { get; set; }
|
public ICollection<Comments>? Comments { get; set; }
|
||||||
public ICollection<DispatchWorkOrder>? DispatchWorkOrders { get; set; }
|
public ICollection<DispatchWorkOrder>? DispatchWorkOrders { get; set; }
|
||||||
public ICollection<DispatchChecklistItem>? ChecklistItems { get; set; }
|
public ICollection<DispatchChecklistItem>? ChecklistItems { get; set; }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue