mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 16:19:07 +00:00
refactor(api): complete feature service boundaries
This commit is contained in:
parent
aa59399b78
commit
445cd68468
35 changed files with 2993 additions and 610 deletions
77
Api.SeaHavenIndustries.Tests/DispatchControllerTests.cs
Normal file
77
Api.SeaHavenIndustries.Tests/DispatchControllerTests.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using Api.SeaHavenIndustries.Controllers;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class DispatchControllerTests
|
||||
{
|
||||
private static DispatchController NewController(Mock<IDispatchService> service) =>
|
||||
new(service.Object);
|
||||
|
||||
private static object Prop(object source, string name) =>
|
||||
source.GetType().GetProperty(name)!.GetValue(source)!;
|
||||
|
||||
[Fact]
|
||||
public async Task List_ReturnsDataResponseWithPaginationShape()
|
||||
{
|
||||
var service = new Mock<IDispatchService>();
|
||||
service.Setup(s => s.GetDispatchListAsync("a", null, null, null, null, "dispatchedAt", "desc", 1, 25, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new DispatchListResultDTO
|
||||
{
|
||||
Total = 2,
|
||||
Page = 1,
|
||||
PageSize = 25,
|
||||
Items = new List<DispatchListEntryDTO>
|
||||
{
|
||||
new() { Id = 1, DispatchNumber = "D1" },
|
||||
new() { Id = 2, DispatchNumber = "D2" }
|
||||
}
|
||||
});
|
||||
|
||||
var result = await NewController(service).List("a", null, null, null, null, "dispatchedAt", "desc", 1, 25, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
response.Status.Should().Be("Success");
|
||||
object data = response.Data!;
|
||||
Prop(data, "total").Should().Be(2);
|
||||
Prop(data, "page").Should().Be(1);
|
||||
Prop(data, "pageSize").Should().Be(25);
|
||||
((System.Collections.IEnumerable)Prop(data, "items")).Cast<object>().Count().Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetById_WhenMissing_ReturnsNotFound()
|
||||
{
|
||||
var service = new Mock<IDispatchService>();
|
||||
service.Setup(s => s.GetDispatchDetailAsync(99, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((DispatchDetailResultDTO?)null);
|
||||
|
||||
var result = await NewController(service).GetById(99, CancellationToken.None);
|
||||
|
||||
var notFound = result.Should().BeOfType<NotFoundObjectResult>().Subject;
|
||||
notFound.Value.Should().BeOfType<Response>().Subject.Message.Should().Be("Dispatch not found");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetById_WhenFound_ReturnsDetailAsData()
|
||||
{
|
||||
var service = new Mock<IDispatchService>();
|
||||
var detail = new DispatchDetailResultDTO { Id = 5, DispatchNumber = "D5", Status = "Pending" };
|
||||
service.Setup(s => s.GetDispatchDetailAsync(5, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(detail);
|
||||
|
||||
var result = await NewController(service).GetById(5, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
response.Status.Should().Be("Success");
|
||||
((object?)response.Data).Should().BeSameAs(detail);
|
||||
}
|
||||
}
|
||||
117
Api.SeaHavenIndustries.Tests/DispatchServiceTests.cs
Normal file
117
Api.SeaHavenIndustries.Tests/DispatchServiceTests.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
using FluentAssertions;
|
||||
using Moq;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class DispatchServiceTests
|
||||
{
|
||||
private static DispatchService NewService(Mock<IDispatchDataService> data) =>
|
||||
new(data.Object, new CreateDispatchValidation(), new UpdateDispatchValidation());
|
||||
|
||||
[Fact]
|
||||
public async Task GetDispatchListAsync_MapsDataReadModelToServiceDtoPreservingFields()
|
||||
{
|
||||
var data = new Mock<IDispatchDataService>();
|
||||
var source = new DispatchListResult
|
||||
{
|
||||
Total = 1,
|
||||
Page = 2,
|
||||
PageSize = 25,
|
||||
Items = new List<DispatchListItemDTO>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 7,
|
||||
DispatchNumber = "DISP-7",
|
||||
PONumber = "PO-1",
|
||||
NTEAmount = 200m,
|
||||
Status = "Pending",
|
||||
VendorId = 9,
|
||||
VendorName = "Acme",
|
||||
WorkOrderId = 3,
|
||||
PrimaryWOInternalNumber = "WO-3",
|
||||
PrimaryWOTitle = "Fix AC",
|
||||
LocationName = "Site A",
|
||||
WorkOrderCount = 2,
|
||||
PendingUpliftCount = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
data.Setup(d => d.GetListAsync(It.IsAny<DispatchListQuery>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(source);
|
||||
|
||||
var result = await NewService(data).GetDispatchListAsync("a", "Pending", 9, null, null, "ponumber", "asc", 2, 25, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<DispatchListResultDTO>();
|
||||
result.Total.Should().Be(1);
|
||||
result.Page.Should().Be(2);
|
||||
result.PageSize.Should().Be(25);
|
||||
var row = result.Items.Single();
|
||||
row.Id.Should().Be(7);
|
||||
row.DispatchNumber.Should().Be("DISP-7");
|
||||
row.PONumber.Should().Be("PO-1");
|
||||
row.NTEAmount.Should().Be(200m);
|
||||
row.Status.Should().Be("Pending");
|
||||
row.VendorId.Should().Be(9);
|
||||
row.VendorName.Should().Be("Acme");
|
||||
row.WorkOrderId.Should().Be(3);
|
||||
row.PrimaryWOInternalNumber.Should().Be("WO-3");
|
||||
row.PrimaryWOTitle.Should().Be("Fix AC");
|
||||
row.LocationName.Should().Be("Site A");
|
||||
row.WorkOrderCount.Should().Be(2);
|
||||
row.PendingUpliftCount.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDispatchDetailAsync_MapsNestedCollectionsPreservingShape()
|
||||
{
|
||||
var data = new Mock<IDispatchDataService>();
|
||||
var detail = new DispatchDetailDTO
|
||||
{
|
||||
Id = 42,
|
||||
DispatchNumber = "DISP-42",
|
||||
PONumber = "PO-9",
|
||||
NTEAmount = 500m,
|
||||
Status = "Dispatched",
|
||||
Description = "desc",
|
||||
WorkOrderId = 11,
|
||||
EmailSent = true,
|
||||
Vendor = new DispatchDetailVendorDTO { Id = 1, CompanyName = "VendorCo", Email = "v@x.com" },
|
||||
PrimaryLocation = new DispatchPrimaryLocationDTO { Id = 5, Name = "Loc", City = "Austin" },
|
||||
WorkOrders = new List<DispatchLinkedWorkOrderDTO> { new() { WorkOrderId = 11, Title = "T", Priority = "High" } },
|
||||
Checklist = new List<DispatchChecklistDTO> { new() { Id = 1, ItemText = "Check", IsCompleted = true, SortOrder = 1 } },
|
||||
Signoffs = new List<DispatchSignoffDTO> { new() { Id = 1, SignoffType = "Vendor", Name = "J" } },
|
||||
Comments = new List<DispatchCommentDTO> { new() { Id = 1, Commenttext = "Hi", UserName = "Sara" } },
|
||||
UpliftRequests = new List<DispatchUpliftRequestDTO> { new() { Id = 1, RequestedNTE = 600m, CurrentNTE = 500m, Delta = 100m, Status = "Pending" } },
|
||||
AuditLog = new List<DispatchAuditLogDTO> { new() { Id = 1, Action = "Updated", FieldName = "DISP-42:Status" } }
|
||||
};
|
||||
data.Setup(d => d.GetDetailAsync(42, It.IsAny<CancellationToken>())).ReturnsAsync(detail);
|
||||
data.Setup(d => d.GetDetailAsync(999, It.IsAny<CancellationToken>())).ReturnsAsync((DispatchDetailDTO?)null);
|
||||
|
||||
var service = NewService(data);
|
||||
|
||||
var found = await service.GetDispatchDetailAsync(42, CancellationToken.None);
|
||||
var missing = await service.GetDispatchDetailAsync(999, CancellationToken.None);
|
||||
|
||||
missing.Should().BeNull();
|
||||
found.Should().BeOfType<DispatchDetailResultDTO>();
|
||||
found!.Id.Should().Be(42);
|
||||
found.DispatchNumber.Should().Be("DISP-42");
|
||||
found.EmailSent.Should().BeTrue();
|
||||
found.Vendor!.CompanyName.Should().Be("VendorCo");
|
||||
found.PrimaryLocation!.Name.Should().Be("Loc");
|
||||
found.WorkOrders.Single().Title.Should().Be("T");
|
||||
found.Checklist.Single().IsCompleted.Should().BeTrue();
|
||||
found.Signoffs.Single().Name.Should().Be("J");
|
||||
found.Comments.Single().UserName.Should().Be("Sara");
|
||||
found.UpliftRequests.Single().Delta.Should().Be(100m);
|
||||
found.AuditLog.Single().Action.Should().Be("Updated");
|
||||
}
|
||||
}
|
||||
134
Api.SeaHavenIndustries.Tests/FollowUpControllerTests.cs
Normal file
134
Api.SeaHavenIndustries.Tests/FollowUpControllerTests.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System.Security.Claims;
|
||||
using Api.SeaHavenIndustries.Controllers;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class FollowUpControllerTests
|
||||
{
|
||||
private static FollowUpController NewController(Mock<IFollowUpService> service)
|
||||
{
|
||||
var controller = new FollowUpController(service.Object)
|
||||
{
|
||||
ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "user-1")
|
||||
}, "Test"))
|
||||
}
|
||||
}
|
||||
};
|
||||
return controller;
|
||||
}
|
||||
|
||||
private static FollowUpController NewControllerNoUser(Mock<IFollowUpService> service) =>
|
||||
new(service.Object)
|
||||
{
|
||||
ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static object Prop(object source, string name) =>
|
||||
source.GetType().GetProperty(name)!.GetValue(source)!;
|
||||
|
||||
[Fact]
|
||||
public async Task GetFollowUps_ReturnsPaginationEnvelopeWithMappedRows()
|
||||
{
|
||||
var service = new Mock<IFollowUpService>();
|
||||
service.Setup(s => s.GetFollowUpListAsync(1, 12, "", "Open", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new PagedResult<FollowUpListRowDTO>
|
||||
{
|
||||
Items = new List<FollowUpListRowDTO> { new() { Id = 1, Reason = "R", CreatedByName = "Me" } },
|
||||
TotalCount = 1,
|
||||
Page = 1,
|
||||
PageSize = 12
|
||||
});
|
||||
|
||||
var result = await NewController(service).GetFollowUps();
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var envelope = ok.Value.Should().BeOfType<Pagination_DTO>().Subject;
|
||||
envelope.PageSize.Should().Be(12);
|
||||
envelope.TotalCount.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetById_WhenMissing_ReturnsNotFound()
|
||||
{
|
||||
var service = new Mock<IFollowUpService>();
|
||||
service.Setup(s => s.GetFollowUpDetailAsync(9, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((FollowUpDetailDTO?)null);
|
||||
|
||||
var result = await NewController(service).GetById(9, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Create_WhenNoUser_ReturnsUnauthorized()
|
||||
{
|
||||
var service = new Mock<IFollowUpService>();
|
||||
var result = await NewControllerNoUser(service).Create(new FollowUp_DTO { Reason = "x" }, CancellationToken.None);
|
||||
|
||||
var unauthorized = result.Should().BeOfType<UnauthorizedObjectResult>().Subject;
|
||||
unauthorized.Value.Should().BeOfType<Response>().Subject.Message.Should().Be("User not authenticated");
|
||||
service.Verify(s => s.CreateOpenFollowUpAsync(It.IsAny<FollowUpCreateRequestDTO>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Create_WhenAuthenticated_ReturnsCreatedWithId()
|
||||
{
|
||||
var service = new Mock<IFollowUpService>();
|
||||
service.Setup(s => s.CreateOpenFollowUpAsync(It.IsAny<FollowUpCreateRequestDTO>(), "user-1", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(42);
|
||||
|
||||
var result = await NewController(service).Create(new FollowUp_DTO { Reason = "x" }, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
response.Status.Should().Be("200");
|
||||
response.Message.Should().Be("Follow Up created");
|
||||
((object?)response.Data).Should().Be(42);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Complete_WhenMissing_ReturnsNotFound()
|
||||
{
|
||||
var service = new Mock<IFollowUpService>();
|
||||
service.Setup(s => s.SetFollowUpStatusAsync(3, "Completed", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await NewController(service).Complete(3, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Delete_WhenMissing_ReturnsNotFoundEnvelope()
|
||||
{
|
||||
var service = new Mock<IFollowUpService>();
|
||||
service.Setup(s => s.RemoveFollowUpAsync(8, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await NewController(service).Delete(8, CancellationToken.None);
|
||||
|
||||
var notFound = result.Should().BeOfType<NotFoundObjectResult>().Subject;
|
||||
notFound.Value.Should().BeOfType<Response>().Subject.Message.Should().Be("Follow Up not found");
|
||||
}
|
||||
}
|
||||
162
Api.SeaHavenIndustries.Tests/FollowUpServiceTests.cs
Normal file
162
Api.SeaHavenIndustries.Tests/FollowUpServiceTests.cs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using SeaHaven.Services.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class FollowUpServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static FollowUpService NewService(ApplicationDbContext ctx) =>
|
||||
new(new FollowUpDataService(ctx), new CreateFollowUpValidation(), new UpdateFollowUpValidation());
|
||||
|
||||
private static FollowUps SeedFollowUp(ApplicationDbContext ctx, string reason, string status = "Open")
|
||||
{
|
||||
var f = new FollowUps
|
||||
{
|
||||
Reason = reason,
|
||||
ScheduleDate = new DateTime(2026, 8, 1),
|
||||
ScheduleStartTime = new TimeSpan(9, 0, 0),
|
||||
Status = status,
|
||||
createdby = "user-1",
|
||||
CreatedDate = DateTime.Now
|
||||
};
|
||||
ctx.FollowUps.Add(f);
|
||||
ctx.SaveChanges();
|
||||
return f;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateOpenFollowUpAsync_ForcesOpenStatusAndPersistsFields()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var service = NewService(ctx);
|
||||
|
||||
var id = await service.CreateOpenFollowUpAsync(new FollowUpCreateRequestDTO
|
||||
{
|
||||
Reason = "Call back",
|
||||
ScheduleDate = new DateTime(2026, 9, 1),
|
||||
ScheduleStartTime = new TimeSpan(10, 30, 0),
|
||||
EmployeeId = 5,
|
||||
WorkOrderId = 7,
|
||||
AccountId = 9,
|
||||
LocationId = 11
|
||||
}, "user-9", CancellationToken.None);
|
||||
|
||||
var entity = ctx.FollowUps.Single();
|
||||
entity.Id.Should().Be(id);
|
||||
entity.Status.Should().Be("Open");
|
||||
entity.Reason.Should().Be("Call back");
|
||||
entity.createdby.Should().Be("user-9");
|
||||
entity.EmployeeId.Should().Be(5);
|
||||
entity.WorkOrderId.Should().Be(7);
|
||||
entity.CreatedDate.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetFollowUpListAsync_DefaultsToOpenAndMapsShape()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
SeedFollowUp(ctx, "Open one", "Open");
|
||||
SeedFollowUp(ctx, "Done one", "Completed");
|
||||
|
||||
var page = await NewService(ctx).GetFollowUpListAsync(1, 12, "", null, CancellationToken.None);
|
||||
|
||||
page.Items.Should().HaveCount(1);
|
||||
page.Items.Single().Reason.Should().Be("Open one");
|
||||
page.Items.Single().Status.Should().Be("Open");
|
||||
page.PageSize.Should().Be(12);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetFollowUpDetailAsync_MapsRelatedNamesAndResolvesCreator()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var employee = new Employee { FirstName = "Sara", LastName = "Lee" };
|
||||
ctx.Employees.Add(employee);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var account = new Accounts { Name = "Acme" };
|
||||
ctx.Accounts.Add(account);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var user = new ApplicationUser { Id = "user-1", FirstName = "Creator", LastName = "One", UserName = "cone" };
|
||||
var fu = new FollowUps
|
||||
{
|
||||
Reason = "Detail",
|
||||
ScheduleDate = new DateTime(2026, 8, 2),
|
||||
ScheduleStartTime = new TimeSpan(8, 0, 0),
|
||||
Status = "Open",
|
||||
EmployeeId = employee.Id,
|
||||
AccountId = account.Id,
|
||||
createdby = "user-1",
|
||||
CreatedDate = DateTime.Now
|
||||
};
|
||||
ctx.Users.Add(user);
|
||||
ctx.FollowUps.Add(fu);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var detail = await NewService(ctx).GetFollowUpDetailAsync(fu.Id, CancellationToken.None);
|
||||
|
||||
detail.Should().NotBeNull();
|
||||
detail!.AssignedTo.Should().Be("Sara Lee");
|
||||
detail.AccountName.Should().Be("Acme");
|
||||
detail.CreatedById.Should().Be("user-1");
|
||||
detail.CreatedByName.Should().Be("Creator One");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateFollowUpFieldsAsync_UpdatesAndThrowsWhenMissing()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedFollowUp(ctx, "Old reason");
|
||||
|
||||
await NewService(ctx).UpdateFollowUpFieldsAsync(existing.Id, new FollowUpUpdateRequestDTO
|
||||
{
|
||||
Id = existing.Id,
|
||||
Reason = "New reason",
|
||||
ScheduleDate = new DateTime(2026, 10, 1),
|
||||
ScheduleStartTime = new TimeSpan(11, 0, 0)
|
||||
}, CancellationToken.None);
|
||||
|
||||
var row = ctx.FollowUps.Single();
|
||||
row.Reason.Should().Be("New reason");
|
||||
|
||||
var act = () => NewService(ctx).UpdateFollowUpFieldsAsync(existing.Id + 999, new FollowUpUpdateRequestDTO { Id = existing.Id + 999 }, CancellationToken.None);
|
||||
await act.Should().ThrowAsync<KeyNotFoundException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SetFollowUpStatusAsync_TogglesStatusAndReturnsFalseWhenMissing()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedFollowUp(ctx, "To complete");
|
||||
|
||||
(await NewService(ctx).SetFollowUpStatusAsync(existing.Id, "Completed", CancellationToken.None)).Should().BeTrue();
|
||||
ctx.FollowUps.Single().Status.Should().Be("Completed");
|
||||
(await NewService(ctx).SetFollowUpStatusAsync(existing.Id + 999, "Completed", CancellationToken.None)).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveFollowUpAsync_RemovesAndReturnsFalseWhenMissing()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedFollowUp(ctx, "Gone");
|
||||
|
||||
(await NewService(ctx).RemoveFollowUpAsync(existing.Id, CancellationToken.None)).Should().BeTrue();
|
||||
(await NewService(ctx).RemoveFollowUpAsync(existing.Id, CancellationToken.None)).Should().BeFalse();
|
||||
ctx.FollowUps.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
104
Api.SeaHavenIndustries.Tests/LocationControllerTests.cs
Normal file
104
Api.SeaHavenIndustries.Tests/LocationControllerTests.cs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
using Api.SeaHavenIndustries.Controllers;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class LocationControllerTests
|
||||
{
|
||||
private static LocationController NewController(Mock<ILocationService> service) =>
|
||||
new(service.Object);
|
||||
|
||||
private static object Prop(object source, string name) =>
|
||||
source.GetType().GetProperty(name)!.GetValue(source)!;
|
||||
|
||||
[Fact]
|
||||
public async Task GetLocationList_ReturnsPaginationEnvelopeWithServiceData()
|
||||
{
|
||||
var service = new Mock<ILocationService>();
|
||||
service.Setup(s => s.GetLocationListPagedAsync(1, 10, "a", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new PagedResult<LocationDTO>
|
||||
{
|
||||
Items = new List<LocationDTO> { new() { Id = 1, Name = "Site" } },
|
||||
TotalCount = 1,
|
||||
Page = 1,
|
||||
PageSize = 10
|
||||
});
|
||||
|
||||
var result = await NewController(service).GetLocationList("a", 1, 10, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var envelope = ok.Value.Should().BeOfType<Pagination_DTO>().Subject;
|
||||
envelope.PageNumber.Should().Be(1);
|
||||
envelope.PageSize.Should().Be(10);
|
||||
envelope.TotalCount.Should().Be(1);
|
||||
envelope.TotalPages.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLocationById_WhenMissing_ReturnsNotFoundAndDoesNotLeakEntity()
|
||||
{
|
||||
var service = new Mock<ILocationService>();
|
||||
service.Setup(s => s.GetLocationDetailAsync(5, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((LocationDTO?)null);
|
||||
|
||||
var result = await NewController(service).GetLocationById(5, CancellationToken.None);
|
||||
|
||||
var notFound = result.Should().BeOfType<NotFoundObjectResult>().Subject;
|
||||
var response = notFound.Value.Should().BeOfType<Response>().Subject;
|
||||
response.Message.Should().Be("Location not found");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLocationById_WhenFound_MapsServiceDtoToAliasedShape()
|
||||
{
|
||||
var service = new Mock<ILocationService>();
|
||||
service.Setup(s => s.GetLocationDetailAsync(7, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new LocationDTO { Id = 7, Name = "N", Address1 = "A", Zip = "Z", PhoneNumber = "P", Email = "E", Status = "Active" });
|
||||
|
||||
var result = await NewController(service).GetLocationById(7, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var body = ok.Value!;
|
||||
Prop(body, "Id").Should().Be(7);
|
||||
Prop(body, "Name").Should().Be("N");
|
||||
Prop(body, "Address").Should().Be("A");
|
||||
Prop(body, "ZipCode").Should().Be("Z");
|
||||
Prop(body, "Phone").Should().Be("P");
|
||||
Prop(body, "ContactEmail").Should().Be("E");
|
||||
Prop(body, "Status").Should().Be("Active");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddLocation_WhenServiceSucceeds_ReturnsCreatedResponse()
|
||||
{
|
||||
var service = new Mock<ILocationService>();
|
||||
var result = await NewController(service).AddLocation(new Location_DTO { Name = "X" }, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
response.Status.Should().Be("200");
|
||||
response.Message.Should().Be("Location Created Successfully");
|
||||
service.Verify(s => s.CreateLocationFromRequestAsync(It.IsAny<LocationCreateRequestDTO>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditLocation_WhenMissing_ReturnsNotFound()
|
||||
{
|
||||
var service = new Mock<ILocationService>();
|
||||
service.Setup(s => s.UpdateLocationFromRequestAsync(99, It.IsAny<LocationUpdateRequestDTO>(), It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new KeyNotFoundException());
|
||||
|
||||
var result = await NewController(service).EditLocation(99, new EditLocation_DTO { Name = "X" }, CancellationToken.None);
|
||||
|
||||
var notFound = result.Should().BeOfType<NotFoundObjectResult>().Subject;
|
||||
notFound.Value.Should().BeOfType<Response>().Subject.Message.Should().Be("Location not found");
|
||||
}
|
||||
}
|
||||
133
Api.SeaHavenIndustries.Tests/LocationServiceTests.cs
Normal file
133
Api.SeaHavenIndustries.Tests/LocationServiceTests.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using SeaHaven.Services.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class LocationServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static LocationService NewService(ApplicationDbContext ctx) =>
|
||||
new(new LocationDataService(ctx), new CreateLocationValidation(), new UpdateLocationValidation());
|
||||
|
||||
private static Locations SeedLocation(ApplicationDbContext ctx, string name, string? city = null, string? status = "Active")
|
||||
{
|
||||
var loc = new Locations { Name = name, City = city, Status = status, CreatedDate = DateTime.Now };
|
||||
ctx.Locations.Add(loc);
|
||||
ctx.SaveChanges();
|
||||
return loc;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateLocationFromRequestAsync_PersistsMappedFields()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var service = NewService(ctx);
|
||||
|
||||
await service.CreateLocationFromRequestAsync(new LocationCreateRequestDTO
|
||||
{
|
||||
Name = "Warehouse",
|
||||
Title = "Main WH",
|
||||
Address = "1 Depot Rd",
|
||||
City = "Austin",
|
||||
State = "TX",
|
||||
ZipCode = "73301",
|
||||
Phone = "555-1000",
|
||||
ContactEmail = "wh@example.com",
|
||||
Status = "Active"
|
||||
}, CancellationToken.None);
|
||||
|
||||
var entity = ctx.Locations.Single();
|
||||
entity.Name.Should().Be("Warehouse");
|
||||
entity.Title.Should().Be("Main WH");
|
||||
entity.Address1.Should().Be("1 Depot Rd");
|
||||
entity.City.Should().Be("Austin");
|
||||
entity.State.Should().Be("TX");
|
||||
entity.Zip.Should().Be("73301");
|
||||
entity.PhoneNumber.Should().Be("555-1000");
|
||||
entity.Email.Should().Be("wh@example.com");
|
||||
entity.Status.Should().Be("Active");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLocationListPagedAsync_FiltersBySearchAndMapsShape()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
SeedLocation(ctx, "Alpha Site", "Austin");
|
||||
SeedLocation(ctx, "Beta Site", "Dallas");
|
||||
SeedLocation(ctx, "Gamma Yard", "Austin");
|
||||
|
||||
var page = await NewService(ctx).GetLocationListPagedAsync(1, 10, "Austin", CancellationToken.None);
|
||||
|
||||
page.Items.Should().HaveCount(2);
|
||||
page.TotalCount.Should().Be(2);
|
||||
page.Items.All(l => l.City == "Austin").Should().BeTrue();
|
||||
page.Items.Select(l => l.Name).Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLocationDetailAsync_ReturnsMappedDtoOrNull()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedLocation(ctx, "Depot", "Houston");
|
||||
|
||||
var service = NewService(ctx);
|
||||
var found = await service.GetLocationDetailAsync(existing.Id, CancellationToken.None);
|
||||
var missing = await service.GetLocationDetailAsync(existing.Id + 999, CancellationToken.None);
|
||||
|
||||
missing.Should().BeNull();
|
||||
found.Should().NotBeNull();
|
||||
found!.Name.Should().Be("Depot");
|
||||
found.City.Should().Be("Houston");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateLocationFromRequestAsync_UpdatesFieldsAndThrowsWhenMissing()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedLocation(ctx, "Old", "Round Rock");
|
||||
|
||||
await NewService(ctx).UpdateLocationFromRequestAsync(existing.Id, new LocationUpdateRequestDTO
|
||||
{
|
||||
Name = "New",
|
||||
Address = "9 New St",
|
||||
City = "Plano",
|
||||
Status = "Inactive"
|
||||
}, CancellationToken.None);
|
||||
|
||||
var row = ctx.Locations.Single();
|
||||
row.Name.Should().Be("New");
|
||||
row.Address1.Should().Be("9 New St");
|
||||
row.City.Should().Be("Plano");
|
||||
row.Status.Should().Be("Inactive");
|
||||
|
||||
var act = () => NewService(ctx).UpdateLocationFromRequestAsync(existing.Id + 999, new LocationUpdateRequestDTO { Name = "X" }, CancellationToken.None);
|
||||
await act.Should().ThrowAsync<KeyNotFoundException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteLocationByIdAsync_RemovesAndReturnsFalseWhenMissing()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedLocation(ctx, "Gone", "Cedar Park");
|
||||
|
||||
var removed = await NewService(ctx).DeleteLocationByIdAsync(existing.Id, CancellationToken.None);
|
||||
var again = await NewService(ctx).DeleteLocationByIdAsync(existing.Id, CancellationToken.None);
|
||||
|
||||
removed.Should().BeTrue();
|
||||
again.Should().BeFalse();
|
||||
ctx.Locations.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -54,4 +54,26 @@ public class ServiceRegistrationTests
|
|||
provider.GetRequiredService<IDashboardDataService>().Should().BeOfType<DashboardDataService>();
|
||||
provider.GetRequiredService<IDropdownOptionsDataService>().Should().BeOfType<DropdownOptionsDataService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConventionDi_RegistersFollowUpLocationDispatchSettingsServices()
|
||||
{
|
||||
using var provider = BuildProvider();
|
||||
|
||||
provider.GetRequiredService<IFollowUpService>().Should().BeOfType<FollowUpService>();
|
||||
provider.GetRequiredService<ILocationService>().Should().BeOfType<LocationService>();
|
||||
provider.GetRequiredService<IDispatchService>().Should().BeOfType<DispatchService>();
|
||||
provider.GetRequiredService<ISettingsService>().Should().BeOfType<SettingsService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConventionDi_RegistersFollowUpLocationDispatchSettingsDataServices()
|
||||
{
|
||||
using var provider = BuildProvider();
|
||||
|
||||
provider.GetRequiredService<IFollowUpDataService>().Should().BeOfType<FollowUpDataService>();
|
||||
provider.GetRequiredService<ILocationDataService>().Should().BeOfType<LocationDataService>();
|
||||
provider.GetRequiredService<IDispatchDataService>().Should().BeOfType<DispatchDataService>();
|
||||
provider.GetRequiredService<ISettingsDataService>().Should().BeOfType<SettingsDataService>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
119
Api.SeaHavenIndustries.Tests/SettingsControllerTests.cs
Normal file
119
Api.SeaHavenIndustries.Tests/SettingsControllerTests.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
using Api.SeaHavenIndustries.Controllers;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Api.SeaHavenIndustries.Helper.ViewModel;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class SettingsControllerTests
|
||||
{
|
||||
private static SettingsController NewController(Mock<ISettingsService> service) =>
|
||||
new(service.Object);
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryList_ReturnsPaginationEnvelopeWithServiceItems()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
service.Setup(s => s.GetCategoryListAsync(1, 10, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((new List<SettingsCategoryDTO> { new() { Id = 1, Name = "A" } }, 1));
|
||||
|
||||
var result = await NewController(service).GetCategoryList(1, 10, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var envelope = ok.Value.Should().BeOfType<Pagination_DTO>().Subject;
|
||||
envelope.TotalCount.Should().Be(1);
|
||||
envelope.TotalPages.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetTemplates_ReturnsTypedServiceDtosInEnvelope()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
service.Setup(s => s.GetTemplatesAsync(1, 10, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((new List<SettingsTemplateDTO> { new() { Id = 1, WorkerOrderTitle = "T" } }, 1));
|
||||
|
||||
var result = await NewController(service).GetTemplates(1, 10, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var envelope = ok.Value.Should().BeOfType<Pagination_DTO>().Subject;
|
||||
envelope.TotalCount.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddCategory_WhenServiceSucceeds_ReturnsSaved()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
var result = await NewController(service).AddCategory(new Category_DTO { Name = "X" }, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
response.Status.Should().Be("200");
|
||||
response.Message.Should().Be("Successfully Saved Data");
|
||||
service.Verify(s => s.AddCategoryAsync("X", It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditCategory_WhenMissing_ReturnsBadRequest()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
service.Setup(s => s.UpdateCategoryAsync(9, "X", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await NewController(service).EditCategory(new EditCategory_DTO { Id = 9, Name = "X" }, CancellationToken.None);
|
||||
|
||||
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
|
||||
bad.Value.Should().BeOfType<Response>().Subject.Message.Should().Be("Category not found");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteCategory_WhenMissing_ReturnsIdNotMatched()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
service.Setup(s => s.DeleteCategoryAsync(9, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await NewController(service).DeleteCategory(9, CancellationToken.None);
|
||||
|
||||
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
|
||||
bad.Value.Should().BeOfType<Response>().Subject.Message.Should().Be("ID not matched!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetUsers_MapsServiceUsersToUserRoleViewModel()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
service.Setup(s => s.GetUsersAsync("", 1, 10, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((new List<SettingsUserDTO>
|
||||
{
|
||||
new() { Id = "u1", UserName = "Sara", Email = "s@x.com", RoleName = "User", Status = "Active", Date = "2026-01-01" }
|
||||
}, 1));
|
||||
|
||||
var result = await NewController(service).GetUsers("", 1, 10, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var envelope = ok.Value.Should().BeOfType<Pagination_DTO>().Subject;
|
||||
object data = envelope.Data!;
|
||||
var rows = ((System.Collections.IEnumerable)data).Cast<UserRoleViewModel>().ToList();
|
||||
rows.Should().ContainSingle(u => u.UserName == "Sara" && u.RoleName == "User" && u.Email == "s@x.com");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetRoles_MapsServiceRolesToRolesDto()
|
||||
{
|
||||
var service = new Mock<ISettingsService>();
|
||||
service.Setup(s => s.GetRolesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<RoleDTO> { new() { Name = "Admin", Description = "Full Access" } });
|
||||
|
||||
var result = await NewController(service).GetRoles(CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var roles = ((System.Collections.IEnumerable)ok.Value!).Cast<Roles_DTO>().ToList();
|
||||
roles.Should().ContainSingle(r => r.Name == "Admin" && r.Description == "Full Access");
|
||||
}
|
||||
}
|
||||
168
Api.SeaHavenIndustries.Tests/SettingsServiceTests.cs
Normal file
168
Api.SeaHavenIndustries.Tests/SettingsServiceTests.cs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class SettingsServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static SettingsService NewService(ApplicationDbContext ctx) =>
|
||||
new(new SettingsDataService(ctx), null!);
|
||||
|
||||
private static (ApplicationDbContext ctx, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager) NewIdentityContext()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddDbContext<ApplicationDbContext>(o => o.UseInMemoryDatabase(Guid.NewGuid().ToString()));
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
var provider = services.BuildServiceProvider();
|
||||
var scope = provider.CreateScope();
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
return (ctx, userManager, roleManager);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddCategoryAsync_PersistsName()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
await NewService(ctx).AddCategoryAsync("Plumbing", CancellationToken.None);
|
||||
ctx.Categories.Single().Name.Should().Be("Plumbing");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateCategoryAsync_UpdatesNameAndReturnsFalseWhenMissing()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.Categories.Add(new Category { Name = "Old" });
|
||||
ctx.SaveChanges();
|
||||
|
||||
var service = NewService(ctx);
|
||||
(await service.UpdateCategoryAsync(1, "New", CancellationToken.None)).Should().BeTrue();
|
||||
ctx.Categories.Single().Name.Should().Be("New");
|
||||
(await service.UpdateCategoryAsync(999, "X", CancellationToken.None)).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteCategoryAsync_CascadesTemplatesAndWorkOrderCategories()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.Categories.Add(new Category { Id = 1, Name = "C1" });
|
||||
ctx.Templates.Add(new Template { CategoryId = 1, WorkerOrderTitle = "T" });
|
||||
ctx.workOrderCategories.Add(new WorkOrderCategories { CategoryId = 1 });
|
||||
ctx.SaveChanges();
|
||||
|
||||
var service = NewService(ctx);
|
||||
(await service.DeleteCategoryAsync(1, CancellationToken.None)).Should().BeTrue();
|
||||
(await service.DeleteCategoryAsync(1, CancellationToken.None)).Should().BeFalse();
|
||||
|
||||
ctx.Categories.Should().BeEmpty();
|
||||
ctx.Templates.Should().BeEmpty();
|
||||
ctx.workOrderCategories.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryListAsync_MapsToServiceDtoAndPages()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.Categories.Add(new Category { Name = "A" });
|
||||
ctx.Categories.Add(new Category { Name = "B" });
|
||||
ctx.SaveChanges();
|
||||
|
||||
var (items, total) = await NewService(ctx).GetCategoryListAsync(1, 10, CancellationToken.None);
|
||||
|
||||
total.Should().Be(2);
|
||||
items.Should().OnlyContain(c => c is SettingsCategoryDTO);
|
||||
items.Select(c => c.Name).Should().Contain(new[] { "A", "B" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetTemplatesAsync_MapsWorkOrderScalarsToTypedDto()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var wo = new WorkOrder
|
||||
{
|
||||
istemplate = true,
|
||||
InternalWONumber = "WO-100",
|
||||
WorkerOrderTitle = "Template A",
|
||||
Status = "Active",
|
||||
Priority = "High",
|
||||
Trade = "HVAC",
|
||||
VendorNTE = 1500m,
|
||||
Customer = "Cust"
|
||||
};
|
||||
ctx.workOrders.Add(wo);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var (items, total) = await NewService(ctx).GetTemplatesAsync(1, 10, CancellationToken.None);
|
||||
|
||||
total.Should().Be(1);
|
||||
var dto = items.Single();
|
||||
dto.Should().BeOfType<SettingsTemplateDTO>();
|
||||
dto.InternalWONumber.Should().Be("WO-100");
|
||||
dto.WorkerOrderTitle.Should().Be("Template A");
|
||||
dto.Status.Should().Be("Active");
|
||||
dto.Priority.Should().Be("High");
|
||||
dto.Trade.Should().Be("HVAC");
|
||||
dto.VendorNTE.Should().Be(1500m);
|
||||
dto.Customer.Should().Be("Cust");
|
||||
dto.istemplate.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetUsersAsync_ExcludesAdminsAndResolvesRole()
|
||||
{
|
||||
var (ctx, userManager, roleManager) = NewIdentityContext();
|
||||
var t1 = roleManager.CreateAsync(new IdentityRole("Admin"));
|
||||
var t2 = roleManager.CreateAsync(new IdentityRole("User"));
|
||||
await Task.WhenAll(t1, t2);
|
||||
|
||||
var admin = new ApplicationUser { UserName = "admin1", Email = "a@x.com", FirstName = "Boss" };
|
||||
var worker = new ApplicationUser { UserName = "worker1", Email = "w@x.com", FirstName = "Hand" };
|
||||
await userManager.CreateAsync(admin);
|
||||
await userManager.CreateAsync(worker);
|
||||
await userManager.AddToRoleAsync(admin, "Admin");
|
||||
await userManager.AddToRoleAsync(worker, "User");
|
||||
|
||||
ctx.SaveChanges();
|
||||
|
||||
var service = new SettingsService(new SettingsDataService(ctx), userManager);
|
||||
var (users, total) = await service.GetUsersAsync("", 1, 10, CancellationToken.None);
|
||||
|
||||
total.Should().BeGreaterThanOrEqualTo(2);
|
||||
users.Should().NotContain(u => u.RoleName == "Admin");
|
||||
users.Should().Contain(u => u.UserName == "Hand" && u.RoleName == "User");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetRolesAsync_MapsKnownRoleNamesToDescriptions()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.Roles.Add(new IdentityRole("Admin"));
|
||||
ctx.Roles.Add(new IdentityRole("Manager"));
|
||||
ctx.Roles.Add(new IdentityRole("Unknown"));
|
||||
ctx.SaveChanges();
|
||||
|
||||
var roles = await NewService(ctx).GetRolesAsync(CancellationToken.None);
|
||||
|
||||
var byName = roles.ToDictionary(r => r.Name!, r => r.Description);
|
||||
byName["Admin"].Should().Be("Full Access");
|
||||
byName["Manager"].Should().Be("Full access accept quotes");
|
||||
roles.Should().NotContain(r => r.Name == "Unknown");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -14,12 +12,10 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
public class DispatchController : Controller
|
||||
{
|
||||
private readonly IDispatchService _dispatchService;
|
||||
private readonly ApplicationDbContext _db; // Keep for complex queries
|
||||
|
||||
public DispatchController(IDispatchService dispatchService, ApplicationDbContext db)
|
||||
public DispatchController(IDispatchService dispatchService)
|
||||
{
|
||||
_dispatchService = dispatchService;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -32,317 +28,29 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[FromQuery] string? sortBy = "dispatchedAt",
|
||||
[FromQuery] string? sortDir = "desc",
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 25)
|
||||
[FromQuery] int pageSize = 25,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var q = _db.Dispatches
|
||||
.Include(d => d.Vendor)
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.Where(d => d.IsDeleted == null || d.IsDeleted == false);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
q = q.Where(d =>
|
||||
(d.DispatchNumber ?? "").ToLower().Contains(s) ||
|
||||
(d.PONumber ?? "").ToLower().Contains(s) ||
|
||||
(d.Vendor != null && (d.Vendor.CompanyName ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && (d.WorkOrder.InternalWONumber ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && (d.WorkOrder.WorkerOrderTitle ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && d.WorkOrder.Locations != null && (d.WorkOrder.Locations.Name ?? "").ToLower().Contains(s))
|
||||
);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
q = q.Where(d => d.Status == status);
|
||||
|
||||
if (vendorId.HasValue)
|
||||
q = q.Where(d => d.VendorId == vendorId.Value);
|
||||
|
||||
if (dateFrom.HasValue)
|
||||
q = q.Where(d => d.DispatchedAt >= dateFrom.Value);
|
||||
|
||||
if (dateTo.HasValue)
|
||||
{
|
||||
var end = dateTo.Value.Date.AddDays(1);
|
||||
q = q.Where(d => d.DispatchedAt < end);
|
||||
}
|
||||
|
||||
var total = await q.CountAsync();
|
||||
|
||||
var dir = (sortDir ?? "desc").ToLowerInvariant() == "asc";
|
||||
switch ((sortBy ?? "").ToLowerInvariant())
|
||||
{
|
||||
case "ponumber":
|
||||
q = dir ? q.OrderBy(d => d.PONumber) : q.OrderByDescending(d => d.PONumber);
|
||||
break;
|
||||
case "dispatchnumber":
|
||||
q = dir ? q.OrderBy(d => d.DispatchNumber) : q.OrderByDescending(d => d.DispatchNumber);
|
||||
break;
|
||||
case "vendor":
|
||||
q = dir ? q.OrderBy(d => d.Vendor!.CompanyName) : q.OrderByDescending(d => d.Vendor!.CompanyName);
|
||||
break;
|
||||
case "nte":
|
||||
case "nteamount":
|
||||
q = dir ? q.OrderBy(d => d.NTEAmount) : q.OrderByDescending(d => d.NTEAmount);
|
||||
break;
|
||||
case "status":
|
||||
q = dir ? q.OrderBy(d => d.Status) : q.OrderByDescending(d => d.Status);
|
||||
break;
|
||||
case "scheduleddate":
|
||||
q = dir ? q.OrderBy(d => d.ScheduledDate) : q.OrderByDescending(d => d.ScheduledDate);
|
||||
break;
|
||||
case "completeddate":
|
||||
q = dir ? q.OrderBy(d => d.CompletedDate) : q.OrderByDescending(d => d.CompletedDate);
|
||||
break;
|
||||
default:
|
||||
q = dir ? q.OrderBy(d => d.DispatchedAt) : q.OrderByDescending(d => d.DispatchedAt);
|
||||
break;
|
||||
}
|
||||
|
||||
var rows = await q
|
||||
.Skip((Math.Max(page, 1) - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(d => new
|
||||
{
|
||||
d.Id,
|
||||
d.DispatchNumber,
|
||||
d.PONumber,
|
||||
d.NTEAmount,
|
||||
d.Status,
|
||||
d.DispatchedAt,
|
||||
d.ScheduledDate,
|
||||
d.CompletedDate,
|
||||
d.AcknowledgedAt,
|
||||
d.VerifiedAt,
|
||||
VendorId = d.VendorId,
|
||||
VendorName = d.Vendor != null ? d.Vendor.CompanyName : null,
|
||||
WorkOrderId = d.WorkOrderId,
|
||||
PrimaryWOInternalNumber = d.WorkOrder != null ? d.WorkOrder.InternalWONumber : null,
|
||||
PrimaryWOTitle = d.WorkOrder != null ? d.WorkOrder.WorkerOrderTitle : null,
|
||||
LocationName = d.WorkOrder != null && d.WorkOrder.Locations != null ? d.WorkOrder.Locations.Name : null,
|
||||
WorkOrderCount = _db.DispatchWorkOrders.Count(dwo => dwo.DispatchId == d.Id),
|
||||
PendingUpliftCount = _db.DispatchUpliftRequests.Count(u => u.DispatchId == d.Id && u.Status == "Pending" && (u.IsDeleted == null || u.IsDeleted == false))
|
||||
})
|
||||
.ToListAsync();
|
||||
var result = await _dispatchService.GetDispatchListAsync(
|
||||
search, status, vendorId, dateFrom, dateTo, sortBy, sortDir, page, pageSize, cancellationToken);
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new { total, page, pageSize, items = rows }
|
||||
Data = new { total = result.Total, page = result.Page, pageSize = result.PageSize, items = result.Items }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
public async Task<IActionResult> GetById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var dispatch = await _db.Dispatches
|
||||
.Include(d => d.Vendor)
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.FirstOrDefaultAsync(d => d.Id == id);
|
||||
var dispatch = await _dispatchService.GetDispatchDetailAsync(id, cancellationToken);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
var linkedWOs = await _db.DispatchWorkOrders
|
||||
.Where(dwo => dwo.DispatchId == id)
|
||||
.Select(dwo => new
|
||||
{
|
||||
dwo.WorkOrderId,
|
||||
InternalWONumber = dwo.WorkOrder != null ? dwo.WorkOrder.InternalWONumber : null,
|
||||
Title = dwo.WorkOrder != null ? dwo.WorkOrder.WorkerOrderTitle : null,
|
||||
Priority = dwo.WorkOrder != null ? dwo.WorkOrder.Priority : null,
|
||||
Status = dwo.WorkOrder != null ? dwo.WorkOrder.Status : null,
|
||||
LocationName = dwo.WorkOrder != null && dwo.WorkOrder.Locations != null ? dwo.WorkOrder.Locations.Name : null,
|
||||
Trade = dwo.WorkOrder != null ? dwo.WorkOrder.Trade : null,
|
||||
SubTrade = dwo.WorkOrder != null ? dwo.WorkOrder.SubTrade : null,
|
||||
Problem = dwo.WorkOrder != null ? dwo.WorkOrder.Problem : null
|
||||
}).ToListAsync();
|
||||
|
||||
if (linkedWOs.Count == 0 && dispatch.WorkOrderId.HasValue)
|
||||
{
|
||||
var wo = await _db.workOrders
|
||||
.Include(w => w.Locations)
|
||||
.FirstOrDefaultAsync(w => w.Id == dispatch.WorkOrderId.Value);
|
||||
if (wo != null)
|
||||
{
|
||||
linkedWOs = new[] { new
|
||||
{
|
||||
WorkOrderId = wo.Id,
|
||||
InternalWONumber = wo.InternalWONumber,
|
||||
Title = wo.WorkerOrderTitle,
|
||||
Priority = wo.Priority,
|
||||
Status = wo.Status,
|
||||
LocationName = wo.Locations != null ? wo.Locations.Name : null,
|
||||
Trade = wo.Trade,
|
||||
SubTrade = wo.SubTrade,
|
||||
Problem = wo.Problem
|
||||
}}.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
var checklist = await _db.DispatchChecklistItems
|
||||
.Where(c => c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false))
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.Select(c => new { c.Id, c.ItemText, c.IsCompleted, c.CompletedBy, c.CompletedAt, c.SortOrder, c.WorkOrderId })
|
||||
.ToListAsync();
|
||||
|
||||
var signoffs = await _db.DispatchSignoffs
|
||||
.Where(s => s.DispatchId == id && (s.IsDeleted == null || s.IsDeleted == false))
|
||||
.Select(s => new { s.Id, s.SignoffType, s.Name, s.Signature, s.SignatureMethod, s.SignedAt })
|
||||
.ToListAsync();
|
||||
|
||||
var rawComments = await (from c in _db.Comments
|
||||
where c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false)
|
||||
join u in _db.Users on c.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby c.CreatedDate
|
||||
select new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
c.Commenter,
|
||||
c.CommentType,
|
||||
c.RecordType,
|
||||
c.CreatedDate,
|
||||
c.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var comments = rawComments.Select(c => new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
c.Commenter,
|
||||
c.CommentType,
|
||||
c.RecordType,
|
||||
c.CreatedDate,
|
||||
UserName = string.Join(" ", new[] { c.UserFirstName, c.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var rawUplifts = await (from u in _db.DispatchUpliftRequests
|
||||
where u.DispatchId == id && (u.IsDeleted == null || u.IsDeleted == false)
|
||||
join dec in _db.Users on u.DecidedByUserId equals dec.Id into decs
|
||||
from dec in decs.DefaultIfEmpty()
|
||||
orderby u.CreatedDate descending
|
||||
select new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
RequestedAt = u.CreatedDate,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByFirstName = dec != null ? dec.FirstName : null,
|
||||
DecidedByLastName = dec != null ? dec.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var upliftRequests = rawUplifts.Select(u => new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
Delta = u.RequestedNTE - (u.CurrentNTE ?? 0m),
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
u.RequestedAt,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByName = string.Join(" ", new[] { u.DecidedByFirstName, u.DecidedByLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var dispatchNumber = dispatch.DispatchNumber ?? "";
|
||||
var workOrderIds = linkedWOs.Select(l => l.WorkOrderId).Where(wid => wid > 0).ToList();
|
||||
|
||||
var rawAudit = await (from a in _db.WorkOrderAuditLogs
|
||||
where workOrderIds.Contains(a.WorkOrderId)
|
||||
&& a.FieldName != null
|
||||
&& a.FieldName.Contains(dispatchNumber)
|
||||
join u in _db.Users on a.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby a.CreatedAt descending
|
||||
select new
|
||||
{
|
||||
a.Id,
|
||||
a.Action,
|
||||
a.FieldName,
|
||||
a.OldValue,
|
||||
a.NewValue,
|
||||
a.CreatedAt,
|
||||
a.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var auditLog = rawAudit.Select(a => new
|
||||
{
|
||||
Id = a.Id,
|
||||
Action = a.Action,
|
||||
FieldName = a.FieldName,
|
||||
OldValue = a.OldValue,
|
||||
NewValue = a.NewValue,
|
||||
CreatedAt = a.CreatedAt,
|
||||
UserName = string.Join(" ", new[] { a.UserFirstName, a.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new
|
||||
{
|
||||
dispatch.Id,
|
||||
dispatch.DispatchNumber,
|
||||
dispatch.PONumber,
|
||||
dispatch.NTEAmount,
|
||||
dispatch.Description,
|
||||
dispatch.Status,
|
||||
dispatch.ScheduledDate,
|
||||
dispatch.CompletedDate,
|
||||
dispatch.DispatchedAt,
|
||||
dispatch.AcknowledgedAt,
|
||||
dispatch.VerifiedAt,
|
||||
dispatch.VerifiedBy,
|
||||
dispatch.EmailSent,
|
||||
dispatch.WorkOrderId,
|
||||
Vendor = dispatch.Vendor == null ? null : new
|
||||
{
|
||||
dispatch.Vendor.Id,
|
||||
dispatch.Vendor.CompanyName,
|
||||
dispatch.Vendor.ContactName,
|
||||
dispatch.Vendor.Email,
|
||||
dispatch.Vendor.Phone,
|
||||
dispatch.Vendor.Address,
|
||||
dispatch.Vendor.City,
|
||||
dispatch.Vendor.State,
|
||||
dispatch.Vendor.Zip,
|
||||
dispatch.Vendor.TradeSpecialties
|
||||
},
|
||||
PrimaryLocation = dispatch.WorkOrder?.Locations == null ? null : new
|
||||
{
|
||||
dispatch.WorkOrder.Locations.Id,
|
||||
dispatch.WorkOrder.Locations.Name,
|
||||
dispatch.WorkOrder.Locations.Address1,
|
||||
dispatch.WorkOrder.Locations.City,
|
||||
dispatch.WorkOrder.Locations.State,
|
||||
dispatch.WorkOrder.Locations.Zip
|
||||
},
|
||||
WorkOrders = linkedWOs,
|
||||
Checklist = checklist,
|
||||
Signoffs = signoffs,
|
||||
Comments = comments,
|
||||
UpliftRequests = upliftRequests,
|
||||
AuditLog = auditLog
|
||||
}
|
||||
Data = dispatch
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@ using Api.SeaHavenIndustries.DTOs;
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
|
|
@ -17,16 +14,10 @@ using SeaHaven.DataServices.Interfaces;
|
|||
public class FollowUpController : ControllerBase
|
||||
{
|
||||
private readonly IFollowUpService _followUpService;
|
||||
private readonly IFollowUpDataService _followUpDataService; // Keep for direct entity operations
|
||||
private readonly ApplicationDbContext _db; // Keep for User lookups
|
||||
private readonly UserManager<ApplicationUser> _users;
|
||||
|
||||
public FollowUpController(IFollowUpService followUpService, IFollowUpDataService followUpDataService, ApplicationDbContext db, UserManager<ApplicationUser> users)
|
||||
public FollowUpController(IFollowUpService followUpService)
|
||||
{
|
||||
_followUpService = followUpService;
|
||||
_followUpDataService = followUpDataService;
|
||||
_db = db;
|
||||
_users = users;
|
||||
}
|
||||
|
||||
[HttpGet("GetFollowUpList")]
|
||||
|
|
@ -34,70 +25,31 @@ public class FollowUpController : ControllerBase
|
|||
string? search = "",
|
||||
string? status = "Open",
|
||||
int page = 1,
|
||||
int pageSize = 12)
|
||||
int pageSize = 12,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
|
||||
|
||||
var pagedResult = await _followUpService.GetFollowUpsPagedAsync(page, pageSize, search, status);
|
||||
var pagedResult = await _followUpService.GetFollowUpListAsync(page, pageSize, search, status, cancellationToken);
|
||||
var items = pagedResult.Items;
|
||||
|
||||
var rows = items.Select(x => new
|
||||
var data = items.Select(x => new
|
||||
{
|
||||
Id = x.Id,
|
||||
Reason = x.Reason,
|
||||
ScheduleDate = x.ScheduleDate,
|
||||
ScheduleStartTime = x.ScheduleStartTime,
|
||||
EmployeeId = x.EmployeeId,
|
||||
WorkOrderId = x.WorkOrderId,
|
||||
AccountId = x.AccountId,
|
||||
LocationId = x.LocationId,
|
||||
AccountName = x.AccountName ?? "",
|
||||
AssignedTo = x.EmployeeName ?? "",
|
||||
LocationName = x.LocationName ?? "",
|
||||
WorkOrderNumber = "",
|
||||
InternalWONumber = "",
|
||||
Status = x.Status,
|
||||
CreatedDate = x.CreatedDate,
|
||||
CreatedById = (string?)null
|
||||
}).ToList();
|
||||
|
||||
var creatorIds = rows
|
||||
.Select(r => r.CreatedById)
|
||||
.Where(id => !string.IsNullOrEmpty(id))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var creators = await _db.Users
|
||||
.Where(u => creatorIds.Contains(u.Id))
|
||||
.Select(u => new { u.Id, u.FirstName, u.LastName, u.UserName })
|
||||
.ToDictionaryAsync(u => u.Id, u => u);
|
||||
|
||||
string NameFor(string? userId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(userId) || !creators.TryGetValue(userId, out var u)) return "";
|
||||
var full = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim();
|
||||
return string.IsNullOrWhiteSpace(full) ? (u.UserName ?? "") : full;
|
||||
}
|
||||
|
||||
var data = rows.Select(r => new
|
||||
{
|
||||
r.Id,
|
||||
r.Reason,
|
||||
r.ScheduleDate,
|
||||
r.ScheduleStartTime,
|
||||
r.EmployeeId,
|
||||
r.WorkOrderId,
|
||||
r.AccountId,
|
||||
r.LocationId,
|
||||
r.AccountName,
|
||||
r.AssignedTo,
|
||||
r.LocationName,
|
||||
r.WorkOrderNumber,
|
||||
r.InternalWONumber,
|
||||
r.Status,
|
||||
r.CreatedDate,
|
||||
r.CreatedById,
|
||||
CreatedByName = NameFor(r.CreatedById)
|
||||
x.Id,
|
||||
x.Reason,
|
||||
x.ScheduleDate,
|
||||
x.ScheduleStartTime,
|
||||
x.EmployeeId,
|
||||
x.WorkOrderId,
|
||||
x.AccountId,
|
||||
x.LocationId,
|
||||
x.AccountName,
|
||||
x.AssignedTo,
|
||||
x.LocationName,
|
||||
x.WorkOrderNumber,
|
||||
x.InternalWONumber,
|
||||
x.Status,
|
||||
x.CreatedDate,
|
||||
x.CreatedById,
|
||||
x.CreatedByName
|
||||
}).ToList();
|
||||
|
||||
return Ok(new Pagination_DTO
|
||||
|
|
@ -111,26 +63,12 @@ public class FollowUpController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("GetById")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
public async Task<IActionResult> GetById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdWithDetailsAsync(id);
|
||||
var model = await _followUpService.GetFollowUpDetailAsync(id, cancellationToken);
|
||||
|
||||
if (model == null) return NotFound();
|
||||
|
||||
string createdByName = "";
|
||||
if (!string.IsNullOrEmpty(model.createdby))
|
||||
{
|
||||
var u = await _db.Users
|
||||
.Where(x => x.Id == model.createdby)
|
||||
.Select(x => new { x.FirstName, x.LastName, x.UserName })
|
||||
.FirstOrDefaultAsync();
|
||||
if (u != null)
|
||||
{
|
||||
var n = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim();
|
||||
createdByName = string.IsNullOrWhiteSpace(n) ? (u.UserName ?? "") : n;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
model.Id,
|
||||
|
|
@ -141,22 +79,20 @@ public class FollowUpController : ControllerBase
|
|||
model.WorkOrderId,
|
||||
model.AccountId,
|
||||
model.LocationId,
|
||||
AccountName = model.Account?.Name ?? "",
|
||||
AssignedTo = model.Employee != null
|
||||
? (model.Employee.FirstName + " " + model.Employee.LastName)
|
||||
: "",
|
||||
LocationName = model.Location?.Name ?? "",
|
||||
WorkOrderNumber = model.WorkOrder?.WorkerOrderNumber ?? "",
|
||||
InternalWONumber = model.WorkOrder?.InternalWONumber ?? "",
|
||||
model.AccountName,
|
||||
model.AssignedTo,
|
||||
model.LocationName,
|
||||
model.WorkOrderNumber,
|
||||
model.InternalWONumber,
|
||||
model.Status,
|
||||
model.CreatedDate,
|
||||
CreatedById = model.createdby,
|
||||
CreatedByName = createdByName
|
||||
model.CreatedById,
|
||||
model.CreatedByName
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> Create(FollowUp_DTO dto)
|
||||
public async Task<IActionResult> Create(FollowUp_DTO dto, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -164,23 +100,9 @@ public class FollowUpController : ControllerBase
|
|||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
var model = new FollowUps
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
ScheduleDate = dto.ScheduleDate,
|
||||
ScheduleStartTime = dto.ScheduleStartTime,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
WorkOrderId = dto.WorkOrderId,
|
||||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId,
|
||||
Status = "Open",
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
createdby = userId
|
||||
};
|
||||
var id = await _followUpService.CreateOpenFollowUpAsync(MapToCreateRequest(dto), userId, cancellationToken);
|
||||
|
||||
await _followUpDataService.AddAsync(model);
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up created", Data = model.Id });
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up created", Data = id });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
|
|
@ -194,24 +116,11 @@ public class FollowUpController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpPost("Update")]
|
||||
public async Task<IActionResult> Update(FollowUp_DTO dto)
|
||||
public async Task<IActionResult> Update(FollowUp_DTO dto, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(dto.Id);
|
||||
if (model == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Follow Up not found" });
|
||||
|
||||
model.Reason = dto.Reason;
|
||||
model.ScheduleDate = dto.ScheduleDate;
|
||||
model.ScheduleStartTime = dto.ScheduleStartTime;
|
||||
model.EmployeeId = dto.EmployeeId;
|
||||
model.WorkOrderId = dto.WorkOrderId;
|
||||
model.AccountId = dto.AccountId;
|
||||
model.LocationId = dto.LocationId;
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
await _followUpService.UpdateFollowUpFieldsAsync(dto.Id, MapToUpdateRequest(dto), cancellationToken);
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up updated" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
|
|
@ -230,42 +139,32 @@ public class FollowUpController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpPost("Complete")]
|
||||
public async Task<IActionResult> Complete(int id)
|
||||
public async Task<IActionResult> Complete(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null) return NotFound();
|
||||
|
||||
model.Status = "Completed";
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
var found = await _followUpService.SetFollowUpStatusAsync(id, "Completed", cancellationToken);
|
||||
if (!found) return NotFound();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("Cancel")]
|
||||
public async Task<IActionResult> Cancel(int id)
|
||||
public async Task<IActionResult> Cancel(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null) return NotFound();
|
||||
|
||||
model.Status = "Cancelled";
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
var found = await _followUpService.SetFollowUpStatusAsync(id, "Cancelled", cancellationToken);
|
||||
if (!found) return NotFound();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
public async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null)
|
||||
var found = await _followUpService.RemoveFollowUpAsync(id, cancellationToken);
|
||||
if (!found)
|
||||
return NotFound(new Response { Status = "Error", Message = "Follow Up not found" });
|
||||
|
||||
await _followUpDataService.DeleteAsync(id);
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up deleted" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
|
|
@ -277,4 +176,33 @@ public class FollowUpController : ControllerBase
|
|||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
private static FollowUpCreateRequestDTO MapToCreateRequest(FollowUp_DTO dto)
|
||||
{
|
||||
return new FollowUpCreateRequestDTO
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
ScheduleDate = dto.ScheduleDate,
|
||||
ScheduleStartTime = dto.ScheduleStartTime,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
WorkOrderId = dto.WorkOrderId,
|
||||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId
|
||||
};
|
||||
}
|
||||
|
||||
private static FollowUpUpdateRequestDTO MapToUpdateRequest(FollowUp_DTO dto)
|
||||
{
|
||||
return new FollowUpUpdateRequestDTO
|
||||
{
|
||||
Id = dto.Id,
|
||||
Reason = dto.Reason,
|
||||
ScheduleDate = dto.ScheduleDate,
|
||||
ScheduleStartTime = dto.ScheduleStartTime,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
WorkOrderId = dto.WorkOrderId,
|
||||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@ using Data.SeaHavenIndustries;
|
|||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using System.Security.Claims;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -18,18 +15,16 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
public class LocationController : Controller
|
||||
{
|
||||
private readonly ILocationService _locationService;
|
||||
private readonly ILocationDataService _locationDataService; // Keep for direct entity operations
|
||||
|
||||
public LocationController(ILocationService locationService, ILocationDataService locationDataService)
|
||||
public LocationController(ILocationService locationService)
|
||||
{
|
||||
_locationService = locationService;
|
||||
_locationDataService = locationDataService;
|
||||
}
|
||||
|
||||
[HttpGet("GetLocationList")]
|
||||
public async Task<IActionResult> GetLocationList(string? search = "", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetLocationList(string? search = "", int page = 1, int pageSize = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var pagedResult = await _locationService.GetLocationsPagedAsync(page, pageSize, search);
|
||||
var pagedResult = await _locationService.GetLocationListPagedAsync(page, pageSize, search, cancellationToken);
|
||||
|
||||
var data = pagedResult.Items.Select(l => new
|
||||
{
|
||||
|
|
@ -61,9 +56,9 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetLocationById(int id)
|
||||
public async Task<IActionResult> GetLocationById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var location = await _locationDataService.GetByIdAsync(id);
|
||||
var location = await _locationService.GetLocationDetailAsync(id, cancellationToken);
|
||||
|
||||
if (location == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
|
@ -89,25 +84,11 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddLocation([FromBody] Location_DTO model)
|
||||
public async Task<IActionResult> AddLocation([FromBody] Location_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var location = new Locations
|
||||
{
|
||||
Name = model.Name,
|
||||
Title = model.Title,
|
||||
Address1 = model.Address,
|
||||
City = model.City,
|
||||
State = model.State,
|
||||
Zip = model.ZipCode,
|
||||
PhoneNumber = model.Phone,
|
||||
Email = model.ContactEmail,
|
||||
Status = model.Status,
|
||||
CreatedDate = DateTime.Now
|
||||
};
|
||||
|
||||
await _locationDataService.AddAsync(location);
|
||||
await _locationService.CreateLocationFromRequestAsync(MapToCreateRequest(model), cancellationToken);
|
||||
return Ok(new DataResponse { Message = "Location Created Successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
|
|
@ -122,26 +103,11 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> EditLocation(int id, [FromBody] EditLocation_DTO model)
|
||||
public async Task<IActionResult> EditLocation(int id, [FromBody] EditLocation_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = await _locationDataService.GetByIdAsync(id);
|
||||
if (exist == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
||||
exist.Name = model.Name;
|
||||
exist.Title = model.Title;
|
||||
exist.Address1 = model.Address;
|
||||
exist.City = model.City;
|
||||
exist.State = model.State;
|
||||
exist.Zip = model.ZipCode;
|
||||
exist.PhoneNumber = model.Phone;
|
||||
exist.Email = model.ContactEmail;
|
||||
exist.Status = model.Status;
|
||||
exist.LastModificationTime = DateTime.Now;
|
||||
|
||||
await _locationDataService.UpdateAsync(exist);
|
||||
await _locationService.UpdateLocationFromRequestAsync(id, MapToUpdateRequest(model), cancellationToken);
|
||||
return Ok(new DataResponse { Message = "Location Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
|
|
@ -160,15 +126,14 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteLocation(int id)
|
||||
public async Task<IActionResult> DeleteLocation(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = await _locationDataService.GetByIdAsync(id);
|
||||
if (exist == null)
|
||||
var found = await _locationService.DeleteLocationByIdAsync(id, cancellationToken);
|
||||
if (!found)
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
||||
await _locationDataService.DeleteAsync(id);
|
||||
return Ok(new DataResponse { Message = "Location Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
|
|
@ -182,9 +147,41 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("DeleteLocation")]
|
||||
public async Task<IActionResult> DeleteLocationByQuery(int id)
|
||||
public async Task<IActionResult> DeleteLocationByQuery(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await DeleteLocation(id);
|
||||
return await DeleteLocation(id, cancellationToken);
|
||||
}
|
||||
|
||||
private static LocationCreateRequestDTO MapToCreateRequest(Location_DTO model)
|
||||
{
|
||||
return new LocationCreateRequestDTO
|
||||
{
|
||||
Name = model.Name,
|
||||
Title = model.Title,
|
||||
Address = model.Address,
|
||||
City = model.City,
|
||||
State = model.State,
|
||||
ZipCode = model.ZipCode,
|
||||
Phone = model.Phone,
|
||||
ContactEmail = model.ContactEmail,
|
||||
Status = model.Status
|
||||
};
|
||||
}
|
||||
|
||||
private static LocationUpdateRequestDTO MapToUpdateRequest(EditLocation_DTO model)
|
||||
{
|
||||
return new LocationUpdateRequestDTO
|
||||
{
|
||||
Name = model.Name,
|
||||
Title = model.Title,
|
||||
Address = model.Address,
|
||||
City = model.City,
|
||||
State = model.State,
|
||||
ZipCode = model.ZipCode,
|
||||
Phone = model.Phone,
|
||||
ContactEmail = model.ContactEmail,
|
||||
Status = model.Status
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@
|
|||
using Api.SeaHavenIndustries.Helper.ViewModel;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -14,24 +12,20 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("[controller]")]
|
||||
public class SettingsController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ApplicationDbContext _db;
|
||||
public SettingsController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
public SettingsController(ISettingsService settingsService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_db = db;
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("AddCategory")]
|
||||
public async Task<IActionResult> AddCategory(Category_DTO model)
|
||||
public async Task<IActionResult> AddCategory(Category_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Category category = new Category();
|
||||
category.Name = model.Name;
|
||||
_db.Categories.Add(category);
|
||||
await _db.SaveChangesAsync();
|
||||
await _settingsService.AddCategoryAsync(model.Name, cancellationToken);
|
||||
return Ok(new DataResponse { Message = "Successfully Saved Data", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -42,16 +36,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpPost]
|
||||
[Route("EditCategory")]
|
||||
public async Task<IActionResult> EditCategory(EditCategory_DTO model)
|
||||
public async Task<IActionResult> EditCategory(EditCategory_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Categories.Where(w => w.Id == model.Id).FirstOrDefault();
|
||||
if (exist == null)
|
||||
var found = await _settingsService.UpdateCategoryAsync(model.Id, model.Name, cancellationToken);
|
||||
if (!found)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Category not found" });
|
||||
exist.Name = model.Name;
|
||||
_db.Categories.Update(exist);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -62,30 +53,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("DeleteCategory")]
|
||||
public async Task<IActionResult> DeleteCategory(int id)
|
||||
public async Task<IActionResult> DeleteCategory(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Categories.Where(w => w.Id == id).FirstOrDefault();
|
||||
if (exist != null)
|
||||
{
|
||||
var chkexist = _db.Templates.Where(w => w.CategoryId == id).ToList();
|
||||
if (chkexist != null)
|
||||
{
|
||||
_db.RemoveRange(chkexist);
|
||||
}
|
||||
var chkworkordercategory = _db.workOrderCategories.Where(w => w.CategoryId == id).ToList();
|
||||
if (chkworkordercategory != null)
|
||||
{
|
||||
_db.RemoveRange(chkworkordercategory);
|
||||
}
|
||||
_db.Remove(exist);
|
||||
}
|
||||
else
|
||||
{
|
||||
var found = await _settingsService.DeleteCategoryAsync(id, cancellationToken);
|
||||
if (!found)
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Message = "Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -93,28 +67,21 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpGet]
|
||||
[Route("GetCategoryList")]
|
||||
public IActionResult GetCategoryList(int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetCategoryList(int page = 1, int pageSize = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var totalCount = _db.Categories.Count();
|
||||
var data = _db.Categories.Select(s => new
|
||||
{
|
||||
s.Id,
|
||||
s.Name
|
||||
}).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
var (items, total) = await _settingsService.GetCategoryListAsync(page, pageSize, cancellationToken);
|
||||
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
var totalPages = (int)Math.Ceiling(total / (double)pageSize);
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
Data = items,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalCount = total,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
return Ok(viewModel);
|
||||
|
|
@ -122,24 +89,20 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetTemplates")]
|
||||
public IActionResult GetTemplates(int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetTemplates(int page = 1, int pageSize = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var totalCount = _db.workOrders.Where(w=> w.istemplate == true).Count();
|
||||
var data = _db.workOrders.Where(w => w.istemplate == true).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
var (items, total) = await _settingsService.GetTemplatesAsync(page, pageSize, cancellationToken);
|
||||
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
var totalPages = (int)Math.Ceiling(total / (double)pageSize);
|
||||
|
||||
// Create a view model containing the paged items and pagination metadata
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
Data = items,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalCount = total,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
return Ok(viewModel);
|
||||
|
|
@ -152,42 +115,30 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetUsers")]
|
||||
public async Task<IActionResult> GetUsers(string? search = "", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetUsers(string? search = "", int page = 1, int pageSize = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var totalCount = _db.Users.Count();
|
||||
var allUsers = _db.Users.Where(w => (w.FirstName ?? "").ToLower().Contains((search ?? "").ToLower())).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
var (users, total) = await _settingsService.GetUsersAsync(search, page, pageSize, cancellationToken);
|
||||
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
||||
var filteredUsers = new List<UserRoleViewModel>();
|
||||
|
||||
foreach (var user in allUsers)
|
||||
var filteredUsers = users.Select(u => new UserRoleViewModel
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (!roles.Contains("Admin"))
|
||||
{
|
||||
filteredUsers.Add(new UserRoleViewModel
|
||||
{
|
||||
RoleName = roles.Count() > 0 ? roles.First() : "",
|
||||
Email = user.Email,
|
||||
UserName = user.FirstName,
|
||||
Date = user.CreatedDate?.Date.ToString(),
|
||||
Status = user.UniqueName,
|
||||
Id = user.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
// Create a view model containing the paged items and pagination metadata
|
||||
RoleName = u.RoleName,
|
||||
Email = u.Email,
|
||||
UserName = u.UserName,
|
||||
Date = u.Date,
|
||||
Status = u.Status,
|
||||
Id = u.Id
|
||||
}).ToList();
|
||||
|
||||
var totalPages = (int)Math.Ceiling(total / (double)pageSize);
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = filteredUsers,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalCount = total,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
return Ok(viewModel);
|
||||
|
|
@ -200,35 +151,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetRoles")]
|
||||
public async Task<IActionResult> GetRoles()
|
||||
public async Task<IActionResult> GetRoles(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _db.Roles.ToList();
|
||||
List<Roles_DTO> roles = new List<Roles_DTO>();
|
||||
foreach (var role in data)
|
||||
{
|
||||
if (role.Name == "Admin")
|
||||
{
|
||||
roles.Add(new Roles_DTO { Name = role.Name, Description = "Full Access" });
|
||||
}
|
||||
else if(role.Name == "Supervisor")
|
||||
{
|
||||
roles.Add(new Roles_DTO { Name = role.Name, Description = "Access to Team's Work Orders Only" });
|
||||
|
||||
}
|
||||
else if(role.Name == "Manager")
|
||||
{
|
||||
roles.Add(new Roles_DTO { Name = role.Name, Description = "Full access accept quotes" });
|
||||
|
||||
}
|
||||
else if(role.Name =="User")
|
||||
{
|
||||
roles.Add(new Roles_DTO { Name = role.Name, Description = "Access to Assigned Work Orders Only" });
|
||||
|
||||
}
|
||||
}
|
||||
return Ok(roles);
|
||||
var roles = await _settingsService.GetRolesAsync(cancellationToken);
|
||||
var result = roles.Select(r => new Roles_DTO { Name = r.Name, Description = r.Description }).ToList();
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
169
SeaHaven.DataServices/Dto/DispatchReadModels.cs
Normal file
169
SeaHaven.DataServices/Dto/DispatchReadModels.cs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
namespace SeaHaven.DataServices.Dto
|
||||
{
|
||||
public class DispatchListItemDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? DispatchNumber { get; set; }
|
||||
public string? PONumber { get; set; }
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public DateTime? DispatchedAt { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public DateTime? AcknowledgedAt { get; set; }
|
||||
public DateTime? VerifiedAt { get; set; }
|
||||
public int? VendorId { get; set; }
|
||||
public string? VendorName { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public string? PrimaryWOInternalNumber { get; set; }
|
||||
public string? PrimaryWOTitle { get; set; }
|
||||
public string? LocationName { get; set; }
|
||||
public int WorkOrderCount { get; set; }
|
||||
public int PendingUpliftCount { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchListResult
|
||||
{
|
||||
public IReadOnlyList<DispatchListItemDTO> Items { get; set; } = Array.Empty<DispatchListItemDTO>();
|
||||
public int Total { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchListQuery
|
||||
{
|
||||
public string? Search { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public int? VendorId { get; set; }
|
||||
public DateTime? DateFrom { get; set; }
|
||||
public DateTime? DateTo { get; set; }
|
||||
public string? SortBy { get; set; }
|
||||
public string? SortDir { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailVendorDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? CompanyName { get; set; }
|
||||
public string? ContactName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
public string? TradeSpecialties { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchPrimaryLocationDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Address1 { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchLinkedWorkOrderDTO
|
||||
{
|
||||
public int WorkOrderId { get; set; }
|
||||
public string? InternalWONumber { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? LocationName { get; set; }
|
||||
public string? Trade { get; set; }
|
||||
public string? SubTrade { get; set; }
|
||||
public string? Problem { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchChecklistDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? ItemText { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public string? CompletedBy { get; set; }
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchSignoffDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? SignoffType { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Signature { get; set; }
|
||||
public string? SignatureMethod { get; set; }
|
||||
public DateTime? SignedAt { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchCommentDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Commenttext { get; set; }
|
||||
public string? Commenter { get; set; }
|
||||
public string? CommentType { get; set; }
|
||||
public string? RecordType { get; set; }
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string UserName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DispatchUpliftRequestDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public decimal? CurrentNTE { get; set; }
|
||||
public decimal RequestedNTE { get; set; }
|
||||
public decimal Delta { get; set; }
|
||||
public string? VendorReason { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public int RequiredTier { get; set; }
|
||||
public string? RequestedByVendorName { get; set; }
|
||||
public DateTime? RequestedAt { get; set; }
|
||||
public DateTime? DecidedAt { get; set; }
|
||||
public string? DecisionNote { get; set; }
|
||||
public string DecidedByName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DispatchAuditLogDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Action { get; set; }
|
||||
public string? FieldName { get; set; }
|
||||
public string? OldValue { get; set; }
|
||||
public string? NewValue { get; set; }
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string UserName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DispatchDetailDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? DispatchNumber { get; set; }
|
||||
public string? PONumber { get; set; }
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public DateTime? DispatchedAt { get; set; }
|
||||
public DateTime? AcknowledgedAt { get; set; }
|
||||
public DateTime? VerifiedAt { get; set; }
|
||||
public string? VerifiedBy { get; set; }
|
||||
public bool EmailSent { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public DispatchDetailVendorDTO? Vendor { get; set; }
|
||||
public DispatchPrimaryLocationDTO? PrimaryLocation { get; set; }
|
||||
public IReadOnlyList<DispatchLinkedWorkOrderDTO> WorkOrders { get; set; } = Array.Empty<DispatchLinkedWorkOrderDTO>();
|
||||
public IReadOnlyList<DispatchChecklistDTO> Checklist { get; set; } = Array.Empty<DispatchChecklistDTO>();
|
||||
public IReadOnlyList<DispatchSignoffDTO> Signoffs { get; set; } = Array.Empty<DispatchSignoffDTO>();
|
||||
public IReadOnlyList<DispatchCommentDTO> Comments { get; set; } = Array.Empty<DispatchCommentDTO>();
|
||||
public IReadOnlyList<DispatchUpliftRequestDTO> UpliftRequests { get; set; } = Array.Empty<DispatchUpliftRequestDTO>();
|
||||
public IReadOnlyList<DispatchAuditLogDTO> AuditLog { get; set; } = Array.Empty<DispatchAuditLogDTO>();
|
||||
}
|
||||
}
|
||||
8
SeaHaven.DataServices/Dto/SettingsReadModels.cs
Normal file
8
SeaHaven.DataServices/Dto/SettingsReadModels.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace SeaHaven.DataServices.Dto
|
||||
{
|
||||
public class SettingsCategoryItemDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
|
|
@ -216,5 +217,346 @@ namespace SeaHaven.DataServices.Implementation
|
|||
await _context.DispatchWorkOrders.AddAsync(dispatchWorkOrder);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<DispatchListResult> GetListAsync(DispatchListQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var q = _context.Dispatches
|
||||
.Include(d => d.Vendor)
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.AsNoTracking()
|
||||
.Where(d => d.IsDeleted == null || d.IsDeleted == false);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query.Search))
|
||||
{
|
||||
var s = query.Search.ToLower();
|
||||
q = q.Where(d =>
|
||||
(d.DispatchNumber ?? "").ToLower().Contains(s) ||
|
||||
(d.PONumber ?? "").ToLower().Contains(s) ||
|
||||
(d.Vendor != null && (d.Vendor.CompanyName ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && (d.WorkOrder.InternalWONumber ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && (d.WorkOrder.WorkerOrderTitle ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && d.WorkOrder.Locations != null && (d.WorkOrder.Locations.Name ?? "").ToLower().Contains(s))
|
||||
);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query.Status))
|
||||
q = q.Where(d => d.Status == query.Status);
|
||||
|
||||
if (query.VendorId.HasValue)
|
||||
q = q.Where(d => d.VendorId == query.VendorId.Value);
|
||||
|
||||
if (query.DateFrom.HasValue)
|
||||
q = q.Where(d => d.DispatchedAt >= query.DateFrom.Value);
|
||||
|
||||
if (query.DateTo.HasValue)
|
||||
{
|
||||
var end = query.DateTo.Value.Date.AddDays(1);
|
||||
q = q.Where(d => d.DispatchedAt < end);
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(cancellationToken);
|
||||
|
||||
var dir = (query.SortDir ?? "desc").ToLowerInvariant() == "asc";
|
||||
switch ((query.SortBy ?? "").ToLowerInvariant())
|
||||
{
|
||||
case "ponumber":
|
||||
q = dir ? q.OrderBy(d => d.PONumber) : q.OrderByDescending(d => d.PONumber);
|
||||
break;
|
||||
case "dispatchnumber":
|
||||
q = dir ? q.OrderBy(d => d.DispatchNumber) : q.OrderByDescending(d => d.DispatchNumber);
|
||||
break;
|
||||
case "vendor":
|
||||
q = dir ? q.OrderBy(d => d.Vendor!.CompanyName) : q.OrderByDescending(d => d.Vendor!.CompanyName);
|
||||
break;
|
||||
case "nte":
|
||||
case "nteamount":
|
||||
q = dir ? q.OrderBy(d => d.NTEAmount) : q.OrderByDescending(d => d.NTEAmount);
|
||||
break;
|
||||
case "status":
|
||||
q = dir ? q.OrderBy(d => d.Status) : q.OrderByDescending(d => d.Status);
|
||||
break;
|
||||
case "scheduleddate":
|
||||
q = dir ? q.OrderBy(d => d.ScheduledDate) : q.OrderByDescending(d => d.ScheduledDate);
|
||||
break;
|
||||
case "completeddate":
|
||||
q = dir ? q.OrderBy(d => d.CompletedDate) : q.OrderByDescending(d => d.CompletedDate);
|
||||
break;
|
||||
default:
|
||||
q = dir ? q.OrderBy(d => d.DispatchedAt) : q.OrderByDescending(d => d.DispatchedAt);
|
||||
break;
|
||||
}
|
||||
|
||||
var rows = await q
|
||||
.Skip((Math.Max(query.Page, 1) - 1) * query.PageSize)
|
||||
.Take(query.PageSize)
|
||||
.Select(d => new DispatchListItemDTO
|
||||
{
|
||||
Id = d.Id,
|
||||
DispatchNumber = d.DispatchNumber,
|
||||
PONumber = d.PONumber,
|
||||
NTEAmount = d.NTEAmount,
|
||||
Status = d.Status,
|
||||
DispatchedAt = d.DispatchedAt,
|
||||
ScheduledDate = d.ScheduledDate,
|
||||
CompletedDate = d.CompletedDate,
|
||||
AcknowledgedAt = d.AcknowledgedAt,
|
||||
VerifiedAt = d.VerifiedAt,
|
||||
VendorId = d.VendorId,
|
||||
VendorName = d.Vendor != null ? d.Vendor.CompanyName : null,
|
||||
WorkOrderId = d.WorkOrderId,
|
||||
PrimaryWOInternalNumber = d.WorkOrder != null ? d.WorkOrder.InternalWONumber : null,
|
||||
PrimaryWOTitle = d.WorkOrder != null ? d.WorkOrder.WorkerOrderTitle : null,
|
||||
LocationName = d.WorkOrder != null && d.WorkOrder.Locations != null ? d.WorkOrder.Locations.Name : null,
|
||||
WorkOrderCount = _context.DispatchWorkOrders.Count(dwo => dwo.DispatchId == d.Id),
|
||||
PendingUpliftCount = _context.DispatchUpliftRequests.Count(u => u.DispatchId == d.Id && u.Status == "Pending" && (u.IsDeleted == null || u.IsDeleted == false))
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return new DispatchListResult
|
||||
{
|
||||
Items = rows,
|
||||
Total = total,
|
||||
Page = query.Page,
|
||||
PageSize = query.PageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<DispatchDetailDTO?> GetDetailAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var dispatch = await _context.Dispatches
|
||||
.Include(d => d.Vendor)
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(d => d.Id == id, cancellationToken);
|
||||
if (dispatch == null) return null;
|
||||
|
||||
var linked = await _context.DispatchWorkOrders
|
||||
.AsNoTracking()
|
||||
.Where(dwo => dwo.DispatchId == id)
|
||||
.Select(dwo => new DispatchLinkedWorkOrderDTO
|
||||
{
|
||||
WorkOrderId = dwo.WorkOrderId,
|
||||
InternalWONumber = dwo.WorkOrder != null ? dwo.WorkOrder.InternalWONumber : null,
|
||||
Title = dwo.WorkOrder != null ? dwo.WorkOrder.WorkerOrderTitle : null,
|
||||
Priority = dwo.WorkOrder != null ? dwo.WorkOrder.Priority : null,
|
||||
Status = dwo.WorkOrder != null ? dwo.WorkOrder.Status : null,
|
||||
LocationName = dwo.WorkOrder != null && dwo.WorkOrder.Locations != null ? dwo.WorkOrder.Locations.Name : null,
|
||||
Trade = dwo.WorkOrder != null ? dwo.WorkOrder.Trade : null,
|
||||
SubTrade = dwo.WorkOrder != null ? dwo.WorkOrder.SubTrade : null,
|
||||
Problem = dwo.WorkOrder != null ? dwo.WorkOrder.Problem : null
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var linkedWOs = linked;
|
||||
if (linkedWOs.Count == 0 && dispatch.WorkOrderId.HasValue)
|
||||
{
|
||||
var wo = await _context.workOrders
|
||||
.Include(w => w.Locations)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(w => w.Id == dispatch.WorkOrderId.Value, cancellationToken);
|
||||
if (wo != null)
|
||||
{
|
||||
linkedWOs = new List<DispatchLinkedWorkOrderDTO>
|
||||
{
|
||||
new DispatchLinkedWorkOrderDTO
|
||||
{
|
||||
WorkOrderId = wo.Id,
|
||||
InternalWONumber = wo.InternalWONumber,
|
||||
Title = wo.WorkerOrderTitle,
|
||||
Priority = wo.Priority,
|
||||
Status = wo.Status,
|
||||
LocationName = wo.Locations != null ? wo.Locations.Name : null,
|
||||
Trade = wo.Trade,
|
||||
SubTrade = wo.SubTrade,
|
||||
Problem = wo.Problem
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var checklist = await _context.DispatchChecklistItems
|
||||
.AsNoTracking()
|
||||
.Where(c => c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false))
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.Select(c => new DispatchChecklistDTO
|
||||
{
|
||||
Id = c.Id,
|
||||
ItemText = c.ItemText,
|
||||
IsCompleted = c.IsCompleted,
|
||||
CompletedBy = c.CompletedBy,
|
||||
CompletedAt = c.CompletedAt,
|
||||
SortOrder = c.SortOrder,
|
||||
WorkOrderId = c.WorkOrderId
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var signoffs = await _context.DispatchSignoffs
|
||||
.AsNoTracking()
|
||||
.Where(s => s.DispatchId == id && (s.IsDeleted == null || s.IsDeleted == false))
|
||||
.Select(s => new DispatchSignoffDTO
|
||||
{
|
||||
Id = s.Id,
|
||||
SignoffType = s.SignoffType,
|
||||
Name = s.Name,
|
||||
Signature = s.Signature,
|
||||
SignatureMethod = s.SignatureMethod,
|
||||
SignedAt = s.SignedAt
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var rawComments = await (from c in _context.Comments
|
||||
where c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false)
|
||||
join u in _context.Users on c.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby c.CreatedDate
|
||||
select new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
c.Commenter,
|
||||
c.CommentType,
|
||||
c.RecordType,
|
||||
c.CreatedDate,
|
||||
c.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var comments = rawComments.Select(c => new DispatchCommentDTO
|
||||
{
|
||||
Id = c.Id,
|
||||
Commenttext = c.Commenttext,
|
||||
Commenter = c.Commenter,
|
||||
CommentType = c.CommentType,
|
||||
RecordType = c.RecordType,
|
||||
CreatedDate = c.CreatedDate,
|
||||
UserId = c.UserId,
|
||||
UserName = string.Join(" ", new[] { c.UserFirstName, c.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var rawUplifts = await (from u in _context.DispatchUpliftRequests
|
||||
where u.DispatchId == id && (u.IsDeleted == null || u.IsDeleted == false)
|
||||
join dec in _context.Users on u.DecidedByUserId equals dec.Id into decs
|
||||
from dec in decs.DefaultIfEmpty()
|
||||
orderby u.CreatedDate descending
|
||||
select new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
RequestedAt = u.CreatedDate,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByFirstName = dec != null ? dec.FirstName : null,
|
||||
DecidedByLastName = dec != null ? dec.LastName : null
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var upliftRequests = rawUplifts.Select(u => new DispatchUpliftRequestDTO
|
||||
{
|
||||
Id = u.Id,
|
||||
CurrentNTE = u.CurrentNTE,
|
||||
RequestedNTE = u.RequestedNTE,
|
||||
Delta = u.RequestedNTE - (u.CurrentNTE ?? 0m),
|
||||
VendorReason = u.VendorReason,
|
||||
Status = u.Status,
|
||||
RequiredTier = u.RequiredTier,
|
||||
RequestedByVendorName = u.RequestedByVendorName,
|
||||
RequestedAt = u.RequestedAt,
|
||||
DecidedAt = u.DecidedAt,
|
||||
DecisionNote = u.DecisionNote,
|
||||
DecidedByName = string.Join(" ", new[] { u.DecidedByFirstName, u.DecidedByLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var dispatchNumber = dispatch.DispatchNumber ?? "";
|
||||
var workOrderIds = linkedWOs.Select(l => l.WorkOrderId).Where(wid => wid > 0).ToList();
|
||||
|
||||
var rawAudit = await (from a in _context.WorkOrderAuditLogs
|
||||
where workOrderIds.Contains(a.WorkOrderId)
|
||||
&& a.FieldName != null
|
||||
&& a.FieldName.Contains(dispatchNumber)
|
||||
join u in _context.Users on a.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby a.CreatedAt descending
|
||||
select new
|
||||
{
|
||||
a.Id,
|
||||
a.Action,
|
||||
a.FieldName,
|
||||
a.OldValue,
|
||||
a.NewValue,
|
||||
a.CreatedAt,
|
||||
a.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var auditLog = rawAudit.Select(a => new DispatchAuditLogDTO
|
||||
{
|
||||
Id = a.Id,
|
||||
Action = a.Action,
|
||||
FieldName = a.FieldName,
|
||||
OldValue = a.OldValue,
|
||||
NewValue = a.NewValue,
|
||||
CreatedAt = a.CreatedAt,
|
||||
UserId = a.UserId,
|
||||
UserName = string.Join(" ", new[] { a.UserFirstName, a.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
return new DispatchDetailDTO
|
||||
{
|
||||
Id = dispatch.Id,
|
||||
DispatchNumber = dispatch.DispatchNumber,
|
||||
PONumber = dispatch.PONumber,
|
||||
NTEAmount = dispatch.NTEAmount,
|
||||
Description = dispatch.Description,
|
||||
Status = dispatch.Status,
|
||||
ScheduledDate = dispatch.ScheduledDate,
|
||||
CompletedDate = dispatch.CompletedDate,
|
||||
DispatchedAt = dispatch.DispatchedAt,
|
||||
AcknowledgedAt = dispatch.AcknowledgedAt,
|
||||
VerifiedAt = dispatch.VerifiedAt,
|
||||
VerifiedBy = dispatch.VerifiedBy,
|
||||
EmailSent = dispatch.EmailSent,
|
||||
WorkOrderId = dispatch.WorkOrderId,
|
||||
Vendor = dispatch.Vendor == null ? null : new DispatchDetailVendorDTO
|
||||
{
|
||||
Id = dispatch.Vendor.Id,
|
||||
CompanyName = dispatch.Vendor.CompanyName,
|
||||
ContactName = dispatch.Vendor.ContactName,
|
||||
Email = dispatch.Vendor.Email,
|
||||
Phone = dispatch.Vendor.Phone,
|
||||
Address = dispatch.Vendor.Address,
|
||||
City = dispatch.Vendor.City,
|
||||
State = dispatch.Vendor.State,
|
||||
Zip = dispatch.Vendor.Zip,
|
||||
TradeSpecialties = dispatch.Vendor.TradeSpecialties
|
||||
},
|
||||
PrimaryLocation = dispatch.WorkOrder?.Locations == null ? null : new DispatchPrimaryLocationDTO
|
||||
{
|
||||
Id = dispatch.WorkOrder.Locations.Id,
|
||||
Name = dispatch.WorkOrder.Locations.Name,
|
||||
Address1 = dispatch.WorkOrder.Locations.Address1,
|
||||
City = dispatch.WorkOrder.Locations.City,
|
||||
State = dispatch.WorkOrder.Locations.State,
|
||||
Zip = dispatch.WorkOrder.Locations.Zip
|
||||
},
|
||||
WorkOrders = linkedWOs,
|
||||
Checklist = checklist,
|
||||
Signoffs = signoffs,
|
||||
Comments = comments,
|
||||
UpliftRequests = upliftRequests,
|
||||
AuditLog = auditLog
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,5 +120,138 @@ namespace SeaHaven.DataServices.Implementation
|
|||
{
|
||||
return await _context.FollowUps.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<List<FollowUps>> GetListPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search,
|
||||
string? status,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
search ??= "";
|
||||
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
|
||||
|
||||
var query = _context.FollowUps
|
||||
.Include(x => x.Employee)
|
||||
.Include(x => x.Account)
|
||||
.Include(x => x.Location)
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Status == status);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
query = query.Where(x =>
|
||||
(x.Reason ?? "").ToLower().Contains(s) ||
|
||||
(x.Account != null && (x.Account.Name ?? "").ToLower().Contains(s)) ||
|
||||
(x.Employee != null &&
|
||||
(((x.Employee.FirstName ?? "") + " " + (x.Employee.LastName ?? ""))
|
||||
.ToLower().Contains(s))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderByDescending(f => f.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<int> GetListTotalAsync(string? search, string? status, CancellationToken cancellationToken)
|
||||
{
|
||||
search ??= "";
|
||||
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
|
||||
|
||||
var query = _context.FollowUps
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Status == status);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
query = query.Where(x =>
|
||||
(x.Reason ?? "").ToLower().Contains(s) ||
|
||||
(x.Account != null && (x.Account.Name ?? "").ToLower().Contains(s)) ||
|
||||
(x.Employee != null &&
|
||||
(((x.Employee.FirstName ?? "") + " " + (x.Employee.LastName ?? ""))
|
||||
.ToLower().Contains(s))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return await query.CountAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<FollowUps?> GetDetailByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.FollowUps
|
||||
.Include(x => x.Employee)
|
||||
.Include(x => x.Account)
|
||||
.Include(x => x.Location)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(f => f.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<FollowUps?> GetByIdForUpdateAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.FollowUps.FindAsync(new object[] { id }, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<string> GetUserNameAsync(string? userId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
return "";
|
||||
|
||||
var u = await _context.Users
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Id == userId)
|
||||
.Select(x => new { x.FirstName, x.LastName, x.UserName })
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (u == null)
|
||||
return "";
|
||||
|
||||
var n = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim();
|
||||
return string.IsNullOrWhiteSpace(n) ? (u.UserName ?? "") : n;
|
||||
}
|
||||
|
||||
public async Task<FollowUps> AddAsync(FollowUps followUp, CancellationToken cancellationToken)
|
||||
{
|
||||
followUp.CreatedDate = DateTime.Now;
|
||||
await _context.FollowUps.AddAsync(followUp, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return followUp;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(FollowUps followUp, CancellationToken cancellationToken)
|
||||
{
|
||||
followUp.LastModificationTime = DateTime.Now;
|
||||
_context.FollowUps.Update(followUp);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> SetStatusAsync(int id, string status, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await _context.FollowUps.FindAsync(new object[] { id }, cancellationToken);
|
||||
if (entity == null)
|
||||
return false;
|
||||
|
||||
entity.Status = status;
|
||||
entity.LastModificationTime = DateTime.Now;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await _context.FollowUps.FindAsync(new object[] { id }, cancellationToken);
|
||||
if (entity == null)
|
||||
return false;
|
||||
|
||||
_context.FollowUps.Remove(entity);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,69 @@ namespace SeaHaven.DataServices.Implementation
|
|||
return await _context.Locations.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<(List<Locations> Items, int TotalCount)> GetListPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Locations.AsNoTracking();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
query = query.Where(l =>
|
||||
l.Name!.Contains(search) ||
|
||||
l.City!.Contains(search));
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
var items = await query
|
||||
.OrderBy(l => l.Name)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (items, totalCount);
|
||||
}
|
||||
|
||||
public async Task<Locations?> GetDetailByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Locations
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(l => l.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Locations?> GetByIdForUpdateAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Locations.FindAsync(new object[] { id }, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Locations> AddAsync(Locations location, CancellationToken cancellationToken)
|
||||
{
|
||||
location.CreatedDate = DateTime.Now;
|
||||
await _context.Locations.AddAsync(location, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return location;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Locations location, CancellationToken cancellationToken)
|
||||
{
|
||||
location.LastModificationTime = DateTime.Now;
|
||||
_context.Locations.Update(location);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await _context.Locations.FindAsync(new object[] { id }, cancellationToken);
|
||||
if (entity == null)
|
||||
return false;
|
||||
|
||||
_context.Locations.Remove(entity);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<object> Items, int TotalCount)> GetAddressbookPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
|
|
|
|||
125
SeaHaven.DataServices/Implementation/SettingsDataService.cs
Normal file
125
SeaHaven.DataServices/Implementation/SettingsDataService.cs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class SettingsDataService : ISettingsDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public SettingsDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task AddCategoryAsync(string name, CancellationToken cancellationToken)
|
||||
{
|
||||
var category = new Category { Name = name };
|
||||
await _context.Categories.AddAsync(category, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateCategoryAsync(int id, string name, CancellationToken cancellationToken)
|
||||
{
|
||||
var exist = await _context.Categories
|
||||
.Where(w => w.Id == id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (exist == null)
|
||||
return false;
|
||||
|
||||
exist.Name = name;
|
||||
_context.Categories.Update(exist);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteCategoryCascadeAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exist = await _context.Categories
|
||||
.Where(w => w.Id == id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (exist == null)
|
||||
return false;
|
||||
|
||||
var templates = await _context.Templates.Where(w => w.CategoryId == id).ToListAsync(cancellationToken);
|
||||
if (templates != null)
|
||||
{
|
||||
_context.RemoveRange(templates);
|
||||
}
|
||||
|
||||
var workOrderCategories = await _context.workOrderCategories.Where(w => w.CategoryId == id).ToListAsync(cancellationToken);
|
||||
if (workOrderCategories != null)
|
||||
{
|
||||
_context.RemoveRange(workOrderCategories);
|
||||
}
|
||||
|
||||
_context.Remove(exist);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<SettingsCategoryItemDTO> Items, int Total)> GetCategoryListPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var totalCount = await _context.Categories.CountAsync(cancellationToken);
|
||||
var data = await _context.Categories
|
||||
.AsNoTracking()
|
||||
.Select(s => new SettingsCategoryItemDTO
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name
|
||||
})
|
||||
.OrderByDescending(d => d.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (data, totalCount);
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<WorkOrder> Items, int Total)> GetTemplatesPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var totalCount = await _context.workOrders.CountAsync(w => w.istemplate == true, cancellationToken);
|
||||
var data = await _context.workOrders
|
||||
.AsNoTracking()
|
||||
.Where(w => w.istemplate == true)
|
||||
.OrderByDescending(d => d.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (data, totalCount);
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<ApplicationUser> Users, int Total)> GetUsersPagedAsync(
|
||||
string? search,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var totalCount = await _context.Users.CountAsync(cancellationToken);
|
||||
var users = await _context.Users
|
||||
.AsNoTracking()
|
||||
.Where(w => (w.FirstName ?? "").ToLower().Contains((search ?? "").ToLower()))
|
||||
.OrderByDescending(d => d.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (users, totalCount);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetRoleNamesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var roles = await _context.Roles.AsNoTracking().ToListAsync(cancellationToken);
|
||||
return roles.Select(r => r.Name!).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
|
|
@ -28,5 +29,8 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task<bool> HasSignoffTypeAsync(int dispatchId, string signoffType);
|
||||
Task<IEnumerable<DispatchWorkOrder>> GetDispatchWorkOrdersAsync(int dispatchId);
|
||||
Task AddDispatchWorkOrderAsync(DispatchWorkOrder dispatchWorkOrder);
|
||||
|
||||
Task<DispatchListResult> GetListAsync(DispatchListQuery query, CancellationToken cancellationToken);
|
||||
Task<DispatchDetailDTO?> GetDetailAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,15 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
Task<int> CountAsync();
|
||||
|
||||
Task<List<FollowUps>> GetListPagedAsync(int page, int pageSize, string? search, string? status, CancellationToken cancellationToken);
|
||||
Task<int> GetListTotalAsync(string? search, string? status, CancellationToken cancellationToken);
|
||||
Task<FollowUps?> GetDetailByIdAsync(int id, CancellationToken cancellationToken);
|
||||
Task<FollowUps?> GetByIdForUpdateAsync(int id, CancellationToken cancellationToken);
|
||||
Task<string> GetUserNameAsync(string? userId, CancellationToken cancellationToken);
|
||||
Task<FollowUps> AddAsync(FollowUps followUp, CancellationToken cancellationToken);
|
||||
Task UpdateAsync(FollowUps followUp, CancellationToken cancellationToken);
|
||||
Task<bool> SetStatusAsync(int id, string status, CancellationToken cancellationToken);
|
||||
Task<bool> RemoveByIdAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,5 +20,12 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
Task<int> CountAsync();
|
||||
|
||||
Task<(List<Locations> Items, int TotalCount)> GetListPagedAsync(int page, int pageSize, string? search, CancellationToken cancellationToken);
|
||||
Task<Locations?> GetDetailByIdAsync(int id, CancellationToken cancellationToken);
|
||||
Task<Locations?> GetByIdForUpdateAsync(int id, CancellationToken cancellationToken);
|
||||
Task<Locations> AddAsync(Locations location, CancellationToken cancellationToken);
|
||||
Task UpdateAsync(Locations location, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteByIdAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
SeaHaven.DataServices/Interfaces/ISettingsDataService.cs
Normal file
16
SeaHaven.DataServices/Interfaces/ISettingsDataService.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface ISettingsDataService
|
||||
{
|
||||
Task AddCategoryAsync(string name, CancellationToken cancellationToken);
|
||||
Task<bool> UpdateCategoryAsync(int id, string name, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteCategoryCascadeAsync(int id, CancellationToken cancellationToken);
|
||||
Task<(IReadOnlyList<SettingsCategoryItemDTO> Items, int Total)> GetCategoryListPagedAsync(int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<(IReadOnlyList<WorkOrder> Items, int Total)> GetTemplatesPagedAsync(int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<(IReadOnlyList<ApplicationUser> Users, int Total)> GetUsersPagedAsync(string? search, int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<string>> GetRoleNamesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,4 +27,158 @@ namespace SeaHaven.Services.DTOs
|
|||
public DateTime? CompletedDate { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchListEntryDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? DispatchNumber { get; set; }
|
||||
public string? PONumber { get; set; }
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public DateTime? DispatchedAt { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public DateTime? AcknowledgedAt { get; set; }
|
||||
public DateTime? VerifiedAt { get; set; }
|
||||
public int? VendorId { get; set; }
|
||||
public string? VendorName { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public string? PrimaryWOInternalNumber { get; set; }
|
||||
public string? PrimaryWOTitle { get; set; }
|
||||
public string? LocationName { get; set; }
|
||||
public int WorkOrderCount { get; set; }
|
||||
public int PendingUpliftCount { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchListResultDTO
|
||||
{
|
||||
public IReadOnlyList<DispatchListEntryDTO> Items { get; set; } = Array.Empty<DispatchListEntryDTO>();
|
||||
public int Total { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailVendorResultDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? CompanyName { get; set; }
|
||||
public string? ContactName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
public string? TradeSpecialties { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailLocationResultDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Address1 { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailLinkedWorkOrderDTO
|
||||
{
|
||||
public int WorkOrderId { get; set; }
|
||||
public string? InternalWONumber { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? LocationName { get; set; }
|
||||
public string? Trade { get; set; }
|
||||
public string? SubTrade { get; set; }
|
||||
public string? Problem { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailChecklistDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? ItemText { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public string? CompletedBy { get; set; }
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailSignoffDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? SignoffType { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Signature { get; set; }
|
||||
public string? SignatureMethod { get; set; }
|
||||
public DateTime? SignedAt { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchDetailCommentDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Commenttext { get; set; }
|
||||
public string? Commenter { get; set; }
|
||||
public string? CommentType { get; set; }
|
||||
public string? RecordType { get; set; }
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string UserName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DispatchDetailUpliftDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public decimal? CurrentNTE { get; set; }
|
||||
public decimal RequestedNTE { get; set; }
|
||||
public decimal Delta { get; set; }
|
||||
public string? VendorReason { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public int RequiredTier { get; set; }
|
||||
public string? RequestedByVendorName { get; set; }
|
||||
public DateTime? RequestedAt { get; set; }
|
||||
public DateTime? DecidedAt { get; set; }
|
||||
public string? DecisionNote { get; set; }
|
||||
public string DecidedByName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DispatchDetailAuditLogDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Action { get; set; }
|
||||
public string? FieldName { get; set; }
|
||||
public string? OldValue { get; set; }
|
||||
public string? NewValue { get; set; }
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string UserName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DispatchDetailResultDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? DispatchNumber { get; set; }
|
||||
public string? PONumber { get; set; }
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public DateTime? DispatchedAt { get; set; }
|
||||
public DateTime? AcknowledgedAt { get; set; }
|
||||
public DateTime? VerifiedAt { get; set; }
|
||||
public string? VerifiedBy { get; set; }
|
||||
public bool EmailSent { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public DispatchDetailVendorResultDTO? Vendor { get; set; }
|
||||
public DispatchDetailLocationResultDTO? PrimaryLocation { get; set; }
|
||||
public IReadOnlyList<DispatchDetailLinkedWorkOrderDTO> WorkOrders { get; set; } = Array.Empty<DispatchDetailLinkedWorkOrderDTO>();
|
||||
public IReadOnlyList<DispatchDetailChecklistDTO> Checklist { get; set; } = Array.Empty<DispatchDetailChecklistDTO>();
|
||||
public IReadOnlyList<DispatchDetailSignoffDTO> Signoffs { get; set; } = Array.Empty<DispatchDetailSignoffDTO>();
|
||||
public IReadOnlyList<DispatchDetailCommentDTO> Comments { get; set; } = Array.Empty<DispatchDetailCommentDTO>();
|
||||
public IReadOnlyList<DispatchDetailUpliftDTO> UpliftRequests { get; set; } = Array.Empty<DispatchDetailUpliftDTO>();
|
||||
public IReadOnlyList<DispatchDetailAuditLogDTO> AuditLog { get; set; } = Array.Empty<DispatchDetailAuditLogDTO>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,4 +40,69 @@ namespace SeaHaven.Services.DTOs
|
|||
public int? AccountId { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
}
|
||||
|
||||
public class FollowUpCreateRequestDTO
|
||||
{
|
||||
public string? Reason { get; set; }
|
||||
public DateTime ScheduleDate { get; set; }
|
||||
public TimeSpan ScheduleStartTime { get; set; }
|
||||
public int? EmployeeId { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
}
|
||||
|
||||
public class FollowUpUpdateRequestDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
public DateTime ScheduleDate { get; set; }
|
||||
public TimeSpan ScheduleStartTime { get; set; }
|
||||
public int? EmployeeId { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
}
|
||||
|
||||
public class FollowUpListRowDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
public DateTime ScheduleDate { get; set; }
|
||||
public TimeSpan ScheduleStartTime { get; set; }
|
||||
public int? EmployeeId { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public string AccountName { get; set; } = "";
|
||||
public string AssignedTo { get; set; } = "";
|
||||
public string LocationName { get; set; } = "";
|
||||
public string WorkOrderNumber { get; set; } = "";
|
||||
public string InternalWONumber { get; set; } = "";
|
||||
public string? Status { get; set; }
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? CreatedById { get; set; }
|
||||
public string CreatedByName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class FollowUpDetailDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
public DateTime ScheduleDate { get; set; }
|
||||
public TimeSpan ScheduleStartTime { get; set; }
|
||||
public int? EmployeeId { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public string AccountName { get; set; } = "";
|
||||
public string AssignedTo { get; set; } = "";
|
||||
public string LocationName { get; set; } = "";
|
||||
public string WorkOrderNumber { get; set; } = "";
|
||||
public string InternalWONumber { get; set; } = "";
|
||||
public string? Status { get; set; }
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? CreatedById { get; set; }
|
||||
public string CreatedByName { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,30 @@ namespace SeaHaven.Services.DTOs
|
|||
public string? Zipcode { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
}
|
||||
|
||||
public class LocationCreateRequestDTO
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? ZipCode { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? ContactEmail { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
|
||||
public class LocationUpdateRequestDTO
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? ZipCode { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? ContactEmail { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
98
SeaHaven.Services/DTOs/SettingsDTOs.cs
Normal file
98
SeaHaven.Services/DTOs/SettingsDTOs.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using Data.SeaHavenIndustries.Enums;
|
||||
|
||||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class RoleDTO
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
public class SettingsUserDTO
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? RoleName { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Date { get; set; }
|
||||
}
|
||||
|
||||
public class SettingsCategoryDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
|
||||
public class SettingsTemplateDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool? IsDeleted { get; set; }
|
||||
public string? createdby { get; set; }
|
||||
public string? DeleterUserId { get; set; }
|
||||
public DateTime? DeletionTime { get; set; }
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public DateTime? LastModificationTime { get; set; }
|
||||
public int? LastModifierUserId { get; set; }
|
||||
|
||||
public string? InternalWONumber { get; set; }
|
||||
public string? ExternalWorkOrderId { get; set; }
|
||||
public string? WorkerOrderNumber { get; set; }
|
||||
public string? WorkerOrderTitle { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public string? SiteCode { get; set; }
|
||||
public string? Building { get; set; }
|
||||
public string? Severity { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
public string? AssignTo { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public LifecycleStatus? LifecycleStatus { get; set; }
|
||||
public string? LegacyStatus { get; set; }
|
||||
public WorkOrderType? WorkOrderType { get; set; }
|
||||
public string? Service { get; set; }
|
||||
public int? PrimaryDispatchId { get; set; }
|
||||
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? ScheduledStart { get; set; }
|
||||
public DateTime? ScheduledEnd { get; set; }
|
||||
public DateOnly? TargetWeek { get; set; }
|
||||
public bool? ScheduleWeekOnly { get; set; }
|
||||
|
||||
public DateOnly? OriginalWeek { get; set; }
|
||||
public DateOnly? OriginalDate { get; set; }
|
||||
|
||||
public int RescheduleCount { get; set; }
|
||||
public int CarriedOver { get; set; }
|
||||
public OperationalFlags OperationalFlags { get; set; }
|
||||
|
||||
public DocStatus? DocStatus { get; set; }
|
||||
|
||||
public string? AvettaTask { get; set; }
|
||||
public DateOnly? AssignDate { get; set; }
|
||||
|
||||
public DateTime? DateReported { get; set; }
|
||||
public string? SourceEmailS3Key { get; set; }
|
||||
public string? Problem { get; set; }
|
||||
public string? Trade { get; set; }
|
||||
public string? SubTrade { get; set; }
|
||||
public decimal? VendorNTE { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public string? Source { get; set; }
|
||||
public bool? istemplate { get; set; }
|
||||
public string? PO { get; set; }
|
||||
public string? TT { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Attachments { get; set; }
|
||||
public string? BeforPhotoIssue { get; set; }
|
||||
public string? BeforPhotoAttachment { get; set; }
|
||||
public string? AfterPhotoIssue { get; set; }
|
||||
public string? AfterPhotoAttachment { get; set; }
|
||||
public string? SignOffName { get; set; }
|
||||
public string? SignOffAttachment { get; set; }
|
||||
public string? SignOffSignature { get; set; }
|
||||
|
||||
public byte[]? RowVersion { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
|
@ -141,6 +142,184 @@ namespace SeaHaven.Services.Implementation
|
|||
return await _dispatchDataService.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<DispatchListResultDTO> GetDispatchListAsync(
|
||||
string? search,
|
||||
string? status,
|
||||
int? vendorId,
|
||||
DateTime? dateFrom,
|
||||
DateTime? dateTo,
|
||||
string? sortBy,
|
||||
string? sortDir,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var query = new DispatchListQuery
|
||||
{
|
||||
Search = search,
|
||||
Status = status,
|
||||
VendorId = vendorId,
|
||||
DateFrom = dateFrom,
|
||||
DateTo = dateTo,
|
||||
SortBy = sortBy,
|
||||
SortDir = sortDir,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
|
||||
var result = await _dispatchDataService.GetListAsync(query, cancellationToken);
|
||||
|
||||
return new DispatchListResultDTO
|
||||
{
|
||||
Items = result.Items.Select(MapListEntry).ToList(),
|
||||
Total = result.Total,
|
||||
Page = result.Page,
|
||||
PageSize = result.PageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<DispatchDetailResultDTO?> GetDispatchDetailAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var detail = await _dispatchDataService.GetDetailAsync(id, cancellationToken);
|
||||
return detail == null ? null : MapDetail(detail);
|
||||
}
|
||||
|
||||
private static DispatchListEntryDTO MapListEntry(DispatchListItemDTO d)
|
||||
{
|
||||
return new DispatchListEntryDTO
|
||||
{
|
||||
Id = d.Id,
|
||||
DispatchNumber = d.DispatchNumber,
|
||||
PONumber = d.PONumber,
|
||||
NTEAmount = d.NTEAmount,
|
||||
Status = d.Status,
|
||||
DispatchedAt = d.DispatchedAt,
|
||||
ScheduledDate = d.ScheduledDate,
|
||||
CompletedDate = d.CompletedDate,
|
||||
AcknowledgedAt = d.AcknowledgedAt,
|
||||
VerifiedAt = d.VerifiedAt,
|
||||
VendorId = d.VendorId,
|
||||
VendorName = d.VendorName,
|
||||
WorkOrderId = d.WorkOrderId,
|
||||
PrimaryWOInternalNumber = d.PrimaryWOInternalNumber,
|
||||
PrimaryWOTitle = d.PrimaryWOTitle,
|
||||
LocationName = d.LocationName,
|
||||
WorkOrderCount = d.WorkOrderCount,
|
||||
PendingUpliftCount = d.PendingUpliftCount
|
||||
};
|
||||
}
|
||||
|
||||
private static DispatchDetailResultDTO MapDetail(DispatchDetailDTO d)
|
||||
{
|
||||
return new DispatchDetailResultDTO
|
||||
{
|
||||
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,
|
||||
AcknowledgedAt = d.AcknowledgedAt,
|
||||
VerifiedAt = d.VerifiedAt,
|
||||
VerifiedBy = d.VerifiedBy,
|
||||
EmailSent = d.EmailSent,
|
||||
WorkOrderId = d.WorkOrderId,
|
||||
Vendor = d.Vendor == null ? null : new DispatchDetailVendorResultDTO
|
||||
{
|
||||
Id = d.Vendor.Id,
|
||||
CompanyName = d.Vendor.CompanyName,
|
||||
ContactName = d.Vendor.ContactName,
|
||||
Email = d.Vendor.Email,
|
||||
Phone = d.Vendor.Phone,
|
||||
Address = d.Vendor.Address,
|
||||
City = d.Vendor.City,
|
||||
State = d.Vendor.State,
|
||||
Zip = d.Vendor.Zip,
|
||||
TradeSpecialties = d.Vendor.TradeSpecialties
|
||||
},
|
||||
PrimaryLocation = d.PrimaryLocation == null ? null : new DispatchDetailLocationResultDTO
|
||||
{
|
||||
Id = d.PrimaryLocation.Id,
|
||||
Name = d.PrimaryLocation.Name,
|
||||
Address1 = d.PrimaryLocation.Address1,
|
||||
City = d.PrimaryLocation.City,
|
||||
State = d.PrimaryLocation.State,
|
||||
Zip = d.PrimaryLocation.Zip
|
||||
},
|
||||
WorkOrders = d.WorkOrders.Select(w => new DispatchDetailLinkedWorkOrderDTO
|
||||
{
|
||||
WorkOrderId = w.WorkOrderId,
|
||||
InternalWONumber = w.InternalWONumber,
|
||||
Title = w.Title,
|
||||
Priority = w.Priority,
|
||||
Status = w.Status,
|
||||
LocationName = w.LocationName,
|
||||
Trade = w.Trade,
|
||||
SubTrade = w.SubTrade,
|
||||
Problem = w.Problem
|
||||
}).ToList(),
|
||||
Checklist = d.Checklist.Select(c => new DispatchDetailChecklistDTO
|
||||
{
|
||||
Id = c.Id,
|
||||
ItemText = c.ItemText,
|
||||
IsCompleted = c.IsCompleted,
|
||||
CompletedBy = c.CompletedBy,
|
||||
CompletedAt = c.CompletedAt,
|
||||
SortOrder = c.SortOrder,
|
||||
WorkOrderId = c.WorkOrderId
|
||||
}).ToList(),
|
||||
Signoffs = d.Signoffs.Select(s => new DispatchDetailSignoffDTO
|
||||
{
|
||||
Id = s.Id,
|
||||
SignoffType = s.SignoffType,
|
||||
Name = s.Name,
|
||||
Signature = s.Signature,
|
||||
SignatureMethod = s.SignatureMethod,
|
||||
SignedAt = s.SignedAt
|
||||
}).ToList(),
|
||||
Comments = d.Comments.Select(c => new DispatchDetailCommentDTO
|
||||
{
|
||||
Id = c.Id,
|
||||
Commenttext = c.Commenttext,
|
||||
Commenter = c.Commenter,
|
||||
CommentType = c.CommentType,
|
||||
RecordType = c.RecordType,
|
||||
CreatedDate = c.CreatedDate,
|
||||
UserId = c.UserId,
|
||||
UserName = c.UserName
|
||||
}).ToList(),
|
||||
UpliftRequests = d.UpliftRequests.Select(u => new DispatchDetailUpliftDTO
|
||||
{
|
||||
Id = u.Id,
|
||||
CurrentNTE = u.CurrentNTE,
|
||||
RequestedNTE = u.RequestedNTE,
|
||||
Delta = u.Delta,
|
||||
VendorReason = u.VendorReason,
|
||||
Status = u.Status,
|
||||
RequiredTier = u.RequiredTier,
|
||||
RequestedByVendorName = u.RequestedByVendorName,
|
||||
RequestedAt = u.RequestedAt,
|
||||
DecidedAt = u.DecidedAt,
|
||||
DecisionNote = u.DecisionNote,
|
||||
DecidedByName = u.DecidedByName
|
||||
}).ToList(),
|
||||
AuditLog = d.AuditLog.Select(a => new DispatchDetailAuditLogDTO
|
||||
{
|
||||
Id = a.Id,
|
||||
Action = a.Action,
|
||||
FieldName = a.FieldName,
|
||||
OldValue = a.OldValue,
|
||||
NewValue = a.NewValue,
|
||||
CreatedAt = a.CreatedAt,
|
||||
UserId = a.UserId,
|
||||
UserName = a.UserName
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private DispatchDTO MapToDTO(Dispatch dispatch)
|
||||
{
|
||||
return new DispatchDTO
|
||||
|
|
|
|||
|
|
@ -148,6 +148,131 @@ namespace SeaHaven.Services.Implementation
|
|||
return await _followUpDataService.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<PagedResult<FollowUpListRowDTO>> GetFollowUpListAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search,
|
||||
string? status,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
|
||||
|
||||
var items = await _followUpDataService.GetListPagedAsync(page, pageSize, search, status, cancellationToken);
|
||||
var totalCount = await _followUpDataService.GetListTotalAsync(search, status, cancellationToken);
|
||||
|
||||
return new PagedResult<FollowUpListRowDTO>
|
||||
{
|
||||
Items = items.Select(MapToListRow).ToList(),
|
||||
TotalCount = totalCount,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<FollowUpDetailDTO?> GetFollowUpDetailAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var followUp = await _followUpDataService.GetDetailByIdAsync(id, cancellationToken);
|
||||
if (followUp == null)
|
||||
return null;
|
||||
|
||||
var createdByName = await _followUpDataService.GetUserNameAsync(followUp.createdby, cancellationToken);
|
||||
|
||||
return new FollowUpDetailDTO
|
||||
{
|
||||
Id = followUp.Id,
|
||||
Reason = followUp.Reason,
|
||||
ScheduleDate = followUp.ScheduleDate,
|
||||
ScheduleStartTime = followUp.ScheduleStartTime,
|
||||
EmployeeId = followUp.EmployeeId,
|
||||
WorkOrderId = followUp.WorkOrderId,
|
||||
AccountId = followUp.AccountId,
|
||||
LocationId = followUp.LocationId,
|
||||
AccountName = followUp.Account?.Name ?? "",
|
||||
AssignedTo = followUp.Employee != null
|
||||
? $"{followUp.Employee.FirstName} {followUp.Employee.LastName}"
|
||||
: "",
|
||||
LocationName = followUp.Location?.Name ?? "",
|
||||
WorkOrderNumber = "",
|
||||
InternalWONumber = "",
|
||||
Status = followUp.Status,
|
||||
CreatedDate = followUp.CreatedDate,
|
||||
CreatedById = followUp.createdby,
|
||||
CreatedByName = createdByName
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<int> CreateOpenFollowUpAsync(FollowUpCreateRequestDTO dto, string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var followUp = new FollowUps
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
ScheduleDate = dto.ScheduleDate,
|
||||
ScheduleStartTime = dto.ScheduleStartTime,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
WorkOrderId = dto.WorkOrderId,
|
||||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId,
|
||||
Status = "Open",
|
||||
createdby = userId
|
||||
};
|
||||
|
||||
var created = await _followUpDataService.AddAsync(followUp, cancellationToken);
|
||||
return created.Id;
|
||||
}
|
||||
|
||||
public async Task UpdateFollowUpFieldsAsync(int id, FollowUpUpdateRequestDTO dto, CancellationToken cancellationToken)
|
||||
{
|
||||
var followUp = await _followUpDataService.GetByIdForUpdateAsync(id, cancellationToken);
|
||||
if (followUp == null)
|
||||
throw new KeyNotFoundException($"Follow-Up with ID {id} not found");
|
||||
|
||||
followUp.Reason = dto.Reason;
|
||||
followUp.ScheduleDate = dto.ScheduleDate;
|
||||
followUp.ScheduleStartTime = dto.ScheduleStartTime;
|
||||
followUp.EmployeeId = dto.EmployeeId;
|
||||
followUp.WorkOrderId = dto.WorkOrderId;
|
||||
followUp.AccountId = dto.AccountId;
|
||||
followUp.LocationId = dto.LocationId;
|
||||
|
||||
await _followUpDataService.UpdateAsync(followUp, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<bool> SetFollowUpStatusAsync(int id, string status, CancellationToken cancellationToken)
|
||||
{
|
||||
return _followUpDataService.SetStatusAsync(id, status, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<bool> RemoveFollowUpAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return _followUpDataService.RemoveByIdAsync(id, cancellationToken);
|
||||
}
|
||||
|
||||
private static FollowUpListRowDTO MapToListRow(FollowUps followUp)
|
||||
{
|
||||
return new FollowUpListRowDTO
|
||||
{
|
||||
Id = followUp.Id,
|
||||
Reason = followUp.Reason,
|
||||
ScheduleDate = followUp.ScheduleDate,
|
||||
ScheduleStartTime = followUp.ScheduleStartTime,
|
||||
EmployeeId = followUp.EmployeeId,
|
||||
WorkOrderId = followUp.WorkOrderId,
|
||||
AccountId = followUp.AccountId,
|
||||
LocationId = followUp.LocationId,
|
||||
AccountName = followUp.Account?.Name ?? "",
|
||||
AssignedTo = followUp.Employee != null
|
||||
? $"{followUp.Employee.FirstName} {followUp.Employee.LastName}"
|
||||
: "",
|
||||
LocationName = followUp.Location?.Name ?? "",
|
||||
WorkOrderNumber = "",
|
||||
InternalWONumber = "",
|
||||
Status = followUp.Status,
|
||||
CreatedDate = followUp.CreatedDate,
|
||||
CreatedById = null,
|
||||
CreatedByName = ""
|
||||
};
|
||||
}
|
||||
|
||||
private FollowUpDTO MapToDTO(FollowUps followUp)
|
||||
{
|
||||
return new FollowUpDTO
|
||||
|
|
|
|||
|
|
@ -139,6 +139,70 @@ namespace SeaHaven.Services.Implementation
|
|||
return await _locationDataService.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<PagedResult<LocationDTO>> GetLocationListPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var (items, totalCount) = await _locationDataService.GetListPagedAsync(page, pageSize, search, cancellationToken);
|
||||
return new PagedResult<LocationDTO>
|
||||
{
|
||||
Items = items.Select(MapToDTO).ToList(),
|
||||
TotalCount = totalCount,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<LocationDTO?> GetLocationDetailAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var location = await _locationDataService.GetDetailByIdAsync(id, cancellationToken);
|
||||
return location == null ? null : MapToDTO(location);
|
||||
}
|
||||
|
||||
public async Task CreateLocationFromRequestAsync(LocationCreateRequestDTO request, CancellationToken cancellationToken)
|
||||
{
|
||||
var location = new Locations
|
||||
{
|
||||
Name = request.Name,
|
||||
Title = request.Title,
|
||||
Address1 = request.Address,
|
||||
City = request.City,
|
||||
State = request.State,
|
||||
Zip = request.ZipCode,
|
||||
PhoneNumber = request.Phone,
|
||||
Email = request.ContactEmail,
|
||||
Status = request.Status
|
||||
};
|
||||
|
||||
await _locationDataService.AddAsync(location, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task UpdateLocationFromRequestAsync(int id, LocationUpdateRequestDTO request, CancellationToken cancellationToken)
|
||||
{
|
||||
var location = await _locationDataService.GetByIdForUpdateAsync(id, cancellationToken);
|
||||
if (location == null)
|
||||
throw new KeyNotFoundException($"Location with ID {id} not found");
|
||||
|
||||
location.Name = request.Name;
|
||||
location.Title = request.Title;
|
||||
location.Address1 = request.Address;
|
||||
location.City = request.City;
|
||||
location.State = request.State;
|
||||
location.Zip = request.ZipCode;
|
||||
location.PhoneNumber = request.Phone;
|
||||
location.Email = request.ContactEmail;
|
||||
location.Status = request.Status;
|
||||
|
||||
await _locationDataService.UpdateAsync(location, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<bool> DeleteLocationByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return _locationDataService.DeleteByIdAsync(id, cancellationToken);
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
private LocationDTO MapToDTO(Locations location)
|
||||
{
|
||||
|
|
|
|||
166
SeaHaven.Services/Implementation/SettingsService.cs
Normal file
166
SeaHaven.Services/Implementation/SettingsService.cs
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using SeaHaven.DataServices.Dto;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class SettingsService : ISettingsService
|
||||
{
|
||||
private readonly ISettingsDataService _dataService;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public SettingsService(ISettingsDataService dataService, UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_dataService = dataService;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task AddCategoryAsync(string? name, CancellationToken cancellationToken)
|
||||
{
|
||||
await _dataService.AddCategoryAsync(name ?? "", cancellationToken);
|
||||
}
|
||||
|
||||
public Task<bool> UpdateCategoryAsync(int? id, string? name, CancellationToken cancellationToken)
|
||||
{
|
||||
return _dataService.UpdateCategoryAsync(id ?? 0, name ?? "", cancellationToken);
|
||||
}
|
||||
|
||||
public Task<bool> DeleteCategoryAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return _dataService.DeleteCategoryCascadeAsync(id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<SettingsCategoryDTO> Items, int Total)> GetCategoryListAsync(int page, int pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var (items, total) = await _dataService.GetCategoryListPagedAsync(page, pageSize, cancellationToken);
|
||||
return (items.Select(c => new SettingsCategoryDTO { Id = c.Id, Name = c.Name }).ToList(), total);
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<SettingsTemplateDTO> Items, int Total)> GetTemplatesAsync(int page, int pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var (items, total) = await _dataService.GetTemplatesPagedAsync(page, pageSize, cancellationToken);
|
||||
return (items.Select(MapTemplate).ToList(), total);
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<SettingsUserDTO> Users, int Total)> GetUsersAsync(string? search, int page, int pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var (users, total) = await _dataService.GetUsersPagedAsync(search, page, pageSize, cancellationToken);
|
||||
|
||||
var filtered = new List<SettingsUserDTO>();
|
||||
foreach (var user in users)
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (!roles.Contains("Admin"))
|
||||
{
|
||||
filtered.Add(new SettingsUserDTO
|
||||
{
|
||||
RoleName = roles.Count() > 0 ? roles.First() : "",
|
||||
Email = user.Email,
|
||||
UserName = user.FirstName,
|
||||
Date = user.CreatedDate?.Date.ToString(),
|
||||
Status = user.UniqueName,
|
||||
Id = user.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (filtered, total);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<RoleDTO>> GetRolesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var names = await _dataService.GetRoleNamesAsync(cancellationToken);
|
||||
var roles = new List<RoleDTO>();
|
||||
foreach (var name in names)
|
||||
{
|
||||
if (name == "Admin")
|
||||
{
|
||||
roles.Add(new RoleDTO { Name = name, Description = "Full Access" });
|
||||
}
|
||||
else if (name == "Supervisor")
|
||||
{
|
||||
roles.Add(new RoleDTO { Name = name, Description = "Access to Team's Work Orders Only" });
|
||||
}
|
||||
else if (name == "Manager")
|
||||
{
|
||||
roles.Add(new RoleDTO { Name = name, Description = "Full access accept quotes" });
|
||||
}
|
||||
else if (name == "User")
|
||||
{
|
||||
roles.Add(new RoleDTO { Name = name, Description = "Access to Assigned Work Orders Only" });
|
||||
}
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
private static SettingsTemplateDTO MapTemplate(WorkOrder w)
|
||||
{
|
||||
return new SettingsTemplateDTO
|
||||
{
|
||||
Id = w.Id,
|
||||
IsDeleted = w.IsDeleted,
|
||||
createdby = w.createdby,
|
||||
DeleterUserId = w.DeleterUserId,
|
||||
DeletionTime = w.DeletionTime,
|
||||
CreatedDate = w.CreatedDate,
|
||||
LastModificationTime = w.LastModificationTime,
|
||||
LastModifierUserId = w.LastModifierUserId,
|
||||
InternalWONumber = w.InternalWONumber,
|
||||
ExternalWorkOrderId = w.ExternalWorkOrderId,
|
||||
WorkerOrderNumber = w.WorkerOrderNumber,
|
||||
WorkerOrderTitle = w.WorkerOrderTitle,
|
||||
Description = w.Description,
|
||||
Customer = w.Customer,
|
||||
SiteCode = w.SiteCode,
|
||||
Building = w.Building,
|
||||
Severity = w.Severity,
|
||||
DueDate = w.DueDate,
|
||||
AssignTo = w.AssignTo,
|
||||
LocationId = w.LocationId,
|
||||
LifecycleStatus = w.LifecycleStatus,
|
||||
LegacyStatus = w.LegacyStatus,
|
||||
WorkOrderType = w.WorkOrderType,
|
||||
Service = w.Service,
|
||||
PrimaryDispatchId = w.PrimaryDispatchId,
|
||||
ScheduledDate = w.ScheduledDate,
|
||||
ScheduledStart = w.ScheduledStart,
|
||||
ScheduledEnd = w.ScheduledEnd,
|
||||
TargetWeek = w.TargetWeek,
|
||||
ScheduleWeekOnly = w.ScheduleWeekOnly,
|
||||
OriginalWeek = w.OriginalWeek,
|
||||
OriginalDate = w.OriginalDate,
|
||||
RescheduleCount = w.RescheduleCount,
|
||||
CarriedOver = w.CarriedOver,
|
||||
OperationalFlags = w.OperationalFlags,
|
||||
DocStatus = w.DocStatus,
|
||||
AvettaTask = w.AvettaTask,
|
||||
AssignDate = w.AssignDate,
|
||||
DateReported = w.DateReported,
|
||||
SourceEmailS3Key = w.SourceEmailS3Key,
|
||||
Problem = w.Problem,
|
||||
Trade = w.Trade,
|
||||
SubTrade = w.SubTrade,
|
||||
VendorNTE = w.VendorNTE,
|
||||
CompletedDate = w.CompletedDate,
|
||||
Source = w.Source,
|
||||
istemplate = w.istemplate,
|
||||
PO = w.PO,
|
||||
TT = w.TT,
|
||||
Priority = w.Priority,
|
||||
Status = w.Status,
|
||||
Attachments = w.Attachments,
|
||||
BeforPhotoIssue = w.BeforPhotoIssue,
|
||||
BeforPhotoAttachment = w.BeforPhotoAttachment,
|
||||
AfterPhotoIssue = w.AfterPhotoIssue,
|
||||
AfterPhotoAttachment = w.AfterPhotoAttachment,
|
||||
SignOffName = w.SignOffName,
|
||||
SignOffAttachment = w.SignOffAttachment,
|
||||
SignOffSignature = w.SignOffSignature,
|
||||
RowVersion = w.RowVersion
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,5 +15,8 @@ namespace SeaHaven.Services.Interfaces
|
|||
Task<bool> DispatchExistsAsync(int id);
|
||||
Task<string> GenerateNextDispatchNumberAsync();
|
||||
Task<int> GetTotalDispatchCountAsync();
|
||||
|
||||
Task<DispatchListResultDTO> GetDispatchListAsync(string? search, string? status, int? vendorId, DateTime? dateFrom, DateTime? dateTo, string? sortBy, string? sortDir, int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<DispatchDetailResultDTO?> GetDispatchDetailAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,12 @@ namespace SeaHaven.Services.Interfaces
|
|||
Task DeleteFollowUpAsync(int id, string userId);
|
||||
Task<bool> FollowUpExistsAsync(int id);
|
||||
Task<int> GetTotalFollowUpCountAsync();
|
||||
|
||||
Task<PagedResult<FollowUpListRowDTO>> GetFollowUpListAsync(int page, int pageSize, string? search, string? status, CancellationToken cancellationToken);
|
||||
Task<FollowUpDetailDTO?> GetFollowUpDetailAsync(int id, CancellationToken cancellationToken);
|
||||
Task<int> CreateOpenFollowUpAsync(FollowUpCreateRequestDTO dto, string userId, CancellationToken cancellationToken);
|
||||
Task UpdateFollowUpFieldsAsync(int id, FollowUpUpdateRequestDTO dto, CancellationToken cancellationToken);
|
||||
Task<bool> SetFollowUpStatusAsync(int id, string status, CancellationToken cancellationToken);
|
||||
Task<bool> RemoveFollowUpAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,11 @@ namespace SeaHaven.Services.Interfaces
|
|||
Task DeleteLocationAsync(int id, string userId);
|
||||
Task<bool> LocationExistsAsync(int id);
|
||||
Task<int> GetTotalLocationCountAsync();
|
||||
|
||||
Task<PagedResult<LocationDTO>> GetLocationListPagedAsync(int page, int pageSize, string? search, CancellationToken cancellationToken);
|
||||
Task<LocationDTO?> GetLocationDetailAsync(int id, CancellationToken cancellationToken);
|
||||
Task CreateLocationFromRequestAsync(LocationCreateRequestDTO request, CancellationToken cancellationToken);
|
||||
Task UpdateLocationFromRequestAsync(int id, LocationUpdateRequestDTO request, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteLocationByIdAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
SeaHaven.Services/Interfaces/ISettingsService.cs
Normal file
15
SeaHaven.Services/Interfaces/ISettingsService.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Interfaces
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
Task AddCategoryAsync(string? name, CancellationToken cancellationToken);
|
||||
Task<bool> UpdateCategoryAsync(int? id, string? name, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteCategoryAsync(int id, CancellationToken cancellationToken);
|
||||
Task<(IReadOnlyList<SettingsCategoryDTO> Items, int Total)> GetCategoryListAsync(int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<(IReadOnlyList<SettingsTemplateDTO> Items, int Total)> GetTemplatesAsync(int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<(IReadOnlyList<SettingsUserDTO> Users, int Total)> GetUsersAsync(string? search, int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<RoleDTO>> GetRolesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue