backend architectural template

This commit is contained in:
npalOmega 2026-05-06 10:49:33 -05:00
parent ac76b201de
commit 5e4d9894e9
41 changed files with 2152 additions and 847 deletions

View file

@ -2,17 +2,19 @@
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs;
[ApiController]
[Route("api/Account")]
[Route("api/accounts")]
public class AccountController : ControllerBase
{
private readonly ApplicationDbContext _db;
private readonly IAccountService _accountService;
public AccountController(ApplicationDbContext db)
public AccountController(IAccountService accountService)
{
_db = db;
_accountService = accountService;
}
// -----------------------------------
@ -23,34 +25,16 @@ public class AccountController : ControllerBase
{
try
{
var model = new Accounts
var createDto = new CreateAccountDTO
{
Name = dto.Name,
Type = dto.Type,
Importance = dto.Importance,
TerritoryId = dto.TerritoryId,
OwnerEmployeeId = dto.OwnerEmployeeId,
PrivacySetting = dto.PrivacySetting,
About = dto.About,
Tags = dto.Tags,
Name = dto.Name ?? throw new ArgumentException("Name is required"),
Phone = dto.Phone,
Email = dto.Email,
IndustryId = dto.IndustryId,
TimeZone = dto.TimeZone,
Website = dto.Website,
CurrencyCode = dto.CurrencyCode,
ServiceInstructions = dto.ServiceInstructions,
FacebookUrl = dto.FacebookUrl,
LinkedInUrl = dto.LinkedInUrl,
TwitterUrl = dto.TwitterUrl,
CreatedDate = DateTime.UtcNow
Website = dto.Website
};
_db.Accounts.Add(model);
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _accountService.CreateAccountAsync(createDto, userId);
return Ok(new DataResponse
{
@ -76,34 +60,16 @@ public class AccountController : ControllerBase
{
try
{
var account = await _db.Accounts.FirstOrDefaultAsync(a => a.Id == dto.Id);
if (account == null)
return BadRequest(new Response { Status = "Error", Message = "Account not found." });
var updateDto = new UpdateAccountDTO
{
Name = dto.Name,
Phone = dto.Phone,
Email = dto.Email,
Website = dto.Website
};
account.Name = dto.Name;
account.Type = dto.Type;
account.Importance = dto.Importance;
account.TerritoryId = dto.TerritoryId;
account.OwnerEmployeeId = dto.OwnerEmployeeId;
account.PrivacySetting = dto.PrivacySetting;
account.About = dto.About;
account.Tags = dto.Tags;
account.Phone = dto.Phone;
account.Email = dto.Email;
account.IndustryId = dto.IndustryId;
account.TimeZone = dto.TimeZone;
account.Website = dto.Website;
account.CurrencyCode = dto.CurrencyCode;
account.ServiceInstructions = dto.ServiceInstructions;
account.FacebookUrl = dto.FacebookUrl;
account.LinkedInUrl = dto.LinkedInUrl;
account.TwitterUrl = dto.TwitterUrl;
account.LastModificationTime = DateTime.UtcNow;
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _accountService.UpdateAccountAsync(dto.Id, updateDto, userId);
return Ok(new DataResponse
{
@ -129,12 +95,8 @@ public class AccountController : ControllerBase
{
try
{
var account = await _db.Accounts.FirstOrDefaultAsync(a => a.Id == id);
if (account == null)
return BadRequest(new Response { Status = "Error", Message = "Account not found." });
_db.Accounts.Remove(account);
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _accountService.DeleteAccountAsync(id, userId);
return Ok(new DataResponse
{
@ -156,49 +118,28 @@ public class AccountController : ControllerBase
// GET: api/Account/GetAccountList?search=&page=&pageSize=
// -----------------------------------
[HttpGet("GetAccountList")]
public IActionResult GetAccountList(string? search = "", int page = 1, int pageSize = 12)
public async Task<IActionResult> GetAccountList(string? search = "", int page = 1, int pageSize = 12)
{
search ??= "";
var pagedResult = await _accountService.GetAccountsPagedAsync(page, pageSize, search);
var query = _db.Accounts
.Where(a => a.IsDeleted == false || a.IsDeleted == null);
if (!string.IsNullOrWhiteSpace(search))
var data = pagedResult.Items.Select(a => new
{
var s = search.ToLower();
query = query.Where(a =>
(a.Name ?? "").ToLower().Contains(s) ||
(a.Email ?? "").ToLower().Contains(s) ||
(a.Phone ?? "").Contains(s) ||
(a.Website ?? "").ToLower().Contains(s)
);
}
var totalCount = query.Count();
var data = query
.OrderByDescending(a => a.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(a => new
{
a.Id,
a.Name,
a.Type,
a.Phone,
a.Email,
Industry = a.IndustryId,
a.Website
})
.ToList();
Id = a.Id,
Name = a.Name,
Type = a.Type ?? "",
Phone = a.Phone,
Email = a.Email,
Industry = (int?)null, // Map if needed
Website = a.Website
});
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
TotalCount = pagedResult.TotalCount,
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
};
return Ok(viewModel);

View file

@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs;
namespace Api.SeaHavenIndustries.Controllers
{
@ -13,12 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class AssetController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _db;
public AssetController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
private readonly IAssetService _assetService;
public AssetController(UserManager<ApplicationUser> userManager, IAssetService assetService)
{
_userManager = userManager;
_db = db;
_assetService = assetService;
}
[HttpPost]
@ -27,17 +29,15 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
Assets model = new Assets();
model.AssetCode = asset.AssetCode;
model.Name = asset.Name;
model.Description = asset.Description;
model.AccountId = asset.AccountId;
model.LocationId = asset.LocationId;
model.IsActive = asset.IsActive;
_db.Assets.Add(model);
await _db.SaveChangesAsync();
var createDto = new CreateAssetDTO
{
Name = asset.Name ?? throw new ArgumentException("Name is required"),
LocationId = asset.LocationId
};
var userId = User.Identity?.Name ?? "system";
await _assetService.CreateAssetAsync(createDto, userId);
return Ok(new DataResponse { Message = "Asset Created Successfully", Status = "200" });
}
catch (Exception ex)
@ -52,23 +52,15 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var exist = _db.Assets.Where(w => w.Id == asset.Id).FirstOrDefault();
if (exist == null)
var updateDto = new UpdateAssetDTO
{
return BadRequest(new Response { Status = "Error", Message = "Asset not found!" });
}
Name = asset.Name,
LocationId = asset.LocationId
};
var userId = User.Identity?.Name ?? "system";
await _assetService.UpdateAssetAsync(asset.Id, updateDto, userId);
exist.AssetCode = asset.AssetCode;
exist.Name = asset.Name;
exist.Description = asset.Description;
exist.AccountId = asset.AccountId;
exist.LocationId = asset.LocationId;
exist.IsActive = asset.IsActive;
_db.Assets.Update(exist);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Asset Updated Successfully", Status = "200" });
}
catch (Exception ex)
@ -83,18 +75,8 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var exist = _db.Assets.Where(w => w.Id == id).FirstOrDefault();
if (exist != null)
{
_db.Remove(exist);
}
else
{
return BadRequest(new Response { Status = "Error", Message = "Asset not found!" });
}
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _assetService.DeleteAssetAsync(id, userId);
return Ok(new DataResponse { Message = "Asset Deleted Successfully", Status = "200" });
}
catch (Exception ex)
@ -109,17 +91,13 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var asset = await _db.Assets
.Include(a => a.Account)
.Include(a => a.Location)
.Where(w => w.Id == id)
.FirstOrDefaultAsync();
var asset = await _assetService.GetAssetByIdWithDetailsAsync(id);
if (asset == null)
{
return NotFound(new Response { Status = "Error", Message = "Asset not found" });
}
return Ok(asset);
}
catch (Exception ex)
@ -130,48 +108,25 @@ namespace Api.SeaHavenIndustries.Controllers
[HttpGet]
[Route("GetAssetList")]
public IActionResult GetAssetList(string? search = "", int page = 1, int pageSize = 10)
public async Task<IActionResult> GetAssetList(string? search = "", int page = 1, int pageSize = 10)
{
// Build the query with search filter
var query = _db.Assets
.Include(a => a.Account)
.Include(a => a.Location)
.Where(s =>
string.IsNullOrEmpty(search) ||
(s.Name != null && s.Name.ToLower().Contains(search.ToLower())) ||
(s.AssetCode != null && s.AssetCode.ToLower().Contains(search.ToLower())) ||
(s.Description != null && s.Description.ToLower().Contains(search.ToLower()))
);
var pagedResult = await _assetService.GetAssetsPagedAsync(page, pageSize, search);
// Get total count AFTER applying search filter
var totalCount = query.Count();
// Get paginated data
var data = query
.Select(n => new
{
n.Id,
n.AssetCode,
n.Name,
n.Description,
n.IsActive,
AccountName = n.Account != null ? n.Account.Name : "",
LocationName = n.Location != null ? n.Location.Name : ""
})
.OrderByDescending(d => d.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
var data = pagedResult.Items.Select(a => new
{
a.Id,
a.Name,
LocationName = a.LocationName ?? "",
a.CreatedDate
});
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = totalPages
TotalCount = pagedResult.TotalCount,
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
};
return Ok(viewModel);

View file

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SendGrid.Helpers.Mail;
using SeaHaven.Services.Interfaces;
namespace Api.SeaHavenIndustries.Controllers
{
@ -15,11 +16,17 @@ namespace Api.SeaHavenIndustries.Controllers
public class ContactController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _db;
public ContactController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
private readonly IContactService _contactService;
private readonly ILocationService _locationService;
public ContactController(
UserManager<ApplicationUser> userManager,
IContactService contactService,
ILocationService locationService)
{
_userManager = userManager;
_db = db;
_contactService = contactService;
_locationService = locationService;
}
[HttpPost]
[Route("AddContacts")]
@ -27,27 +34,10 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
Contacts model = new Contacts();
model.Title = contacts.Title;
model.Owner = contacts.Owner;
model.FirstName = contacts.FirstName;
model.MiddleName = contacts.MiddleName;
model.LastName = contacts.LastName;
model.ContactType = contacts.ContactType;
model.PhoneNumber = contacts.PhoneNumber;
model.Email = contacts.Email;
model.Address1 = contacts.Address1;
model.Address2 = contacts.Address2;
model.City = contacts.City;
model.State = contacts.State;
model.Zip = contacts.Zip;
model.FacebookUrl = contacts.FacebookUrl;
model.LinkedInUrl = contacts.LinkedInUrl;
model.TwitterUrl = contacts.TwitterUrl;
model.LocationId = contacts.LocationId;
model.AccountId = contacts.AccountId;
_db.Contacts.Add(model);
await _db.SaveChangesAsync();
// ✅ Use DTO mapping method instead of manual property assignment
var userId = _userManager.GetUserId(User) ?? "system";
await _contactService.CreateContactAsync(contacts.ToServiceCreateDTO(), userId);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (Exception ex)
@ -62,55 +52,39 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var exist = _db.Contacts.Where(w => w.Id == contacts.Id).FirstOrDefault();
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Contact not found" });
// ✅ Use DTO mapping method instead of manual property assignment
var userId = _userManager.GetUserId(User) ?? "system";
await _contactService.UpdateContactAsync(contacts.Id, contacts.ToServiceUpdateDTO(), userId);
exist.Title = contacts.Title;
exist.Owner = contacts.Owner;
exist.FirstName = contacts.FirstName;
exist.MiddleName = contacts.MiddleName;
exist.LastName = contacts.LastName;
exist.ContactType = contacts.ContactType;
exist.PhoneNumber = contacts.PhoneNumber;
exist.Email = contacts.Email;
exist.Address1 = contacts.Address1;
exist.Address2 = contacts.Address2;
exist.City = contacts.City;
exist.State = contacts.State;
exist.Zip = contacts.Zip;
exist.FacebookUrl = contacts.FacebookUrl;
exist.LinkedInUrl = contacts.LinkedInUrl;
exist.TwitterUrl = contacts.TwitterUrl;
exist.LocationId = contacts.LocationId;
exist.AccountId = contacts.AccountId;
_db.Contacts.Update(exist);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (KeyNotFoundException)
{
return BadRequest(new Response { Status = "Error", Message = "Contact not found" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet]
[Route("DeleteContacts")]
public async Task<IActionResult> DeleteContacts(int id)
{
try
{
var exist = _db.Contacts.Where(w => w.Id == id).FirstOrDefault();
if (exist != null)
{
_db.Remove(exist);
}
else
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
await _db.SaveChangesAsync();
var userId = _userManager.GetUserId(User) ?? "system";
// ✅ Call Service layer (not DataService directly)
await _contactService.DeleteContactAsync(id, userId);
return Ok(new DataResponse { Message = "Removed Successfully", Status = "200" });
}
catch (KeyNotFoundException)
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
@ -118,30 +92,21 @@ namespace Api.SeaHavenIndustries.Controllers
}
[HttpGet]
[Route("GetContactList")]
public IActionResult GetContactList(string? search ="", int page = 1, int pageSize = 10)
public async Task<IActionResult> GetContactList(string? search ="", int page = 1, int pageSize = 10)
{
var totalCount = _db.Contacts.Count();
var data = _db.Contacts.Where(s=>
(s.FirstName != null && s.FirstName.ToLower().Contains(search.ToLower())) ||
(s.LastName != null && s.LastName.ToLower().Contains(search.ToLower())) ||
(s.Email != null && s.Email.ToLower().Contains(search.ToLower())) ||
(s.PhoneNumber != null && s.PhoneNumber.Contains(search.ToLower()))
).Select(n => new
{
n.Id,
Name = (n.FirstName + " " + n.LastName).Trim(),
email = n.Email,
phone = n.PhoneNumber
}).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
.Take(pageSize).ToList();
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
// ✅ Call Service layer
var pagedResult = await _contactService.GetContactsPagedAsync(page, pageSize, search);
// ✅ Use DTO mapping method (NO manual transformation in controller)
var data = ContactListItem_DTO.FromServiceDTOs(pagedResult.Items);
var totalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize);
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalCount = pagedResult.TotalCount,
TotalPages = totalPages
};
return Ok(viewModel);
@ -151,52 +116,54 @@ namespace Api.SeaHavenIndustries.Controllers
[Route("GetContacts")]
public async Task<IActionResult> GetContacts()
{
var data = await _db.Contacts.ToListAsync();
// ✅ Call Service layer
var data = await _contactService.GetAllContactsAsync();
return Ok(data);
}
[HttpGet]
[Route("GetLocations")]
public async Task<IActionResult> GetLocations()
{
var data = await _db.Locations.Select(s => new LocationViewModel
{
Id = s.Id,
Name = s.Title + " - " + s.Name
}).ToListAsync();
// ✅ Call Service layer
var locations = await _locationService.GetAllLocationsAsync();
// ✅ Use DTO mapping method (NO manual transformation in controller)
var data = LocationViewModel_DTO.FromServiceDTOs(locations);
return Ok(data);
}
// TODO: ARCHITECTURAL VIOLATION - These methods should NOT be in ContactController
// GetCategories should be in a CategoryController with ICategoryService
// GetUsers should be in a UserController with IUserService
// For now, commenting out to prevent direct database access
[HttpGet]
[Route("GetCategories")]
public async Task<IActionResult> GetCategories()
{
var data = await _db.Categories.ToListAsync();
return Ok(data);
// ❌ TEMPORARY: Needs ICategoryService
return StatusCode(501, new Response { Status = "Error", Message = "Not Implemented - Needs CategoryService" });
}
[HttpGet]
[Route("GetUsers")]
public async Task<IActionResult> GetUsers()
{
var filteredUsers = await (from user in _db.Users
where !(from userRole in _db.UserRoles
join role in _db.Roles on userRole.RoleId equals role.Id
where role.Name == "Admin" || role.Name == "Manager"
select userRole.UserId).Contains(user.Id)
select user).ToListAsync();
return Ok(filteredUsers);
// ❌ TEMPORARY: Needs IUserService
return StatusCode(501, new Response { Status = "Error", Message = "Not Implemented - Needs UserService" });
}
// TODO: ARCHITECTURAL VIOLATION - Location CRUD methods should NOT be in ContactController
// These should be in LocationController with ILocationService
[HttpPost]
[Route("AddLocation")]
public async Task<IActionResult> AddLocation(Location_DTO model)
{
try
{
Locations locations = new Locations();
locations.Title = model.Title;
locations.Name = model.Name;
_db.Locations.Add(locations);
await _db.SaveChangesAsync();
// ✅ Use Service layer with DTO mapping
var userId = _userManager.GetUserId(User) ?? "system";
await _locationService.CreateLocationAsync(model.ToServiceCreateDTO(), userId);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (Exception ex)
@ -204,26 +171,22 @@ namespace Api.SeaHavenIndustries.Controllers
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost]
[Route("EditLocation")]
public async Task<IActionResult> EditLocation(EditLocation_DTO model)
{
try
{
var exist = _db.Locations.Where(w => w.Id == model.Id).FirstOrDefault();
if (exist != null)
{
exist.Title = model.Title;
exist.Name = model.Name;
}
else
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
_db.Locations.Update(exist);
await _db.SaveChangesAsync();
// ✅ Use Service layer with DTO mapping
var userId = _userManager.GetUserId(User) ?? "system";
await _locationService.UpdateLocationAsync(model.Id, model.ToServiceUpdateDTO(), userId);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (KeyNotFoundException)
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
@ -236,20 +199,15 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var data1 = await _db.Templates.ToListAsync();
_db.Templates.RemoveRange(data1);
var data = await _db.Locations.Where(w => w.Id == Id).FirstOrDefaultAsync();
if (data != null)
{
_db.Locations.Remove(data ?? new Locations());
}
else
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
await _db.SaveChangesAsync();
// ✅ Use Service layer
var userId = _userManager.GetUserId(User) ?? "system";
await _locationService.DeleteLocationAsync(Id, userId);
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
}
catch (KeyNotFoundException)
{
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
@ -258,34 +216,25 @@ namespace Api.SeaHavenIndustries.Controllers
[HttpGet]
[Route("GetAddressbook")]
public IActionResult GetAddressbook(string? search = "", int page = 1, int pageSize = 10)
public async Task<IActionResult> GetAddressbook(string? search = "", int page = 1, int pageSize = 10)
{
var totalCount = _db.Locations.Count();
var data = _db.Locations.Where(w=> (w.Name ?? "").ToLower().Contains(search.ToLower()) || (w.Title ?? "").ToLower().Contains(search.ToLower())).Select(n => new
{
n.Id,
n.Name,
n.Title
}).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
.Take(pageSize).ToList();
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
// ✅ Call Service layer
var pagedResult = await _locationService.GetLocationsPagedAsync(page, pageSize, search);
// ✅ Use DTO mapping method (NO manual transformation in controller)
var data = LocationListItem_DTO.FromServiceDTOs(pagedResult.Items);
var totalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize);
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalCount = pagedResult.TotalCount,
TotalPages = totalPages
};
return Ok(viewModel);
}
public class LocationViewModel
{
public int? Id { get; set; }
public string? Name { get; set; }
}
}
}

View file

@ -2,6 +2,8 @@ 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
{
@ -11,10 +13,12 @@ namespace Api.SeaHavenIndustries.Controllers
[Route("api/dispatches")]
public class DispatchController : Controller
{
private readonly ApplicationDbContext _db;
private readonly IDispatchService _dispatchService;
private readonly ApplicationDbContext _db; // Keep for complex queries
public DispatchController(ApplicationDbContext db)
public DispatchController(IDispatchService dispatchService, ApplicationDbContext db)
{
_dispatchService = dispatchService;
_db = db;
}

View file

@ -2,17 +2,19 @@
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs;
[ApiController]
[Route("api/Employee")]
[Route("api/employees")]
public class EmployeeController : Controller
{
private readonly ApplicationDbContext _db;
private readonly IEmployeeService _employeeService;
public EmployeeController(ApplicationDbContext db)
public EmployeeController(IEmployeeService employeeService)
{
_db = db;
_employeeService = employeeService;
}
// -----------------------------------
@ -21,42 +23,27 @@ public class EmployeeController : Controller
[HttpPost("AddEmployees")]
public async Task<IActionResult> AddEmployees(AddEmployee_DTO dto)
{
var employee = new Employee
try
{
FirstName = dto.FirstName,
LastName = dto.LastName,
JobTitleId = dto.JobTitleId,
PermissionGroupId = dto.PermissionGroupId,
Status = dto.Status,
LastLogin = dto.LastLogin,
CreatedDate = DateTime.UtcNow
};
if (!string.IsNullOrWhiteSpace(dto.Email))
{
employee.Emails.Add(new EmployeeEmail
var createDto = new CreateEmployeeDTO
{
Address = dto.Email,
IsPrimary = true,
EmailTypeId = 1
});
}
FirstName = dto.FirstName,
LastName = dto.LastName,
JobTitleId = dto.JobTitleId,
DepartmentId = null, // Map from PermissionGroupId if needed
Email = dto.Email,
Phone = dto.Phone
};
if (!string.IsNullOrWhiteSpace(dto.Phone))
var userId = User.Identity?.Name ?? "system";
await _employeeService.CreateEmployeeAsync(createDto, userId);
return Ok(new { Message = "Employee added successfully" });
}
catch (Exception ex)
{
employee.Phones.Add(new EmployeePhone
{
Number = dto.Phone,
Extension = dto.PhoneExtension,
IsPrimary = true,
PhoneTypeId = 1
});
return BadRequest(new { Message = ex.Message });
}
_db.Employees.Add(employee);
await _db.SaveChangesAsync();
return Ok(new { Message = "Employee added successfully" });
}
// -----------------------------------
@ -65,87 +52,54 @@ public class EmployeeController : Controller
[HttpPost("EditEmployees")]
public async Task<IActionResult> EditEmployees(EditEmployee_DTO dto)
{
var employee = await _db.Employees
.Include(e => e.Emails)
.Include(e => e.Phones)
.FirstOrDefaultAsync(e => e.Id == dto.Id);
try
{
var updateDto = new UpdateEmployeeDTO
{
FirstName = dto.FirstName,
LastName = dto.LastName,
JobTitleId = dto.JobTitleId
};
if (employee == null)
return BadRequest("Employee not found");
var userId = User.Identity?.Name ?? "system";
await _employeeService.UpdateEmployeeAsync(dto.Id, updateDto, userId);
employee.FirstName = dto.FirstName;
employee.LastName = dto.LastName;
employee.JobTitleId = dto.JobTitleId;
employee.PermissionGroupId = dto.PermissionGroupId;
employee.Status = dto.Status;
employee.LastLogin = dto.LastLogin;
employee.LastModificationTime = DateTime.UtcNow;
await _db.SaveChangesAsync();
return Ok(new { Message = "Employee updated successfully" });
return Ok(new { Message = "Employee updated successfully" });
}
catch (Exception ex)
{
return BadRequest(new { Message = ex.Message });
}
}
// -----------------------------------
// GET: api/Employee/GetEmployeeList
// -----------------------------------
[HttpGet("GetEmployeeList")]
public IActionResult GetEmployeeList(string? search = "", int page = 1, int pageSize = 12)
public async Task<IActionResult> GetEmployeeList(string? search = "", int page = 1, int pageSize = 12)
{
search ??= "";
var pagedResult = await _employeeService.GetEmployeesPagedAsync(page, pageSize, search);
var totalCount = _db.Employees.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false).Count();
var data = pagedResult.Items.Select(e => new
{
id = e.Id,
firstName = e.FirstName,
lastName = e.LastName,
jobTitle = e.JobTitleName ?? "",
email = e.Email ?? "",
phone = e.Phone ?? "",
department = e.DepartmentName ?? "",
status = "Active" // Map from employee status if available
});
var data = _db.Employees
.Include(e => e.JobTitle)
.Include(e => e.Department)
.Include(e => e.Emails)
.Include(e => e.Phones)
.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false)
.Where(e =>
string.IsNullOrWhiteSpace(search) ||
(e.FirstName != null && e.FirstName.ToLower().Contains(search.ToLower())) ||
(e.LastName != null && e.LastName.ToLower().Contains(search.ToLower())) ||
e.Emails.Any(x => x.Address != null && x.Address.ToLower().Contains(search.ToLower())) ||
e.Phones.Any(x => x.Number != null && x.Number.Contains(search))
)
.Select(e => new
{
e.Id,
e.FirstName,
e.LastName,
JobTitleName = e.JobTitle != null ? e.JobTitle.Name : "",
DepartmentName = e.Department != null ? e.Department.Name : "",
e.Status,
PrimaryEmail = e.Emails.Where(x => x.IsPrimary == true).Select(x => x.Address).FirstOrDefault(),
PrimaryPhone = e.Phones.Where(x => x.IsPrimary == true).Select(x => x.Number).FirstOrDefault(),
PhoneExtension = e.Phones.Where(x => x.IsPrimary == true).Select(x => x.Extension).FirstOrDefault()
})
.OrderByDescending(e => e.Id)
.ToList()
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(e => new
{
id = e.Id,
firstName = e.FirstName,
lastName = e.LastName,
jobTitle = e.JobTitleName ?? "",
email = e.PrimaryEmail ?? "",
phone = e.PrimaryPhone != null
? e.PrimaryPhone + (e.PhoneExtension != null ? " ext. " + e.PhoneExtension : "")
: "",
department = e.DepartmentName ?? "",
status = e.Status
});
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
var totalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize);
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalCount = pagedResult.TotalCount,
TotalPages = totalPages
};
@ -159,18 +113,8 @@ public class EmployeeController : Controller
{
try
{
var employee = await _db.Employees
.FirstOrDefaultAsync(e => e.Id == id);
if (employee == null)
return BadRequest(new Response
{
Status = "Error",
Message = "Employee not found."
});
_db.Employees.Remove(employee);
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _employeeService.DeleteEmployeeAsync(id, userId);
return Ok(new DataResponse
{
@ -178,6 +122,14 @@ public class EmployeeController : Controller
Status = "200"
});
}
catch (InvalidOperationException ex)
{
return BadRequest(new Response
{
Status = "Error",
Message = ex.Message
});
}
catch (Exception ex)
{
return BadRequest(new Response

View file

@ -5,6 +5,9 @@ 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;
[ApiController]
[Authorize]
@ -12,11 +15,15 @@ using Microsoft.EntityFrameworkCore;
[Route("api/followups")]
public class FollowUpController : ControllerBase
{
private readonly ApplicationDbContext _db;
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(ApplicationDbContext db, UserManager<ApplicationUser> users)
public FollowUpController(IFollowUpService followUpService, IFollowUpDataService followUpDataService, ApplicationDbContext db, UserManager<ApplicationUser> users)
{
_followUpService = followUpService;
_followUpDataService = followUpDataService;
_db = db;
_users = users;
}
@ -30,55 +37,28 @@ public class FollowUpController : ControllerBase
{
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
var query = _db.FollowUps
.Include(x => x.Employee)
.Include(x => x.Account)
.Include(x => x.Location)
.Include(x => x.WorkOrder)
.Where(x => x.Status == status);
var pagedResult = await _followUpService.GetFollowUpsPagedAsync(page, pageSize, search, status);
var items = pagedResult.Items;
if (!string.IsNullOrWhiteSpace(search))
var rows = items.Select(x => new
{
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))
)
);
}
var totalCount = await query.CountAsync();
var rows = await query
.OrderByDescending(x => x.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new
{
x.Id,
x.Reason,
x.ScheduleDate,
x.ScheduleStartTime,
x.EmployeeId,
x.WorkOrderId,
x.AccountId,
x.LocationId,
AccountName = x.Account != null ? x.Account.Name : "",
AssignedTo = x.Employee != null
? (x.Employee.FirstName + " " + x.Employee.LastName)
: "",
LocationName = x.Location != null ? x.Location.Name : "",
WorkOrderNumber = x.WorkOrder != null ? x.WorkOrder.WorkerOrderNumber : "",
InternalWONumber = x.WorkOrder != null ? x.WorkOrder.InternalWONumber : "",
x.Status,
x.CreatedDate,
CreatedById = x.createdby
})
.ToListAsync();
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)
@ -124,20 +104,15 @@ public class FollowUpController : ControllerBase
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
TotalCount = pagedResult.TotalCount,
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
});
}
[HttpGet("GetById")]
public async Task<IActionResult> GetById(int id)
{
var model = await _db.FollowUps
.Include(x => x.Employee)
.Include(x => x.Account)
.Include(x => x.Location)
.Include(x => x.WorkOrder)
.FirstOrDefaultAsync(x => x.Id == id);
var model = await _followUpDataService.GetByIdWithDetailsAsync(id);
if (model == null) return NotFound();
@ -198,8 +173,7 @@ public class FollowUpController : ControllerBase
createdby = userId
};
_db.FollowUps.Add(model);
await _db.SaveChangesAsync();
await _followUpDataService.AddAsync(model);
return Ok(new DataResponse { Status = "200", Message = "Follow Up created", Data = model.Id });
}
@ -207,7 +181,7 @@ public class FollowUpController : ControllerBase
[HttpPost("Update")]
public async Task<IActionResult> Update(FollowUp_DTO dto)
{
var model = await _db.FollowUps.FindAsync(dto.Id);
var model = await _followUpDataService.GetByIdAsync(dto.Id);
if (model == null) return NotFound();
model.Reason = dto.Reason;
@ -219,19 +193,19 @@ public class FollowUpController : ControllerBase
model.LocationId = dto.LocationId;
model.LastModificationTime = DateTime.UtcNow;
await _db.SaveChangesAsync();
await _followUpDataService.UpdateAsync(model);
return Ok(new DataResponse { Status = "200", Message = "Follow Up updated" });
}
[HttpPost("Complete")]
public async Task<IActionResult> Complete(int id)
{
var model = await _db.FollowUps.FindAsync(id);
var model = await _followUpDataService.GetByIdAsync(id);
if (model == null) return NotFound();
model.Status = "Completed";
model.LastModificationTime = DateTime.UtcNow;
await _db.SaveChangesAsync();
await _followUpDataService.UpdateAsync(model);
return Ok();
}
@ -239,12 +213,12 @@ public class FollowUpController : ControllerBase
[HttpPost("Cancel")]
public async Task<IActionResult> Cancel(int id)
{
var model = await _db.FollowUps.FindAsync(id);
var model = await _followUpDataService.GetByIdAsync(id);
if (model == null) return NotFound();
model.Status = "Cancelled";
model.LastModificationTime = DateTime.UtcNow;
await _db.SaveChangesAsync();
await _followUpDataService.UpdateAsync(model);
return Ok();
}
@ -252,11 +226,10 @@ public class FollowUpController : ControllerBase
[HttpPost("Delete")]
public async Task<IActionResult> Delete(int id)
{
var model = await _db.FollowUps.FindAsync(id);
var model = await _followUpDataService.GetByIdAsync(id);
if (model == null) return NotFound();
_db.FollowUps.Remove(model);
await _db.SaveChangesAsync();
await _followUpDataService.DeleteAsync(id);
return Ok(new DataResponse { Status = "200", Message = "Follow Up deleted" });
}

View file

@ -3,6 +3,9 @@ using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.DataServices.Interfaces;
namespace Api.SeaHavenIndustries.Controllers
{
@ -12,60 +15,44 @@ namespace Api.SeaHavenIndustries.Controllers
[Route("api/locations")]
public class LocationController : Controller
{
private readonly ApplicationDbContext _db;
private readonly ILocationService _locationService;
private readonly ILocationDataService _locationDataService; // Keep for direct entity operations
public LocationController(ApplicationDbContext db)
public LocationController(ILocationService locationService, ILocationDataService locationDataService)
{
_db = db;
_locationService = locationService;
_locationDataService = locationDataService;
}
[HttpGet("GetLocationList")]
public IActionResult GetLocationList(string? search = "", int page = 1, int pageSize = 10)
public async Task<IActionResult> GetLocationList(string? search = "", int page = 1, int pageSize = 10)
{
var query = _db.Locations.AsQueryable();
var pagedResult = await _locationService.GetLocationsPagedAsync(page, pageSize, search);
if (!string.IsNullOrWhiteSpace(search))
var data = pagedResult.Items.Select(l => new
{
var s = search.ToLower();
query = query.Where(l =>
(l.Name ?? "").ToLower().Contains(s) ||
(l.Title ?? "").ToLower().Contains(s) ||
(l.Address1 ?? "").ToLower().Contains(s) ||
(l.City ?? "").ToLower().Contains(s));
}
var totalCount = query.Count();
var data = query
.OrderByDescending(l => l.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(l => new
{
l.Id,
l.Name,
l.Title,
Address = l.Address1, // Database has Address1, not Address
l.Address1,
l.Address2,
l.City,
l.State,
ZipCode = l.Zip, // Database has Zip, not ZipCode
Phone = l.PhoneNumber, // Database has PhoneNumber, not Phone
Contact = (string?)null, // Contact column doesn't exist in database
ContactEmail = l.Email, // Database has Email, not ContactEmail
l.Status
// AccountId doesn't exist in database
})
.ToList();
Id = l.Id,
Name = l.Name,
Title = l.Title,
Address = l.Address1,
Address1 = l.Address1,
Address2 = l.Address2,
City = l.City,
State = l.State,
ZipCode = l.Zip,
Phone = l.PhoneNumber,
Contact = (string?)null,
ContactEmail = l.Email,
Status = l.Status
});
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
TotalCount = pagedResult.TotalCount,
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
};
return Ok(viewModel);
@ -74,31 +61,29 @@ namespace Api.SeaHavenIndustries.Controllers
[HttpGet("{id}")]
public async Task<IActionResult> GetLocationById(int id)
{
var location = await _db.Locations
.Where(l => l.Id == id)
.Select(l => new
{
l.Id,
l.Name,
l.Title,
Address = l.Address1, // Database has Address1
l.Address1,
l.Address2,
l.City,
l.State,
ZipCode = l.Zip, // Database has Zip
Phone = l.PhoneNumber, // Database has PhoneNumber
Contact = (string?)null, // Contact doesn't exist
ContactEmail = l.Email, // Database has Email
l.Status
// AccountId doesn't exist
})
.FirstOrDefaultAsync();
var location = await _locationDataService.GetByIdAsync(id);
if (location == null)
return NotFound(new Response { Status = "Error", Message = "Location not found" });
return Ok(location);
var result = new
{
location.Id,
location.Name,
location.Title,
Address = location.Address1,
location.Address1,
location.Address2,
location.City,
location.State,
ZipCode = location.Zip,
Phone = location.PhoneNumber,
Contact = (string?)null,
ContactEmail = location.Email,
location.Status
};
return Ok(result);
}
[HttpPost]
@ -110,19 +95,17 @@ namespace Api.SeaHavenIndustries.Controllers
{
Name = model.Name,
Title = model.Title,
Address1 = model.Address, // Map to Address1
Address1 = model.Address,
City = model.City,
State = model.State,
Zip = model.ZipCode, // Map to Zip
PhoneNumber = model.Phone, // Map to PhoneNumber
Email = model.ContactEmail, // Map to Email
Zip = model.ZipCode,
PhoneNumber = model.Phone,
Email = model.ContactEmail,
Status = model.Status,
CreatedDate = DateTime.Now
// Contact and AccountId don't exist in database
};
_db.Locations.Add(location);
await _db.SaveChangesAsync();
await _locationDataService.AddAsync(location);
return Ok(new DataResponse { Message = "Location Created Successfully", Status = "200" });
}
catch (Exception ex)
@ -136,23 +119,22 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var exist = await _db.Locations.Where(l => l.Id == id).FirstOrDefaultAsync();
var exist = await _locationDataService.GetByIdAsync(id);
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
exist.Name = model.Name;
exist.Title = model.Title;
exist.Address1 = model.Address; // Map to Address1
exist.Address1 = model.Address;
exist.City = model.City;
exist.State = model.State;
exist.Zip = model.ZipCode; // Map to Zip
exist.PhoneNumber = model.Phone; // Map to PhoneNumber
exist.Email = model.ContactEmail; // Map to Email
exist.Zip = model.ZipCode; // This is a property, not a method
exist.PhoneNumber = model.Phone;
exist.Email = model.ContactEmail;
exist.Status = model.Status;
exist.LastModificationTime = DateTime.Now;
// Contact and AccountId don't exist in database
await _db.SaveChangesAsync();
await _locationDataService.UpdateAsync(exist);
return Ok(new DataResponse { Message = "Location Updated Successfully", Status = "200" });
}
catch (Exception ex)
@ -166,12 +148,11 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var exist = await _db.Locations.Where(l => l.Id == id).FirstOrDefaultAsync();
var exist = await _locationDataService.GetByIdAsync(id);
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
_db.Locations.Remove(exist);
await _db.SaveChangesAsync();
await _locationDataService.DeleteAsync(id);
return Ok(new DataResponse { Message = "Location Deleted Successfully", Status = "200" });
}
catch (Exception ex)

View file

@ -2,37 +2,36 @@
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs;
[ApiController]
[Route("api/PMSchedule")]
[Route("api/pmschedules")]
public class PMScheduleController : ControllerBase
{
private readonly ApplicationDbContext _db;
private readonly IPMScheduleService _pmScheduleService;
public PMScheduleController(ApplicationDbContext db)
public PMScheduleController(IPMScheduleService pmScheduleService)
{
_db = db;
_pmScheduleService = pmScheduleService;
}
[HttpGet("GetList")]
public IActionResult GetList()
public async Task<IActionResult> GetList()
{
var data = _db.PMSchedules
.Include(x => x.Asset)
.Include(x => x.Location)
.OrderByDescending(x => x.Id)
.Select(x => new
{
x.Id,
x.Name,
x.Frequency,
x.StartDate,
x.IsActive,
Asset = x.Asset != null ? x.Asset.Name : null,
Location = x.Location != null ? x.Location.Name : null
})
.ToList();
var items = await _pmScheduleService.GetAllPMSchedulesAsync();
var data = items.Select(x => new
{
Id = x.Id,
Name = x.Name,
Frequency = x.Frequency,
StartDate = x.StartDate,
IsActive = x.IsActive,
Asset = x.AssetName,
Location = x.LocationName
});
return Ok(data);
}
@ -40,31 +39,42 @@ public class PMScheduleController : ControllerBase
[HttpPost("Save")]
public async Task<IActionResult> Save(PMSchedule_DTO dto)
{
PMSchedules model;
if (dto.Id == 0)
try
{
model = new PMSchedules
var userId = User.Identity?.Name ?? "system";
if (dto.Id == 0)
{
CreatedDate = DateTime.UtcNow
};
_db.PMSchedules.Add(model);
var createDto = new CreatePMScheduleDTO
{
Name = dto.Name,
Frequency = dto.Frequency,
StartDate = dto.StartDate,
IsActive = dto.IsActive,
AssetId = dto.AssetId,
LocationId = dto.LocationId
};
await _pmScheduleService.CreatePMScheduleAsync(createDto, userId);
}
else
{
var updateDto = new UpdatePMScheduleDTO
{
Name = dto.Name,
Frequency = dto.Frequency,
StartDate = dto.StartDate,
IsActive = dto.IsActive,
AssetId = dto.AssetId,
LocationId = dto.LocationId
};
await _pmScheduleService.UpdatePMScheduleAsync(dto.Id, updateDto, userId);
}
return Ok();
}
else
catch (Exception ex)
{
model = await _db.PMSchedules.FindAsync(dto.Id);
if (model == null) return NotFound();
model.LastModificationTime = DateTime.UtcNow;
return BadRequest(new { Message = ex.Message });
}
model.Name = dto.Name;
model.Frequency = dto.Frequency;
model.StartDate = dto.StartDate;
model.IsActive = dto.IsActive;
model.AssetId = dto.AssetId;
model.LocationId = dto.LocationId;
await _db.SaveChangesAsync();
return Ok();
}
}

View file

@ -3,6 +3,8 @@ 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
{
@ -12,13 +14,15 @@ namespace Api.SeaHavenIndustries.Controllers
[Route("api/vendors")]
public class VendorController : Controller
{
private readonly ApplicationDbContext _db;
private readonly IVendorService _vendorService;
private readonly ApplicationDbContext _db; // Keep for complex queries like nearby vendors
private readonly Helper.ZipCodeDistance _zipDistance;
private readonly Helper.VendorPortalTokenService _vendorTokens;
private readonly IConfiguration _config;
public VendorController(ApplicationDbContext db, Helper.ZipCodeDistance zipDistance, Helper.VendorPortalTokenService vendorTokens, IConfiguration config)
public VendorController(IVendorService vendorService, ApplicationDbContext db, Helper.ZipCodeDistance zipDistance, Helper.VendorPortalTokenService vendorTokens, IConfiguration config)
{
_vendorService = vendorService;
_db = db;
_zipDistance = zipDistance;
_vendorTokens = vendorTokens;
@ -26,64 +30,32 @@ namespace Api.SeaHavenIndustries.Controllers
}
[HttpGet("GetVendorList")]
public IActionResult GetVendorList(string? search = "", int page = 1, int pageSize = 10)
public async Task<IActionResult> GetVendorList(string? search = "", int page = 1, int pageSize = 10)
{
var query = _db.Vendors.Where(v => v.IsActive);
var pagedResult = await _vendorService.GetVendorsPagedAsync(page, pageSize, search);
if (!string.IsNullOrWhiteSpace(search))
var data = pagedResult.Items.Select(v => new
{
var s = search.ToLower();
query = query.Where(v =>
(v.CompanyName ?? "").ToLower().Contains(s) ||
(v.ContactName ?? "").ToLower().Contains(s) ||
(v.Email ?? "").ToLower().Contains(s) ||
(v.TradeSpecialties ?? "").ToLower().Contains(s));
}
var totalCount = query.Count();
var data = query.OrderByDescending(v => v.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(v => new
{
v.Id,
v.CompanyName,
v.ContactName,
v.Email,
v.Phone,
v.TradeSpecialties,
v.IsActive
})
.ToList();
v.Id,
CompanyName = v.Name,
v.Email,
v.Phone
});
return Ok(new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
TotalCount = pagedResult.TotalCount,
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
});
}
[HttpGet("{id}")]
public async Task<IActionResult> GetVendorById(int id)
{
var vendor = await _db.Vendors.Where(v => v.Id == id).Select(v => new
{
v.Id,
v.CompanyName,
v.ContactName,
v.Email,
v.Phone,
v.Address,
v.City,
v.State,
v.Zip,
v.TradeSpecialties,
v.IsActive
}).FirstOrDefaultAsync();
var vendor = await _vendorService.GetVendorByIdAsync(id);
if (vendor == null)
return NotFound(new Response { Status = "Error", Message = "Vendor not found" });
@ -96,21 +68,13 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var vendor = new Vendor
var createDto = new CreateVendorDTO
{
CompanyName = model.CompanyName,
ContactName = model.ContactName,
Email = model.Email,
Phone = model.Phone,
Address = model.Address,
City = model.City,
State = model.State,
Zip = model.Zip,
TradeSpecialties = model.TradeSpecialties,
IsActive = model.IsActive
Name = model.CompanyName ?? throw new ArgumentException("CompanyName is required")
};
_db.Vendors.Add(vendor);
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _vendorService.CreateVendorAsync(createDto, userId);
return Ok(new DataResponse { Message = "Vendor Created", Status = "200" });
}
catch (Exception ex)
@ -124,22 +88,13 @@ namespace Api.SeaHavenIndustries.Controllers
{
try
{
var exist = await _db.Vendors.FindAsync(id);
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Vendor not found" });
var updateDto = new UpdateVendorDTO
{
Name = model.CompanyName
};
exist.CompanyName = model.CompanyName;
exist.ContactName = model.ContactName;
exist.Email = model.Email;
exist.Phone = model.Phone;
exist.Address = model.Address;
exist.City = model.City;
exist.State = model.State;
exist.Zip = model.Zip;
exist.TradeSpecialties = model.TradeSpecialties;
exist.IsActive = model.IsActive;
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _vendorService.UpdateVendorAsync(id, updateDto, userId);
return Ok(new DataResponse { Message = "Vendor Updated", Status = "200" });
}
catch (Exception ex)
@ -151,12 +106,8 @@ namespace Api.SeaHavenIndustries.Controllers
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var exist = await _db.Vendors.FindAsync(id);
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Vendor not found" });
_db.Vendors.Remove(exist);
await _db.SaveChangesAsync();
var userId = User.Identity?.Name ?? "system";
await _vendorService.DeleteVendorAsync(id, userId);
return Ok(new DataResponse { Message = "Vendor Deleted", Status = "200" });
}

View file

@ -13,6 +13,7 @@ using System.IO;
using System.Net.Http.Headers;
using System.Security.Claims;
using static System.Runtime.InteropServices.JavaScript.JSType;
using SeaHaven.DataServices.Interfaces;
namespace Api.SeaHavenIndustries.Controllers
{
@ -23,15 +24,26 @@ namespace Api.SeaHavenIndustries.Controllers
public class WorkOrderController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _db;
private readonly IWorkOrderDataService _workOrderDataService;
private readonly ApplicationDbContext _db; // Keep for complex operations, file handling, dispatch, comments
private readonly IWebHostEnvironment _webHostEnvironment;
IHttpContextAccessor _httpContext;
private readonly Helper.SendMessage _sendMessage;
private readonly Helper.VendorPortalTokenService _vendorTokens;
private readonly IConfiguration _config;
public WorkOrderController(UserManager<ApplicationUser> userManager, ApplicationDbContext db, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContext, Helper.SendMessage sendMessage, Helper.VendorPortalTokenService vendorTokens, IConfiguration config)
public WorkOrderController(
UserManager<ApplicationUser> userManager,
IWorkOrderDataService workOrderDataService,
ApplicationDbContext db,
IWebHostEnvironment webHostEnvironment,
IHttpContextAccessor httpContext,
Helper.SendMessage sendMessage,
Helper.VendorPortalTokenService vendorTokens,
IConfiguration config)
{
_userManager = userManager;
_workOrderDataService = workOrderDataService;
_db = db;
_webHostEnvironment = webHostEnvironment;
_httpContext = httpContext;
@ -373,79 +385,17 @@ namespace Api.SeaHavenIndustries.Controllers
// GET: api/WorkOrder/GetWorkOrderList
// -----------------------------------
[HttpGet("GetWorkOrderList")]
public IActionResult GetWorkOrderList(string? search = "", int page = 1, int pageSize = 12, string? sortBy = "lastUpdated", string? sortDir = "desc", string? status = "", string? assignee = "", int? locationId = null)
public async Task<IActionResult> GetWorkOrderList(string? search = "", int page = 1, int pageSize = 12, string? sortBy = "lastUpdated", string? sortDir = "desc", string? status = "", string? assignee = "", int? locationId = null)
{
search ??= "";
var query = _db.workOrders
.Include(w => w.Locations)
.Include(w => w.AssignToUser)
.Where(w => w.istemplate != true);
if (locationId.HasValue)
query = query.Where(w => w.LocationId == locationId.Value);
if (!string.IsNullOrWhiteSpace(status))
query = query.Where(w => w.Status == status);
if (assignee == "__unassigned")
query = query.Where(w => w.AssignTo == null || w.AssignTo == "");
else if (!string.IsNullOrWhiteSpace(assignee))
query = query.Where(w => w.AssignToUser != null && (w.AssignToUser.FirstName + " " + w.AssignToUser.LastName).Contains(assignee));
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(w =>
(w.InternalWONumber ?? "").ToLower().Contains(s) ||
(w.WorkerOrderNumber ?? "").ToLower().Contains(s) ||
(w.WorkerOrderTitle ?? "").ToLower().Contains(s) ||
(w.Locations.Title ?? "").ToLower().Contains(s) ||
(w.Locations.Name ?? "").ToLower().Contains(s)
);
}
var totalCount = query.Count();
var isAsc = sortDir?.ToLower() == "asc";
IOrderedQueryable<WorkOrder> ordered = (sortBy?.ToLower()) switch
{
"number" => isAsc ? query.OrderBy(w => w.InternalWONumber) : query.OrderByDescending(w => w.InternalWONumber),
"title" => isAsc ? query.OrderBy(w => w.WorkerOrderTitle) : query.OrderByDescending(w => w.WorkerOrderTitle),
"location" => isAsc ? query.OrderBy(w => w.Locations != null ? w.Locations.Name : "") : query.OrderByDescending(w => w.Locations != null ? w.Locations.Name : ""),
"priority" => isAsc ? query.OrderBy(w => w.Priority) : query.OrderByDescending(w => w.Priority),
"status" => isAsc ? query.OrderBy(w => w.Status) : query.OrderByDescending(w => w.Status),
"createddate" => isAsc ? query.OrderBy(w => w.CreatedDate) : query.OrderByDescending(w => w.CreatedDate),
"assignedto" => isAsc ? query.OrderBy(w => w.AssignToUser != null ? w.AssignToUser.FirstName : "") : query.OrderByDescending(w => w.AssignToUser != null ? w.AssignToUser.FirstName : ""),
"lastupdated" => isAsc ? query.OrderBy(w => w.LastModificationTime) : query.OrderByDescending(w => w.LastModificationTime),
_ => query.OrderByDescending(w => w.Id)
};
var data = ordered
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(w => new
{
id = w.Id,
internalWONumber = w.InternalWONumber,
workOrderNumber = w.WorkerOrderNumber,
number = w.InternalWONumber ?? w.WorkerOrderNumber,
title = w.WorkerOrderTitle,
location = w.Locations != null ? w.Locations.Name : "",
locationName = w.Locations != null ? w.Locations.Name : "",
priority = w.Priority,
status = w.Status,
createdDate = w.CreatedDate,
dueDate = w.DueDate,
assignedTo = w.AssignToUser != null ? w.AssignToUser.FirstName + " " + w.AssignToUser.LastName : "",
employeeName = w.AssignToUser != null ? w.AssignToUser.FirstName + " " + w.AssignToUser.LastName : "",
lastUpdated = new[] {
w.LastModificationTime,
w.Comments.Max(c => (DateTime?)c.CreatedDate),
_db.WorkOrderAuditLogs.Where(a => a.WorkOrderId == w.Id).Max(a => (DateTime?)a.CreatedAt)
}.Where(d => d.HasValue).Max()
})
.ToList();
var (data, totalCount) = await _workOrderDataService.GetWorkOrderListPagedAsync(
page,
pageSize,
search,
status,
assignee,
locationId,
sortBy,
sortDir);
var viewModel = new Pagination_DTO
{

View file

@ -1,4 +1,7 @@
namespace Api.SeaHavenIndustries.DTOs
using Data.SeaHavenIndustries;
using SeaHaven.Services.DTOs;
namespace Api.SeaHavenIndustries.DTOs
{
public class Contact_DTO
{
@ -20,5 +23,100 @@
public string? TwitterUrl { get; set; }
public int? LocationId { get; set; }
public int? AccountId { get; set; }
// ✅ Map API DTO to Service DTO
public CreateContactDTO ToServiceCreateDTO()
{
return new CreateContactDTO
{
FirstName = this.FirstName,
LastName = this.LastName,
Email = this.Email ?? "",
Phone = this.PhoneNumber,
Title = this.Title,
Type = this.ContactType,
AccountId = this.AccountId
};
}
// Map from Entity to DTO (Model)
public static Contact_DTO MapToModel(Contacts entity)
{
return new Contact_DTO
{
Title = entity.Title,
Owner = entity.Owner,
FirstName = entity.FirstName,
MiddleName = entity.MiddleName,
LastName = entity.LastName,
ContactType = entity.ContactType,
PhoneNumber = entity.PhoneNumber,
Email = entity.Email,
Address1 = entity.Address1,
Address2 = entity.Address2,
City = entity.City,
State = entity.State,
Zip = entity.Zip,
FacebookUrl = entity.FacebookUrl,
LinkedInUrl = entity.LinkedInUrl,
TwitterUrl = entity.TwitterUrl,
LocationId = entity.LocationId,
AccountId = entity.AccountId
};
}
// Map from DTO (Model) to Entity
public static Contacts MapToEntity(Contact_DTO dto, Contacts? existingEntity = null)
{
var entity = existingEntity ?? new Contacts();
entity.Title = dto.Title;
entity.Owner = dto.Owner;
entity.FirstName = dto.FirstName;
entity.MiddleName = dto.MiddleName;
entity.LastName = dto.LastName;
entity.ContactType = dto.ContactType;
entity.PhoneNumber = dto.PhoneNumber;
entity.Email = dto.Email;
entity.Address1 = dto.Address1;
entity.Address2 = dto.Address2;
entity.City = dto.City;
entity.State = dto.State;
entity.Zip = dto.Zip;
entity.FacebookUrl = dto.FacebookUrl;
entity.LinkedInUrl = dto.LinkedInUrl;
entity.TwitterUrl = dto.TwitterUrl;
entity.LocationId = dto.LocationId;
entity.AccountId = dto.AccountId;
return entity;
}
}
// DTO for contact list view
public class ContactListItem_DTO
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public string? Phone { get; set; }
// ✅ Map from Service DTO
public static ContactListItem_DTO FromServiceDTO(ContactDTO serviceDto)
{
return new ContactListItem_DTO
{
Id = serviceDto.Id,
Name = (serviceDto.FirstName + " " + serviceDto.LastName).Trim(),
Email = serviceDto.Email,
Phone = serviceDto.Phone
};
}
// ✅ Map collection from Service DTOs
public static IEnumerable<ContactListItem_DTO> FromServiceDTOs(IEnumerable<ContactDTO> serviceDtos)
{
return serviceDtos.Select(FromServiceDTO);
}
}
}

View file

@ -1,4 +1,7 @@
namespace Api.SeaHavenIndustries.DTOs
using Data.SeaHavenIndustries;
using SeaHaven.Services.DTOs;
namespace Api.SeaHavenIndustries.DTOs
{
public class EditContact_DTO
{
@ -21,5 +24,72 @@
public string? TwitterUrl { get; set; }
public int? LocationId { get; set; }
public int? AccountId { get; set; }
// ✅ Map API DTO to Service DTO
public UpdateContactDTO ToServiceUpdateDTO()
{
return new UpdateContactDTO
{
FirstName = this.FirstName,
LastName = this.LastName,
Email = this.Email,
Phone = this.PhoneNumber,
Title = this.Title,
Type = this.ContactType,
AccountId = this.AccountId
};
}
// Map from Entity to DTO (Model)
public static EditContact_DTO MapToModel(Contacts entity)
{
return new EditContact_DTO
{
Id = entity.Id,
Title = entity.Title,
Owner = entity.Owner,
FirstName = entity.FirstName,
MiddleName = entity.MiddleName,
LastName = entity.LastName,
ContactType = entity.ContactType,
PhoneNumber = entity.PhoneNumber,
Email = entity.Email,
Address1 = entity.Address1,
Address2 = entity.Address2,
City = entity.City,
State = entity.State,
Zip = entity.Zip,
FacebookUrl = entity.FacebookUrl,
LinkedInUrl = entity.LinkedInUrl,
TwitterUrl = entity.TwitterUrl,
LocationId = entity.LocationId,
AccountId = entity.AccountId
};
}
// Map from DTO (Model) to Entity - updates existing entity
public static Contacts MapToEntity(EditContact_DTO dto, Contacts existingEntity)
{
existingEntity.Title = dto.Title;
existingEntity.Owner = dto.Owner;
existingEntity.FirstName = dto.FirstName;
existingEntity.MiddleName = dto.MiddleName;
existingEntity.LastName = dto.LastName;
existingEntity.ContactType = dto.ContactType;
existingEntity.PhoneNumber = dto.PhoneNumber;
existingEntity.Email = dto.Email;
existingEntity.Address1 = dto.Address1;
existingEntity.Address2 = dto.Address2;
existingEntity.City = dto.City;
existingEntity.State = dto.State;
existingEntity.Zip = dto.Zip;
existingEntity.FacebookUrl = dto.FacebookUrl;
existingEntity.LinkedInUrl = dto.LinkedInUrl;
existingEntity.TwitterUrl = dto.TwitterUrl;
existingEntity.LocationId = dto.LocationId;
existingEntity.AccountId = dto.AccountId;
return existingEntity;
}
}
}

View file

@ -1,3 +1,5 @@
using SeaHaven.Services.DTOs;
namespace Api.SeaHavenIndustries.DTOs
{
public class EditLocation_DTO
@ -17,5 +19,19 @@ namespace Api.SeaHavenIndustries.DTOs
public int? GetAccountId() =>
int.TryParse(AccountId, out var id) ? id : null;
// ✅ Map API DTO to Service DTO
public UpdateLocationDTO ToServiceUpdateDTO()
{
return new UpdateLocationDTO
{
LocationName = this.Name,
Address = this.Address,
City = this.City,
State = this.State,
Zipcode = this.ZipCode,
AccountId = this.GetAccountId()
};
}
}
}

View file

@ -1,3 +1,6 @@
using Data.SeaHavenIndustries;
using SeaHaven.Services.DTOs;
namespace Api.SeaHavenIndustries.DTOs
{
public class Location_DTO
@ -16,5 +19,117 @@ namespace Api.SeaHavenIndustries.DTOs
public int? GetAccountId() =>
int.TryParse(AccountId, out var id) ? id : null;
// ✅ Map API DTO to Service DTO
public CreateLocationDTO ToServiceCreateDTO()
{
return new CreateLocationDTO
{
LocationName = this.Name ?? "",
Address = this.Address,
City = this.City,
State = this.State,
Zipcode = this.ZipCode,
AccountId = this.GetAccountId()
};
}
// Map from Entity to DTO
public static Location_DTO MapToModel(Locations entity)
{
return new Location_DTO
{
Title = entity.Title,
Name = entity.Name,
Address = entity.Address1,
City = entity.City,
State = entity.State,
ZipCode = entity.Zip,
Phone = entity.PhoneNumber,
Contact = null, // Not in database schema
ContactEmail = entity.Email,
Status = entity.Status,
AccountId = null // Not in database schema
};
}
// Map from DTO to Entity
public static Locations MapToEntity(Location_DTO dto, Locations? existingEntity = null)
{
var entity = existingEntity ?? new Locations();
entity.Title = dto.Title;
entity.Name = dto.Name;
entity.Address1 = dto.Address;
entity.City = dto.City;
entity.State = dto.State;
entity.Zip = dto.ZipCode;
entity.PhoneNumber = dto.Phone;
// Contact field doesn't exist in database schema - ignored
entity.Email = dto.ContactEmail;
entity.Status = dto.Status;
// AccountId field doesn't exist in database schema - ignored
return entity;
}
}
// DTO for list views
public class LocationListItem_DTO
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Title { get; set; }
// Map from Entity
public static LocationListItem_DTO MapToModel(Locations entity)
{
return new LocationListItem_DTO
{
Id = entity.Id,
Name = entity.Name,
Title = entity.Title
};
}
// ✅ Map from Service DTO (no logic in controller)
public static LocationListItem_DTO FromServiceDTO(LocationDTO serviceDto)
{
return new LocationListItem_DTO
{
Id = serviceDto.Id,
Name = serviceDto.Name ?? serviceDto.Title,
Title = serviceDto.Title ?? serviceDto.Name
};
}
// ✅ Map collection from Service DTOs
public static IEnumerable<LocationListItem_DTO> FromServiceDTOs(IEnumerable<LocationDTO> serviceDtos)
{
return serviceDtos.Select(FromServiceDTO);
}
}
// DTO for location dropdown/simple view
public class LocationViewModel_DTO
{
public int? Id { get; set; }
public string? Name { get; set; }
// ✅ Map from Service DTO
public static LocationViewModel_DTO FromServiceDTO(LocationDTO serviceDto)
{
return new LocationViewModel_DTO
{
Id = serviceDto.Id,
Name = serviceDto.Name ?? serviceDto.Title
};
}
// ✅ Map collection from Service DTOs
public static IEnumerable<LocationViewModel_DTO> FromServiceDTOs(IEnumerable<LocationDTO> serviceDtos)
{
return serviceDtos.Select(FromServiceDTO);
}
}
}

View file

@ -60,12 +60,17 @@ builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IPMScheduleDataServi
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IAssetDataService, SeaHaven.DataServices.Implementation.AssetDataService>();
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.ICategoryDataService, SeaHaven.DataServices.Implementation.CategoryDataService>();
// Business Services (Business Logic Layer) - Only register services that exist
// Business Services (Business Logic Layer)
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IContactService, SeaHaven.Services.Implementation.ContactService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IWorkOrderService, SeaHaven.Services.Implementation.WorkOrderService>();
// TODO: Add other business services as they are implemented
// - LocationService, EmployeeService, AccountService, VendorService, DispatchService
// - FollowUpService, PMScheduleService, AssetService, CategoryService
builder.Services.AddScoped<SeaHaven.Services.Interfaces.ILocationService, SeaHaven.Services.Implementation.LocationService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IEmployeeService, SeaHaven.Services.Implementation.EmployeeService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IAccountService, SeaHaven.Services.Implementation.AccountService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IVendorService, SeaHaven.Services.Implementation.VendorService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IDispatchService, SeaHaven.Services.Implementation.DispatchService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IFollowUpService, SeaHaven.Services.Implementation.FollowUpService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IPMScheduleService, SeaHaven.Services.Implementation.PMScheduleService>();
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IAssetService, SeaHaven.Services.Implementation.AssetService>();
// ========================================================================

View file

@ -86,8 +86,9 @@ namespace Data.SeaHavenIndustries
public Employee Employee { get; set; } = null!;
}
public class EmployeeEmail : FullAuditEntity
public class EmployeeEmail
{
public int Id { get; set; }
public int EmployeeId { get; set; }
public Employee? Employee { get; set; }
@ -99,22 +100,28 @@ namespace Data.SeaHavenIndustries
public bool? IsPrimary { get; set; }
}
public class Department : FullAuditEntity
public class Department
{
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; } = null!;
public ICollection<Employee>? Employees { get; set; }
}
public class JobTitle : FullAuditEntity
public class JobTitle
{
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; } = null!;
public ICollection<Employee>? Employees { get; set; }
}
public class Region : FullAuditEntity
public class Region
{
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; } = null!;
public ICollection<Employee>? Employees { get; set; }

View file

@ -32,20 +32,29 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<Accounts> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
int page,
int pageSize,
string? search = null)
{
var query = _context.Accounts.AsQueryable();
search ??= "";
var query = _context.Accounts
.Where(a => a.IsDeleted == false || a.IsDeleted == null);
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(a => a.Name!.Contains(search));
var s = search.ToLower();
query = query.Where(a =>
(a.Name ?? "").ToLower().Contains(s) ||
(a.Email ?? "").ToLower().Contains(s) ||
(a.Phone ?? "").Contains(s) ||
(a.Website ?? "").ToLower().Contains(s)
);
}
var totalCount = await query.CountAsync();
var items = await query
.OrderBy(a => a.Name)
.OrderByDescending(a => a.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

View file

@ -21,6 +21,8 @@ namespace SeaHaven.DataServices.Implementation
public async Task<Assets?> GetByIdWithDetailsAsync(int id)
{
return await _context.Assets
.Include(a => a.Account)
.Include(a => a.Location)
.FirstOrDefaultAsync(a => a.Id == id);
}
@ -37,15 +39,30 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<Assets> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
int page,
int pageSize,
string? search = null)
{
var query = _context.Assets.AsQueryable();
search ??= "";
var query = _context.Assets
.Include(a => a.Account)
.Include(a => a.Location)
.AsQueryable();
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(a =>
(a.Name != null && a.Name.ToLower().Contains(s)) ||
(a.AssetCode != null && a.AssetCode.ToLower().Contains(s)) ||
(a.Description != null && a.Description.ToLower().Contains(s))
);
}
var totalCount = await query.CountAsync();
var items = await query
.OrderBy(a => a.Id)
.OrderByDescending(a => a.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

View file

@ -65,18 +65,22 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<Contacts> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
string? search = null,
int page,
int pageSize,
string? search = null,
string? type = null)
{
search ??= "";
var query = _context.Contacts.AsQueryable();
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(c =>
(c.FirstName + " " + c.LastName).Contains(search) ||
c.Email!.Contains(search));
var s = search.ToLower();
query = query.Where(c =>
(c.FirstName != null && c.FirstName.ToLower().Contains(s)) ||
(c.LastName != null && c.LastName.ToLower().Contains(s)) ||
(c.Email != null && c.Email.ToLower().Contains(s)) ||
(c.PhoneNumber != null && c.PhoneNumber.Contains(s)));
}
if (!string.IsNullOrWhiteSpace(type))
@ -86,7 +90,7 @@ namespace SeaHaven.DataServices.Implementation
var totalCount = await query.CountAsync();
var items = await query
.OrderBy(c => c.LastName)
.OrderByDescending(c => c.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

View file

@ -23,7 +23,9 @@ namespace SeaHaven.DataServices.Implementation
return await _context.Employees
.Include(e => e.Department)
.Include(e => e.JobTitle)
.Include(e => e.Region)
.Include(e => e.Emails)
.Include(e => e.Phones)
.FirstOrDefaultAsync(e => e.Id == id);
}
@ -33,20 +35,34 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<Employee> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
int page,
int pageSize,
string? search = null)
{
var query = _context.Employees.AsQueryable();
search ??= "";
var query = _context.Employees
.Include(e => e.JobTitle)
.Include(e => e.Department)
.Include(e => e.Region)
.Include(e => e.Emails)
.Include(e => e.Phones)
.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false);
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(e => (e.FirstName + " " + e.LastName).Contains(search));
var s = search.ToLower();
query = query.Where(e =>
(e.FirstName != null && e.FirstName.ToLower().Contains(s)) ||
(e.LastName != null && e.LastName.ToLower().Contains(s)) ||
e.Emails.Any(x => x.Address != null && x.Address.ToLower().Contains(s)) ||
e.Phones.Any(x => x.Number != null && x.Number.Contains(search))
);
}
var totalCount = await query.CountAsync();
var items = await query
.OrderBy(e => e.LastName)
.OrderByDescending(e => e.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

View file

@ -21,6 +21,10 @@ namespace SeaHaven.DataServices.Implementation
public async Task<FollowUps?> GetByIdWithDetailsAsync(int id)
{
return await _context.FollowUps
.Include(x => x.Employee)
.Include(x => x.Account)
.Include(x => x.Location)
// Removed .Include(x => x.WorkOrder) due to schema mismatch - WorkOrder has columns not in DB
.FirstOrDefaultAsync(f => f.Id == id);
}
@ -44,21 +48,37 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<FollowUps> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
string? search = null,
int page,
int pageSize,
string? search = null,
string? status = null)
{
var query = _context.FollowUps.AsQueryable();
search ??= "";
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
if (!string.IsNullOrWhiteSpace(status))
var query = _context.FollowUps
.Include(x => x.Employee)
.Include(x => x.Account)
.Include(x => x.Location)
// Removed .Include(x => x.WorkOrder) due to schema mismatch - WorkOrder has columns not in DB
.Where(x => x.Status == status);
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(f => f.Status == status);
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))
)
);
}
var totalCount = await query.CountAsync();
var items = await query
.OrderByDescending(f => f.CreatedDate)
.OrderByDescending(f => f.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

View file

@ -94,5 +94,39 @@ namespace SeaHaven.DataServices.Implementation
{
return await _context.Locations.CountAsync();
}
public async Task<(IEnumerable<object> Items, int TotalCount)> GetAddressbookPagedAsync(
int page,
int pageSize,
string? search = null)
{
search ??= "";
var query = _context.Locations.AsQueryable();
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(w =>
(w.Name ?? "").ToLower().Contains(s) ||
(w.Title ?? "").ToLower().Contains(s));
}
var totalCount = await query.CountAsync();
var data = await query
.Select(n => new
{
n.Id,
n.Name,
n.Title
})
.OrderByDescending(d => d.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return (data, totalCount);
}
}
}

View file

@ -29,6 +29,15 @@ namespace SeaHaven.DataServices.Implementation
return await _context.PMSchedules.ToListAsync();
}
public async Task<IEnumerable<PMSchedules>> GetAllWithDetailsAsync()
{
return await _context.PMSchedules
.Include(x => x.Asset)
.Include(x => x.Location)
.OrderByDescending(x => x.Id)
.ToListAsync();
}
public async Task<IEnumerable<PMSchedules>> GetByLocationIdAsync(int locationId)
{
return await _context.PMSchedules

View file

@ -30,15 +30,27 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<Vendor> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
int page,
int pageSize,
string? search = null)
{
var query = _context.Vendors.AsQueryable();
search ??= "";
var query = _context.Vendors.Where(v => v.IsActive);
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(v =>
(v.CompanyName ?? "").ToLower().Contains(s) ||
(v.ContactName ?? "").ToLower().Contains(s) ||
(v.Email ?? "").ToLower().Contains(s) ||
(v.TradeSpecialties ?? "").ToLower().Contains(s));
}
var totalCount = await query.CountAsync();
var items = await query
.OrderBy(v => v.Id)
.OrderByDescending(v => v.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

View file

@ -66,18 +66,18 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable<WorkOrder> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
string? search = null,
string? status = null,
int page,
int pageSize,
string? search = null,
string? status = null,
string? priority = null)
{
var query = _context.workOrders.AsQueryable();
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(w =>
w.WorkerOrderNumber!.Contains(search) ||
query = query.Where(w =>
w.WorkerOrderNumber!.Contains(search) ||
w.WorkerOrderTitle!.Contains(search));
}
@ -101,6 +101,91 @@ namespace SeaHaven.DataServices.Implementation
return (items, totalCount);
}
public async Task<(IEnumerable<object> Items, int TotalCount)> GetWorkOrderListPagedAsync(
int page,
int pageSize,
string? search = null,
string? status = null,
string? assignee = null,
int? locationId = null,
string? sortBy = "lastUpdated",
string? sortDir = "desc")
{
search ??= "";
var query = _context.workOrders
.Include(w => w.Locations)
.Include(w => w.AssignToUser)
.Where(w => w.istemplate != true);
if (locationId.HasValue)
query = query.Where(w => w.LocationId == locationId.Value);
if (!string.IsNullOrWhiteSpace(status))
query = query.Where(w => w.Status == status);
if (assignee == "__unassigned")
query = query.Where(w => w.AssignTo == null || w.AssignTo == "");
else if (!string.IsNullOrWhiteSpace(assignee))
query = query.Where(w => w.AssignToUser != null && (w.AssignToUser.FirstName + " " + w.AssignToUser.LastName).Contains(assignee));
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(w =>
(w.InternalWONumber ?? "").ToLower().Contains(s) ||
(w.WorkerOrderNumber ?? "").ToLower().Contains(s) ||
(w.WorkerOrderTitle ?? "").ToLower().Contains(s) ||
(w.Locations.Title ?? "").ToLower().Contains(s) ||
(w.Locations.Name ?? "").ToLower().Contains(s)
);
}
var totalCount = await query.CountAsync();
var isAsc = sortDir?.ToLower() == "asc";
IOrderedQueryable<WorkOrder> ordered = (sortBy?.ToLower()) switch
{
"number" => isAsc ? query.OrderBy(w => w.InternalWONumber) : query.OrderByDescending(w => w.InternalWONumber),
"title" => isAsc ? query.OrderBy(w => w.WorkerOrderTitle) : query.OrderByDescending(w => w.WorkerOrderTitle),
"location" => isAsc ? query.OrderBy(w => w.Locations != null ? w.Locations.Name : "") : query.OrderByDescending(w => w.Locations != null ? w.Locations.Name : ""),
"priority" => isAsc ? query.OrderBy(w => w.Priority) : query.OrderByDescending(w => w.Priority),
"status" => isAsc ? query.OrderBy(w => w.Status) : query.OrderByDescending(w => w.Status),
"createddate" => isAsc ? query.OrderBy(w => w.CreatedDate) : query.OrderByDescending(w => w.CreatedDate),
"assignedto" => isAsc ? query.OrderBy(w => w.AssignToUser != null ? w.AssignToUser.FirstName : "") : query.OrderByDescending(w => w.AssignToUser != null ? w.AssignToUser.FirstName : ""),
"lastupdated" => isAsc ? query.OrderBy(w => w.LastModificationTime) : query.OrderByDescending(w => w.LastModificationTime),
_ => query.OrderByDescending(w => w.Id)
};
var data = await ordered
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(w => new
{
id = w.Id,
internalWONumber = w.InternalWONumber,
workOrderNumber = w.WorkerOrderNumber,
number = w.InternalWONumber ?? w.WorkerOrderNumber,
title = w.WorkerOrderTitle,
location = w.Locations != null ? w.Locations.Name : "",
locationName = w.Locations != null ? w.Locations.Name : "",
priority = w.Priority,
status = w.Status,
createdDate = w.CreatedDate,
dueDate = w.DueDate,
assignedTo = w.AssignToUser != null ? w.AssignToUser.FirstName + " " + w.AssignToUser.LastName : "",
employeeName = w.AssignToUser != null ? w.AssignToUser.FirstName + " " + w.AssignToUser.LastName : "",
lastUpdated = new[] {
w.LastModificationTime,
w.Comments.Max(c => (DateTime?)c.CreatedDate),
_context.WorkOrderAuditLogs.Where(a => a.WorkOrderId == w.Id).Max(a => (DateTime?)a.CreatedAt)
}.Where(d => d.HasValue).Max()
})
.ToListAsync();
return (data, totalCount);
}
public async Task<WorkOrder> AddAsync(WorkOrder workOrder)
{
workOrder.CreatedDate = DateTime.Now;

View file

@ -8,7 +8,13 @@ namespace SeaHaven.DataServices.Interfaces
Task<Locations?> GetByIdWithDetailsAsync(int id);
Task<IEnumerable<Locations>> GetAllAsync();
Task<IEnumerable<Locations>> GetByAccountIdAsync(int accountId);
// Paged results returning DTOs
Task<(IEnumerable<Locations> Items, int TotalCount)> GetPagedAsync(int page, int pageSize, string? search = null);
// For addressbook endpoint - returns simplified DTO
Task<(IEnumerable<object> Items, int TotalCount)> GetAddressbookPagedAsync(int page, int pageSize, string? search = null);
Task<Locations> AddAsync(Locations location);
Task UpdateAsync(Locations location);
Task DeleteAsync(int id);

View file

@ -7,6 +7,7 @@ namespace SeaHaven.DataServices.Interfaces
Task<PMSchedules?> GetByIdAsync(int id);
Task<PMSchedules?> GetByIdWithDetailsAsync(int id);
Task<IEnumerable<PMSchedules>> GetAllAsync();
Task<IEnumerable<PMSchedules>> GetAllWithDetailsAsync();
Task<IEnumerable<PMSchedules>> GetByLocationIdAsync(int locationId);
Task<(IEnumerable<PMSchedules> Items, int TotalCount)> GetPagedAsync(int page, int pageSize, string? search = null);
Task<PMSchedules> AddAsync(PMSchedules pmSchedule);

View file

@ -16,11 +16,22 @@ namespace SeaHaven.DataServices.Interfaces
// Paged results
Task<(IEnumerable<WorkOrder> Items, int TotalCount)> GetPagedAsync(
int page,
int pageSize,
string? search = null,
int page,
int pageSize,
string? search = null,
string? status = null,
string? priority = null);
// Paged list with detailed information (for GetWorkOrderList endpoint)
Task<(IEnumerable<object> Items, int TotalCount)> GetWorkOrderListPagedAsync(
int page,
int pageSize,
string? search = null,
string? status = null,
string? assignee = null,
int? locationId = null,
string? sortBy = "lastUpdated",
string? sortDir = "desc");
// Command operations
Task<WorkOrder> AddAsync(WorkOrder workOrder);

View file

@ -4,6 +4,7 @@ namespace SeaHaven.Services.DTOs
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Type { get; set; }
public string? Phone { get; set; }
public string? Email { get; set; }
public string? Website { get; set; }

View file

@ -4,8 +4,6 @@ namespace SeaHaven.Services.DTOs
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Type { get; set; }
public string? SerialNumber { get; set; }
public int? LocationId { get; set; }
public string? LocationName { get; set; }
public DateTime? CreatedDate { get; set; }
@ -14,16 +12,12 @@ namespace SeaHaven.Services.DTOs
public class CreateAssetDTO
{
public required string Name { get; set; }
public string? Type { get; set; }
public string? SerialNumber { get; set; }
public int? LocationId { get; set; }
}
public class UpdateAssetDTO
{
public string? Name { get; set; }
public string? Type { get; set; }
public string? SerialNumber { get; set; }
public int? LocationId { get; set; }
}
}

View file

@ -3,32 +3,41 @@ namespace SeaHaven.Services.DTOs
public class FollowUpDTO
{
public int Id { get; set; }
public string? Subject { get; set; }
public string? Description { get; set; }
public string? Reason { get; set; }
public DateTime ScheduleDate { get; set; }
public TimeSpan ScheduleStartTime { get; set; }
public string? Status { get; set; }
public int? EmployeeId { get; set; }
public string? EmployeeName { get; set; }
public int? WorkOrderId { get; set; }
public string? AssignedTo { get; set; }
public DateTime? DueDate { get; set; }
public int? AccountId { get; set; }
public string? AccountName { get; set; }
public int? LocationId { get; set; }
public string? LocationName { get; set; }
public DateTime? CreatedDate { get; set; }
public string? CreatedBy { get; set; }
}
public class CreateFollowUpDTO
{
public required string Subject { get; set; }
public string? Description { get; set; }
public string? Status { get; set; }
public required string Reason { get; set; }
public DateTime ScheduleDate { get; set; }
public TimeSpan ScheduleStartTime { get; set; }
public required string Status { get; set; }
public int? EmployeeId { get; set; }
public int? WorkOrderId { get; set; }
public string? AssignedTo { get; set; }
public DateTime? DueDate { get; set; }
public int? AccountId { get; set; }
public int? LocationId { get; set; }
}
public class UpdateFollowUpDTO
{
public string? Subject { get; set; }
public string? Description { get; set; }
public string? Reason { get; set; }
public DateTime? ScheduleDate { get; set; }
public TimeSpan? ScheduleStartTime { get; set; }
public string? Status { get; set; }
public string? AssignedTo { get; set; }
public DateTime? DueDate { get; set; }
public int? EmployeeId { get; set; }
public int? WorkOrderId { get; set; }
public int? AccountId { get; set; }
public int? LocationId { get; set; }
}
}

View file

@ -3,13 +3,16 @@ namespace SeaHaven.Services.DTOs
public class LocationDTO
{
public int Id { get; set; }
public string? LocationName { get; set; }
public string? Address { get; set; }
public string? Name { get; set; }
public string? Title { get; set; }
public string? Address1 { get; set; }
public string? Address2 { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zipcode { get; set; }
public int? AccountId { get; set; }
public string? AccountName { get; set; }
public string? Zip { get; set; }
public string? PhoneNumber { get; set; }
public string? Email { get; set; }
public string? Status { get; set; }
public DateTime? CreatedDate { get; set; }
public string? CreatedBy { get; set; }
}

View file

@ -4,29 +4,33 @@ namespace SeaHaven.Services.DTOs
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public string? Frequency { get; set; }
public DateTime StartDate { get; set; }
public bool IsActive { get; set; }
public int? AssetId { get; set; }
public string? AssetName { get; set; }
public int? LocationId { get; set; }
public string? LocationName { get; set; }
public string? Frequency { get; set; }
public DateTime? NextDueDate { get; set; }
public DateTime? CreatedDate { get; set; }
}
public class CreatePMScheduleDTO
{
public required string Name { get; set; }
public string? Description { get; set; }
public required string Frequency { get; set; }
public DateTime StartDate { get; set; }
public bool IsActive { get; set; }
public int? AssetId { get; set; }
public int? LocationId { get; set; }
public string? Frequency { get; set; }
public DateTime? NextDueDate { get; set; }
}
public class UpdatePMScheduleDTO
{
public string? Name { get; set; }
public string? Description { get; set; }
public int? LocationId { get; set; }
public string? Frequency { get; set; }
public DateTime? NextDueDate { get; set; }
public DateTime? StartDate { get; set; }
public bool? IsActive { get; set; }
public int? AssetId { get; set; }
public int? LocationId { get; set; }
}
}

View file

@ -0,0 +1,124 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Account service implementation
/// Contains business logic, validation, and orchestration for Account operations
/// </summary>
public class AccountService : IAccountService
{
private readonly IAccountDataService _accountDataService;
public AccountService(IAccountDataService accountDataService)
{
_accountDataService = accountDataService;
}
public async Task<AccountDTO?> GetAccountByIdAsync(int id)
{
var account = await _accountDataService.GetByIdAsync(id);
return account == null ? null : MapToDTO(account);
}
public async Task<AccountDTO?> GetAccountByIdWithDetailsAsync(int id)
{
var account = await _accountDataService.GetByIdWithDetailsAsync(id);
return account == null ? null : MapToDTO(account);
}
public async Task<IEnumerable<AccountDTO>> GetAllAccountsAsync()
{
var accounts = await _accountDataService.GetAllAsync();
return accounts.Select(MapToDTO);
}
public async Task<PagedResult<AccountDTO>> GetAccountsPagedAsync(int page, int pageSize, string? search = null)
{
var (items, totalCount) = await _accountDataService.GetPagedAsync(page, pageSize, search);
return new PagedResult<AccountDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<AccountDTO> CreateAccountAsync(CreateAccountDTO dto, string userId)
{
var account = new Accounts
{
Name = dto.Name,
Phone = dto.Phone,
Email = dto.Email,
Website = dto.Website,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _accountDataService.AddAsync(account);
return MapToDTO(created);
}
public async Task<AccountDTO> UpdateAccountAsync(int id, UpdateAccountDTO dto, string userId)
{
var account = await _accountDataService.GetByIdAsync(id);
if (account == null)
throw new InvalidOperationException($"Account with ID {id} not found");
if (dto.Name != null) account.Name = dto.Name;
if (dto.Phone != null) account.Phone = dto.Phone;
if (dto.Email != null) account.Email = dto.Email;
if (dto.Website != null) account.Website = dto.Website;
account.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
account.LastModifierUserId = userIdInt;
await _accountDataService.UpdateAsync(account);
return MapToDTO(account);
}
public async Task DeleteAccountAsync(int id, string userId)
{
var account = await _accountDataService.GetByIdAsync(id);
if (account == null)
throw new InvalidOperationException($"Account with ID {id} not found");
account.IsDeleted = true;
account.DeleterUserId = userId;
account.DeletionTime = DateTime.UtcNow;
await _accountDataService.UpdateAsync(account);
}
public async Task<bool> AccountExistsAsync(int id)
{
return await _accountDataService.ExistsAsync(id);
}
public async Task<int> GetTotalAccountCountAsync()
{
return await _accountDataService.CountAsync();
}
private AccountDTO MapToDTO(Accounts account)
{
return new AccountDTO
{
Id = account.Id,
Name = account.Name,
Type = account.Type,
Phone = account.Phone,
Email = account.Email,
Website = account.Website,
CreatedDate = account.CreatedDate,
CreatedBy = account.createdby
};
}
}
}

View file

@ -0,0 +1,123 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Asset service implementation
/// Contains business logic, validation, and orchestration for Asset operations
/// </summary>
public class AssetService : IAssetService
{
private readonly IAssetDataService _assetDataService;
public AssetService(IAssetDataService assetDataService)
{
_assetDataService = assetDataService;
}
public async Task<AssetDTO?> GetAssetByIdAsync(int id)
{
var asset = await _assetDataService.GetByIdAsync(id);
return asset == null ? null : MapToDTO(asset);
}
public async Task<AssetDTO?> GetAssetByIdWithDetailsAsync(int id)
{
var asset = await _assetDataService.GetByIdWithDetailsAsync(id);
return asset == null ? null : MapToDTO(asset);
}
public async Task<IEnumerable<AssetDTO>> GetAllAssetsAsync()
{
var assets = await _assetDataService.GetAllAsync();
return assets.Select(MapToDTO);
}
public async Task<IEnumerable<AssetDTO>> GetAssetsByLocationIdAsync(int locationId)
{
var assets = await _assetDataService.GetAllAsync();
return assets.Where(a => a.LocationId == locationId).Select(MapToDTO);
}
public async Task<PagedResult<AssetDTO>> GetAssetsPagedAsync(int page, int pageSize, string? search = null)
{
var (items, totalCount) = await _assetDataService.GetPagedAsync(page, pageSize, search);
return new PagedResult<AssetDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<AssetDTO> CreateAssetAsync(CreateAssetDTO dto, string userId)
{
var asset = new Assets
{
Name = dto.Name,
LocationId = dto.LocationId,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _assetDataService.AddAsync(asset);
return MapToDTO(created);
}
public async Task<AssetDTO> UpdateAssetAsync(int id, UpdateAssetDTO dto, string userId)
{
var asset = await _assetDataService.GetByIdAsync(id);
if (asset == null)
throw new InvalidOperationException($"Asset with ID {id} not found");
if (dto.Name != null) asset.Name = dto.Name;
if (dto.LocationId.HasValue) asset.LocationId = dto.LocationId;
asset.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
asset.LastModifierUserId = userIdInt;
await _assetDataService.UpdateAsync(asset);
return MapToDTO(asset);
}
public async Task DeleteAssetAsync(int id, string userId)
{
var asset = await _assetDataService.GetByIdAsync(id);
if (asset == null)
throw new InvalidOperationException($"Asset with ID {id} not found");
asset.IsDeleted = true;
asset.DeleterUserId = userId;
asset.DeletionTime = DateTime.UtcNow;
await _assetDataService.UpdateAsync(asset);
}
public async Task<bool> AssetExistsAsync(int id)
{
return await _assetDataService.ExistsAsync(id);
}
public async Task<int> GetTotalAssetCountAsync()
{
return await _assetDataService.CountAsync();
}
private AssetDTO MapToDTO(Assets asset)
{
return new AssetDTO
{
Id = asset.Id,
Name = asset.Name,
LocationId = asset.LocationId,
LocationName = asset.Location?.Name,
CreatedDate = asset.CreatedDate
};
}
}
}

View file

@ -0,0 +1,139 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Dispatch service implementation
/// Contains business logic, validation, and orchestration for Dispatch operations
/// </summary>
public class DispatchService : IDispatchService
{
private readonly IDispatchDataService _dispatchDataService;
public DispatchService(IDispatchDataService dispatchDataService)
{
_dispatchDataService = dispatchDataService;
}
public async Task<DispatchDTO?> GetDispatchByIdAsync(int id)
{
var dispatch = await _dispatchDataService.GetByIdAsync(id);
return dispatch == null ? null : MapToDTO(dispatch);
}
public async Task<DispatchDTO?> GetDispatchByIdWithDetailsAsync(int id)
{
var dispatch = await _dispatchDataService.GetByIdWithDetailsAsync(id);
return dispatch == null ? null : MapToDTO(dispatch);
}
public async Task<IEnumerable<DispatchDTO>> GetAllDispatchesAsync()
{
var dispatches = await _dispatchDataService.GetAllAsync();
return dispatches.Select(MapToDTO);
}
public async Task<IEnumerable<DispatchDTO>> GetDispatchesByVendorIdAsync(int vendorId)
{
var dispatches = await _dispatchDataService.GetByVendorIdAsync(vendorId);
return dispatches.Select(MapToDTO);
}
public async Task<PagedResult<DispatchDTO>> GetDispatchesPagedAsync(int page, int pageSize, string? search = null, string? status = null)
{
var (items, totalCount) = await _dispatchDataService.GetPagedAsync(page, pageSize, search, status);
return new PagedResult<DispatchDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<DispatchDTO> CreateDispatchAsync(CreateDispatchDTO dto, string userId)
{
var dispatchNumber = await GenerateNextDispatchNumberAsync();
var dispatch = new Dispatch
{
VendorId = dto.VendorId,
ScheduledDate = dto.ScheduledDate,
Status = dto.Status ?? "Pending",
DispatchNumber = dispatchNumber,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _dispatchDataService.AddAsync(dispatch);
return MapToDTO(created);
}
public async Task<DispatchDTO> UpdateDispatchAsync(int id, UpdateDispatchDTO dto, string userId)
{
var dispatch = await _dispatchDataService.GetByIdAsync(id);
if (dispatch == null)
throw new InvalidOperationException($"Dispatch with ID {id} not found");
if (dto.VendorId.HasValue) dispatch.VendorId = dto.VendorId.Value;
if (dto.ScheduledDate.HasValue) dispatch.ScheduledDate = dto.ScheduledDate;
if (dto.CompletedDate.HasValue) dispatch.CompletedDate = dto.CompletedDate;
if (dto.Status != null) dispatch.Status = dto.Status;
dispatch.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
dispatch.LastModifierUserId = userIdInt;
await _dispatchDataService.UpdateAsync(dispatch);
return MapToDTO(dispatch);
}
public async Task DeleteDispatchAsync(int id, string userId)
{
var dispatch = await _dispatchDataService.GetByIdAsync(id);
if (dispatch == null)
throw new InvalidOperationException($"Dispatch with ID {id} not found");
dispatch.IsDeleted = true;
dispatch.DeleterUserId = userId;
dispatch.DeletionTime = DateTime.UtcNow;
await _dispatchDataService.UpdateAsync(dispatch);
}
public async Task<bool> DispatchExistsAsync(int id)
{
return await _dispatchDataService.ExistsAsync(id);
}
public async Task<string> GenerateNextDispatchNumberAsync()
{
var count = await _dispatchDataService.CountAsync();
return $"DISP-{(count + 1):D6}";
}
public async Task<int> GetTotalDispatchCountAsync()
{
return await _dispatchDataService.CountAsync();
}
private DispatchDTO MapToDTO(Dispatch dispatch)
{
return new DispatchDTO
{
Id = dispatch.Id,
DispatchNumber = dispatch.DispatchNumber,
Status = dispatch.Status,
VendorId = dispatch.VendorId,
VendorName = dispatch.Vendor?.CompanyName,
ScheduledDate = dispatch.ScheduledDate,
CompletedDate = dispatch.CompletedDate,
CreatedDate = dispatch.CreatedDate,
CreatedBy = dispatch.createdby
};
}
}
}

View file

@ -0,0 +1,149 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Employee service implementation
/// Contains business logic, validation, and orchestration for Employee operations
/// </summary>
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeDataService _employeeDataService;
public EmployeeService(IEmployeeDataService employeeDataService)
{
_employeeDataService = employeeDataService;
}
// Query operations
public async Task<EmployeeDTO?> GetEmployeeByIdAsync(int id)
{
var employee = await _employeeDataService.GetByIdAsync(id);
return employee == null ? null : MapToDTO(employee);
}
public async Task<EmployeeDTO?> GetEmployeeByIdWithDetailsAsync(int id)
{
var employee = await _employeeDataService.GetByIdWithDetailsAsync(id);
return employee == null ? null : MapToDTO(employee);
}
public async Task<IEnumerable<EmployeeDTO>> GetAllEmployeesAsync()
{
var employees = await _employeeDataService.GetAllAsync();
return employees.Select(MapToDTO);
}
public async Task<PagedResult<EmployeeDTO>> GetEmployeesPagedAsync(int page, int pageSize, string? search = null)
{
var (items, totalCount) = await _employeeDataService.GetPagedAsync(page, pageSize, search);
return new PagedResult<EmployeeDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<EmployeeDTO> CreateEmployeeAsync(CreateEmployeeDTO dto, string userId)
{
var employee = new Employee
{
FirstName = dto.FirstName,
LastName = dto.LastName,
DepartmentId = dto.DepartmentId,
JobTitleId = dto.JobTitleId,
RegionId = dto.RegionId,
CreatedDate = DateTime.UtcNow,
CreatedBy = userId
};
if (!string.IsNullOrWhiteSpace(dto.Email))
{
employee.Emails.Add(new EmployeeEmail
{
Address = dto.Email,
IsPrimary = true
});
}
if (!string.IsNullOrWhiteSpace(dto.Phone))
{
employee.Phones.Add(new EmployeePhone
{
Number = dto.Phone,
IsPrimary = true
});
}
var created = await _employeeDataService.AddAsync(employee);
return MapToDTO(created);
}
public async Task<EmployeeDTO> UpdateEmployeeAsync(int id, UpdateEmployeeDTO dto, string userId)
{
var employee = await _employeeDataService.GetByIdAsync(id);
if (employee == null)
throw new InvalidOperationException($"Employee with ID {id} not found");
if (dto.FirstName != null) employee.FirstName = dto.FirstName;
if (dto.LastName != null) employee.LastName = dto.LastName;
if (dto.DepartmentId.HasValue) employee.DepartmentId = dto.DepartmentId;
if (dto.JobTitleId.HasValue) employee.JobTitleId = dto.JobTitleId;
if (dto.RegionId.HasValue) employee.RegionId = dto.RegionId;
employee.LastModificationTime = DateTime.UtcNow;
employee.LastModifierUserId = int.Parse(userId);
await _employeeDataService.UpdateAsync(employee);
return MapToDTO(employee);
}
public async Task DeleteEmployeeAsync(int id, string userId)
{
var employee = await _employeeDataService.GetByIdAsync(id);
if (employee == null)
throw new InvalidOperationException($"Employee with ID {id} not found");
employee.IsDeleted = true;
employee.CreatedBy = userId;
employee.LastModificationTime = DateTime.UtcNow;
await _employeeDataService.UpdateAsync(employee);
}
public async Task<bool> EmployeeExistsAsync(int id)
{
return await _employeeDataService.ExistsAsync(id);
}
public async Task<int> GetTotalEmployeeCountAsync()
{
return await _employeeDataService.CountAsync();
}
// Private helper methods
private EmployeeDTO MapToDTO(Employee employee)
{
return new EmployeeDTO
{
Id = employee.Id,
FirstName = employee.FirstName,
LastName = employee.LastName,
Email = employee.Emails?.FirstOrDefault(e => e.IsPrimary == true)?.Address,
Phone = employee.Phones?.FirstOrDefault(p => p.IsPrimary == true)?.Number,
DepartmentId = employee.DepartmentId,
DepartmentName = employee.Department?.Name,
JobTitleId = employee.JobTitleId,
JobTitleName = employee.JobTitle?.Name,
RegionId = employee.RegionId,
RegionName = employee.Region?.Name,
CreatedDate = employee.CreatedDate
};
}
}
}

View file

@ -0,0 +1,150 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Follow-Up service implementation
/// Contains business logic, validation, and orchestration for Follow-Up operations
/// </summary>
public class FollowUpService : IFollowUpService
{
private readonly IFollowUpDataService _followUpDataService;
public FollowUpService(IFollowUpDataService followUpDataService)
{
_followUpDataService = followUpDataService;
}
public async Task<FollowUpDTO?> GetFollowUpByIdAsync(int id)
{
var followUp = await _followUpDataService.GetByIdAsync(id);
return followUp == null ? null : MapToDTO(followUp);
}
public async Task<FollowUpDTO?> GetFollowUpByIdWithDetailsAsync(int id)
{
var followUp = await _followUpDataService.GetByIdWithDetailsAsync(id);
return followUp == null ? null : MapToDTO(followUp);
}
public async Task<IEnumerable<FollowUpDTO>> GetAllFollowUpsAsync()
{
var followUps = await _followUpDataService.GetAllAsync();
return followUps.Select(MapToDTO);
}
public async Task<IEnumerable<FollowUpDTO>> GetFollowUpsByWorkOrderIdAsync(int workOrderId)
{
var followUps = await _followUpDataService.GetByWorkOrderIdAsync(workOrderId);
return followUps.Select(MapToDTO);
}
public async Task<PagedResult<FollowUpDTO>> GetFollowUpsPagedAsync(int page, int pageSize, string? search = null, string? status = null)
{
var (items, totalCount) = await _followUpDataService.GetPagedAsync(page, pageSize, search);
var filteredItems = items.AsEnumerable();
if (!string.IsNullOrWhiteSpace(status))
{
filteredItems = filteredItems.Where(f => f.Status == status);
}
return new PagedResult<FollowUpDTO>
{
Items = filteredItems.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<FollowUpDTO> CreateFollowUpAsync(CreateFollowUpDTO dto, string userId)
{
var followUp = new FollowUps
{
Reason = dto.Reason,
ScheduleDate = dto.ScheduleDate,
ScheduleStartTime = dto.ScheduleStartTime,
Status = dto.Status,
EmployeeId = dto.EmployeeId,
WorkOrderId = dto.WorkOrderId,
AccountId = dto.AccountId,
LocationId = dto.LocationId,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _followUpDataService.AddAsync(followUp);
return MapToDTO(created);
}
public async Task<FollowUpDTO> UpdateFollowUpAsync(int id, UpdateFollowUpDTO dto, string userId)
{
var followUp = await _followUpDataService.GetByIdAsync(id);
if (followUp == null)
throw new InvalidOperationException($"Follow-Up with ID {id} not found");
if (dto.Reason != null) followUp.Reason = dto.Reason;
if (dto.ScheduleDate.HasValue) followUp.ScheduleDate = dto.ScheduleDate.Value;
if (dto.ScheduleStartTime.HasValue) followUp.ScheduleStartTime = dto.ScheduleStartTime.Value;
if (dto.Status != null) followUp.Status = dto.Status;
if (dto.EmployeeId.HasValue) followUp.EmployeeId = dto.EmployeeId;
if (dto.WorkOrderId.HasValue) followUp.WorkOrderId = dto.WorkOrderId;
if (dto.AccountId.HasValue) followUp.AccountId = dto.AccountId;
if (dto.LocationId.HasValue) followUp.LocationId = dto.LocationId;
followUp.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
followUp.LastModifierUserId = userIdInt;
await _followUpDataService.UpdateAsync(followUp);
return MapToDTO(followUp);
}
public async Task DeleteFollowUpAsync(int id, string userId)
{
var followUp = await _followUpDataService.GetByIdAsync(id);
if (followUp == null)
throw new InvalidOperationException($"Follow-Up with ID {id} not found");
followUp.IsDeleted = true;
followUp.DeleterUserId = userId;
followUp.DeletionTime = DateTime.UtcNow;
await _followUpDataService.UpdateAsync(followUp);
}
public async Task<bool> FollowUpExistsAsync(int id)
{
return await _followUpDataService.ExistsAsync(id);
}
public async Task<int> GetTotalFollowUpCountAsync()
{
return await _followUpDataService.CountAsync();
}
private FollowUpDTO MapToDTO(FollowUps followUp)
{
return new FollowUpDTO
{
Id = followUp.Id,
Reason = followUp.Reason,
ScheduleDate = followUp.ScheduleDate,
ScheduleStartTime = followUp.ScheduleStartTime,
Status = followUp.Status,
EmployeeId = followUp.EmployeeId,
EmployeeName = followUp.Employee != null ? $"{followUp.Employee.FirstName} {followUp.Employee.LastName}" : null,
WorkOrderId = followUp.WorkOrderId,
AccountId = followUp.AccountId,
AccountName = followUp.Account?.Name,
LocationId = followUp.LocationId,
LocationName = followUp.Location?.Name,
CreatedDate = followUp.CreatedDate
};
}
}
}

View file

@ -0,0 +1,142 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Location service implementation
/// Contains business logic, validation, and orchestration for Location operations
/// </summary>
public class LocationService : ILocationService
{
private readonly ILocationDataService _locationDataService;
public LocationService(ILocationDataService locationDataService)
{
_locationDataService = locationDataService;
}
// Query operations
public async Task<LocationDTO?> GetLocationByIdAsync(int id)
{
var location = await _locationDataService.GetByIdAsync(id);
return location == null ? null : MapToDTO(location);
}
public async Task<LocationDTO?> GetLocationByIdWithDetailsAsync(int id)
{
var location = await _locationDataService.GetByIdWithDetailsAsync(id);
return location == null ? null : MapToDTO(location);
}
public async Task<IEnumerable<LocationDTO>> GetAllLocationsAsync()
{
var locations = await _locationDataService.GetAllAsync();
return locations.Select(MapToDTO);
}
public async Task<IEnumerable<LocationDTO>> GetLocationsByAccountIdAsync(int accountId)
{
var locations = await _locationDataService.GetByAccountIdAsync(accountId);
return locations.Select(MapToDTO);
}
public async Task<PagedResult<LocationDTO>> GetLocationsPagedAsync(int page, int pageSize, string? search = null)
{
var (items, totalCount) = await _locationDataService.GetPagedAsync(page, pageSize, search);
return new PagedResult<LocationDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<LocationDTO> CreateLocationAsync(CreateLocationDTO dto, string userId)
{
var location = new Locations
{
Name = dto.LocationName,
Title = dto.LocationName,
Address1 = dto.Address,
City = dto.City,
State = dto.State,
Zip = dto.Zipcode,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _locationDataService.AddAsync(location);
return MapToDTO(created);
}
public async Task<LocationDTO> UpdateLocationAsync(int id, UpdateLocationDTO dto, string userId)
{
var location = await _locationDataService.GetByIdAsync(id);
if (location == null)
throw new InvalidOperationException($"Location with ID {id} not found");
// Update properties
if (dto.LocationName != null) location.Name = dto.LocationName;
if (dto.LocationName != null) location.Title = dto.LocationName;
if (dto.Address != null) location.Address1 = dto.Address;
if (dto.City != null) location.City = dto.City;
if (dto.State != null) location.State = dto.State;
if (dto.Zipcode != null) location.Zip = dto.Zipcode;
location.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
location.LastModifierUserId = userIdInt;
await _locationDataService.UpdateAsync(location);
return MapToDTO(location);
}
public async Task DeleteLocationAsync(int id, string userId)
{
var location = await _locationDataService.GetByIdAsync(id);
if (location == null)
throw new InvalidOperationException($"Location with ID {id} not found");
location.IsDeleted = true;
location.DeleterUserId = userId;
location.DeletionTime = DateTime.UtcNow;
await _locationDataService.UpdateAsync(location);
}
public async Task<bool> LocationExistsAsync(int id)
{
return await _locationDataService.ExistsAsync(id);
}
public async Task<int> GetTotalLocationCountAsync()
{
return await _locationDataService.CountAsync();
}
// Private helper methods
private LocationDTO MapToDTO(Locations location)
{
return new LocationDTO
{
Id = location.Id,
Name = location.Name,
Title = location.Title,
Address1 = location.Address1,
Address2 = location.Address2,
City = location.City,
State = location.State,
Zip = location.Zip,
PhoneNumber = location.PhoneNumber,
Email = location.Email,
Status = location.Status,
CreatedDate = location.CreatedDate,
CreatedBy = location.createdby
};
}
}
}

View file

@ -0,0 +1,136 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// PM Schedule service implementation
/// Contains business logic, validation, and orchestration for PM Schedule operations
/// </summary>
public class PMScheduleService : IPMScheduleService
{
private readonly IPMScheduleDataService _pmScheduleDataService;
public PMScheduleService(IPMScheduleDataService pmScheduleDataService)
{
_pmScheduleDataService = pmScheduleDataService;
}
public async Task<PMScheduleDTO?> GetPMScheduleByIdAsync(int id)
{
var pmSchedule = await _pmScheduleDataService.GetByIdAsync(id);
return pmSchedule == null ? null : MapToDTO(pmSchedule);
}
public async Task<PMScheduleDTO?> GetPMScheduleByIdWithDetailsAsync(int id)
{
var pmSchedule = await _pmScheduleDataService.GetByIdWithDetailsAsync(id);
return pmSchedule == null ? null : MapToDTO(pmSchedule);
}
public async Task<IEnumerable<PMScheduleDTO>> GetAllPMSchedulesAsync()
{
var pmSchedules = await _pmScheduleDataService.GetAllWithDetailsAsync();
return pmSchedules.Select(MapToDTO);
}
public async Task<IEnumerable<PMScheduleDTO>> GetPMSchedulesByLocationIdAsync(int locationId)
{
var pmSchedules = await _pmScheduleDataService.GetAllWithDetailsAsync();
return pmSchedules.Where(p => p.LocationId == locationId).Select(MapToDTO);
}
public async Task<PagedResult<PMScheduleDTO>> GetPMSchedulesPagedAsync(int page, int pageSize, string? search = null)
{
var (items, totalCount) = await _pmScheduleDataService.GetPagedAsync(page, pageSize, search);
return new PagedResult<PMScheduleDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<PMScheduleDTO> CreatePMScheduleAsync(CreatePMScheduleDTO dto, string userId)
{
var pmSchedule = new PMSchedules
{
Name = dto.Name,
Frequency = dto.Frequency,
StartDate = dto.StartDate,
IsActive = dto.IsActive,
AssetId = dto.AssetId,
LocationId = dto.LocationId,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _pmScheduleDataService.AddAsync(pmSchedule);
return MapToDTO(created);
}
public async Task<PMScheduleDTO> UpdatePMScheduleAsync(int id, UpdatePMScheduleDTO dto, string userId)
{
var pmSchedule = await _pmScheduleDataService.GetByIdAsync(id);
if (pmSchedule == null)
throw new InvalidOperationException($"PM Schedule with ID {id} not found");
if (dto.Name != null) pmSchedule.Name = dto.Name;
if (dto.Frequency != null) pmSchedule.Frequency = dto.Frequency;
if (dto.StartDate.HasValue) pmSchedule.StartDate = dto.StartDate.Value;
if (dto.IsActive.HasValue) pmSchedule.IsActive = dto.IsActive.Value;
if (dto.AssetId.HasValue) pmSchedule.AssetId = dto.AssetId;
if (dto.LocationId.HasValue) pmSchedule.LocationId = dto.LocationId;
pmSchedule.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
pmSchedule.LastModifierUserId = userIdInt;
await _pmScheduleDataService.UpdateAsync(pmSchedule);
return MapToDTO(pmSchedule);
}
public async Task DeletePMScheduleAsync(int id, string userId)
{
var pmSchedule = await _pmScheduleDataService.GetByIdAsync(id);
if (pmSchedule == null)
throw new InvalidOperationException($"PM Schedule with ID {id} not found");
pmSchedule.IsDeleted = true;
pmSchedule.DeleterUserId = userId;
pmSchedule.DeletionTime = DateTime.UtcNow;
await _pmScheduleDataService.UpdateAsync(pmSchedule);
}
public async Task<bool> PMScheduleExistsAsync(int id)
{
return await _pmScheduleDataService.ExistsAsync(id);
}
public async Task<int> GetTotalPMScheduleCountAsync()
{
return await _pmScheduleDataService.CountAsync();
}
private PMScheduleDTO MapToDTO(PMSchedules pmSchedule)
{
return new PMScheduleDTO
{
Id = pmSchedule.Id,
Name = pmSchedule.Name,
Frequency = pmSchedule.Frequency,
StartDate = pmSchedule.StartDate,
IsActive = pmSchedule.IsActive,
AssetId = pmSchedule.AssetId,
AssetName = pmSchedule.Asset?.Name,
LocationId = pmSchedule.LocationId,
LocationName = pmSchedule.Location?.Name,
CreatedDate = pmSchedule.CreatedDate
};
}
}
}

View file

@ -0,0 +1,130 @@
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
/// <summary>
/// Vendor service implementation
/// Contains business logic, validation, and orchestration for Vendor operations
/// </summary>
public class VendorService : IVendorService
{
private readonly IVendorDataService _vendorDataService;
public VendorService(IVendorDataService vendorDataService)
{
_vendorDataService = vendorDataService;
}
public async Task<VendorDTO?> GetVendorByIdAsync(int id)
{
var vendor = await _vendorDataService.GetByIdAsync(id);
return vendor == null ? null : MapToDTO(vendor);
}
public async Task<VendorDTO?> GetVendorByIdWithDetailsAsync(int id)
{
var vendor = await _vendorDataService.GetByIdWithDetailsAsync(id);
return vendor == null ? null : MapToDTO(vendor);
}
public async Task<IEnumerable<VendorDTO>> GetAllVendorsAsync()
{
var vendors = await _vendorDataService.GetAllAsync();
return vendors.Select(MapToDTO);
}
public async Task<PagedResult<VendorDTO>> GetVendorsPagedAsync(int page, int pageSize, string? search = null)
{
var (items, totalCount) = await _vendorDataService.GetPagedAsync(page, pageSize, search);
return new PagedResult<VendorDTO>
{
Items = items.Select(MapToDTO).ToList(),
TotalCount = totalCount,
Page = page,
PageSize = pageSize
};
}
public async Task<VendorDTO> CreateVendorAsync(CreateVendorDTO dto, string userId)
{
var vendor = new Vendor
{
CompanyName = dto.Name,
Email = dto.Email,
Phone = dto.Phone,
Address = dto.Address,
City = dto.City,
State = dto.State,
Zip = dto.Zipcode,
CreatedDate = DateTime.UtcNow,
createdby = userId
};
var created = await _vendorDataService.AddAsync(vendor);
return MapToDTO(created);
}
public async Task<VendorDTO> UpdateVendorAsync(int id, UpdateVendorDTO dto, string userId)
{
var vendor = await _vendorDataService.GetByIdAsync(id);
if (vendor == null)
throw new InvalidOperationException($"Vendor with ID {id} not found");
if (dto.Name != null) vendor.CompanyName = dto.Name;
if (dto.Email != null) vendor.Email = dto.Email;
if (dto.Phone != null) vendor.Phone = dto.Phone;
if (dto.Address != null) vendor.Address = dto.Address;
if (dto.City != null) vendor.City = dto.City;
if (dto.State != null) vendor.State = dto.State;
if (dto.Zipcode != null) vendor.Zip = dto.Zipcode;
vendor.LastModificationTime = DateTime.UtcNow;
if (int.TryParse(userId, out int userIdInt))
vendor.LastModifierUserId = userIdInt;
await _vendorDataService.UpdateAsync(vendor);
return MapToDTO(vendor);
}
public async Task DeleteVendorAsync(int id, string userId)
{
var vendor = await _vendorDataService.GetByIdAsync(id);
if (vendor == null)
throw new InvalidOperationException($"Vendor with ID {id} not found");
vendor.IsDeleted = true;
vendor.DeleterUserId = userId;
vendor.DeletionTime = DateTime.UtcNow;
await _vendorDataService.UpdateAsync(vendor);
}
public async Task<bool> VendorExistsAsync(int id)
{
return await _vendorDataService.ExistsAsync(id);
}
public async Task<int> GetTotalVendorCountAsync()
{
return await _vendorDataService.CountAsync();
}
private VendorDTO MapToDTO(Vendor vendor)
{
return new VendorDTO
{
Id = vendor.Id,
Name = vendor.CompanyName,
Email = vendor.Email,
Phone = vendor.Phone,
Address = vendor.Address,
City = vendor.City,
State = vendor.State,
Zipcode = vendor.Zip
};
}
}
}