Files
Distribution_GL/Services/CostCenterService.cs

130 lines
5.1 KiB
C#

using General_Ledger_Service.DTOs.Common;
using General_Ledger_Service.DTOs.CostCenters;
using General_Ledger_Service.Enums;
using General_Ledger_Service.Exceptions;
using General_Ledger_Service.Infra.UoW;
using General_Ledger_Service.Interfaces.Repositories;
using General_Ledger_Service.Interfaces.Services;
using General_Ledger_Service.Models;
namespace General_Ledger_Service.Services
{
public class CostCenterService(
ICostCenterRepository costCenterRepository,
ISystemConfigRepository systemConfigRepository,
IAuditLogRepository auditLogRepository,
IUnitOfWork unitOfWork) : ICostCenterService
{
private readonly ICostCenterRepository _costCenterRepository = costCenterRepository;
private readonly ISystemConfigRepository _systemConfigRepository = systemConfigRepository;
private readonly IAuditLogRepository _auditLogRepository = auditLogRepository;
private readonly IUnitOfWork _unitOfWork = unitOfWork;
public async Task<ApiResponse> CreateCostCenterAsync(CreateCostCenterRequest request)
{
CostCenter? parent = null;
if (request.ParentCostCenterId.HasValue)
{
parent = await _costCenterRepository.GetByIdAsync(request.ParentCostCenterId.Value)
?? throw new NotFoundException("Parent cost center not found");
var resultingDepth = await _costCenterRepository.GetAncestorDepthAsync(parent.CostCenterId);
await DepthGuard.EnsureWithinMaxDepthAsync(
_systemConfigRepository, "COST_CENTER_MAX_DEPTH", null, resultingDepth, "Maximum Cost Center depth exceeded");
}
var code = await GenerateCostCenterCodeAsync(parent);
var costCenter = new CostCenter
{
Code = code,
Name = request.Name,
ParentCostCenterId = request.ParentCostCenterId,
IsActive = request.IsActive
};
await _unitOfWork.BeginAsync();
try
{
await _costCenterRepository.AddAsync(costCenter);
await _unitOfWork.SaveChangesAsync();
await _auditLogRepository.AddAsync(new AuditLog
{
Category = AuditCategory.Administrative,
EntityName = "CostCenter",
EntityId = costCenter.CostCenterId.ToString(),
Action = AuditAction.Create,
Description = $"Cost center {costCenter.Code} created",
PerformedBy = null
});
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitAsync();
}
catch
{
await _unitOfWork.RollbackAsync();
throw;
}
return ResponseHelpers.Created("Cost center created successfully", costCenter);
}
private async Task<string> GenerateCostCenterCodeAsync(CostCenter? parent)
{
if (parent != null)
{
for (var suffix = 1; suffix <= 99; suffix++)
{
var code = $"{parent.Code}-{suffix:D2}";
if (await _costCenterRepository.GetByCodeAsync(code) is null)
{
return code;
}
}
throw new BadRequestException("Maximum number of child cost centers under this parent has been reached.");
}
// Probes each candidate slot directly (same fix as AccountService.GenerateAccountCodeAsync)
// rather than trusting a root count, so a pre-existing, manually-coded cost center can
// never collide with a generated one.
for (var slot = 1; slot <= 999; slot++)
{
var code = $"CC-{slot:D3}";
if (await _costCenterRepository.GetByCodeAsync(code) is null)
{
return code;
}
}
throw new BadRequestException("Maximum number of root-level cost centers has been reached.");
}
public async Task<ApiResponse> GetCostCenterByCodeAsync(string costCenterCode, string? expand)
{
var costCenter = await _costCenterRepository.GetByCodeAsync(costCenterCode)
?? throw new NotFoundException("Cost center not found");
var breadcrumb = await _costCenterRepository.GetAncestorBreadcrumbAsync(costCenter.CostCenterId);
List<CostCenterTreeNode>? descendantTree = null;
if (string.Equals(expand, "tree", StringComparison.OrdinalIgnoreCase))
{
descendantTree = await _costCenterRepository.GetDescendantTreeAsync(costCenter.CostCenterId);
}
var data = new CostCenterDetailResponse
{
CostCenter = costCenter,
AncestorBreadcrumb = breadcrumb,
DescendantTree = descendantTree
};
return ResponseHelpers.Ok("Cost center retrieved successfully", data);
}
}
}