Compare commits
No commits in common. "c4baf55c91c825ca891dfb5e52055a361196b455" and "29cebcbb815eb781be2b907bd7e353bae499ee66" have entirely different histories.
c4baf55c91
...
29cebcbb81
|
|
@ -947,7 +947,7 @@ Web: Store/GameState/, Components/Game/, Hubs/GameHub
|
||||||
|
|
||||||
| ☐ | Phase | Bereich | Beschreibung | Dateien |
|
| ☐ | Phase | Bereich | Beschreibung | Dateien |
|
||||||
|---|-------|---------|--------------|---------|
|
|---|-------|---------|--------------|---------|
|
||||||
| ✓ | H0 | Trigger-Engine | ITriggerService + Repository | 5 |
|
| ☐ | **H0** | **Trigger-Engine** | **ITriggerService + Repository** | **5** |
|
||||||
| ☐ | H1 | Foundation | GameState Fluxor + Enums | 6 |
|
| ☐ | H1 | Foundation | GameState Fluxor + Enums | 6 |
|
||||||
| ☐ | H2 | Components | Pin Input Components (neu schreiben) | 5 |
|
| ☐ | H2 | Components | Pin Input Components (neu schreiben) | 5 |
|
||||||
| ☐ | H3 | Framework | Game Definition Framework | 5 |
|
| ☐ | H3 | Framework | Game Definition Framework | 5 |
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
using Koogle.Domain.Enums;
|
|
||||||
|
|
||||||
namespace Koogle.Application.DTOs;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data transfer object representing a trigger.
|
|
||||||
/// </summary>
|
|
||||||
public record TriggerDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unique identifier of the trigger.
|
|
||||||
/// </summary>
|
|
||||||
public Guid Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the trigger.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of expense trigger.
|
|
||||||
/// </summary>
|
|
||||||
public ExpenseTriggerType ExpenseTriggerType { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Expenses linked to this trigger for the current club.
|
|
||||||
/// </summary>
|
|
||||||
public List<ExpenseSummaryDto> LinkedExpenses { get; init; } = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data transfer object for linking/unlinking an expense to a trigger.
|
|
||||||
/// </summary>
|
|
||||||
public record ExpenseTriggerLinkDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The expense identifier.
|
|
||||||
/// </summary>
|
|
||||||
public Guid ExpenseId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The trigger identifier.
|
|
||||||
/// </summary>
|
|
||||||
public Guid TriggerId { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data transfer object for firing a trigger.
|
|
||||||
/// </summary>
|
|
||||||
public record FireTriggerDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The trigger type to fire.
|
|
||||||
/// </summary>
|
|
||||||
public ExpenseTriggerType TriggerType { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The person receiving the expense(s).
|
|
||||||
/// </summary>
|
|
||||||
public Guid PersonId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The day context for the expense.
|
|
||||||
/// </summary>
|
|
||||||
public Guid DayId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Optional game context for the expense.
|
|
||||||
/// </summary>
|
|
||||||
public Guid? GameId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Multiplier for expense price (e.g., remaining points). Default is 1.
|
|
||||||
/// </summary>
|
|
||||||
public int Multiplier { get; init; } = 1;
|
|
||||||
}
|
|
||||||
|
|
@ -31,7 +31,6 @@ namespace Koogle.Application
|
||||||
services.AddScoped<IPersonExpenseService, PersonExpenseService>();
|
services.AddScoped<IPersonExpenseService, PersonExpenseService>();
|
||||||
services.AddScoped<IDashboardService, DashboardService>();
|
services.AddScoped<IDashboardService, DashboardService>();
|
||||||
services.AddScoped<IEmailService, StubEmailService>();
|
services.AddScoped<IEmailService, StubEmailService>();
|
||||||
services.AddScoped<ITriggerService, TriggerService>();
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
using Koogle.Application.DTOs;
|
|
||||||
using Koogle.Domain.Enums;
|
|
||||||
|
|
||||||
namespace Koogle.Application.Interfaces;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Service interface for managing triggers and firing automatic expense assignments.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITriggerService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all available trigger types with their associated expenses for the current club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>A list of trigger DTOs with their linked expenses.</returns>
|
|
||||||
Task<List<TriggerDto>> GetAllTriggersAsync(CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all expenses linked to a specific trigger type for the current club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The expense trigger type.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>A list of expenses linked to the trigger type.</returns>
|
|
||||||
Task<List<ExpenseDto>> GetExpensesForTriggerAsync(ExpenseTriggerType type, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a trigger type has any linked expenses for the current club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The expense trigger type.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>True if the trigger has linked expenses; otherwise, false.</returns>
|
|
||||||
Task<bool> HasExpensesForTriggerAsync(ExpenseTriggerType type, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fires a trigger, creating PersonExpenses for all linked expenses.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The trigger type (e.g., ExpensePoint, Circle, NinePins).</param>
|
|
||||||
/// <param name="personId">The person receiving the expense(s).</param>
|
|
||||||
/// <param name="dayId">The day context for the expense.</param>
|
|
||||||
/// <param name="gameId">Optional game context for the expense.</param>
|
|
||||||
/// <param name="multiplier">Multiplier for expense price (e.g., remaining points in Scheiss-Spiel). Default is 1.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>A list of created PersonExpense DTOs.</returns>
|
|
||||||
Task<List<PersonExpenseDto>> FireTriggerAsync(
|
|
||||||
ExpenseTriggerType type,
|
|
||||||
Guid personId,
|
|
||||||
Guid dayId,
|
|
||||||
Guid? gameId = null,
|
|
||||||
int multiplier = 1,
|
|
||||||
CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Links an expense to a trigger for the current club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="expenseId">The expense to link.</param>
|
|
||||||
/// <param name="triggerId">The trigger to link to.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
Task LinkExpenseToTriggerAsync(Guid expenseId, Guid triggerId, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unlinks an expense from a trigger for the current club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="expenseId">The expense to unlink.</param>
|
|
||||||
/// <param name="triggerId">The trigger to unlink from.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>True if unlinked successfully; otherwise, false.</returns>
|
|
||||||
Task<bool> UnlinkExpenseFromTriggerAsync(Guid expenseId, Guid triggerId, CancellationToken ct = default);
|
|
||||||
}
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
using AutoMapper;
|
|
||||||
using Koogle.Application.DTOs;
|
|
||||||
using Koogle.Application.Interfaces;
|
|
||||||
using Koogle.Domain.Entities;
|
|
||||||
using Koogle.Domain.Enums;
|
|
||||||
using Koogle.Domain.Interfaces;
|
|
||||||
using KoogleApp.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Koogle.Application.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Service for managing triggers and firing automatic expense assignments.
|
|
||||||
/// </summary>
|
|
||||||
public class TriggerService : ITriggerService
|
|
||||||
{
|
|
||||||
private readonly IDbContextFactory<AppDbContext> _contextFactory;
|
|
||||||
private readonly ITriggerRepository _triggerRepository;
|
|
||||||
private readonly ICurrentClubContext _clubContext;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TriggerService"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="contextFactory">The database context factory.</param>
|
|
||||||
/// <param name="triggerRepository">The trigger repository.</param>
|
|
||||||
/// <param name="clubContext">The current club context.</param>
|
|
||||||
/// <param name="mapper">The AutoMapper instance.</param>
|
|
||||||
public TriggerService(
|
|
||||||
IDbContextFactory<AppDbContext> contextFactory,
|
|
||||||
ITriggerRepository triggerRepository,
|
|
||||||
ICurrentClubContext clubContext,
|
|
||||||
IMapper mapper)
|
|
||||||
{
|
|
||||||
_contextFactory = contextFactory;
|
|
||||||
_triggerRepository = triggerRepository;
|
|
||||||
_clubContext = clubContext;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<List<TriggerDto>> GetAllTriggersAsync(CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var triggers = await _triggerRepository.GetAllAsync(ct);
|
|
||||||
var expenseTriggers = await _triggerRepository.GetExpenseTriggersByClubIdAsync(_clubContext.ClubId, ct);
|
|
||||||
|
|
||||||
var result = triggers.Select(t => new TriggerDto
|
|
||||||
{
|
|
||||||
Id = t.Id,
|
|
||||||
Name = t.Name,
|
|
||||||
ExpenseTriggerType = t.ExpenseTriggerType,
|
|
||||||
LinkedExpenses = expenseTriggers
|
|
||||||
.Where(et => et.TriggerId == t.Id && !et.Expense.IsDeleted)
|
|
||||||
.Select(et => new ExpenseSummaryDto
|
|
||||||
{
|
|
||||||
Id = et.Expense.Id,
|
|
||||||
Name = et.Expense.Name,
|
|
||||||
Price = et.Expense.Price,
|
|
||||||
IsOneClick = et.Expense.IsOneClick
|
|
||||||
})
|
|
||||||
.ToList()
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<List<ExpenseDto>> GetExpensesForTriggerAsync(ExpenseTriggerType type, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var expenses = await _triggerRepository.GetExpensesByTriggerTypeAsync(_clubContext.ClubId, type, ct);
|
|
||||||
return _mapper.Map<List<ExpenseDto>>(expenses);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<bool> HasExpensesForTriggerAsync(ExpenseTriggerType type, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var expenses = await _triggerRepository.GetExpensesByTriggerTypeAsync(_clubContext.ClubId, type, ct);
|
|
||||||
return expenses.Count > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<List<PersonExpenseDto>> FireTriggerAsync(
|
|
||||||
ExpenseTriggerType type,
|
|
||||||
Guid personId,
|
|
||||||
Guid dayId,
|
|
||||||
Guid? gameId = null,
|
|
||||||
int multiplier = 1,
|
|
||||||
CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await _contextFactory.CreateDbContextAsync(ct);
|
|
||||||
|
|
||||||
// Get all expenses linked to this trigger type for the current club
|
|
||||||
var expenses = await _triggerRepository.GetExpensesByTriggerTypeAsync(_clubContext.ClubId, type, ct);
|
|
||||||
|
|
||||||
if (expenses.Count == 0)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
// Validate day exists and is not closed
|
|
||||||
var day = await context.Days
|
|
||||||
.Where(d => d.Id == dayId && d.ClubId == _clubContext.ClubId && !d.IsDeleted)
|
|
||||||
.FirstOrDefaultAsync(ct)
|
|
||||||
?? throw new InvalidOperationException($"Day with id {dayId} not found.");
|
|
||||||
|
|
||||||
if (day.Status == DayStatus.Closed)
|
|
||||||
throw new InvalidOperationException("Cannot add expenses to a closed day.");
|
|
||||||
|
|
||||||
// Validate person exists
|
|
||||||
var person = await context.Persons
|
|
||||||
.Where(p => p.Id == personId && p.ClubId == _clubContext.ClubId && !p.IsDeleted)
|
|
||||||
.FirstOrDefaultAsync(ct)
|
|
||||||
?? throw new InvalidOperationException($"Person with id {personId} not found.");
|
|
||||||
|
|
||||||
var createdExpenses = new List<PersonExpense>();
|
|
||||||
|
|
||||||
foreach (var expense in expenses)
|
|
||||||
{
|
|
||||||
// Calculate price with multiplier
|
|
||||||
var price = expense.Price * multiplier;
|
|
||||||
|
|
||||||
var personExpense = new PersonExpense
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
PersonId = personId,
|
|
||||||
ExpenseId = expense.Id,
|
|
||||||
DayId = dayId,
|
|
||||||
GameId = gameId,
|
|
||||||
ClubId = _clubContext.ClubId,
|
|
||||||
Name = expense.Name,
|
|
||||||
Price = price,
|
|
||||||
ExpenseType = expense.ExpenseType,
|
|
||||||
PersonExpenseStatus = PersonExpenseStatus.Open,
|
|
||||||
AssignedById = Guid.Empty, // TODO: Get from ICurrentUserService when available
|
|
||||||
CreatedAt = DateTime.UtcNow,
|
|
||||||
IsDeleted = false
|
|
||||||
};
|
|
||||||
|
|
||||||
context.PersonExpenses.Add(personExpense);
|
|
||||||
createdExpenses.Add(personExpense);
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.SaveChangesAsync(ct);
|
|
||||||
|
|
||||||
// Reload with navigation properties
|
|
||||||
var result = new List<PersonExpenseDto>();
|
|
||||||
foreach (var pe in createdExpenses)
|
|
||||||
{
|
|
||||||
var loaded = await context.PersonExpenses
|
|
||||||
.Where(p => p.Id == pe.Id)
|
|
||||||
.Include(p => p.Person)
|
|
||||||
.Include(p => p.Day)
|
|
||||||
.FirstOrDefaultAsync(ct);
|
|
||||||
|
|
||||||
if (loaded is not null)
|
|
||||||
{
|
|
||||||
result.Add(MapToDto(loaded));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task LinkExpenseToTriggerAsync(Guid expenseId, Guid triggerId, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await _contextFactory.CreateDbContextAsync(ct);
|
|
||||||
|
|
||||||
// Validate expense exists for current club
|
|
||||||
var expense = await context.Expenses
|
|
||||||
.Where(e => e.Id == expenseId && e.ClubId == _clubContext.ClubId && !e.IsDeleted)
|
|
||||||
.FirstOrDefaultAsync(ct)
|
|
||||||
?? throw new InvalidOperationException($"Expense with id {expenseId} not found.");
|
|
||||||
|
|
||||||
// Validate trigger exists
|
|
||||||
var trigger = await _triggerRepository.GetByIdAsync(triggerId, ct)
|
|
||||||
?? throw new InvalidOperationException($"Trigger with id {triggerId} not found.");
|
|
||||||
|
|
||||||
// Check if mapping already exists
|
|
||||||
var existing = await context.ExpenseTriggers
|
|
||||||
.FirstOrDefaultAsync(et =>
|
|
||||||
et.ClubId == _clubContext.ClubId &&
|
|
||||||
et.ExpenseId == expenseId &&
|
|
||||||
et.TriggerId == triggerId, ct);
|
|
||||||
|
|
||||||
if (existing is not null)
|
|
||||||
return; // Already linked
|
|
||||||
|
|
||||||
var expenseTrigger = new ExpenseTrigger
|
|
||||||
{
|
|
||||||
ClubId = _clubContext.ClubId,
|
|
||||||
ExpenseId = expenseId,
|
|
||||||
TriggerId = triggerId,
|
|
||||||
AssignedAt = DateTime.UtcNow,
|
|
||||||
AssignedById = Guid.Empty // TODO: Get from ICurrentUserService when available
|
|
||||||
};
|
|
||||||
|
|
||||||
await _triggerRepository.AddExpenseTriggerAsync(expenseTrigger, ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<bool> UnlinkExpenseFromTriggerAsync(Guid expenseId, Guid triggerId, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
return await _triggerRepository.RemoveExpenseTriggerAsync(_clubContext.ClubId, expenseId, triggerId, ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps a PersonExpense entity to a PersonExpenseDto.
|
|
||||||
/// </summary>
|
|
||||||
private static PersonExpenseDto MapToDto(PersonExpense entity)
|
|
||||||
{
|
|
||||||
return new PersonExpenseDto
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
PersonId = entity.PersonId,
|
|
||||||
PersonName = entity.Person?.Name ?? string.Empty,
|
|
||||||
ExpenseId = entity.ExpenseId,
|
|
||||||
DayId = entity.DayId,
|
|
||||||
DayPostDate = entity.Day?.PostDate ?? DateTime.MinValue,
|
|
||||||
GameId = entity.GameId,
|
|
||||||
ClubId = entity.ClubId,
|
|
||||||
Name = entity.Name,
|
|
||||||
Price = entity.Price,
|
|
||||||
ExpenseType = entity.ExpenseType,
|
|
||||||
PersonExpenseStatus = entity.PersonExpenseStatus,
|
|
||||||
AssignedById = entity.AssignedById,
|
|
||||||
CreatedAt = entity.CreatedAt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
using Koogle.Domain.Entities;
|
|
||||||
using Koogle.Domain.Enums;
|
|
||||||
|
|
||||||
namespace Koogle.Domain.Interfaces;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Repository interface for managing trigger entities and expense-trigger relationships.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITriggerRepository
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves all triggers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>A list of all triggers.</returns>
|
|
||||||
Task<List<Trigger>> GetAllAsync(CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves a trigger by its unique identifier.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The unique identifier of the trigger.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>The trigger if found; otherwise, null.</returns>
|
|
||||||
Task<Trigger?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves a trigger by its type.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The expense trigger type.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>The trigger if found; otherwise, null.</returns>
|
|
||||||
Task<Trigger?> GetByTypeAsync(ExpenseTriggerType type, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves all expense-trigger mappings for a specific club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clubId">The unique identifier of the club.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>A list of expense-trigger mappings for the club.</returns>
|
|
||||||
Task<List<ExpenseTrigger>> GetExpenseTriggersByClubIdAsync(Guid clubId, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves all expenses linked to a specific trigger type for a club.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clubId">The unique identifier of the club.</param>
|
|
||||||
/// <param name="type">The expense trigger type.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>A list of expenses linked to the trigger type.</returns>
|
|
||||||
Task<List<Expense>> GetExpensesByTriggerTypeAsync(Guid clubId, ExpenseTriggerType type, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds an expense-trigger mapping.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="expenseTrigger">The expense-trigger mapping to add.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
Task AddExpenseTriggerAsync(ExpenseTrigger expenseTrigger, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes an expense-trigger mapping.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clubId">The club identifier.</param>
|
|
||||||
/// <param name="expenseId">The expense identifier.</param>
|
|
||||||
/// <param name="triggerId">The trigger identifier.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
/// <returns>True if removed successfully; otherwise, false.</returns>
|
|
||||||
Task<bool> RemoveExpenseTriggerAsync(Guid clubId, Guid expenseId, Guid triggerId, CancellationToken ct = default);
|
|
||||||
}
|
|
||||||
|
|
@ -79,7 +79,6 @@ public static class DependencyInjection
|
||||||
services.AddScoped<IExpenseRepository, ExpenseRepository>();
|
services.AddScoped<IExpenseRepository, ExpenseRepository>();
|
||||||
services.AddScoped<IDayRepository, DayRepository>();
|
services.AddScoped<IDayRepository, DayRepository>();
|
||||||
services.AddScoped<IPersonExpenseRepository, PersonExpenseRepository>();
|
services.AddScoped<IPersonExpenseRepository, PersonExpenseRepository>();
|
||||||
services.AddScoped<ITriggerRepository, TriggerRepository>();
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
//services.AddScoped<IEmailService, StubEmailService>();
|
//services.AddScoped<IEmailService, StubEmailService>();
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
using Koogle.Domain.Entities;
|
|
||||||
using Koogle.Domain.Enums;
|
|
||||||
using Koogle.Domain.Interfaces;
|
|
||||||
using KoogleApp.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Koogle.Infrastructure.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Repository implementation for managing trigger entities and expense-trigger relationships.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="contextFactory">The database context factory for creating scoped contexts.</param>
|
|
||||||
public class TriggerRepository(IDbContextFactory<AppDbContext> contextFactory) : ITriggerRepository
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<List<Trigger>> GetAllAsync(CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
return await context.Triggers
|
|
||||||
.Where(t => !t.IsDeleted)
|
|
||||||
.OrderBy(t => t.Name)
|
|
||||||
.ToListAsync(ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<Trigger?> GetByIdAsync(Guid id, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
return await context.Triggers
|
|
||||||
.FirstOrDefaultAsync(t => t.Id == id && !t.IsDeleted, ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<Trigger?> GetByTypeAsync(ExpenseTriggerType type, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
return await context.Triggers
|
|
||||||
.FirstOrDefaultAsync(t => t.ExpenseTriggerType == type && !t.IsDeleted, ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<List<ExpenseTrigger>> GetExpenseTriggersByClubIdAsync(Guid clubId, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
return await context.ExpenseTriggers
|
|
||||||
.Where(et => et.ClubId == clubId)
|
|
||||||
.Include(et => et.Expense)
|
|
||||||
.Include(et => et.Trigger)
|
|
||||||
.ToListAsync(ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<List<Expense>> GetExpensesByTriggerTypeAsync(Guid clubId, ExpenseTriggerType type, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
return await context.ExpenseTriggers
|
|
||||||
.Where(et => et.ClubId == clubId && et.Trigger.ExpenseTriggerType == type)
|
|
||||||
.Select(et => et.Expense)
|
|
||||||
.Where(e => !e.IsDeleted)
|
|
||||||
.ToListAsync(ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task AddExpenseTriggerAsync(ExpenseTrigger expenseTrigger, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
expenseTrigger.AssignedAt = DateTime.UtcNow;
|
|
||||||
await context.ExpenseTriggers.AddAsync(expenseTrigger, ct);
|
|
||||||
await context.SaveChangesAsync(ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<bool> RemoveExpenseTriggerAsync(Guid clubId, Guid expenseId, Guid triggerId, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(ct);
|
|
||||||
var entity = await context.ExpenseTriggers
|
|
||||||
.FirstOrDefaultAsync(et => et.ClubId == clubId && et.ExpenseId == expenseId && et.TriggerId == triggerId, ct);
|
|
||||||
|
|
||||||
if (entity is null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
context.ExpenseTriggers.Remove(entity);
|
|
||||||
await context.SaveChangesAsync(ct);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -408,15 +408,4 @@ internal class ClubRepository : Koogle.Domain.Interfaces.IClubRepository
|
||||||
await _context.SaveChangesAsync(ct);
|
await _context.SaveChangesAsync(ct);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Club?> GetByNameAsync(string clubName, Guid? excludeGuid, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var query = _context.Clubs
|
|
||||||
.Where(c => !c.IsDeleted && c.Name.ToLower() == clubName.ToLower());
|
|
||||||
if (excludeGuid.HasValue)
|
|
||||||
{
|
|
||||||
query = query.Where(c => c.Id != excludeGuid.Value);
|
|
||||||
}
|
|
||||||
return await query.FirstOrDefaultAsync(ct);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue