Completed payment service implementation
This commit is contained in:
@@ -2,6 +2,24 @@
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Products.Models;
|
||||
|
||||
public sealed record ReserveStock
|
||||
{
|
||||
public required long ProductId { get; set; }
|
||||
|
||||
public required long ProductPriceId { get; set; }
|
||||
|
||||
public int Reservation { get; set; }
|
||||
}
|
||||
|
||||
public sealed record AllocateStock
|
||||
{
|
||||
public required long ProductId { get; set; }
|
||||
|
||||
public required long ProductPriceId { get; set; }
|
||||
|
||||
public int Allocation { get; set; }
|
||||
}
|
||||
|
||||
public sealed record CreateProduct
|
||||
{
|
||||
public required ProductTypes Type { get; set; }
|
||||
|
||||
@@ -4,11 +4,129 @@ using LiteCharms.Features.MidrandBooks.Extensions;
|
||||
using LiteCharms.Features.MidrandBooks.Postgres;
|
||||
using LiteCharms.Features.MidrandBooks.Products.Models;
|
||||
using LiteCharms.Features.Models;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Products;
|
||||
|
||||
public sealed class ProductService(IDbContextFactory<MidrandBooksDbContext> contextFactory) : IService
|
||||
{
|
||||
public async ValueTask<Result<ProductInventory>> CheckProductStockAvailabilityAsync(long productId, long productPriceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var inventory = await context.Inventories
|
||||
.AsNoTracking()
|
||||
.Where(i => i.ProductPriceId == productPriceId && i.ProductId == productId)
|
||||
.OrderByDescending(o => o.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
return inventory is not null
|
||||
? Result.Ok(inventory.ToModel())
|
||||
: Result.Fail<ProductInventory>("Product sold out");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<ProductInventory>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result<long>> ReserveProductInventoryAsync(ReserveStock request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var oldInventory = await context.Inventories
|
||||
.AsNoTracking()
|
||||
.Where(i => i.ProductPriceId == request.ProductPriceId && i.ProductId == request.ProductId)
|
||||
.OrderByDescending(o => o.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
var newAllocation = 0;
|
||||
var newReservation = 0;
|
||||
|
||||
if (oldInventory is not null)
|
||||
{
|
||||
newAllocation = oldInventory.TotalAllocated;
|
||||
newReservation = oldInventory.TotalReserved + request.Reservation;
|
||||
}
|
||||
else
|
||||
{
|
||||
newAllocation = 0;
|
||||
newReservation = request.Reservation;
|
||||
}
|
||||
|
||||
if (newAllocation - newReservation < 0)
|
||||
return Result.Fail<long>("Allocation failure: The requested book quantity exceeds current physical inventory availability.");
|
||||
|
||||
var inventory = context.Inventories.Add(new Entities.ProductInventory
|
||||
{
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
ProductId = request.ProductId,
|
||||
ProductPriceId = request.ProductPriceId,
|
||||
Status = InventoryStatuses.Reserved,
|
||||
TotalAllocated = newAllocation,
|
||||
TotalReserved = newReservation,
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(inventory.Entity.Id)
|
||||
: Result.Fail<long>("Failed to create inventory entry");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<long>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result<long>> AllocateProductInventoryAsync(AllocateStock request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var oldInventory = await context.Inventories
|
||||
.AsNoTracking()
|
||||
.Where(i => i.ProductPriceId == request.ProductPriceId && i.ProductId == request.ProductId)
|
||||
.OrderByDescending(o => o.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
var newAllocation = 0;
|
||||
var newReservation = 0;
|
||||
|
||||
if (oldInventory is not null)
|
||||
{
|
||||
newAllocation = oldInventory.TotalAllocated + request.Allocation;
|
||||
newReservation = oldInventory.TotalReserved;
|
||||
}
|
||||
else
|
||||
{
|
||||
newAllocation = request.Allocation;
|
||||
newReservation = 0;
|
||||
}
|
||||
|
||||
var inventory = context.Inventories.Add(new Entities.ProductInventory
|
||||
{
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
ProductId = request.ProductId,
|
||||
ProductPriceId = request.ProductPriceId,
|
||||
Status = InventoryStatuses.Adjustment,
|
||||
TotalAllocated = newAllocation,
|
||||
TotalReserved = newReservation,
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(inventory.Entity.Id)
|
||||
: Result.Fail<long>("Failed to create inventory entry");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<long>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result> AddProductCategoryAsync(long productId, long categoryId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user