Files
baya-monorepo/server/src/Infrastructure/Baya.Infrastructure.Persistence/Repositories/PartnerCenterRepository.cs
T
2026-07-10 03:22:29 +03:30

146 lines
6.7 KiB
C#

#nullable enable
using Baya.Application.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Application.Models.PartnerCenters;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Invoices;
using Baya.Domain.Entities.PartnerCenters;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;
namespace Baya.Infrastructure.Persistence.Repositories;
internal sealed class PartnerCenterRepository : BaseAsyncRepository<PartnerCenter>, IPartnerCenterRepository
{
private const int DashboardNurseCap = 50;
public PartnerCenterRepository(ApplicationDbContext dbContext) : base(dbContext)
{
}
public Task AddAsync(PartnerCenter center, CancellationToken cancellationToken) => base.AddAsync(center);
public Task<PartnerCenter?> GetTrackedAsync(long centerId, CancellationToken cancellationToken)
=> Entities.FirstOrDefaultAsync(c => c.Id == centerId, cancellationToken);
public Task<bool> ExistsAsync(long centerId, CancellationToken cancellationToken)
=> Entities.AnyAsync(c => c.Id == centerId, cancellationToken);
public async Task<PartnerCenterDetailDto?> GetDetailAsync(long centerId, CancellationToken cancellationToken)
{
// Project (the converter decrypts settlement_iban here); the plaintext IBAN is masked in memory below and
// never leaves this method.
var raw = await Entities.AsNoTracking()
.Where(c => c.Id == centerId)
.Select(c => new
{
c.Id, c.Name, c.LegalEntityType, c.MohEstablishmentPermitNo, c.TechnicalDirectorNurseUserId,
c.TechnicalDirectorLicenseNo, c.EnamadCode, c.SettlementIban, c.IsMerchantOfRecord, c.CommissionRate,
c.AdminUserId, c.IsActive, c.VerifiedAt, c.CreatedAt,
SponsoredCount = DbContext.Set<NurseProfile>().Count(n => n.PartnerCenterId == c.Id)
})
.FirstOrDefaultAsync(cancellationToken);
if (raw is null)
return null;
return new PartnerCenterDetailDto(
raw.Id, raw.Name, raw.LegalEntityType, raw.MohEstablishmentPermitNo, raw.TechnicalDirectorNurseUserId,
raw.TechnicalDirectorLicenseNo, raw.EnamadCode,
string.IsNullOrEmpty(raw.SettlementIban) ? null : Mask.IbanTail(raw.SettlementIban),
raw.IsMerchantOfRecord, raw.CommissionRate, raw.AdminUserId, raw.IsActive, raw.VerifiedAt,
raw.SponsoredCount, raw.CreatedAt);
}
public async Task<PagedResult<PartnerCenterListItemDto>> ListAsync(bool? isActive, int page, int pageSize, CancellationToken cancellationToken)
{
var query = Entities.AsNoTracking().AsQueryable();
if (isActive is { } active)
query = query.Where(c => c.IsActive == active);
var total = await query.CountAsync(cancellationToken);
var items = await query
.OrderByDescending(c => c.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(c => new PartnerCenterListItemDto(
c.Id, c.Name, c.LegalEntityType, c.IsMerchantOfRecord, c.IsActive, c.VerifiedAt,
DbContext.Set<NurseProfile>().Count(n => n.PartnerCenterId == c.Id),
c.AdminUserId))
.ToListAsync(cancellationToken);
return new PagedResult<PartnerCenterListItemDto>(items, total, page, pageSize);
}
public async Task<CenterForBookingDto?> ResolveCenterForBookingAsync(long bookingId, CancellationToken cancellationToken)
{
var booking = await DbContext.Set<Booking>().AsNoTracking()
.Where(b => b.Id == bookingId)
.Select(b => new { b.NurseId })
.FirstOrDefaultAsync(cancellationToken);
if (booking is null)
return null;
var centerId = await DbContext.Set<NurseProfile>().AsNoTracking()
.Where(n => n.Id == booking.NurseId)
.Select(n => n.PartnerCenterId)
.FirstOrDefaultAsync(cancellationToken);
if (centerId is { } id)
{
var center = await Entities.AsNoTracking()
.Where(c => c.Id == id)
.Select(c => new { c.Id, c.Name, c.IsMerchantOfRecord })
.FirstOrDefaultAsync(cancellationToken);
// Merchant-of-record center → it is the invoice issuer + settlement target. Otherwise the platform
// issues (a non-MoR sponsor does not change the issuer).
if (center is { IsMerchantOfRecord: true })
return new CenterForBookingDto(bookingId, InvoiceIssuingEntityType.PartnerCenter, center.Id, center.Name, true);
}
return new CenterForBookingDto(bookingId, InvoiceIssuingEntityType.Platform, null, null, false);
}
public Task<int?> GetAdminUserIdAsync(long centerId, CancellationToken cancellationToken)
=> Entities.AsNoTracking()
.Where(c => c.Id == centerId)
.Select(c => (int?)c.AdminUserId)
.FirstOrDefaultAsync(cancellationToken);
public async Task<CenterDashboardDto?> GetDashboardAsync(long centerId, CancellationToken cancellationToken)
{
var center = await Entities.AsNoTracking()
.Where(c => c.Id == centerId)
.Select(c => new { c.Id, c.Name, c.IsMerchantOfRecord, c.IsActive, c.SettlementIban })
.FirstOrDefaultAsync(cancellationToken);
if (center is null)
return null;
var sponsoredNurseCount = await DbContext.Set<NurseProfile>().AsNoTracking()
.CountAsync(n => n.PartnerCenterId == centerId, cancellationToken);
var sponsoredNurses = await DbContext.Set<NurseProfile>().AsNoTracking()
.Where(n => n.PartnerCenterId == centerId)
.OrderBy(n => n.Id)
.Take(DashboardNurseCap)
.Select(n => new SponsoredNurseDto(n.Id, n.UserId, n.IsVerified, n.AverageRating, n.TotalCompletedBookings))
.ToListAsync(cancellationToken);
var sponsoredBookingCount = await DbContext.Set<Booking>().AsNoTracking()
.CountAsync(b => DbContext.Set<NurseProfile>().Any(n => n.Id == b.NurseId && n.PartnerCenterId == centerId), cancellationToken);
var invoiceCount = await DbContext.Set<Invoice>().AsNoTracking()
.CountAsync(i => i.PartnerCenterId == centerId, cancellationToken);
return new CenterDashboardDto(
center.Id, center.Name, center.IsMerchantOfRecord, center.IsActive,
string.IsNullOrEmpty(center.SettlementIban) ? null : Mask.IbanTail(center.SettlementIban),
sponsoredNurseCount, sponsoredBookingCount, invoiceCount, sponsoredNurses);
}
}