using MyWarehouse.Application.Common.Dependencies.DataAccess;
using MyWarehouse.Application.Common.Exceptions;
using MyWarehouse.Domain;
using System.ComponentModel;
namespace MyWarehouse.Application.Transactions.CreateTransaction;
public record CreateTransactionCommand : IRequest<int>
{
public int PartnerId { get; init; }
public TransactionType TransactionType { get; init; }
public TransactionLine[] TransactionLines { get; init; } = Array.Empty<TransactionLine>();
public struct TransactionLine
{
public int ProductId { get; init; }
public int ProductQuantity { get; init; }
}
}
public class CreateTransactionCommandHandler : IRequestHandler<CreateTransactionCommand, int>
{
private readonly IUnitOfWork _unitOfWork;
public CreateTransactionCommandHandler(IUnitOfWork unitOfWork)
=> _unitOfWork = unitOfWork;
public async Task<int> Handle(CreateTransactionCommand request, CancellationToken cancellationToken)
{
var partner = await _unitOfWork.Partners.GetByIdAsync(request.PartnerId)
?? throw new InputValidationException((nameof(request.PartnerId), $"Partner (id: {request.PartnerId}) was not found."));
// Try not to confuse DB transaction with the "Transaction" domain entity. :)
await _unitOfWork.BeginTransactionAsync();
int createdTransactionId = 0;
try
{
var orderedProductIds = request.TransactionLines.Select(x => x.ProductId).Distinct();
var orderedProducts = await _unitOfWork.Products.GetFiltered(x => orderedProductIds.Contains(x.Id));
var validLines = request.TransactionLines.Select(line =>
(
product: orderedProducts.FirstOrDefault(p => p.Id == line.ProductId)
?? throw new InputValidationException((nameof(line.ProductId), $"Product (id: {line.ProductId}) was not found.")),
qty: line.ProductQuantity
)
);
var transaction = request.TransactionType switch
{
TransactionType.Sales => partner.SellTo(validLines),
TransactionType.Procurement => partner.ProcureFrom(validLines),
_ => throw new InvalidEnumArgumentException($"No operation is defined for {nameof(TransactionType)} of '{request.TransactionType}'.")
};
await _unitOfWork.SaveChanges();
createdTransactionId = transaction.Id;
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
await _unitOfWork.CommitTransactionAsync();
return createdTransactionId;
}
}