130 lines
5.1 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|