206 lines
6.9 KiB
C#
206 lines
6.9 KiB
C#
using Fitness_core_BE.Interfaces;
|
|
using Fitness_core_BE.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Fitness_core_BE.Repos
|
|
{
|
|
public class StaffRepository : IStaffRepository
|
|
{
|
|
private readonly TenantDbContext _context;
|
|
private readonly CentralDbContext _central;
|
|
|
|
public StaffRepository(TenantDbContext context, CentralDbContext central)
|
|
{
|
|
_context = context;
|
|
_central = central;
|
|
}
|
|
|
|
// Staff.User/Staff.Client are central-bucket entities (not a real relationship once
|
|
// physically split) -- these load the central User rows separately and attach them
|
|
// in memory onto the already-fetched Staff objects, so callers reading staff.User.*
|
|
// still see the right data without needing to change.
|
|
private async Task AttachUsersAsync(IEnumerable<Staff> staffList)
|
|
{
|
|
var userIds = staffList.Select(s => s.FKUserId).Distinct().ToList();
|
|
if (userIds.Count == 0) return;
|
|
|
|
var users = await _central.Users
|
|
.AsNoTracking()
|
|
.Where(u => userIds.Contains(u.UserId))
|
|
.ToDictionaryAsync(u => u.UserId);
|
|
|
|
foreach (var staff in staffList)
|
|
{
|
|
if (users.TryGetValue(staff.FKUserId, out var user))
|
|
staff.User = user;
|
|
}
|
|
}
|
|
|
|
public async Task<List<Staff>> GetAllStaff(bool includeInactive = false)
|
|
{
|
|
var query = _context.Staff
|
|
.Include(s => s.GymRole)
|
|
.AsQueryable();
|
|
|
|
if (!includeInactive)
|
|
{
|
|
query = query.Where(s => s.IsActive);
|
|
}
|
|
|
|
var staffList = await query.ToListAsync();
|
|
await AttachUsersAsync(staffList);
|
|
return staffList.OrderBy(s => s.User?.UserName).ToList();
|
|
}
|
|
|
|
public async Task<Staff?> GetStaffById(Guid staffId)
|
|
{
|
|
var staff = await _context.Staff
|
|
.Include(s => s.GymRole)
|
|
.FirstOrDefaultAsync(s => s.StaffId == staffId);
|
|
if (staff != null)
|
|
await AttachUsersAsync(new[] { staff });
|
|
return staff;
|
|
}
|
|
|
|
public async Task<List<Staff>> GetStaffByClientId(Guid clientId, bool includeInactive = false)
|
|
{
|
|
var query = _context.Staff
|
|
.Include(s => s.GymRole)
|
|
.Where(s => s.FKClientId == clientId);
|
|
|
|
if (!includeInactive)
|
|
{
|
|
query = query.Where(s => s.IsActive);
|
|
}
|
|
|
|
var staffList = await query.ToListAsync();
|
|
await AttachUsersAsync(staffList);
|
|
return staffList.OrderBy(s => s.User?.UserName).ToList();
|
|
}
|
|
|
|
public async Task<Staff> CreateStaff(Staff staff, User user)
|
|
{
|
|
// Create User first (central)
|
|
user.UserId = Guid.NewGuid();
|
|
user.CreateDate = DateTime.UtcNow;
|
|
user.IsActive = true;
|
|
_central.Users.Add(user);
|
|
|
|
// Create Staff (tenant)
|
|
staff.StaffId = Guid.NewGuid();
|
|
staff.FKUserId = user.UserId;
|
|
staff.CreatedAt = DateTime.UtcNow;
|
|
staff.HireDate = staff.HireDate == default ? DateTime.UtcNow : staff.HireDate;
|
|
staff.IsActive = true;
|
|
|
|
// Verify Client exists (central)
|
|
var clientExists = await _central.Client.AnyAsync(c => c.ClientId == staff.FKClientId);
|
|
if (!clientExists)
|
|
{
|
|
throw new Exception("Client not found");
|
|
}
|
|
|
|
_context.Staff.Add(staff);
|
|
|
|
return staff;
|
|
}
|
|
|
|
public async Task<Staff> UpdateStaff(Staff staff)
|
|
{
|
|
var existingStaff = await _context.Staff
|
|
.FirstOrDefaultAsync(s => s.StaffId == staff.StaffId);
|
|
|
|
if (existingStaff == null)
|
|
{
|
|
throw new Exception("Staff not found");
|
|
}
|
|
|
|
// Update User fields if provided -- User lives in the central DB.
|
|
if (staff.User != null && (!string.IsNullOrEmpty(staff.User.UserName)
|
|
|| !string.IsNullOrEmpty(staff.User.Email)
|
|
|| !string.IsNullOrEmpty(staff.User.PhoneNumber)))
|
|
{
|
|
var centralUser = await _central.Users.FirstOrDefaultAsync(u => u.UserId == existingStaff.FKUserId);
|
|
if (centralUser != null)
|
|
{
|
|
if (!string.IsNullOrEmpty(staff.User.UserName))
|
|
centralUser.UserName = staff.User.UserName;
|
|
if (!string.IsNullOrEmpty(staff.User.Email))
|
|
centralUser.Email = staff.User.Email;
|
|
if (!string.IsNullOrEmpty(staff.User.PhoneNumber))
|
|
centralUser.PhoneNumber = staff.User.PhoneNumber;
|
|
existingStaff.User = centralUser;
|
|
}
|
|
}
|
|
|
|
// Update Staff fields if provided
|
|
if (!string.IsNullOrEmpty(staff.StaffPosition))
|
|
{
|
|
existingStaff.StaffPosition = staff.StaffPosition;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(staff.Department))
|
|
{
|
|
existingStaff.Department = staff.Department;
|
|
}
|
|
|
|
// Update IsActive explicitly
|
|
existingStaff.IsActive = staff.IsActive;
|
|
|
|
return existingStaff;
|
|
}
|
|
|
|
public async Task<bool> DeleteStaff(Guid staffId, bool softDelete = true)
|
|
{
|
|
var staff = await _context.Staff.FindAsync(staffId);
|
|
|
|
if (staff == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (softDelete)
|
|
{
|
|
// Soft delete: just mark as inactive
|
|
staff.IsActive = false;
|
|
}
|
|
else
|
|
{
|
|
// Hard delete: remove from database
|
|
_context.Staff.Remove(staff);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public async Task<StaffPayRate?> GetActivePayRateAsync(Guid staffId)
|
|
{
|
|
return await _context.StaffPayRates
|
|
.Where(r => r.FKStaffId == staffId && r.IsActive)
|
|
.OrderByDescending(r => r.EffectiveFrom)
|
|
.FirstOrDefaultAsync();
|
|
}
|
|
|
|
public async Task<StaffPayRate> AddPayRateAsync(StaffPayRate rate)
|
|
{
|
|
await _context.StaffPayRates.AddAsync(rate);
|
|
return rate;
|
|
}
|
|
|
|
public async Task<List<StaffPayment>> GetPaymentsAsync(Guid staffId)
|
|
{
|
|
return await _context.StaffPayments
|
|
.AsNoTracking()
|
|
.Where(p => p.FKStaffId == staffId)
|
|
.OrderByDescending(p => p.PaidAt)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<StaffPayment> AddPaymentAsync(StaffPayment payment)
|
|
{
|
|
_context.StaffPayments.Add(payment);
|
|
return payment;
|
|
}
|
|
}
|
|
}
|