shoc-backend/SeaHaven.Services/Implementation/LocationService.cs
Arthur Bassi 8c4102b1cf
feat(locations): add sites options endpoint and vendor contactName (#20)
* fix(migrations): make Phase6 completion slide-over migration idempotent

Use IF EXISTS / IF COL_LENGTH guards and explicit EF migration metadata
so partial environments can apply Phase 6 safely.

* feat(locations): add sites options endpoint and vendor contactName

Expose GET /api/locations/sites for the WO create wizard and include
vendor contactName on dropdown responses.

* fix(work-orders): make stacked tip build for CI quality gates

---------

Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
2026-07-22 17:36:11 -03:00

177 lines
6.4 KiB
C#

using Data.SeaHavenIndustries;
using FluentValidation;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.Validation;
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;
private readonly ICreateLocationValidation _createValidator;
private readonly IUpdateLocationValidation _updateValidator;
public LocationService(
ILocationDataService locationDataService,
ICreateLocationValidation createValidator,
IUpdateLocationValidation updateValidator)
{
_locationDataService = locationDataService;
_createValidator = createValidator;
_updateValidator = updateValidator;
}
// 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<IReadOnlyList<SiteOptionDTO>> GetSiteOptionsAsync(string? search = null)
{
var rows = await _locationDataService.GetSiteOptionsAsync(search);
return rows
.Select(r => new SiteOptionDTO
{
Id = r.Id,
Code = r.Code,
City = r.City,
State = r.State
})
.ToList();
}
public async Task<LocationDTO> CreateLocationAsync(CreateLocationDTO dto, string userId)
{
var validationResult = await _createValidator.ValidateAsync(dto);
if (!validationResult.IsValid)
{
throw new ValidationException(validationResult.Errors);
}
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 validationResult = await _updateValidator.ValidateAsync(dto);
if (!validationResult.IsValid)
{
throw new ValidationException(validationResult.Errors);
}
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
};
}
}
}