Clean up
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
using AutoMapper;
|
||||
using Imprink.Application.Products.Dtos;
|
||||
using Imprink.Domain.Entities.Product;
|
||||
using MediatR;
|
||||
|
||||
namespace Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
public class CreateProductVariantCommand : IRequest<ProductVariantDto>
|
||||
{
|
||||
public Guid ProductId { get; set; }
|
||||
public string Size { get; set; } = null!;
|
||||
public string? Color { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public string Sku { get; set; } = null!;
|
||||
public int StockQuantity { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
|
||||
public class CreateProductVariantHandler(IUnitOfWork unitOfWork, IMapper mapper)
|
||||
: IRequestHandler<CreateProductVariantCommand, ProductVariantDto>
|
||||
{
|
||||
public async Task<ProductVariantDto> Handle(CreateProductVariantCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await unitOfWork.BeginTransactionAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var productVariant = mapper.Map<ProductVariant>(request);
|
||||
|
||||
productVariant.Product = null!;
|
||||
|
||||
var createdVariant = await unitOfWork.ProductVariantRepository.AddAsync(productVariant, cancellationToken);
|
||||
|
||||
await unitOfWork.SaveAsync(cancellationToken);
|
||||
await unitOfWork.CommitTransactionAsync(cancellationToken);
|
||||
|
||||
return mapper.Map<ProductVariantDto>(createdVariant);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await unitOfWork.RollbackTransactionAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using MediatR;
|
||||
|
||||
namespace Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
public class DeleteProductVariantCommand : IRequest<bool>
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteProductVariantHandler(IUnitOfWork unitOfWork) : IRequestHandler<DeleteProductVariantCommand, bool>
|
||||
{
|
||||
public async Task<bool> Handle(DeleteProductVariantCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await unitOfWork.BeginTransactionAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var exists = await unitOfWork.ProductVariantRepository.ExistsAsync(request.Id, cancellationToken);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
await unitOfWork.RollbackTransactionAsync(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
await unitOfWork.ProductVariantRepository.DeleteAsync(request.Id, cancellationToken);
|
||||
|
||||
await unitOfWork.SaveAsync(cancellationToken);
|
||||
await unitOfWork.CommitTransactionAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await unitOfWork.RollbackTransactionAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using AutoMapper;
|
||||
using Imprink.Application.Products.Dtos;
|
||||
using Imprink.Domain.Entities.Product;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
public class GetProductVariantsQuery : IRequest<IEnumerable<ProductVariantDto>>
|
||||
{
|
||||
public Guid? ProductId { get; set; }
|
||||
public bool? IsActive { get; set; }
|
||||
public bool InStockOnly { get; set; } = false;
|
||||
}
|
||||
|
||||
public class GetProductVariantsHandler(IUnitOfWork unitOfWork, IMapper mapper, ILogger<GetProductVariantsHandler> logger)
|
||||
: IRequestHandler<GetProductVariantsQuery, IEnumerable<ProductVariantDto>>
|
||||
{
|
||||
public async Task<IEnumerable<ProductVariantDto>> Handle(GetProductVariantsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
IEnumerable<ProductVariant> variants;
|
||||
|
||||
if (request.ProductId.HasValue)
|
||||
{
|
||||
if (request.InStockOnly)
|
||||
{
|
||||
variants = await unitOfWork.ProductVariantRepository.GetInStockByProductIdAsync(request.ProductId.Value, cancellationToken);
|
||||
}
|
||||
else if (request.IsActive.HasValue && request.IsActive.Value)
|
||||
{
|
||||
variants = await unitOfWork.ProductVariantRepository.GetActiveByProductIdAsync(request.ProductId.Value, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
variants = await unitOfWork.ProductVariantRepository.GetByProductIdAsync(request.ProductId.Value, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
variants = new List<ProductVariant>();
|
||||
}
|
||||
|
||||
return mapper.Map<IEnumerable<ProductVariantDto>>(variants);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using AutoMapper;
|
||||
using Imprink.Application.Exceptions;
|
||||
using Imprink.Application.Products.Dtos;
|
||||
using MediatR;
|
||||
|
||||
namespace Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
public class UpdateProductVariantCommand : IRequest<ProductVariantDto>
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProductId { get; set; }
|
||||
public string Size { get; set; } = null!;
|
||||
public string? Color { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public string Sku { get; set; } = null!;
|
||||
public int StockQuantity { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateProductVariantHandler(IUnitOfWork unitOfWork, IMapper mapper)
|
||||
: IRequestHandler<UpdateProductVariantCommand, ProductVariantDto>
|
||||
{
|
||||
public async Task<ProductVariantDto> Handle(UpdateProductVariantCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await unitOfWork.BeginTransactionAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var existingVariant = await unitOfWork.ProductVariantRepository.GetByIdAsync(request.Id, cancellationToken);
|
||||
|
||||
if (existingVariant == null)
|
||||
throw new NotFoundException($"Product variant with ID {request.Id} not found.");
|
||||
|
||||
mapper.Map(request, existingVariant);
|
||||
|
||||
var updatedVariant = await unitOfWork.ProductVariantRepository.UpdateAsync(existingVariant, cancellationToken);
|
||||
|
||||
await unitOfWork.SaveAsync(cancellationToken);
|
||||
await unitOfWork.CommitTransactionAsync(cancellationToken);
|
||||
|
||||
return mapper.Map<ProductVariantDto>(updatedVariant);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await unitOfWork.RollbackTransactionAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Imprink.Application.Products.Dtos;
|
||||
|
||||
public class ProductVariantDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProductId { get; set; }
|
||||
public string Size { get; set; } = null!;
|
||||
public string? Color { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public string Sku { get; set; } = null!;
|
||||
public int StockQuantity { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public ProductDto? Product { get; set; }
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public DateTime? ModifiedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using FluentValidation;
|
||||
using Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
namespace Imprink.Application.Validation.ProductVariants;
|
||||
|
||||
public class CreateProductVariantCommandValidator : AbstractValidator<CreateProductVariantCommand>
|
||||
{
|
||||
public CreateProductVariantCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductId)
|
||||
.NotEmpty().WithMessage("ProductId is required.")
|
||||
.NotEqual(Guid.Empty).WithMessage("ProductId must be a valid GUID.");
|
||||
|
||||
RuleFor(x => x.Size)
|
||||
.NotEmpty().WithMessage("Size is required.")
|
||||
.Length(1, 50).WithMessage("Size must be between 1 and 50 characters.");
|
||||
|
||||
RuleFor(x => x.Color)
|
||||
.Length(1, 50).WithMessage("Color must be between 1 and 50 characters.")
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.Color));
|
||||
|
||||
RuleFor(x => x.Price)
|
||||
.GreaterThan(0).WithMessage("Price must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.ImageUrl)
|
||||
.Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl))
|
||||
.WithMessage("ImageUrl must be a valid URL.");
|
||||
|
||||
RuleFor(x => x.Sku)
|
||||
.NotEmpty().WithMessage("SKU is required.")
|
||||
.Length(1, 50).WithMessage("SKU must be between 1 and 50 characters.");
|
||||
|
||||
RuleFor(x => x.StockQuantity)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("StockQuantity cannot be negative.");
|
||||
}
|
||||
|
||||
private static bool BeValidUrl(string? url)
|
||||
{
|
||||
return Uri.TryCreate(url, UriKind.Absolute, out _);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentValidation;
|
||||
using Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
namespace Imprink.Application.Validation.ProductVariants;
|
||||
|
||||
public class DeleteProductVariantCommandValidator : AbstractValidator<DeleteProductVariantCommand>
|
||||
{
|
||||
public DeleteProductVariantCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.Id)
|
||||
.NotEmpty().WithMessage("Id is required.")
|
||||
.NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentValidation;
|
||||
using Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
namespace Imprink.Application.Validation.ProductVariants;
|
||||
|
||||
public class GetProductVariantsQueryValidator : AbstractValidator<GetProductVariantsQuery>
|
||||
{
|
||||
public GetProductVariantsQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductId)
|
||||
.NotEqual(Guid.Empty).When(x => x.ProductId.HasValue)
|
||||
.WithMessage("ProductId must be a valid GUID when provided.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using FluentValidation;
|
||||
using Imprink.Application.Domains.ProductVariants;
|
||||
|
||||
namespace Imprink.Application.Validation.ProductVariants;
|
||||
|
||||
public class UpdateProductVariantCommandValidator : AbstractValidator<UpdateProductVariantCommand>
|
||||
{
|
||||
public UpdateProductVariantCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.Id)
|
||||
.NotEmpty().WithMessage("Id is required.")
|
||||
.NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID.");
|
||||
|
||||
RuleFor(x => x.ProductId)
|
||||
.NotEmpty().WithMessage("ProductId is required.")
|
||||
.NotEqual(Guid.Empty).WithMessage("ProductId must be a valid GUID.");
|
||||
|
||||
RuleFor(x => x.Size)
|
||||
.NotEmpty().WithMessage("Size is required.")
|
||||
.Length(1, 50).WithMessage("Size must be between 1 and 50 characters.");
|
||||
|
||||
RuleFor(x => x.Color)
|
||||
.Length(1, 50).WithMessage("Color must be between 1 and 50 characters.")
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.Color));
|
||||
|
||||
RuleFor(x => x.Price)
|
||||
.GreaterThan(0).WithMessage("Price must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.ImageUrl)
|
||||
.Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl))
|
||||
.WithMessage("ImageUrl must be a valid URL.");
|
||||
|
||||
RuleFor(x => x.Sku)
|
||||
.NotEmpty().WithMessage("SKU is required.")
|
||||
.Length(1, 50).WithMessage("SKU must be between 1 and 50 characters.");
|
||||
|
||||
RuleFor(x => x.StockQuantity)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("StockQuantity cannot be negative.");
|
||||
}
|
||||
|
||||
private static bool BeValidUrl(string? url)
|
||||
{
|
||||
return Uri.TryCreate(url, UriKind.Absolute, out _);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user