mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 18:53:12 +00:00
434 lines
20 KiB
C#
434 lines
20 KiB
C#
|
|
using Data.SeaHavenIndustries;
|
||
|
|
using Data.SeaHavenIndustries.Enums;
|
||
|
|
using Microsoft.Extensions.Options;
|
||
|
|
using SeaHaven.DataServices.Interfaces;
|
||
|
|
using SeaHaven.DataServices.Models;
|
||
|
|
using SeaHaven.Services.Configuration;
|
||
|
|
using SeaHaven.Services.DTOs;
|
||
|
|
using SeaHaven.Services.Interfaces;
|
||
|
|
|
||
|
|
namespace SeaHaven.Services.Implementation
|
||
|
|
{
|
||
|
|
public class WorkOrderDispatchService : IWorkOrderDispatchService
|
||
|
|
{
|
||
|
|
private readonly IWorkOrderDataService _workOrderDataService;
|
||
|
|
private readonly IDispatchDataService _dispatchDataService;
|
||
|
|
private readonly IVendorDataService _vendorDataService;
|
||
|
|
private readonly IUserDataService _userDataService;
|
||
|
|
private readonly ITaskListTemplateDataService _taskListTemplateDataService;
|
||
|
|
private readonly ICommentDataService _commentDataService;
|
||
|
|
private readonly IDispatchEmailPort _emailPort;
|
||
|
|
private readonly IVendorTokenPort _vendorTokenPort;
|
||
|
|
private readonly FrontendOptions _frontendOptions;
|
||
|
|
|
||
|
|
public WorkOrderDispatchService(
|
||
|
|
IWorkOrderDataService workOrderDataService,
|
||
|
|
IDispatchDataService dispatchDataService,
|
||
|
|
IVendorDataService vendorDataService,
|
||
|
|
IUserDataService userDataService,
|
||
|
|
ITaskListTemplateDataService taskListTemplateDataService,
|
||
|
|
ICommentDataService commentDataService,
|
||
|
|
IDispatchEmailPort emailPort,
|
||
|
|
IVendorTokenPort vendorTokenPort,
|
||
|
|
IOptions<FrontendOptions> frontendOptions)
|
||
|
|
{
|
||
|
|
_workOrderDataService = workOrderDataService;
|
||
|
|
_dispatchDataService = dispatchDataService;
|
||
|
|
_vendorDataService = vendorDataService;
|
||
|
|
_userDataService = userDataService;
|
||
|
|
_taskListTemplateDataService = taskListTemplateDataService;
|
||
|
|
_commentDataService = commentDataService;
|
||
|
|
_emailPort = emailPort;
|
||
|
|
_vendorTokenPort = vendorTokenPort;
|
||
|
|
_frontendOptions = frontendOptions.Value;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<DispatchToVendorResult> DispatchToVendorAsync(DispatchToVendorInput input, CancellationToken cancellationToken)
|
||
|
|
{
|
||
|
|
var workOrders = await _workOrderDataService.GetByRequestedIdsWithDetailsAsync(
|
||
|
|
input.WorkOrderIds,
|
||
|
|
cancellationToken);
|
||
|
|
|
||
|
|
if (workOrders.Count == 0)
|
||
|
|
throw new InvalidOperationException("No work orders found");
|
||
|
|
|
||
|
|
var primaryWO = workOrders[0];
|
||
|
|
|
||
|
|
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 currentUser = input.UserId != null ? await _userDataService.GetByIdAsync(input.UserId) : null;
|
||
|
|
var senderName = currentUser != null ? (currentUser.FirstName + " " + currentUser.LastName).Trim() : "";
|
||
|
|
|
||
|
|
var templateItemTexts = await LoadTemplateItemTextsAsync(input.TaskListTemplateId);
|
||
|
|
|
||
|
|
var distinctVendorIds = input.VendorIds.Distinct().ToList();
|
||
|
|
var vendors = await _vendorDataService.GetByIdsOrderedAsync(distinctVendorIds, cancellationToken);
|
||
|
|
|
||
|
|
var dispatches = new List<DispatchResultItem>();
|
||
|
|
|
||
|
|
foreach (var vendor in vendors)
|
||
|
|
{
|
||
|
|
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 = vendor.Id,
|
||
|
|
DispatchNumber = dispatchNumber,
|
||
|
|
PONumber = poNumber,
|
||
|
|
NTEAmount = input.NTEAmount,
|
||
|
|
Description = input.Description,
|
||
|
|
Status = "Sent",
|
||
|
|
ScheduledDate = input.ScheduledDate,
|
||
|
|
DispatchedAt = DateTime.UtcNow,
|
||
|
|
ReplyToAddress = replyTo,
|
||
|
|
DispatchWorkOrders = workOrders.Select(wo => new DispatchWorkOrder { WorkOrderId = wo.Id }).ToList()
|
||
|
|
};
|
||
|
|
|
||
|
|
var checklistItems = BuildChecklistItems(templateItemTexts, input.CustomChecklistItems);
|
||
|
|
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(input.Description ?? primaryWO.Description ?? "");
|
||
|
|
var senderNameHtml = System.Net.WebUtility.HtmlEncode(senderName);
|
||
|
|
|
||
|
|
var vendorToken = await _vendorTokenPort.GetOrCreateActiveTokenAsync(vendor.Id);
|
||
|
|
var frontendBase = _frontendOptions.FrontendBaseUrl?.TrimEnd('/') ?? "";
|
||
|
|
var portalUrl = $"{frontendBase}/v/{vendorToken}/dashboard";
|
||
|
|
|
||
|
|
var html = $@"
|
||
|
|
<h2>Work Order Dispatch — {dispatchNumber}</h2>
|
||
|
|
<p><strong>Location:</strong> {locationName} {locationAddr}</p>
|
||
|
|
<p><strong>Vendor PO:</strong> {poNumber} <strong>NTE:</strong> ${input.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 _emailPort.SendDispatchEmailAsync(vendor.Email!, subject, html, replyTo);
|
||
|
|
dispatch.EmailSent = emailSent;
|
||
|
|
|
||
|
|
var savedDispatch = await _dispatchDataService.AddAsync(dispatch);
|
||
|
|
|
||
|
|
var auditLogs = workOrders.Select(wo => new WorkOrderAuditLog
|
||
|
|
{
|
||
|
|
WorkOrderId = wo.Id,
|
||
|
|
UserId = input.UserId,
|
||
|
|
FieldName = "Dispatch",
|
||
|
|
OldValue = "",
|
||
|
|
NewValue = $"{vendor.CompanyName} ({dispatchNumber})",
|
||
|
|
Action = AuditAction.Dispatch.ToString(),
|
||
|
|
CreatedAt = DateTime.UtcNow
|
||
|
|
}).ToList();
|
||
|
|
await _workOrderDataService.AddAuditLogsAsync(auditLogs);
|
||
|
|
|
||
|
|
dispatches.Add(new DispatchResultItem
|
||
|
|
{
|
||
|
|
Id = savedDispatch.Id,
|
||
|
|
DispatchNumber = dispatchNumber,
|
||
|
|
PONumber = poNumber,
|
||
|
|
VendorName = vendor.CompanyName,
|
||
|
|
Status = savedDispatch.Status,
|
||
|
|
WorkOrderCount = workOrders.Count
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return new DispatchToVendorResult { Message = "Dispatched", Dispatches = dispatches };
|
||
|
|
}
|
||
|
|
|
||
|
|
private async Task<List<string?>?> LoadTemplateItemTextsAsync(int? taskListTemplateId)
|
||
|
|
{
|
||
|
|
if (!taskListTemplateId.HasValue)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var template = await _taskListTemplateDataService.GetByIdWithItemsAsync(taskListTemplateId.Value);
|
||
|
|
if (template?.Items == null)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
return template.Items
|
||
|
|
.OrderBy(i => i.SortOrder)
|
||
|
|
.Select(i => i.ItemText)
|
||
|
|
.ToList();
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<DispatchChecklistItem> BuildChecklistItems(List<string?>? templateItemTexts, List<string>? customChecklistItems)
|
||
|
|
{
|
||
|
|
var checklistItems = new List<DispatchChecklistItem>();
|
||
|
|
|
||
|
|
if (templateItemTexts != null)
|
||
|
|
{
|
||
|
|
checklistItems.AddRange(templateItemTexts.Select((text, idx) =>
|
||
|
|
new DispatchChecklistItem { ItemText = text, SortOrder = idx }));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (customChecklistItems != null)
|
||
|
|
{
|
||
|
|
var startIdx = checklistItems.Count;
|
||
|
|
checklistItems.AddRange(customChecklistItems.Select((text, idx) =>
|
||
|
|
new DispatchChecklistItem { ItemText = text, SortOrder = startIdx + idx }));
|
||
|
|
}
|
||
|
|
|
||
|
|
return checklistItems;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<IEnumerable<DispatchReadModel>> GetDispatchesAsync(int workOrderId)
|
||
|
|
{
|
||
|
|
var dispatches = await _dispatchDataService.GetByWorkOrderIdAsync(workOrderId);
|
||
|
|
return dispatches.OrderByDescending(d => d.DispatchedAt).Select(d => new DispatchReadModel
|
||
|
|
{
|
||
|
|
Id = d.Id,
|
||
|
|
DispatchNumber = d.DispatchNumber,
|
||
|
|
PONumber = d.PONumber,
|
||
|
|
NTEAmount = d.NTEAmount,
|
||
|
|
Description = d.Description,
|
||
|
|
Status = d.Status,
|
||
|
|
ScheduledDate = d.ScheduledDate,
|
||
|
|
CompletedDate = d.CompletedDate,
|
||
|
|
DispatchedAt = d.DispatchedAt,
|
||
|
|
EmailSent = d.EmailSent,
|
||
|
|
VendorName = d.Vendor != null ? d.Vendor.CompanyName : "",
|
||
|
|
VendorEmail = d.Vendor != null ? d.Vendor.Email : "",
|
||
|
|
VendorId = d.VendorId
|
||
|
|
}).ToList();
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<DispatchDetailReadModel?> GetDispatchDetailAsync(int id)
|
||
|
|
{
|
||
|
|
return await _dispatchDataService.GetDispatchDetailAsync(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<bool> UpdateDispatchAsync(UpdateDispatchInput input)
|
||
|
|
{
|
||
|
|
var dispatch = await _dispatchDataService.GetByIdAsync(input.Id);
|
||
|
|
if (dispatch == null)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
if (dispatch.Status != input.Status)
|
||
|
|
{
|
||
|
|
await _workOrderDataService.AddAuditLogAsync(new WorkOrderAuditLog
|
||
|
|
{
|
||
|
|
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||
|
|
UserId = input.UserId,
|
||
|
|
FieldName = $"Dispatch {dispatch.DispatchNumber} Status",
|
||
|
|
OldValue = dispatch.Status ?? "",
|
||
|
|
NewValue = input.Status ?? "",
|
||
|
|
Action = "dispatch_status_change",
|
||
|
|
CreatedAt = DateTime.UtcNow
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
dispatch.Status = input.Status;
|
||
|
|
dispatch.NTEAmount = input.NTEAmount;
|
||
|
|
dispatch.ScheduledDate = input.ScheduledDate;
|
||
|
|
dispatch.CompletedDate = input.CompletedDate;
|
||
|
|
dispatch.Description = input.Description;
|
||
|
|
|
||
|
|
if (input.Status == "Completed" && dispatch.CompletedDate == null)
|
||
|
|
dispatch.CompletedDate = DateTime.UtcNow;
|
||
|
|
|
||
|
|
await _dispatchDataService.UpdateAsync(dispatch);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<AddDispatchCommentResult?> AddDispatchCommentAsync(AddDispatchCommentInput input)
|
||
|
|
{
|
||
|
|
var dispatch = await _dispatchDataService.GetByIdWithVendorAndWorkOrderAsync(input.DispatchId);
|
||
|
|
if (dispatch == null)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var user = !string.IsNullOrEmpty(input.UserId) ? await _userDataService.GetByIdAsync(input.UserId) : null;
|
||
|
|
var senderName = user != null ? (user.FirstName + " " + user.LastName).Trim() : "";
|
||
|
|
|
||
|
|
var comment = new Comments
|
||
|
|
{
|
||
|
|
CreatedDate = DateTime.UtcNow,
|
||
|
|
UserId = input.UserId,
|
||
|
|
WorkerOrderId = dispatch.WorkOrderId,
|
||
|
|
DispatchId = dispatch.Id,
|
||
|
|
Commenttext = input.Text,
|
||
|
|
CommentType = "dispatcher",
|
||
|
|
Commenter = !string.IsNullOrWhiteSpace(senderName) ? senderName : null,
|
||
|
|
};
|
||
|
|
|
||
|
|
await _commentDataService.AddAsync(comment);
|
||
|
|
|
||
|
|
if (input.SendEmail && dispatch.Vendor?.Email != null && dispatch.WorkOrder != null)
|
||
|
|
{
|
||
|
|
var subject = $"[WO-{dispatch.WorkOrder.InternalWONumber}-{dispatch.DispatchNumber}] {dispatch.WorkOrder.WorkerOrderTitle}";
|
||
|
|
var html = $"<p>{input.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 _emailPort.SendDispatchEmailAsync(dispatch.Vendor.Email, subject, html, dispatch.ReplyToAddress);
|
||
|
|
}
|
||
|
|
|
||
|
|
return new AddDispatchCommentResult
|
||
|
|
{
|
||
|
|
Id = comment.Id,
|
||
|
|
CreatedDate = comment.CreatedDate,
|
||
|
|
Commenttext = comment.Commenttext,
|
||
|
|
CommentType = comment.CommentType,
|
||
|
|
UserName = user != null ? (user.FirstName + " " + user.LastName).Trim() : ""
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<VerifyDispatchResult?> VerifyDispatchAsync(int dispatchId, string userId)
|
||
|
|
{
|
||
|
|
var dispatch = await _dispatchDataService.GetByIdAsync(dispatchId);
|
||
|
|
if (dispatch == null)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var checklistItems = await _dispatchDataService.GetChecklistItemsAsync(dispatchId);
|
||
|
|
var incompleteItems = checklistItems.Count(c => !c.IsCompleted);
|
||
|
|
|
||
|
|
var signoffs = await _dispatchDataService.GetSignoffsAsync(dispatchId);
|
||
|
|
var signoffList = signoffs.ToList();
|
||
|
|
var signoffCount = signoffList.Count;
|
||
|
|
var existingTypes = signoffList.Select(s => s.SignoffType).ToList();
|
||
|
|
|
||
|
|
var missing = new List<string>();
|
||
|
|
if (incompleteItems > 0)
|
||
|
|
missing.Add($"{incompleteItems} checklist item(s) incomplete");
|
||
|
|
if (signoffCount < 2)
|
||
|
|
{
|
||
|
|
if (!existingTypes.Contains("Customer")) missing.Add("Missing Customer sign-off");
|
||
|
|
if (!existingTypes.Contains("Vendor")) missing.Add("Missing Vendor sign-off");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (missing.Count > 0)
|
||
|
|
throw new DispatchVerificationException(missing);
|
||
|
|
|
||
|
|
var user = userId != null ? await _userDataService.GetByIdAsync(userId) : null;
|
||
|
|
var userName = user != null ? (user.FirstName + " " + user.LastName).Trim() : "";
|
||
|
|
|
||
|
|
dispatch.Status = "Verified";
|
||
|
|
dispatch.VerifiedBy = userName;
|
||
|
|
dispatch.VerifiedAt = DateTime.UtcNow;
|
||
|
|
|
||
|
|
await _workOrderDataService.AddAuditLogAsync(new WorkOrderAuditLog
|
||
|
|
{
|
||
|
|
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||
|
|
UserId = userId,
|
||
|
|
FieldName = $"Dispatch {dispatch.DispatchNumber}",
|
||
|
|
OldValue = dispatch.Status ?? "",
|
||
|
|
NewValue = "Verified",
|
||
|
|
Action = "dispatch_verified",
|
||
|
|
CreatedAt = DateTime.UtcNow
|
||
|
|
});
|
||
|
|
|
||
|
|
await _dispatchDataService.UpdateAsync(dispatch);
|
||
|
|
|
||
|
|
return new VerifyDispatchResult
|
||
|
|
{
|
||
|
|
Message = "Dispatch verified",
|
||
|
|
VerifiedBy = userName,
|
||
|
|
VerifiedAt = dispatch.VerifiedAt
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<AddDispatchSignoffResult?> AddDispatchSignoffAsync(AddDispatchSignoffInput input)
|
||
|
|
{
|
||
|
|
var dispatch = await _dispatchDataService.GetByIdAsync(input.DispatchId);
|
||
|
|
if (dispatch == null)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var existing = await _dispatchDataService.HasSignoffTypeAsync(input.DispatchId, input.SignoffType!);
|
||
|
|
if (existing)
|
||
|
|
throw new InvalidOperationException($"{input.SignoffType} sign-off already exists");
|
||
|
|
|
||
|
|
var signoff = new DispatchSignoff
|
||
|
|
{
|
||
|
|
DispatchId = input.DispatchId,
|
||
|
|
SignoffType = input.SignoffType,
|
||
|
|
Name = input.Name,
|
||
|
|
Signature = input.Signature,
|
||
|
|
SignatureMethod = input.SignatureMethod,
|
||
|
|
SignedAt = DateTime.UtcNow
|
||
|
|
};
|
||
|
|
await _dispatchDataService.AddSignoffAsync(signoff);
|
||
|
|
|
||
|
|
return new AddDispatchSignoffResult
|
||
|
|
{
|
||
|
|
Id = signoff.Id,
|
||
|
|
SignoffType = signoff.SignoffType,
|
||
|
|
Name = signoff.Name,
|
||
|
|
SignedAt = signoff.SignedAt,
|
||
|
|
SignatureMethod = signoff.SignatureMethod
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<ChecklistItemUpdateResult?> UpdateChecklistItemAsync(ChecklistItemUpdateInput input)
|
||
|
|
{
|
||
|
|
var item = await _dispatchDataService.GetChecklistItemByIdAsync(input.Id);
|
||
|
|
if (item == null)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
item.IsCompleted = input.IsCompleted;
|
||
|
|
item.CompletedBy = input.IsCompleted ? input.CompletedBy : null;
|
||
|
|
item.CompletedAt = input.IsCompleted ? DateTime.UtcNow : null;
|
||
|
|
await _dispatchDataService.UpdateChecklistItemAsync(item);
|
||
|
|
|
||
|
|
return new ChecklistItemUpdateResult
|
||
|
|
{
|
||
|
|
Message = "Updated",
|
||
|
|
Id = item.Id,
|
||
|
|
IsCompleted = item.IsCompleted,
|
||
|
|
CompletedBy = item.CompletedBy,
|
||
|
|
CompletedAt = item.CompletedAt
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<AddChecklistItemResult> AddChecklistItemAsync(AddChecklistItemInput input)
|
||
|
|
{
|
||
|
|
var checklistItems = await _dispatchDataService.GetChecklistItemsAsync(input.DispatchId);
|
||
|
|
var maxSort = checklistItems.Any() ? checklistItems.Max(c => c.SortOrder) : -1;
|
||
|
|
|
||
|
|
var item = new DispatchChecklistItem
|
||
|
|
{
|
||
|
|
DispatchId = input.DispatchId,
|
||
|
|
WorkOrderId = input.WorkOrderId,
|
||
|
|
ItemText = input.ItemText,
|
||
|
|
SortOrder = maxSort + 1,
|
||
|
|
IsCompleted = false
|
||
|
|
};
|
||
|
|
await _dispatchDataService.AddChecklistItemAsync(item);
|
||
|
|
|
||
|
|
return new AddChecklistItemResult
|
||
|
|
{
|
||
|
|
Id = item.Id,
|
||
|
|
ItemText = item.ItemText,
|
||
|
|
SortOrder = item.SortOrder,
|
||
|
|
IsCompleted = item.IsCompleted
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|