ensure unique names for club,person,expense
This commit is contained in:
parent
7a96cf0cc4
commit
29cebcbb81
|
|
@ -3,6 +3,7 @@ using Koogle.Application.DTOs;
|
|||
using Koogle.Application.Interfaces;
|
||||
using Koogle.Domain.Entities;
|
||||
using Koogle.Domain.Interfaces;
|
||||
using Koogle.Infrastructure.Repositories;
|
||||
using KoogleApp.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Cryptography;
|
||||
|
|
@ -48,6 +49,12 @@ public class ClubService : IClubService
|
|||
/// <inheritdoc />
|
||||
public async Task<ClubDto> CreateAsync(CreateClubDto dto, CancellationToken ct = default)
|
||||
{
|
||||
var existingClub = await _clubRepository.GetByNameAsync(dto.Name, null, ct);
|
||||
if (existingClub != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Club with name '{dto.Name}' already exists");
|
||||
}
|
||||
|
||||
var entity = new Club
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -67,6 +74,12 @@ public class ClubService : IClubService
|
|||
var existing = await _clubRepository.GetByIdAsync(dto.Id, ct)
|
||||
?? throw new InvalidOperationException($"Club with id {dto.Id} not found.");
|
||||
|
||||
var existingClub = await _clubRepository.GetByNameAsync(dto.Name, dto.Id, ct);
|
||||
if (existingClub != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Club with name '{dto.Name}' already exists");
|
||||
}
|
||||
|
||||
existing.Name = dto.Name;
|
||||
existing.ExpenseCalculation = dto.ExpenseCalculation;
|
||||
existing.ModifiedAt = DateTime.UtcNow;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Koogle.Application.DTOs;
|
|||
using Koogle.Application.Interfaces;
|
||||
using Koogle.Domain.Entities;
|
||||
using Koogle.Domain.Interfaces;
|
||||
using Koogle.Infrastructure.Repositories;
|
||||
|
||||
namespace Koogle.Application.Services;
|
||||
|
||||
|
|
@ -51,6 +52,12 @@ public class ExpenseService : IExpenseService
|
|||
/// <inheritdoc />
|
||||
public async Task<ExpenseDto> CreateAsync(CreateExpenseDto dto, CancellationToken ct = default)
|
||||
{
|
||||
var existingExpense = await _expenseRepository.GetByNameAsync(_clubContext.ClubId, dto.Name, null, ct);
|
||||
if (existingExpense != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Expense with name '{dto.Name}' already exists");
|
||||
}
|
||||
|
||||
var entity = new Expense
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -79,6 +86,12 @@ public class ExpenseService : IExpenseService
|
|||
if (existing.ClubId != _clubContext.ClubId)
|
||||
throw new InvalidOperationException("Expense does not belong to current club.");
|
||||
|
||||
var existingExpense = await _expenseRepository.GetByNameAsync(_clubContext.ClubId, dto.Name, dto.Id, ct);
|
||||
if (existingExpense != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Expense with name '{dto.Name}' already exists");
|
||||
}
|
||||
|
||||
existing.Name = dto.Name;
|
||||
existing.Description = dto.Description;
|
||||
existing.Price = dto.Price;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class PersonService : IPersonService
|
|||
/// <inheritdoc />
|
||||
public async Task<PersonDto> CreateAsync(CreatePersonDto dto, CancellationToken ct = default)
|
||||
{
|
||||
var existingPerson = await _personRepository.GetByNameAsync(_clubContext.ClubId, dto.Name, ct);
|
||||
var existingPerson = await _personRepository.GetByNameAsync(_clubContext.ClubId, dto.Name, null, ct);
|
||||
if (existingPerson != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Person with name '{dto.Name}' already exists");
|
||||
|
|
@ -81,7 +81,7 @@ public class PersonService : IPersonService
|
|||
if (existing.ClubId != _clubContext.ClubId)
|
||||
throw new InvalidOperationException("Person does not belong to current club.");
|
||||
|
||||
var existingPerson = await _personRepository.GetByNameAsync(_clubContext.ClubId, dto.Name, ct);
|
||||
var existingPerson = await _personRepository.GetByNameAsync(_clubContext.ClubId, dto.Name, dto.Id, ct);
|
||||
if (existingPerson != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Person with name '{dto.Name}' already exists");
|
||||
|
|
|
|||
|
|
@ -45,4 +45,13 @@ public interface IClubRepository
|
|||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>True if deleted successfully; otherwise, false.</returns>
|
||||
Task<bool> DeleteAsync(Guid id, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a club by its name within a specific context.
|
||||
/// </summary>
|
||||
/// <param name="clubName">Name to search for</param>
|
||||
/// <param name="excludeGuid">A unique ID to exclude from search</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns></returns>
|
||||
Task<Club?> GetByNameAsync(string clubName, Guid? excludeGuid, CancellationToken ct = default);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,4 +46,14 @@ public interface IExpenseRepository
|
|||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>True if deleted successfully; otherwise, false.</returns>
|
||||
Task<bool> DeleteAsync(Guid id, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Read an expense by its name within a specific club context.
|
||||
/// </summary>
|
||||
/// <param name="clubId"></param>
|
||||
/// <param name="expenseName"></param>
|
||||
/// <param name="excludeGuid">Guid of an expense, that should be excluded</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns>The expense with the name or null</returns>
|
||||
Task<Expense?> GetByNameAsync(Guid clubId, string expenseName, Guid? excludeGuid, CancellationToken ct = default);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,9 +50,10 @@ public interface IPersonRepository
|
|||
/// <summary>
|
||||
/// Read a person by its name within a specific club context.
|
||||
/// </summary>
|
||||
/// <param name="clubContextClubId"></param>
|
||||
/// <param name="dtoName"></param>
|
||||
/// <param name="clubId"></param>
|
||||
/// <param name="personName"></param>
|
||||
/// <param name="excludeGuid">Guid of a person, that should be excluded</param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns>The person with the name or null</returns>
|
||||
Task<Person?> GetByNameAsync(Guid clubContextClubId, string dtoName, CancellationToken ct);
|
||||
Task<Person?> GetByNameAsync(Guid clubId, string personName, Guid? excludeGuid, CancellationToken ct = default);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,4 +61,18 @@ public class ClubRepository(IDbContextFactory<AppDbContext> contextFactory) : IC
|
|||
await context.SaveChangesAsync(ct);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Club?> GetByNameAsync(string clubName, Guid? excludeGuid, CancellationToken ct = default)
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
||||
var query = context.Clubs
|
||||
.Where(c => c.Name.ToLower() == clubName.ToLower() && !c.IsDeleted);
|
||||
if (excludeGuid.HasValue)
|
||||
{
|
||||
query = query.Where(c => c.Id != excludeGuid.Value);
|
||||
}
|
||||
var entity = await query.FirstOrDefaultAsync(ct);
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,4 +63,18 @@ public class ExpenseRepository(IDbContextFactory<AppDbContext> contextFactory) :
|
|||
await context.SaveChangesAsync(ct);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Expense?> GetByNameAsync(Guid clubId, string expenseName, Guid? excludeGuid, CancellationToken ct = default)
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
||||
var query = context.Expenses
|
||||
.Where(e => e.ClubId == clubId && !e.IsDeleted && e.Name.ToLower() == expenseName.ToLower());
|
||||
if (excludeGuid.HasValue)
|
||||
{
|
||||
query = query.Where(e => e.Id != excludeGuid.Value);
|
||||
}
|
||||
var entity = await query.FirstOrDefaultAsync(ct);
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,11 +65,22 @@ public class PersonRepository(IDbContextFactory<AppDbContext> contextFactory) :
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Person?> GetByNameAsync(Guid clubContextClubId, string dtoName, CancellationToken ct)
|
||||
public async Task<Person?> GetByNameAsync(Guid clubId, string personName, Guid? excludeGuid, CancellationToken ct = default)
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
||||
var entity = await context.Persons
|
||||
.FirstOrDefaultAsync(p => p.ClubId == clubContextClubId && p.Name == dtoName && !p.IsDeleted, ct);
|
||||
Person? entity;
|
||||
if (excludeGuid != null)
|
||||
{
|
||||
entity = await context.Persons
|
||||
.FirstOrDefaultAsync(
|
||||
p => p.ClubId == clubId && p.Name.ToLower() == personName.ToLower() && !p.IsDeleted && p.Id != excludeGuid, ct);
|
||||
} else
|
||||
{
|
||||
entity = await context.Persons
|
||||
.FirstOrDefaultAsync(
|
||||
p => p.ClubId == clubId && p.Name.ToLower() == personName.ToLower() && !p.IsDeleted, ct);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue