115 lines
4.7 KiB
C#
115 lines
4.7 KiB
C#
using Fitness_core_BE.Exceptions;
|
|
using Fitness_core_BE.Helpers;
|
|
using Fitness_core_BE.Infra.UoW;
|
|
using Fitness_core_BE.Interfaces;
|
|
using Fitness_core_BE.DTOs.PaymentsEnablement;
|
|
using Fitness_core_BE.Interfaces.Services;
|
|
using Fitness_core_BE.Models.Enums;
|
|
|
|
namespace Fitness_core_BE.Services
|
|
{
|
|
public class PayoutKycReviewService : IPayoutKycReviewService
|
|
{
|
|
private readonly IPaymentsEnablementRepository _paymentsRepository;
|
|
private readonly UserRoleHelper _userRoleHelper;
|
|
private readonly IUnitOfWork _uow;
|
|
|
|
public PayoutKycReviewService(IPaymentsEnablementRepository paymentsRepository, UserRoleHelper userRoleHelper, IUnitOfWork uow)
|
|
{
|
|
_paymentsRepository = paymentsRepository;
|
|
_userRoleHelper = userRoleHelper;
|
|
_uow = uow;
|
|
}
|
|
|
|
public async Task<List<ClientPayoutKycDto>> ListAsync(string? status)
|
|
{
|
|
var (items, _) = await _paymentsRepository.ListKycAsync(null);
|
|
var latestPerClient = items
|
|
.GroupBy(item => item.FKClientId)
|
|
.Select(group => group
|
|
.OrderByDescending(item => item.SubmittedAt)
|
|
.ThenByDescending(item => item.Id)
|
|
.First());
|
|
|
|
if (!string.IsNullOrWhiteSpace(status))
|
|
{
|
|
if (!Enum.TryParse<KycStatus>(status, true, out var parsedStatus))
|
|
throw new BadRequestException($"Unknown KYC status '{status}'.");
|
|
latestPerClient = latestPerClient
|
|
.Where(item => item.KycStatus == parsedStatus);
|
|
}
|
|
|
|
return latestPerClient
|
|
.OrderByDescending(item => item.SubmittedAt)
|
|
.Select(ToDto)
|
|
.ToList();
|
|
}
|
|
|
|
public async Task<ClientPayoutKycDto> GetByClientIdAsync(Guid clientId)
|
|
{
|
|
var kyc = await _paymentsRepository.GetKycByClientIdAsync(clientId)
|
|
?? throw new NotFoundException("No KYC submission found for this client.");
|
|
return ToDto(kyc);
|
|
}
|
|
|
|
public async Task<ClientPayoutKycDto> ReviewAsync(Guid clientId, ReviewPayoutKycRequest request, HttpContext httpContext)
|
|
{
|
|
var kyc = await _paymentsRepository.GetKycForUpdateAsync(clientId)
|
|
?? throw new NotFoundException("No KYC submission found for this client.");
|
|
|
|
if (!Enum.TryParse<KycStatus>(request.Status, true, out var newStatus))
|
|
throw new BadRequestException($"Unknown KYC status '{request.Status}'.");
|
|
if (newStatus is not KycStatus.Approved and not KycStatus.Rejected)
|
|
throw new BadRequestException("KYC can only be approved or rejected.");
|
|
if (kyc.KycStatus != KycStatus.Pending)
|
|
throw new BadRequestException("Only pending KYC submissions can be reviewed.");
|
|
if (newStatus == KycStatus.Rejected && string.IsNullOrWhiteSpace(request.Reason))
|
|
throw new BadRequestException("A rejection reason is required.");
|
|
|
|
var adminUserId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
var adminId = await _userRoleHelper.GetAdminId(adminUserId)
|
|
?? throw new ForbiddenException("Only admins may review payout KYC.");
|
|
|
|
await _uow.BeginAsync();
|
|
try
|
|
{
|
|
kyc.KycStatus = newStatus;
|
|
kyc.ReviewedAt = DateTime.UtcNow;
|
|
kyc.ReviewedByAdminId = adminId;
|
|
kyc.RejectionReason = newStatus == KycStatus.Rejected
|
|
? request.Reason!.Trim()
|
|
: null;
|
|
|
|
await _uow.CommitAsync();
|
|
return ToDto(kyc);
|
|
}
|
|
catch
|
|
{
|
|
await _uow.RollbackAsync();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// NIC fields used to come from the multi-gym onboarding wizard's OwnerActivation record
|
|
// (collected during signup); that flow no longer exists for a single already-real club,
|
|
// so these are always null here now. If gateway KYC needs NIC info going forward, collect
|
|
// it directly on the KYC submission instead of reintroducing that dependency.
|
|
private static ClientPayoutKycDto ToDto(Models.ClientPayoutKyc kyc)
|
|
{
|
|
return new ClientPayoutKycDto(
|
|
kyc.Id,
|
|
kyc.FKClientId,
|
|
kyc.Client?.ClientName,
|
|
kyc.SelectedGatewayId,
|
|
kyc.SelectedGatewayName,
|
|
kyc.KycStatus.ToString(),
|
|
kyc.SubmittedAt,
|
|
kyc.ReviewedAt,
|
|
kyc.RejectionReason,
|
|
null,
|
|
null,
|
|
null);
|
|
}
|
|
}
|
|
}
|