2024-09-28 14:58:36 -04:00
|
|
|
|
using Api.SeaHavenIndustries.DTOs;
|
2026-07-24 17:35:34 -03:00
|
|
|
|
using Api.SeaHavenIndustries.Helper;
|
2024-09-28 14:58:36 -04:00
|
|
|
|
using Data.SeaHavenIndustries;
|
2026-05-14 11:00:12 -05:00
|
|
|
|
using FluentValidation;
|
2024-09-28 14:58:36 -04:00
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2026-07-24 17:35:34 -03:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using SeaHaven.DataServices.Models;
|
2026-05-14 11:00:12 -05:00
|
|
|
|
using SeaHaven.Services.DTOs;
|
2026-08-11 10:45:37 -03:00
|
|
|
|
using SeaHaven.Services.Exceptions;
|
2026-07-24 17:35:34 -03:00
|
|
|
|
using SeaHaven.Services.Interfaces;
|
|
|
|
|
|
using System.Security.Claims;
|
2024-09-28 14:58:36 -04:00
|
|
|
|
|
|
|
|
|
|
namespace Api.SeaHavenIndustries.Controllers
|
|
|
|
|
|
{
|
2026-07-07 13:11:22 -03:00
|
|
|
|
[Authorize]
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[ApiController]
|
2025-02-18 13:13:33 -06:00
|
|
|
|
[Route("api/WorkOrder")]
|
2026-04-16 17:32:28 -04:00
|
|
|
|
[Route("api/workorders")]
|
2024-09-28 14:58:36 -04:00
|
|
|
|
public class WorkOrderController : Controller
|
|
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
private readonly IWorkOrderService _workOrderService;
|
2026-08-11 10:45:37 -03:00
|
|
|
|
private readonly IWorkOrderAccountResolver _accountResolver;
|
2026-07-24 17:35:34 -03:00
|
|
|
|
private readonly ILogger<WorkOrderController> _logger;
|
2026-05-06 10:49:33 -05:00
|
|
|
|
|
|
|
|
|
|
public WorkOrderController(
|
2026-05-14 11:00:12 -05:00
|
|
|
|
IWorkOrderService workOrderService,
|
2026-08-11 10:45:37 -03:00
|
|
|
|
IWorkOrderAccountResolver accountResolver,
|
2026-07-24 17:35:34 -03:00
|
|
|
|
ILogger<WorkOrderController> logger)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
_workOrderService = workOrderService;
|
2026-08-11 10:45:37 -03:00
|
|
|
|
_accountResolver = accountResolver;
|
2026-07-24 17:35:34 -03:00
|
|
|
|
_logger = logger;
|
2026-07-13 13:13:00 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[HttpPost]
|
|
|
|
|
|
[Route("AddWorkorder")]
|
2026-05-14 11:00:12 -05:00
|
|
|
|
public async Task<IActionResult> Addworkorder([FromForm] Workorder_DTO model)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var input = new CreateWorkOrderWithDetailsInput
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
WorkerOrderTitle = model.WorkerOrderTitle ?? "",
|
|
|
|
|
|
Description = model.Description,
|
2026-07-24 17:35:34 -03:00
|
|
|
|
Priority = model.Priority?.ToString(),
|
2026-05-14 11:00:12 -05:00
|
|
|
|
LocationId = model.LocationId,
|
|
|
|
|
|
AssignTo = model.AssignTo,
|
2026-07-24 17:35:34 -03:00
|
|
|
|
DueDate = model.DueDate,
|
|
|
|
|
|
ContactIds = model.ContactIds,
|
|
|
|
|
|
CategoryIds = model.CategoryIds,
|
|
|
|
|
|
Attachments = model.Attachments,
|
|
|
|
|
|
BeforPhotoAttachment = model.BeforPhotoAttachment,
|
|
|
|
|
|
AfterPhotoAttachment = model.AfterPhotoAttachment,
|
2026-08-11 10:45:37 -03:00
|
|
|
|
SignOffAttachment = model.SignOffAttachment,
|
|
|
|
|
|
Customer = model.Customer
|
2026-05-14 11:00:12 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-08-11 10:45:37 -03:00
|
|
|
|
var workOrderId = await _workOrderService.CreateWorkOrderWithDetailsAsync(input, User, userId!);
|
2026-05-14 11:00:12 -05:00
|
|
|
|
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 });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
2026-08-11 10:45:37 -03:00
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return UnprocessableEntity(new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpPost]
|
2026-04-16 17:44:00 -04:00
|
|
|
|
[HttpPut("{id:int}")]
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[Route("EditWorkorder")]
|
2026-05-14 11:00:12 -05:00
|
|
|
|
public async Task<IActionResult> Editworkorder([FromForm] EditWorkorder_DTO model)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
if (!model.Id.HasValue)
|
|
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = "Work order ID is required" });
|
2026-04-16 18:09:57 -04:00
|
|
|
|
|
|
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var input = new UpdateWorkOrderWithDetailsInput
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
Id = model.Id.Value,
|
2026-05-14 11:00:12 -05:00
|
|
|
|
WorkerOrderTitle = model.WorkerOrderTitle,
|
|
|
|
|
|
Description = model.Description,
|
2026-07-24 17:35:34 -03:00
|
|
|
|
Status = model.Status?.ToString(),
|
|
|
|
|
|
Priority = model.Priority?.ToString(),
|
2026-05-14 11:00:12 -05:00
|
|
|
|
LocationId = model.LocationId,
|
|
|
|
|
|
AssignTo = model.AssignTo,
|
|
|
|
|
|
DueDate = model.DueDate,
|
2026-07-24 17:35:34 -03:00
|
|
|
|
CompletedDate = model.CompletedDate,
|
|
|
|
|
|
ContactIds = model.ContactIds,
|
|
|
|
|
|
CategoryIds = model.CategoryIds,
|
|
|
|
|
|
Attachments = model.Attachments,
|
|
|
|
|
|
BeforPhotoAttachment = model.BeforPhotoAttachment,
|
|
|
|
|
|
AfterPhotoAttachment = model.AfterPhotoAttachment,
|
|
|
|
|
|
SignOffAttachment = model.SignOffAttachment
|
2026-05-14 11:00:12 -05:00
|
|
|
|
};
|
2024-09-28 14:58:36 -04:00
|
|
|
|
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var success = await _workOrderService.UpdateWorkOrderWithDetailsAsync(input, userId!);
|
2026-05-14 11:00:12 -05:00
|
|
|
|
if (!success)
|
|
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
|
|
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
|
|
|
|
|
}
|
2026-05-14 11:00:12 -05:00
|
|
|
|
catch (ValidationException vex)
|
|
|
|
|
|
{
|
|
|
|
|
|
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
|
|
|
|
|
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
|
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-24 17:35:34 -03:00
|
|
|
|
|
2026-03-30 06:49:40 -05:00
|
|
|
|
[HttpGet("GetWorkOrderList")]
|
2026-05-06 10:49:33 -05:00
|
|
|
|
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)
|
2026-03-30 06:49:40 -05:00
|
|
|
|
{
|
2026-08-11 10:45:37 -03:00
|
|
|
|
try
|
2026-03-30 06:49:40 -05:00
|
|
|
|
{
|
2026-08-11 10:45:37 -03:00
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(User);
|
|
|
|
|
|
var (data, totalCount) = await _workOrderService.GetWorkOrderListPagedAsync(
|
|
|
|
|
|
page, pageSize, search, status, assignee, locationId, sortBy, sortDir, accountId);
|
2026-03-30 06:49:40 -05:00
|
|
|
|
|
2026-08-11 10:45:37 -03:00
|
|
|
|
var viewModel = new Pagination_DTO
|
|
|
|
|
|
{
|
|
|
|
|
|
Data = data,
|
|
|
|
|
|
PageNumber = page,
|
|
|
|
|
|
PageSize = pageSize,
|
|
|
|
|
|
TotalCount = totalCount,
|
|
|
|
|
|
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(viewModel);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2026-03-30 06:49:40 -05:00
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
|
2026-07-08 10:17:31 -03:00
|
|
|
|
[Authorize(Roles = "Admin")]
|
2025-03-09 19:39:28 -05:00
|
|
|
|
[HttpDelete]
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[Route("DeleteWorkorder")]
|
2026-05-14 11:00:12 -05:00
|
|
|
|
public async Task<IActionResult> Deleteworkorder([FromBody] EditWorkorder_DTO model)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2025-03-09 19:39:28 -05:00
|
|
|
|
int id = model.Id.HasValue ? model.Id.Value : 0;
|
2026-03-30 06:49:40 -05:00
|
|
|
|
if (id == 0)
|
2025-03-09 19:39:28 -05:00
|
|
|
|
return BadRequest("Id is required");
|
2026-07-24 17:35:34 -03:00
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var success = await _workOrderService.DeleteWorkOrderCascadeAsync(id, userId!);
|
2024-09-28 14:58:36 -04:00
|
|
|
|
|
2026-05-14 11:00:12 -05:00
|
|
|
|
if (!success)
|
|
|
|
|
|
return BadRequest("Work order not found!");
|
|
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
return Ok(new DataResponse { Message = "Removed Successfully", Status = "200" });
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet]
|
|
|
|
|
|
[Route("GetFilteredWorkorder")]
|
2026-07-24 17:35:34 -03:00
|
|
|
|
public async Task<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)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-08-11 11:57:37 -03:00
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(User);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var result = await _workOrderService.GetFilteredWorkOrdersAsync(
|
2026-08-11 11:57:37 -03:00
|
|
|
|
assignto, location, priority, status, duedate, search, sort, sortby, page, pageSize, accountId);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return Ok(result);
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
2026-08-11 11:57:37 -03:00
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2026-07-24 17:35:34 -03:00
|
|
|
|
catch (Exception)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(500, "Internal Server Error");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-16 20:15:30 -03:00
|
|
|
|
// SH-374: two actions, one per route, so each binds id from the source its route provides.
|
|
|
|
|
|
// A single action carrying both routes inferred id as [FromRoute] and the query route got 0.
|
2026-04-16 17:32:28 -04:00
|
|
|
|
[HttpGet("GetWorkorderById")]
|
2026-09-16 20:15:30 -03:00
|
|
|
|
public Task<IActionResult> GetWorkorderById([FromQuery] int id) => GetWorkorderDetail(id);
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet("{id:int}")]
|
|
|
|
|
|
public Task<IActionResult> GetWorkorderByRouteId([FromRoute] int id) => GetWorkorderDetail(id);
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<IActionResult> GetWorkorderDetail(int id)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2026-08-11 11:57:37 -03:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(User);
|
|
|
|
|
|
var detail = await _workOrderService.GetWorkOrderDetailAsync(id, accountId);
|
|
|
|
|
|
if (detail == null)
|
|
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = "ID not found!" });
|
|
|
|
|
|
return Ok(detail);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
2026-07-24 17:35:34 -03:00
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[HttpGet]
|
|
|
|
|
|
[Route("GetFilteredWorkorder2")]
|
2026-07-24 17:35:34 -03:00
|
|
|
|
public async Task<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)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-08-11 11:57:37 -03:00
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(User);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var result = await _workOrderService.GetFilteredWorkOrders2Async(
|
2026-08-11 11:57:37 -03:00
|
|
|
|
assignto, location, priority, status, duedate, search, sort, sortby, page, pageSize, accountId);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return Ok(result);
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
2026-08-11 11:57:37 -03:00
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2026-07-24 17:35:34 -03:00
|
|
|
|
catch (Exception)
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var totalCount = await _workOrderService.GetTotalWorkOrderCountAsync();
|
|
|
|
|
|
return Ok(new Pagination_DTO
|
2024-09-28 14:58:36 -04:00
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
Data = Array.Empty<FilteredWorkOrderReadModel>(),
|
2024-09-28 14:58:36 -04:00
|
|
|
|
PageNumber = page,
|
|
|
|
|
|
PageSize = pageSize,
|
|
|
|
|
|
TotalCount = totalCount,
|
2026-07-24 17:35:34 -03:00
|
|
|
|
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
|
|
|
|
|
|
});
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpPost]
|
|
|
|
|
|
[Route("ChangeStatus")]
|
|
|
|
|
|
public async Task<IActionResult> ChangeStatus(int id, string status)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var result = await _workOrderService.ChangeStatusAsync(id, status, userId!);
|
|
|
|
|
|
if (result == null)
|
|
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
|
2026-05-14 11:00:12 -05:00
|
|
|
|
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200", Data = result });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 19:04:59 -04:00
|
|
|
|
[HttpPost]
|
|
|
|
|
|
[Route("ChangeAssignment")]
|
|
|
|
|
|
public async Task<IActionResult> ChangeAssignment(int id, string? assignTo)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-14 11:00:12 -05:00
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var result = await _workOrderService.ChangeAssignmentAsync(id, assignTo, userId!);
|
|
|
|
|
|
if (result == null)
|
|
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = "Work order not found" });
|
2026-05-14 11:00:12 -05:00
|
|
|
|
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return Ok(result);
|
2026-04-16 19:04:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2026-04-16 19:04:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[HttpPost]
|
|
|
|
|
|
[Route("AddComment")]
|
|
|
|
|
|
public async Task<IActionResult> AddComment([FromForm] Comments_DTO model)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
|
|
|
|
var input = new AddCommentInput
|
2026-05-14 11:00:12 -05:00
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
WorkorderId = model.WorkorderId,
|
|
|
|
|
|
Text = model.Text,
|
|
|
|
|
|
CommentType = model.CommentType,
|
|
|
|
|
|
Document = model.Document
|
2026-05-14 11:00:12 -05:00
|
|
|
|
};
|
2024-09-28 14:58:36 -04:00
|
|
|
|
|
2026-08-11 14:52:02 -03:00
|
|
|
|
var saved = await _workOrderService.AddCommentAsync(input, User, userId!);
|
2026-05-14 11:00:12 -05:00
|
|
|
|
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200", Data = saved });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
2026-08-11 14:52:02 -03:00
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "NotFound")
|
|
|
|
|
|
{
|
|
|
|
|
|
return NotFound(new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 18:22:34 -04:00
|
|
|
|
[HttpPost]
|
|
|
|
|
|
[Route("AddCommentJson")]
|
|
|
|
|
|
public async Task<IActionResult> AddCommentJson([FromBody] Comments_DTO model)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
|
|
|
|
var input = new AddCommentInput
|
2026-04-16 18:22:34 -04:00
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
WorkorderId = model.WorkorderId,
|
|
|
|
|
|
Text = model.Text,
|
|
|
|
|
|
CommentType = model.CommentType
|
2026-04-16 18:22:34 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-08-11 14:52:02 -03:00
|
|
|
|
var result = await _workOrderService.AddCommentJsonAsync(input, User, userId!);
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return Ok(result);
|
2026-04-16 18:22:34 -04:00
|
|
|
|
}
|
2026-08-11 14:52:02 -03:00
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "NotFound")
|
|
|
|
|
|
{
|
|
|
|
|
|
return NotFound(new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2026-04-16 18:22:34 -04:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-24 17:35:34 -03:00
|
|
|
|
return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
|
2026-04-16 18:22:34 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-28 14:58:36 -04:00
|
|
|
|
[HttpGet]
|
|
|
|
|
|
[Route("GetComments")]
|
|
|
|
|
|
public async Task<IActionResult> GetComments()
|
|
|
|
|
|
{
|
2026-08-11 15:18:29 -03:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var data = await _workOrderService.GetCommentsAsync(User);
|
|
|
|
|
|
return Ok(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet]
|
|
|
|
|
|
[Route("GetCommentsByWorkorderId")]
|
|
|
|
|
|
public async Task<IActionResult> GetCommentsByWorkorderId(int woid)
|
|
|
|
|
|
{
|
2026-08-11 14:52:02 -03:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var data = await _workOrderService.GetCommentsByWorkorderIdAsync(woid, User);
|
|
|
|
|
|
if (data == null)
|
|
|
|
|
|
return NotFound(new Response { Status = "Error", Message = "Work order not found." });
|
|
|
|
|
|
return Ok(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
2026-07-24 17:35:34 -03:00
|
|
|
|
|
2025-02-18 13:13:33 -06:00
|
|
|
|
[HttpGet]
|
|
|
|
|
|
[Route("GetworkordersDD")]
|
|
|
|
|
|
public async Task<IActionResult> GetworkordersDD()
|
|
|
|
|
|
{
|
2026-08-11 11:57:37 -03:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(User);
|
|
|
|
|
|
var data = await _workOrderService.GetWorkordersDDAsync(accountId);
|
|
|
|
|
|
return Ok(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
2025-02-18 13:13:33 -06:00
|
|
|
|
}
|
2026-07-24 17:35:34 -03:00
|
|
|
|
|
2025-02-18 13:13:33 -06:00
|
|
|
|
[HttpGet]
|
|
|
|
|
|
[Route("Getworkorders")]
|
|
|
|
|
|
public async Task<IActionResult> Getworkorders()
|
|
|
|
|
|
{
|
2026-08-11 11:57:37 -03:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(User);
|
|
|
|
|
|
var data = await _workOrderService.GetWorkordersAsync(accountId);
|
|
|
|
|
|
return Ok(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = ex.Message });
|
|
|
|
|
|
}
|
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
2026-04-17 11:37:35 -04:00
|
|
|
|
}
|
2024-09-28 14:58:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|