shoc-backend/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs
2026-07-23 19:18:06 -03:00

1387 lines
63 KiB
C#

using Api.SeaHavenIndustries.DTOs;
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualBasic;
using SendGrid.Helpers.Mail;
using System.IO;
using System.Net.Http.Headers;
using System.Security.Claims;
using static System.Runtime.InteropServices.JavaScript.JSType;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.DataServices.Interfaces;
namespace Api.SeaHavenIndustries.Controllers
{
[Authorize]
[ApiController]
[Route("api/WorkOrder")]
[Route("api/workorders")]
public class WorkOrderController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly IWorkOrderService _workOrderService;
private readonly IWorkOrderDataService _workOrderDataService;
private readonly ICommentDataService _commentDataService;
private readonly IQuotesDataService _quotesDataService;
private readonly IUserDataService _userDataService;
private readonly IDispatchDataService _dispatchDataService;
private readonly IVendorDataService _vendorDataService;
private readonly ITaskListTemplateDataService _taskListTemplateDataService;
private readonly ApplicationDbContext _db;
private readonly IWebHostEnvironment _webHostEnvironment;
IHttpContextAccessor _httpContext;
private readonly Helper.SendMessage _sendMessage;
private readonly Helper.VendorPortalTokenService _vendorTokens;
private readonly IConfiguration _config;
public WorkOrderController(
UserManager<ApplicationUser> userManager,
IWorkOrderService workOrderService,
IWorkOrderDataService workOrderDataService,
ICommentDataService commentDataService,
IQuotesDataService quotesDataService,
IUserDataService userDataService,
IDispatchDataService dispatchDataService,
IVendorDataService vendorDataService,
ITaskListTemplateDataService taskListTemplateDataService,
ApplicationDbContext db,
IWebHostEnvironment webHostEnvironment,
IHttpContextAccessor httpContext,
Helper.SendMessage sendMessage,
Helper.VendorPortalTokenService vendorTokens,
IConfiguration config)
{
_userManager = userManager;
_workOrderService = workOrderService;
_workOrderDataService = workOrderDataService;
_commentDataService = commentDataService;
_quotesDataService = quotesDataService;
_userDataService = userDataService;
_dispatchDataService = dispatchDataService;
_vendorDataService = vendorDataService;
_taskListTemplateDataService = taskListTemplateDataService;
_db = db;
_webHostEnvironment = webHostEnvironment;
_httpContext = httpContext;
_sendMessage = sendMessage;
_vendorTokens = vendorTokens;
_config = config;
}
[HttpPost]
[Route("AddWorkorder")]
public async Task<IActionResult> Addworkorder([FromForm] Workorder_DTO model)
{
try
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var createDto = new CreateWorkOrderDTO
{
WorkerOrderTitle = model.WorkerOrderTitle ?? "",
Description = model.Description,
Status = WorkOrderStatus.Open,
Priority = model.Priority ?? WorkOrderPriority.Medium,
LocationId = model.LocationId,
AssignTo = model.AssignTo,
DueDate = model.DueDate
};
var workOrderId = await _workOrderService.CreateWorkOrderAsync(createDto, userId!);
if (model.ContactIds != null && model.ContactIds.Any())
{
await _workOrderDataService.AddWorkOrderContactsAsync(workOrderId, model.ContactIds);
}
if (model.CategoryIds != null && model.CategoryIds.Any())
{
await _workOrderDataService.AddWorkOrderCategoriesAsync(workOrderId, model.CategoryIds);
}
if (model.Attachments != null)
{
foreach (var attachment in model.Attachments)
{
var fileUrl = await SaveFileAsync(attachment);
await _workOrderDataService.AddWorkOrderAttachmentAsync(new WorkOrderAttachments
{
WorkorderId = workOrderId,
Attachments = fileUrl
});
}
}
if (model.BeforPhotoAttachment != null)
{
var fileUrl = await SaveFileAsync(model.BeforPhotoAttachment);
var workOrder = await _workOrderDataService.GetByIdAsync(workOrderId);
if (workOrder != null)
{
workOrder.BeforPhotoAttachment = fileUrl;
await _workOrderDataService.UpdateAsync(workOrder);
}
}
if (model.AfterPhotoAttachment != null)
{
var fileUrl = await SaveFileAsync(model.AfterPhotoAttachment);
var workOrder = await _workOrderDataService.GetByIdAsync(workOrderId);
if (workOrder != null)
{
workOrder.AfterPhotoAttachment = fileUrl;
await _workOrderDataService.UpdateAsync(workOrder);
}
}
if (model.SignOffAttachment != null)
{
var fileUrl = await SaveFileAsync(model.SignOffAttachment);
var workOrder = await _workOrderDataService.GetByIdAsync(workOrderId);
if (workOrder != null)
{
workOrder.SignOffAttachment = fileUrl;
await _workOrderDataService.UpdateAsync(workOrder);
}
}
return Ok(new DataResponse { Message = "Work order created successfully", Status = "200", Data = workOrderId });
}
catch (ValidationException vex)
{
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
return BadRequest(new Response { Status = "Validation Error", Message = errors });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost]
[HttpPut("{id:int}")]
[Route("EditWorkorder")]
public async Task<IActionResult> Editworkorder([FromForm] EditWorkorder_DTO model)
{
try
{
if (!model.Id.HasValue)
return BadRequest(new Response { Status = "Error", Message = "Work order ID is required" });
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var updateDto = new UpdateWorkOrderDTO
{
WorkerOrderTitle = model.WorkerOrderTitle,
Description = model.Description,
Status = model.Status,
Priority = model.Priority,
LocationId = model.LocationId,
AssignTo = model.AssignTo,
DueDate = model.DueDate,
CompletedDate = model.CompletedDate
};
var success = await _workOrderService.UpdateWorkOrderAsync(model.Id.Value, updateDto, userId!);
if (!success)
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
await _workOrderDataService.DeleteWorkOrderContactsAsync(model.Id.Value);
await _workOrderDataService.DeleteWorkOrderCategoriesAsync(model.Id.Value);
if (model.ContactIds != null && model.ContactIds.Any())
{
await _workOrderDataService.AddWorkOrderContactsAsync(model.Id.Value, model.ContactIds);
}
if (model.CategoryIds != null && model.CategoryIds.Any())
{
await _workOrderDataService.AddWorkOrderCategoriesAsync(model.Id.Value, model.CategoryIds);
}
if (model.Attachments != null)
{
foreach (var attachment in model.Attachments)
{
var fileUrl = await SaveFileAsync(attachment);
await _workOrderDataService.AddWorkOrderAttachmentAsync(new WorkOrderAttachments
{
WorkorderId = model.Id.Value,
Attachments = fileUrl
});
}
}
if (model.BeforPhotoAttachment != null)
{
var fileUrl = await SaveFileAsync(model.BeforPhotoAttachment);
var workOrder = await _workOrderDataService.GetByIdAsync(model.Id.Value);
if (workOrder != null)
{
workOrder.BeforPhotoAttachment = fileUrl;
await _workOrderDataService.UpdateAsync(workOrder);
}
}
if (model.AfterPhotoAttachment != null)
{
var fileUrl = await SaveFileAsync(model.AfterPhotoAttachment);
var workOrder = await _workOrderDataService.GetByIdAsync(model.Id.Value);
if (workOrder != null)
{
workOrder.AfterPhotoAttachment = fileUrl;
await _workOrderDataService.UpdateAsync(workOrder);
}
}
if (model.SignOffAttachment != null)
{
var fileUrl = await SaveFileAsync(model.SignOffAttachment);
var workOrder = await _workOrderDataService.GetByIdAsync(model.Id.Value);
if (workOrder != null)
{
workOrder.SignOffAttachment = fileUrl;
await _workOrderDataService.UpdateAsync(workOrder);
}
}
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (ValidationException vex)
{
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
return BadRequest(new Response { Status = "Validation Error", Message = errors });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
// -----------------------------------
// GET: api/WorkOrder/GetWorkOrderList
// -----------------------------------
[HttpGet("GetWorkOrderList")]
public async Task<IActionResult> GetWorkOrderList(string? search = "", int page = 1, int pageSize = 12, string? sortBy = "lastUpdated", string? sortDir = "desc", string? status = "", string? assignee = "", int? locationId = null)
{
var (data, totalCount) = await _workOrderDataService.GetWorkOrderListPagedAsync(
page,
pageSize,
search,
status,
assignee,
locationId,
sortBy,
sortDir);
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
};
return Ok(viewModel);
}
[HttpDelete]
[Route("DeleteWorkorder")]
public async Task<IActionResult> Deleteworkorder([FromBody] EditWorkorder_DTO model)
{
int id = model.Id.HasValue ? model.Id.Value : 0;
if (id == 0)
{
return BadRequest("Id is required");
}
try
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var success = await _workOrderService.DeleteWorkOrderAsync(id, userId!);
if (!success)
return BadRequest("Work order not found!");
await _quotesDataService.UnlinkFromWorkOrderAsync(id);
var comments = await _commentDataService.GetByWorkOrderIdAsync(id);
foreach (var comment in comments)
{
await _commentDataService.DeleteAsync(comment.Id);
}
return Ok(new DataResponse { Message = "Removed Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet]
[Route("GetFilteredWorkorder")]
public IActionResult GetFilteredWorkorder(
[FromQuery] string[] assignto,
[FromQuery] int[]? location,
[FromQuery] string[]? priority,
[FromQuery] string[]? status,
[FromQuery] string[]? duedate,
[FromQuery] string search = "",
[FromQuery] string sort = "Sort Order",
[FromQuery] string sortby = "Sort By",
int page = 1,
int pageSize = 10)
{
try
{
IQueryable<WorkOrder> query = _db.workOrders.Where(w => w.istemplate != true).Include(w => w.Locations);
if (!string.IsNullOrEmpty(search))
{
var s = search.ToLower();
query = query.Where(w =>
(w.WorkerOrderTitle ?? "").ToLower().Contains(s) ||
(w.WorkerOrderNumber ?? "").ToLower().Contains(s) ||
(w.Locations != null && (w.Locations.Title ?? "").ToLower().Contains(s)));
}
if (assignto != null && assignto.Length > 0)
{
if (assignto[0] != "" && assignto[0] != null)
query = query.Where(w => assignto.Contains(w.AssignTo));
}
if (location != null && location.Length > 0)
{
if (location[0] != 0)
query = query.Where(w => location.Contains(w.LocationId ?? 0));
}
if (priority != null && priority.Length > 0)
{
if (priority[0] != "" && priority[0] != null)
query = query.Where(w => priority.Contains(w.Priority));
}
if (status != null && status.Length > 0)
{
if (status[0] != "" && status[0] != null)
query = query.Where(w => status.Contains(w.Status));
}
var data = query.ToList();
// Perform client-side filtering for due dates
data = data.Where(w =>
duedate == null || duedate.Length == 0 || duedate[0] == null ||
duedate.Any(dd => w.DueDate.HasValue && CheckDueDate(dd, w.DueDate.Value.Date, DateTime.UtcNow.Date))).ToList();
// Apply sorting
if (!string.IsNullOrEmpty(sort) && !string.IsNullOrEmpty(sortby) && sortby != "")
{
switch (sortby)
{
case "Name":
data = (sort.ToLower() == "ascending") ? data.OrderBy(w => w.WorkerOrderTitle).ToList() : data.OrderByDescending(w => w.WorkerOrderTitle).ToList();
break;
case "Due Date":
data = (sort.ToLower() == "ascending") ? data.OrderBy(w => w.DueDate).ToList() : data.OrderByDescending(w => w.DueDate).ToList();
break;
case "Severity":
data = (sort.ToLower() == "ascending") ? data.OrderBy(w => w.Priority).ToList() : data.OrderByDescending(w => w.Priority).ToList();
break;
default:
// Handle default case or throw an exception
break;
}
}
var totalCount = data.Count();
var pagedData = data
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(s => new { s.Id, s.WorkerOrderTitle, s.DueDate, s.Status, s.Priority, Name = s.Locations != null ? s.Locations.Name : "" })
.OrderByDescending(d => d.Name)
.ToList();
var totalPages1 = (int)Math.Ceiling(totalCount / (double)pageSize);
// Create a view model containing the paged items and pagination metadata
var viewModel = new Pagination_DTO
{
Data = pagedData,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = totalPages1
};
return Ok(viewModel);
}
catch (Exception ex)
{
// Handle exception and return a default result or an error response
return StatusCode(500, "Internal Server Error");
}
}
[HttpGet("{id:int}")]
[HttpGet("GetWorkorderById")]
public async Task<IActionResult> GetWorkorderById(int id)
{
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.InternalWONumber,
s.WorkerOrderNumber,
title = s.WorkerOrderTitle,
s.Description,
s.CreatedDate,
s.DueDate,
s.ScheduledDate,
s.CompletedDate,
s.Status,
assignee = s.AssignToUser != null ? s.AssignToUser.FirstName : "",
location = s.Locations != null ? s.Locations.Name : "",
locationId = s.LocationId,
locationZip = s.Locations != null ? (s.Locations.Zip ?? ExtractZip(s.Locations.Address1)) : "",
priority = s.Priority,
s.Problem,
s.Trade,
s.SubTrade,
s.VendorNTE,
s.Source,
s.PO,
s.TT,
poc = s.WorkOrderContacts.Select(c => new { c.Id, name = c.POC != null ? (c.POC.FirstName + " " + c.POC.LastName).Trim() : "", email = c.POC != null ? c.POC.Email : "", phone = c.POC != null ? c.POC.PhoneNumber : "" }),
category = s.WorkOrderCategories.Select(c => new { c.Id, name = c.Category != null ? c.Category.Name : "" }),
Attachments = s.workOrderAttachments.Select(a => new { a.Id, attachment = a.Attachments }),
s.BeforPhotoIssue,
s.BeforPhotoAttachment,
s.AfterPhotoIssue,
s.AfterPhotoAttachment,
s.SignOffName,
s.SignOffAttachment,
s.SignOffSignature,
Comments = s.Comments.Select(c => new { c.Id, c.CreatedDate, c.Commenttext, c.Commenter, c.RecordType, c.CommentType, c.Documents, userName = c.ApplicationUser != null ? c.ApplicationUser.FirstName : "" }),
quotes = s.Quotes.Select(q => new { code = q.QuotId, q.Id }),
auditLog = _db.WorkOrderAuditLogs
.Where(a => a.WorkOrderId == s.Id)
.OrderByDescending(a => a.CreatedAt)
.Select(a => new {
a.Id,
a.FieldName,
a.OldValue,
a.NewValue,
a.Action,
a.CreatedAt,
userName = a.User != null ? a.User.FirstName + " " + a.User.LastName : ""
}).ToList(),
dispatches = s.Dispatches.Select(d => new {
d.Id,
d.DispatchNumber,
d.PONumber,
d.NTEAmount,
d.Description,
d.Status,
d.ScheduledDate,
d.EstimatedArrivalAt,
d.EtaManualOverride,
d.CompletedDate,
d.DispatchedAt,
d.EmailSent,
vendorName = d.Vendor != null ? d.Vendor.CompanyName : "",
vendorEmail = d.Vendor != null ? d.Vendor.Email : "",
vendorId = d.VendorId
}).ToList()
}).FirstOrDefaultAsync();
if (exist == null)
{
return BadRequest(new Response { Status = "Error", Message = "ID not found!" });
}
else
{
return Ok(exist);
}
}
[HttpGet]
[Route("GetFilteredWorkorder2")]
public IActionResult GetFilteredWorkorder2([FromQuery] string[] assignto, [FromQuery] int[]? location, [FromQuery] string[]? priority, [FromQuery] string[]? status, [FromQuery] string[]? duedate, [FromQuery] string search = "", [FromQuery] string sort = "Sort Order", [FromQuery] string sortby = "Sort By", int page = 1, int pageSize = 10)
{
try
{
var totalCount = _db.workOrders.Where(w => w.istemplate != true).Count();
if (sort == "Sort Order" && sortby == "Sort By" && assignto.Count() == 0 && (duedate == null || duedate.Count() == 0) && location.Count() == 0 && (priority == null || priority.Count() == 0) && (status == null || status.Count() == 0) && search == "")
{
var data1 = _db.workOrders.Where(w => w.istemplate != true).Include(w => w.Locations).ToListAsync();
return Ok(data1);
}
//if (sort == "Sort Order" && sortby == "Sort By" && assignto.Count() == 0 && (duedate == null || duedate.Count() == 0) && location.Count() == 0 && (priority == null || priority.Count() == 0) && (status == null || status.Count() == 0) && search == "")
//{
// var data1 = _db.workOrders.Where(w => w.istemplate != true).Include(w => w.Locations).Select(s => new {s.Id, s.WorkerOrderTitle, s.DueDate, s.Status, s.Priority, s.Locations.Name }).OrderByDescending(d => d.Name).ToList().Skip((page - 1) * pageSize)
// .Take(pageSize);
// // Calculate the number of pages
// var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
// // Create a view model containing the paged items and pagination metadata
// var viewModel1 = new Pagination_DTO
// {
// Data = data1,
// PageNumber = page,
// PageSize = pageSize,
// TotalCount = totalCount,
// TotalPages = totalPages
// };
// return Ok(viewModel1);
//}
var currentDate = DateTime.UtcNow.Date;
var data = _db.workOrders.Include(w => w.Locations)
.Where(w => w.istemplate != true && (((w.WorkerOrderTitle ?? "").ToLower().Contains(search.ToLower()) || (w.WorkerOrderNumber ?? "").ToLower().Contains(search.ToLower()) || (w.Locations != null && (w.Locations.Title ?? "").ToLower().Contains(search.ToLower()))) &&
(assignto.Count() == 0 || assignto.Contains(w.AssignTo)) &&
(location.Count() == 0 || (w.LocationId.HasValue && location.Contains(w.LocationId.Value))) &&
(priority.Count() == 0 || priority.Contains(w.Priority)) &&
((status.Count() == 0 || status.Contains(w.Status))
)))
.ToList();
// Perform client-side filtering for due dates
data = data.Where(w =>
duedate.Count() == 0 ||
duedate.Any(dd => w.DueDate.HasValue && CheckDueDate(dd, w.DueDate.Value.Date, currentDate))).ToList();
// Apply sorting
if (!string.IsNullOrEmpty(sort) && !string.IsNullOrEmpty(sortby) && sortby != "")
{
switch (sortby)
{
case "Name":
data = (sort.ToLower() == "ascending") ? data.OrderBy(w => w.WorkerOrderTitle).ToList() : data.OrderByDescending(w => w.WorkerOrderTitle).ToList();
break;
case "Due Date":
data = (sort.ToLower() == "ascending") ? data.OrderBy(w => w.DueDate).ToList() : data.OrderByDescending(w => w.DueDate).ToList();
break;
case "Severity":
data = (sort.ToLower() == "ascending") ? data.OrderBy(w => w.Priority).ToList() : data.OrderByDescending(w => w.Priority).ToList();
break;
default:
// Handle default case or throw an exception
break;
}
}
var newdata = data.Select(s => new { s.Id, s.WorkerOrderTitle, s.DueDate, s.Status, s.Priority, Name = s.Locations != null ? s.Locations.Name : "" }).OrderByDescending(d => d.Name).ToList().Skip((page - 1) * pageSize)
.Take(pageSize);
var totalPages1 = (int)Math.Ceiling(totalCount / (double)pageSize);
// Create a view model containing the paged items and pagination metadata
var viewModel = new Pagination_DTO
{
Data = newdata,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = totalPages1
};
return Ok(viewModel);
}
catch (Exception ex)
{
var totalCount = _db.workOrders.Where(w => w.istemplate != true).Count();
var data1 = _db.workOrders.Where(w => w.istemplate != true).Include(w => w.Locations).Select(s => new
{
s.Id,
s.WorkerOrderTitle,
s.DueDate,
s.Status,
s.Priority,
Name = s.Locations != null ? s.Locations.Name : ""
}).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
.Take(pageSize);
// Calculate the number of pages
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
// Create a view model containing the paged items and pagination metadata
var viewModel = new Pagination_DTO
{
Data = data1,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = totalPages
};
return Ok(viewModel);
}
}
bool CheckDueDate(string dd, DateTime dueDate, DateTime currentDate)
{
return dd switch
{
"Today" => dueDate == currentDate,
"Tomorrow" => dueDate == currentDate.AddDays(1),
"Next Week" => dueDate >= currentDate.AddDays(1) && dueDate <= currentDate.AddDays(7),
"Next Month" => dueDate >= currentDate.AddDays(1) && dueDate <= currentDate.AddMonths(1),
"Over Due" => dueDate < currentDate,
_ => false
};
}
[HttpPost]
[Route("ChangeStatus")]
public async Task<IActionResult> ChangeStatus(int id, string status)
{
try
{
var workOrder = await _workOrderDataService.GetByIdAsync(id);
if (workOrder == null)
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (workOrder.Status != status)
{
await _workOrderDataService.AddAuditLogAsync(new WorkOrderAuditLog
{
WorkOrderId = id,
UserId = userId,
FieldName = "Status",
OldValue = workOrder.Status ?? "",
NewValue = status,
Action = AuditAction.StatusChange.ToString(),
CreatedAt = DateTime.UtcNow
});
}
workOrder.Status = status;
await _workOrderDataService.UpdateAsync(workOrder);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200", Data = WorkOrderDTO.MapToModel(workOrder) });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost]
[Route("ChangeAssignment")]
public async Task<IActionResult> ChangeAssignment(int id, string? assignTo)
{
try
{
var workOrder = await _workOrderDataService.GetByIdAsync(id);
if (workOrder == null)
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (workOrder.AssignTo != assignTo)
{
var oldUser = workOrder.AssignTo != null ? await _userDataService.GetByIdAsync(workOrder.AssignTo) : null;
var newUser = assignTo != null ? await _userDataService.GetByIdAsync(assignTo) : null;
await _workOrderDataService.AddAuditLogAsync(new WorkOrderAuditLog
{
WorkOrderId = id,
UserId = userId,
FieldName = "AssignedTo",
OldValue = oldUser != null ? (oldUser.FirstName + " " + oldUser.LastName).Trim() : "",
NewValue = newUser != null ? (newUser.FirstName + " " + newUser.LastName).Trim() : "Unassigned",
Action = AuditAction.AssignmentChange.ToString(),
CreatedAt = DateTime.UtcNow
});
}
workOrder.AssignTo = assignTo;
await _workOrderDataService.UpdateAsync(workOrder);
var assignedUser = assignTo != null ? await _userDataService.GetByIdAsync(assignTo) : null;
var assignedName = assignedUser != null ? (assignedUser.FirstName + " " + assignedUser.LastName).Trim() : "";
return Ok(new { message = "Updated Successfully", assignedTo = assignedName });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost]
[Route("AddComment")]
public async Task<IActionResult> AddComment([FromForm] Comments_DTO model)
{
try
{
var userId = _httpContext?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "";
var comment = new Comments
{
UserId = userId,
WorkerOrderId = model.WorkorderId,
Commenttext = model.Text,
CommentType = model.CommentType ?? "vendor"
};
if (model.Document != null)
{
var fileUrl = await SaveFileAsync(model.Document);
comment.Documents = fileUrl;
}
var saved = await _commentDataService.AddAsync(comment);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200", Data = saved });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost]
[Route("AddCommentJson")]
public async Task<IActionResult> AddCommentJson([FromBody] Comments_DTO model)
{
try
{
var userId = _httpContext?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "";
var comment = new Comments
{
UserId = userId,
WorkerOrderId = model.WorkorderId,
Commenttext = model.Text,
CommentType = model.CommentType ?? "internal",
};
var saved = await _commentDataService.AddAsync(comment);
var user = await _userDataService.GetByIdAsync(userId);
return Ok(new
{
saved.Id,
saved.CreatedDate,
saved.Commenttext,
saved.CommentType,
userName = user != null ? (user.FirstName + " " + user.LastName).Trim() : ""
});
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet]
[Route("GetComments")]
public async Task<IActionResult> GetComments()
{
var comments = await _commentDataService.GetAllAsync();
var data = comments.Select(s => new
{
s.Id,
s.Commenttext,
FirstName = s.ApplicationUser != null ? s.ApplicationUser.FirstName : "",
s.Documents
}).ToList();
return Ok(data);
}
[HttpGet]
[Route("GetCommentsByWorkorderId")]
public async Task<IActionResult> GetCommentsByWorkorderId(int woid)
{
var comments = await _commentDataService.GetByWorkOrderIdAsync(woid);
var data = comments.Select(s => new
{
s.Id,
s.Commenttext,
FirstName = s.ApplicationUser != null ? s.ApplicationUser.FirstName : "",
s.Documents
}).ToList();
return Ok(data);
}
[HttpGet]
[Route("GetworkordersDD")]
public async Task<IActionResult> GetworkordersDD()
{
var data = await _workOrderDataService.GetAllAsync();
return Ok(data.Where(w => w.istemplate != true));
}
[HttpGet]
[Route("Getworkorders")]
public async Task<IActionResult> Getworkorders()
{
var workOrders = await _workOrderDataService.GetAllWithDetailsAsync();
var data = workOrders.Select(s => new WorkorderFilterVM
{
Id = s.Id,
Priority = s.Priority,
Status = s.Status,
WorkerOrderNumber = s.WorkerOrderNumber,
WorkerOrderTitle = s.WorkerOrderTitle,
DueDate = s.DueDate,
AssignToUserName = s.AssignToUser != null ? s.AssignToUser.FirstName : "",
LocationName = s.Locations != null ? s.Locations.Name : "",
LocationId = s.LocationId,
PO = s.PO,
TT = s.TT,
}).ToList();
return Ok(data);
}
[HttpPost]
[Route("DispatchToVendor")]
public async Task<IActionResult> DispatchToVendor([FromBody] Dispatch_DTO model)
{
try
{
var allWorkOrders = await _workOrderDataService.GetAllWithDetailsAsync();
var workOrders = allWorkOrders.Where(w => model.WorkOrderIds.Contains(w.Id)).ToList();
if (workOrders.Count == 0)
return BadRequest(new Response { Status = "Error", Message = "No work orders found" });
var primaryWO = workOrders.First();
var lastPO = await _dispatchDataService.GenerateNextPONumberAsync();
int nextPO = int.TryParse(lastPO, out var po) ? po : 1;
var lastDSP = await _dispatchDataService.GenerateNextDispatchNumberAsync();
int nextDSP = lastDSP.StartsWith("DSP-") && int.TryParse(lastDSP.Substring(4), out var dspNum) ? dspNum : 1;
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var currentUser = userId != null ? await _userDataService.GetByIdAsync(userId) : null;
var senderName = currentUser != null ? (currentUser.FirstName + " " + currentUser.LastName).Trim() : "";
var dispatches = new List<object>();
foreach (var vendorId in model.VendorIds)
{
var vendor = await _vendorDataService.GetByIdAsync(vendorId);
if (vendor == null)
return BadRequest(new Response { Status = "Error", Message = $"Vendor {vendorId} was not found" });
if (!vendor.IsActive || (vendor.IsDeleted ?? false))
return BadRequest(new Response { Status = "Error", Message = $"Vendor {vendorId} is inactive and cannot receive a new assignment" });
var poNumber = $"VPO-{nextPO:D5}";
nextPO++;
var dispatchNumber = $"DSP-{nextDSP:D5}";
nextDSP++;
var replyTo = $"wo-{primaryWO.InternalWONumber}-{dispatchNumber}@int.seahaven.com";
var dispatch = new Dispatch
{
WorkOrderId = primaryWO.Id,
VendorId = vendorId,
DispatchNumber = dispatchNumber,
PONumber = poNumber,
NTEAmount = model.NTEAmount,
Description = model.Description,
Status = "Sent",
ScheduledDate = model.ScheduledDate,
EstimatedArrivalAt = model.EstimatedArrivalAt,
EtaManualOverride = model.EtaManualOverride,
DispatchedAt = DateTime.UtcNow,
ReplyToAddress = replyTo,
DispatchWorkOrders = workOrders.Select(wo => new DispatchWorkOrder { WorkOrderId = wo.Id }).ToList()
};
var checklistItems = new List<DispatchChecklistItem>();
if (model.TaskListTemplateId.HasValue)
{
var template = await _taskListTemplateDataService.GetByIdWithItemsAsync(model.TaskListTemplateId.Value);
if (template?.Items != null)
{
checklistItems.AddRange(template.Items.OrderBy(i => i.SortOrder).Select((i, idx) =>
new DispatchChecklistItem { ItemText = i.ItemText, SortOrder = idx }));
}
}
if (model.CustomChecklistItems != null)
{
var startIdx = checklistItems.Count;
checklistItems.AddRange(model.CustomChecklistItems.Select((text, idx) =>
new DispatchChecklistItem { ItemText = text, SortOrder = startIdx + idx }));
}
dispatch.ChecklistItems = checklistItems;
var woListHtml = string.Join("", workOrders.Select(wo =>
$"<tr><td style='padding:6px 8px;border:1px solid #ddd;'>{System.Net.WebUtility.HtmlEncode(wo.InternalWONumber ?? "")}</td>" +
$"<td style='padding:6px 8px;border:1px solid #ddd;'>{System.Net.WebUtility.HtmlEncode(wo.WorkerOrderTitle ?? "")}</td>" +
$"<td style='padding:6px 8px;border:1px solid #ddd;'>{System.Net.WebUtility.HtmlEncode(wo.Priority ?? "")}</td>" +
$"<td style='padding:6px 8px;border:1px solid #ddd;'>{wo.DueDate?.ToString("yyyy-MM-dd") ?? "N/A"}</td></tr>"));
var locationName = System.Net.WebUtility.HtmlEncode(primaryWO.Locations?.Name ?? "");
var locationAddr = System.Net.WebUtility.HtmlEncode(primaryWO.Locations?.Address1 ?? "");
var subject = $"[{dispatchNumber}] {(workOrders.Count > 1 ? $"{workOrders.Count} Work Orders" : primaryWO.WorkerOrderTitle)} at {primaryWO.Locations?.Name ?? ""}";
var descriptionHtml = System.Net.WebUtility.HtmlEncode(model.Description ?? primaryWO.Description ?? "");
var senderNameHtml = System.Net.WebUtility.HtmlEncode(senderName);
var vendorToken = await _vendorTokens.GetOrCreateActiveTokenAsync(vendorId);
var frontendBase = _config.GetValue<string>("FrontendBaseUrl")?.TrimEnd('/') ?? "";
var portalUrl = $"{frontendBase}/v/{vendorToken.Token}/dashboard";
var html = $@"
<h2>Work Order Dispatch — {dispatchNumber}</h2>
<p><strong>Location:</strong> {locationName} {locationAddr}</p>
<p><strong>Vendor PO:</strong> {poNumber} &nbsp; <strong>NTE:</strong> ${model.NTEAmount?.ToString("F2") ?? "N/A"}</p>
<h3>Work Orders</h3>
<table style='border-collapse:collapse;width:100%;font-family:Arial,sans-serif;'>
<tr style='background:#f5f5f5;'>
<th style='padding:6px 8px;border:1px solid #ddd;text-align:left;'>WO #</th>
<th style='padding:6px 8px;border:1px solid #ddd;text-align:left;'>Title</th>
<th style='padding:6px 8px;border:1px solid #ddd;text-align:left;'>Priority</th>
<th style='padding:6px 8px;border:1px solid #ddd;text-align:left;'>Due Date</th>
</tr>
{woListHtml}
</table>
<h3>Description</h3>
<p>{descriptionHtml}</p>
<br/>
<div style='text-align:center;margin:20px 0;'>
<a href='{portalUrl}' style='display:inline-block;padding:12px 32px;background:#28a745;color:white;text-decoration:none;border-radius:6px;font-weight:bold;font-size:16px;'>View Work Order</a>
</div>
<br/><p style='color:#666;font-size:13px;'>{senderNameHtml}<br/>Sea Haven Industries</p>
<hr/><p style='color:#999;font-size:12px;'>Reply to this email to communicate about this dispatch.</p>";
var emailSent = await _sendMessage.SendDispatchEmail(vendor.Email, subject, html, replyTo);
dispatch.EmailSent = emailSent;
var savedDispatch = await _dispatchDataService.AddAsync(dispatch);
foreach (var wo in workOrders)
{
await _workOrderDataService.AddAuditLogAsync(new WorkOrderAuditLog
{
WorkOrderId = wo.Id,
UserId = userId,
FieldName = "Dispatch",
OldValue = "",
NewValue = $"{vendor.CompanyName} ({dispatchNumber})",
Action = AuditAction.Dispatch.ToString(),
CreatedAt = DateTime.UtcNow
});
if (model.EstimatedArrivalAt.HasValue)
{
await _workOrderDataService.AddAuditLogAsync(new WorkOrderAuditLog
{
WorkOrderId = wo.Id,
UserId = userId,
FieldName = "Vendor ETA",
OldValue = "",
NewValue = model.EstimatedArrivalAt.Value.ToUniversalTime().ToString("O"),
Action = model.EtaManualOverride ? "vendor_eta_manual_override" : "vendor_eta_set",
CreatedAt = DateTime.UtcNow
});
}
}
dispatches.Add(new { savedDispatch.Id, dispatchNumber, poNumber, vendorName = vendor.CompanyName, savedDispatch.Status, workOrderCount = workOrders.Count });
}
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 dispatches = await _dispatchDataService.GetByWorkOrderIdAsync(workOrderId);
var data = dispatches.OrderByDescending(d => d.DispatchedAt).Select(d => new
{
d.Id,
d.DispatchNumber,
d.PONumber,
d.NTEAmount,
d.Description,
d.Status,
d.ScheduledDate,
d.EstimatedArrivalAt,
d.EtaManualOverride,
d.CompletedDate,
d.DispatchedAt,
d.EmailSent,
vendorName = d.Vendor != null ? d.Vendor.CompanyName : "",
vendorEmail = d.Vendor != null ? d.Vendor.Email : "",
vendorId = d.VendorId
}).ToList();
return Ok(data);
}
[HttpGet("GetDispatch/{id}")]
public async Task<IActionResult> GetDispatchById(int id)
{
var d = await _db.Dispatches
.Include(x => x.Vendor)
.Include(x => x.WorkOrder)
.Where(x => x.Id == id)
.Select(x => new
{
x.Id,
x.DispatchNumber,
x.PONumber,
x.NTEAmount,
x.Description,
x.Status,
x.ScheduledDate,
x.EstimatedArrivalAt,
x.EtaManualOverride,
x.CompletedDate,
x.DispatchedAt,
x.EmailSent,
x.WorkOrderId,
x.AcknowledgedAt,
x.VerifiedBy,
x.VerifiedAt,
woNumber = x.WorkOrder != null ? x.WorkOrder.InternalWONumber : "",
woTitle = x.WorkOrder != null ? x.WorkOrder.WorkerOrderTitle : "",
vendorName = x.Vendor != null ? x.Vendor.CompanyName : "",
vendorEmail = x.Vendor != null ? x.Vendor.Email : "",
vendorId = x.VendorId,
workOrders = _db.DispatchWorkOrders
.Where(dwo => dwo.DispatchId == x.Id)
.Select(dwo => new {
dwo.WorkOrderId,
woNumber = dwo.WorkOrder != null ? dwo.WorkOrder.InternalWONumber : "",
woTitle = dwo.WorkOrder != null ? dwo.WorkOrder.WorkerOrderTitle : ""
}).ToList(),
checklistItems = _db.DispatchChecklistItems
.Where(ci => ci.DispatchId == x.Id)
.OrderBy(ci => ci.SortOrder)
.Select(ci => new {
ci.Id,
ci.ItemText,
ci.IsCompleted,
ci.CompletedBy,
ci.CompletedAt,
ci.SortOrder,
ci.WorkOrderId
}).ToList(),
signoffs = _db.DispatchSignoffs
.Where(s => s.DispatchId == x.Id)
.Select(s => new {
s.Id,
s.SignoffType,
s.Name,
s.Signature,
s.SignatureMethod,
s.SignedAt
}).ToList(),
comments = _db.Comments
.Where(c => c.DispatchId == x.Id)
.OrderBy(c => c.CreatedDate)
.Select(c => new
{
c.Id,
c.CreatedDate,
c.Commenttext,
c.Commenter,
c.CommentType,
userName = c.ApplicationUser != null ? c.ApplicationUser.FirstName + " " + c.ApplicationUser.LastName : ""
}).ToList()
}).FirstOrDefaultAsync();
if (d == null)
return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
return Ok(d);
}
[HttpPost("UpdateDispatch")]
public async Task<IActionResult> UpdateDispatch([FromBody] UpdateDispatch_DTO model)
{
try
{
var dispatch = await _db.Dispatches.FindAsync(model.Id);
if (dispatch == null)
return BadRequest(new Response { Status = "Error", Message = "Dispatch not found" });
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (dispatch.Status != model.Status)
{
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
{
WorkOrderId = dispatch.WorkOrderId ?? 0,
UserId = userId,
FieldName = $"Dispatch {dispatch.DispatchNumber} Status",
OldValue = dispatch.Status ?? "",
NewValue = model.Status ?? "",
Action = "dispatch_status_change",
CreatedAt = DateTime.UtcNow
});
}
dispatch.Status = model.Status;
dispatch.NTEAmount = model.NTEAmount;
dispatch.ScheduledDate = model.ScheduledDate;
if (dispatch.EstimatedArrivalAt != model.EstimatedArrivalAt)
{
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
{
WorkOrderId = dispatch.WorkOrderId ?? 0,
UserId = userId,
FieldName = "Vendor ETA",
OldValue = dispatch.EstimatedArrivalAt?.ToUniversalTime().ToString("O") ?? "",
NewValue = model.EstimatedArrivalAt?.ToUniversalTime().ToString("O") ?? "",
Action = model.EtaManualOverride ? "vendor_eta_manual_override" : "vendor_eta_change",
CreatedAt = DateTime.UtcNow
});
}
dispatch.EstimatedArrivalAt = model.EstimatedArrivalAt;
dispatch.EtaManualOverride = model.EtaManualOverride;
dispatch.CompletedDate = model.CompletedDate;
dispatch.Description = model.Description;
if (model.Status == "Completed" && dispatch.CompletedDate == null)
dispatch.CompletedDate = DateTime.UtcNow;
await _db.SaveChangesAsync();
return Ok(new { message = "Dispatch updated" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost("AddDispatchComment")]
public async Task<IActionResult> AddDispatchComment([FromBody] DispatchComment_DTO model)
{
try
{
var dispatch = await _db.Dispatches.Include(d => d.Vendor).Include(d => d.WorkOrder)
.FirstOrDefaultAsync(d => d.Id == model.DispatchId);
if (dispatch == null)
return BadRequest(new Response { Status = "Error", Message = "Dispatch not found" });
var userId = _httpContext?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "";
var user = !string.IsNullOrEmpty(userId) ? await _db.Users.FindAsync(userId) : null;
var senderName = user != null ? (user.FirstName + " " + user.LastName).Trim() : "";
var comment = new Comments
{
CreatedDate = DateTime.UtcNow,
UserId = userId,
WorkerOrderId = dispatch.WorkOrderId,
DispatchId = dispatch.Id,
Commenttext = model.Text,
CommentType = "dispatcher",
Commenter = !string.IsNullOrWhiteSpace(senderName) ? senderName : null,
};
_db.Comments.Add(comment);
await _db.SaveChangesAsync();
if (model.SendEmail && dispatch.Vendor?.Email != null && dispatch.WorkOrder != null)
{
var subject = $"[WO-{dispatch.WorkOrder.InternalWONumber}-{dispatch.DispatchNumber}] {dispatch.WorkOrder.WorkerOrderTitle}";
var html = $"<p>{model.Text}</p><br/><p style='color:#666;font-size:13px;'>{senderName}<br/>Sea Haven Industries</p><hr/><p style='color:#999;font-size:12px;'>Reply to this email to respond.</p>";
await _sendMessage.SendDispatchEmail(dispatch.Vendor.Email, subject, html, dispatch.ReplyToAddress);
}
return Ok(new
{
comment.Id,
comment.CreatedDate,
comment.Commenttext,
comment.CommentType,
userName = user != null ? (user.FirstName + " " + user.LastName).Trim() : ""
});
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
private static string? ExtractZip(string? address)
{
if (string.IsNullOrWhiteSpace(address)) return null;
var match = System.Text.RegularExpressions.Regex.Match(address, @"\b(\d{5})(?:-\d{4})?\b");
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")]
public async Task<IActionResult> AddDispatchSignoff([FromBody] DispatchSignoff_DTO model)
{
try
{
var dispatch = await _db.Dispatches.FindAsync(model.DispatchId);
if (dispatch == null)
return BadRequest(new Response { Status = "Error", Message = "Dispatch not found" });
var existing = await _db.DispatchSignoffs
.AnyAsync(s => s.DispatchId == model.DispatchId && s.SignoffType == model.SignoffType);
if (existing)
return BadRequest(new Response { Status = "Error", Message = $"{model.SignoffType} sign-off already exists" });
var signoff = new DispatchSignoff
{
DispatchId = model.DispatchId,
SignoffType = model.SignoffType,
Name = model.Name,
Signature = model.Signature,
SignatureMethod = model.SignatureMethod,
SignedAt = DateTime.UtcNow
};
_db.DispatchSignoffs.Add(signoff);
await _db.SaveChangesAsync();
return Ok(new { signoff.Id, signoff.SignoffType, signoff.Name, signoff.SignedAt, signoff.SignatureMethod });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost("UpdateChecklistItem")]
public async Task<IActionResult> UpdateChecklistItem([FromBody] ChecklistItemUpdate_DTO model)
{
var item = await _dispatchDataService.GetChecklistItemByIdAsync(model.Id);
if (item == null)
return NotFound(new Response { Status = "Error", Message = "Checklist item not found" });
item.IsCompleted = model.IsCompleted;
item.CompletedBy = model.IsCompleted ? model.CompletedBy : null;
item.CompletedAt = model.IsCompleted ? DateTime.UtcNow : null;
await _dispatchDataService.UpdateChecklistItemAsync(item);
return Ok(new { message = "Updated", item.Id, item.IsCompleted, item.CompletedBy, item.CompletedAt });
}
[HttpPost("AddChecklistItem")]
public async Task<IActionResult> AddChecklistItem([FromBody] AddChecklistItem_DTO model)
{
var checklistItems = await _dispatchDataService.GetChecklistItemsAsync(model.DispatchId);
var maxSort = checklistItems.Any() ? checklistItems.Max(c => c.SortOrder) : -1;
var item = new DispatchChecklistItem
{
DispatchId = model.DispatchId,
WorkOrderId = model.WorkOrderId,
ItemText = model.ItemText,
SortOrder = maxSort + 1,
IsCompleted = false
};
await _dispatchDataService.AddChecklistItemAsync(item);
return Ok(new { item.Id, item.ItemText, item.SortOrder, item.IsCompleted });
}
private async Task<string> GenerateInternalWONumber()
{
return await _workOrderDataService.GenerateNextInternalWONumberAsync();
}
private async Task<string> SaveFileAsync(IFormFile file)
{
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
var uniqueFileName = $"{Guid.NewGuid()}_{fileName}";
var uploadPath = Path.Combine("Assets", "Documents");
var fullPath = Path.Combine(_webHostEnvironment.WebRootPath, uploadPath, uniqueFileName);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
using (var stream = System.IO.File.Create(fullPath))
{
await file.CopyToAsync(stream);
}
var request = _httpContext.HttpContext!.Request;
var domain = $"{request.Scheme}://{request.Host}";
return $"{domain}/{uploadPath.Replace("\\", "/")}/{uniqueFileName}";
}
public class WorkorderFilterVM
{
public int? Id { get; set; }
public int? LocationId { get; set; }
public string? WorkerOrderNumber { get; set; }
public string? BillingAddress { get; set; } = "Amazon.com Services LLC 410 Terry Ave,North Seattle, Washington, 98109";
public string? WorkerOrderTitle { get; set; }
public string? LocationName { get; set; }
public string? AssignToUserName { get; set; }
public string? Priority { get; set; }
public string? Status { get; set; }
public DateTime? DueDate { get; set; }
public string FormattedDueDate
{
get { return DueDate.HasValue ? DueDate.Value.ToString("MM/dd/yyyy") : ""; }
}
public string? Dispatcher { get; set; }
public string? Scheduler { get; set; }
public string? PO { get; set; }
public string? TT { get; set; }
}
}
}