Split Features to create space for more projects
continuous-integration/drone/pr Build is passing

This commit is contained in:
Khwezi Mngoma
2026-05-24 13:19:09 +02:00
parent 032b9e1818
commit 70c6e0bfbc
95 changed files with 621 additions and 314 deletions
@@ -0,0 +1,156 @@
using LiteCharms.Features.Extensions;
using LiteCharms.Features.Models;
using LiteCharms.Features.TechShop.Extensions;
using LiteCharms.Features.TechShop.Postgres;
using LiteCharms.Features.TechShop.Quotes.Models;
namespace LiteCharms.Features.TechShop.Quotes;
public class QuoteService(IDbContextFactory<ShopDbContext> contextFactory)
{
public async ValueTask<Result> AssignQuoteToOrderAsync(Guid quoteId, Guid orderId, CancellationToken cancellationToken = default)
{
try
{
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var quote = await context.Quotes.FirstOrDefaultAsync(o => o.Id == quoteId, cancellationToken);
if (quote is null)
return Result.Fail(new Error($"Quote with id {orderId} not found"));
if (!await context.Orders.AnyAsync(q => q.Id == orderId, cancellationToken))
return Result.Fail(new Error($"Order with id {quoteId} not found"));
if (quote.OrderId == orderId)
return Result.Fail(new Error($"Quote with id {quoteId} is already assigned to order with id {orderId}"));
quote.OrderId = orderId;
quote.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok()
: Result.Fail(new Error($"Failed to assign quote with id {quoteId} to order with id {orderId}"));
}
catch (Exception ex)
{
return Result.Fail(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result> AssignQuoteToShoppingCartAsync(Guid quoteId, Guid shoppingCartId, CancellationToken cancellationToken = default)
{
try
{
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var quote = await context.Quotes.FirstOrDefaultAsync(o => o.Id == quoteId, cancellationToken);
if (quote is null)
return Result.Fail(new Error($"Quote with id {quoteId} not found"));
if (!await context.ShoppingCarts.AnyAsync(q => q.Id == shoppingCartId, cancellationToken))
return Result.Fail(new Error($"Shopping Cart with id {shoppingCartId} not found"));
quote.ShoppingCartId = shoppingCartId;
quote.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok()
: Result.Fail(new Error("Failed to assign quote to shopping cart"));
}
catch (Exception ex)
{
return Result.Fail(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result<Quote[]>> GetCustomerQuotesAsync(Guid customerId, CancellationToken cancellationToken = default)
{
try
{
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken))
return Result.Fail<Quote[]>(new Error($"Customer with Id {customerId} does not exist."));
var quotes = await context.Quotes.AsNoTracking()
.Where(q => q.CustomerId == customerId).ToArrayAsync(cancellationToken);
return quotes?.Length > 0
? Result.Ok(quotes.Select(q => q.ToModel()).ToArray())
: Result.Fail<Quote[]>(new Error($"No quotes found for customer with Id {customerId}."));
}
catch (Exception ex)
{
return Result.Fail<Quote[]>(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result<Quote>> GetQuoteAsync(Guid quoteId, CancellationToken cancellationToken = default)
{
try
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var quote = await context.Quotes.AsNoTracking().FirstOrDefaultAsync(q => q.Id == quoteId, cancellationToken);
return quote is not null
? Result.Ok(quote.ToModel())
: Result.Fail<Quote>(new Error($"Quote with ID {quoteId} not found."));
}
catch (Exception ex)
{
return Result.Fail<Quote>(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result<Quote[]>> GetQuotesAsync(DateRange range, CancellationToken cancellationToken = default)
{
try
{
var fromDate = range.From.ToDateTime(TimeOnly.MinValue);
var toDate = range.To.ToDateTime(TimeOnly.MaxValue);
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var quotes = await context.Quotes.AsNoTracking()
.OrderByDescending(o => o.CreatedAt)
.Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate)
.Take(range.MaxRecords)
.ToArrayAsync(cancellationToken);
return quotes?.Length > 0
? Result.Ok(quotes.Select(o => o.ToModel()).ToArray())
: Result.Fail<Quote[]>(new Error($"No quotes found for the specified date range {range.From} - {range.To}."));
}
catch (Exception ex)
{
return Result.Fail<Quote[]>(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result> UpdateQuoteStatusAsync(Guid quoteId, QuoteStatus status, CancellationToken cancellationToken = default)
{
try
{
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var quote = await context.Quotes.FirstOrDefaultAsync(q => q.Id == quoteId, cancellationToken);
if (quote is null)
return Result.Fail(new Error("Quote not found."));
quote.Status = status;
quote.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok()
: Result.Fail(new Error("Failed to update quote status."));
}
catch (Exception ex)
{
return Result.Fail(new Error(ex.Message).CausedBy(ex));
}
}
}