Remove almost everything :(
This commit is contained in:
@@ -17,8 +17,4 @@
|
|||||||
<ProjectReference Include="..\Printbase.Domain\Printbase.Domain.csproj" />
|
<ProjectReference Include="..\Printbase.Domain\Printbase.Domain.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Mappings\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
|
|
||||||
public class CreateProductCommand : IRequest<ProductDto>
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public Guid TypeId { get; set; }
|
|
||||||
public List<CreateProductVariantDto>? Variants { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
|
|
||||||
public class CreateProductCommandHandler(
|
|
||||||
IProductRepository productRepository,
|
|
||||||
IProductVariantRepository variantRepository,
|
|
||||||
IProductTypeRepository typeRepository)
|
|
||||||
: IRequestHandler<CreateProductCommand, ProductDto>
|
|
||||||
{
|
|
||||||
private readonly IProductRepository _productRepository = productRepository ?? throw new ArgumentNullException(nameof(productRepository));
|
|
||||||
private readonly IProductVariantRepository _variantRepository = variantRepository ?? throw new ArgumentNullException(nameof(variantRepository));
|
|
||||||
private readonly IProductTypeRepository _typeRepository = typeRepository ?? throw new ArgumentNullException(nameof(typeRepository));
|
|
||||||
|
|
||||||
public async Task<ProductDto> Handle(CreateProductCommand request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var productType = await _typeRepository.GetByIdAsync(request.TypeId, includeRelations: true, cancellationToken);
|
|
||||||
if (productType == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Product type with ID {request.TypeId} not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
var product = new Product
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Name = request.Name,
|
|
||||||
Description = request.Description,
|
|
||||||
TypeId = request.TypeId,
|
|
||||||
CreatedAt = DateTime.UtcNow,
|
|
||||||
IsActive = true
|
|
||||||
};
|
|
||||||
|
|
||||||
var createdProduct = await _productRepository.AddAsync(product, cancellationToken);
|
|
||||||
|
|
||||||
var productVariants = new List<ProductVariant>();
|
|
||||||
if (request.Variants != null && request.Variants.Count != 0)
|
|
||||||
{
|
|
||||||
foreach (var variant in request.Variants.Select(variantDto => new ProductVariant
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
ProductId = createdProduct.Id,
|
|
||||||
Color = variantDto.Color,
|
|
||||||
Size = variantDto.Size,
|
|
||||||
Price = variantDto.Price,
|
|
||||||
Discount = variantDto.Discount,
|
|
||||||
Stock = variantDto.Stock,
|
|
||||||
SKU = variantDto.SKU ?? GenerateSku(createdProduct.Name, variantDto.Color, variantDto.Size),
|
|
||||||
CreatedAt = DateTime.UtcNow,
|
|
||||||
IsActive = true
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
var createdVariant = await _variantRepository.AddAsync(variant, cancellationToken);
|
|
||||||
productVariants.Add(createdVariant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var productDto = new ProductDto
|
|
||||||
{
|
|
||||||
Id = createdProduct.Id,
|
|
||||||
Name = createdProduct.Name,
|
|
||||||
Description = createdProduct.Description,
|
|
||||||
TypeId = createdProduct.TypeId,
|
|
||||||
TypeName = productType.Name,
|
|
||||||
GroupName = productType.Group.Name,
|
|
||||||
CreatedAt = createdProduct.CreatedAt,
|
|
||||||
IsActive = createdProduct.IsActive,
|
|
||||||
Variants = productVariants.Select(v => new ProductVariantDto
|
|
||||||
{
|
|
||||||
Id = v.Id,
|
|
||||||
Color = v.Color,
|
|
||||||
Size = v.Size,
|
|
||||||
Price = v.Price,
|
|
||||||
Discount = v.Discount,
|
|
||||||
Stock = v.Stock,
|
|
||||||
SKU = v.SKU,
|
|
||||||
IsActive = v.IsActive
|
|
||||||
}).ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
return productDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GenerateSku(string productName, string? color, string? size)
|
|
||||||
{
|
|
||||||
var prefix = productName.Length >= 3 ? productName[..3].ToUpper() : productName.ToUpper();
|
|
||||||
var colorPart = !string.IsNullOrEmpty(color) ? color[..Math.Min(3, color.Length)].ToUpper() : "XXX";
|
|
||||||
var sizePart = !string.IsNullOrEmpty(size) ? size.ToUpper() : "OS";
|
|
||||||
var randomPart = new Random().Next(100, 999).ToString();
|
|
||||||
|
|
||||||
return $"{prefix}-{colorPart}-{sizePart}-{randomPart}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
|
|
||||||
public class CreateProductVariantDto
|
|
||||||
{
|
|
||||||
public string? Color { get; set; }
|
|
||||||
public string? Size { get; set; }
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
public decimal? Discount { get; set; }
|
|
||||||
public int Stock { get; set; }
|
|
||||||
public string? SKU { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Printbase.Application.Products.Dtos;
|
|
||||||
|
|
||||||
public class AllProductsDto
|
|
||||||
{
|
|
||||||
public ICollection<ProductDto> Products {get;set;}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace Printbase.Application.Products.Dtos;
|
|
||||||
|
|
||||||
public class ProductDto
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public Guid TypeId { get; set; }
|
|
||||||
public string TypeName { get; set; } = string.Empty;
|
|
||||||
public string GroupName { get; set; } = string.Empty;
|
|
||||||
public ICollection<ProductVariantDto> Variants { get; set; } = new List<ProductVariantDto>();
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Printbase.Application.Products.Dtos;
|
|
||||||
|
|
||||||
public class ProductVariantDto
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string? Color { get; set; }
|
|
||||||
public string? Size { get; set; }
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
public decimal? Discount { get; set; }
|
|
||||||
public decimal DiscountedPrice => Discount is > 0 ? Price - Price * Discount.Value / 100m : Price;
|
|
||||||
public int Stock { get; set; }
|
|
||||||
public string? SKU { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Products.Queries.GetAllProducts;
|
|
||||||
|
|
||||||
public class GetAllProductsQuery(bool includeVariants = true) : IRequest<AllProductsDto?>
|
|
||||||
{
|
|
||||||
public bool IncludeVariants { get; } = includeVariants;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Products.Queries.GetAllProducts;
|
|
||||||
|
|
||||||
public class GetAllProductsQueryHandler(IProductRepository productRepository)
|
|
||||||
: IRequestHandler<GetAllProductsQuery, AllProductsDto?>
|
|
||||||
{
|
|
||||||
private readonly IProductRepository _productRepository = productRepository
|
|
||||||
?? throw new ArgumentNullException(nameof(productRepository));
|
|
||||||
|
|
||||||
public async Task<AllProductsDto?> Handle(GetAllProductsQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var products = await _productRepository.GetAllAsync(true, cancellationToken);
|
|
||||||
|
|
||||||
var allProducts = new AllProductsDto
|
|
||||||
{
|
|
||||||
Products = products.Select(p => new ProductDto
|
|
||||||
{
|
|
||||||
Id = p.Id,
|
|
||||||
Name = p.Name,
|
|
||||||
Description = p.Description,
|
|
||||||
TypeId = p.TypeId,
|
|
||||||
TypeName = p.Type.Name,
|
|
||||||
GroupName = p.Type.Group.Name,
|
|
||||||
CreatedAt = p.CreatedAt,
|
|
||||||
UpdatedAt = p.UpdatedAt,
|
|
||||||
IsActive = p.IsActive,
|
|
||||||
Variants = request.IncludeVariants
|
|
||||||
? p.Variants.Select(v => new ProductVariantDto
|
|
||||||
{
|
|
||||||
Id = v.Id,
|
|
||||||
Color = v.Color,
|
|
||||||
Size = v.Size,
|
|
||||||
Price = v.Price,
|
|
||||||
Discount = v.Discount,
|
|
||||||
Stock = v.Stock,
|
|
||||||
SKU = v.SKU,
|
|
||||||
IsActive = v.IsActive
|
|
||||||
}).ToList()
|
|
||||||
: []
|
|
||||||
}).ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
return allProducts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Products.Queries.GetProductById;
|
|
||||||
|
|
||||||
public class GetProductByIdQuery(Guid id, bool includeVariants = true) : IRequest<ProductDto?>
|
|
||||||
{
|
|
||||||
public Guid Id { get; } = id;
|
|
||||||
public bool IncludeVariants { get; } = includeVariants;
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using MediatR;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Products.Queries.GetProductById;
|
|
||||||
|
|
||||||
public class GetProductByIdQueryHandler(IProductRepository productRepository, IMapper mapper)
|
|
||||||
: IRequestHandler<GetProductByIdQuery, ProductDto?>
|
|
||||||
{
|
|
||||||
private readonly IProductRepository _productRepository = productRepository
|
|
||||||
?? throw new ArgumentNullException(nameof(productRepository));
|
|
||||||
private readonly IMapper _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
public async Task<ProductDto?> Handle(GetProductByIdQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var product = await _productRepository.GetByIdAsync(request.Id, includeRelations: true, cancellationToken);
|
|
||||||
|
|
||||||
if (product == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var productDto = new ProductDto
|
|
||||||
{
|
|
||||||
Id = product.Id,
|
|
||||||
Name = product.Name,
|
|
||||||
Description = product.Description,
|
|
||||||
TypeId = product.TypeId,
|
|
||||||
TypeName = product.Type.Name,
|
|
||||||
GroupName = product.Type.Group.Name,
|
|
||||||
CreatedAt = product.CreatedAt,
|
|
||||||
UpdatedAt = product.UpdatedAt,
|
|
||||||
IsActive = product.IsActive
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.IncludeVariants)
|
|
||||||
{
|
|
||||||
productDto.Variants = product.Variants
|
|
||||||
.Select(v => new ProductVariantDto
|
|
||||||
{
|
|
||||||
Id = v.Id,
|
|
||||||
Color = v.Color,
|
|
||||||
Size = v.Size,
|
|
||||||
Price = v.Price,
|
|
||||||
Discount = v.Discount,
|
|
||||||
Stock = v.Stock,
|
|
||||||
SKU = v.SKU,
|
|
||||||
IsActive = v.IsActive
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return productDto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
public class Product
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public Guid TypeId { get; set; }
|
|
||||||
public ProductType Type { get; set; } = null!;
|
|
||||||
public ICollection<ProductVariant> Variants { get; set; } = new List<ProductVariant>();
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
public class ProductGroup
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public ICollection<ProductType> Types { get; set; } = new List<ProductType>();
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
public class ProductType
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public Guid GroupId { get; set; }
|
|
||||||
public ProductGroup Group { get; set; } = null!;
|
|
||||||
public ICollection<Product> Products { get; set; } = new List<Product>();
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
public class ProductVariant
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public Guid ProductId { get; set; }
|
|
||||||
public string? Color { get; set; }
|
|
||||||
public string? Size { get; set; }
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
public decimal? Discount { get; set; }
|
|
||||||
public int Stock { get; set; }
|
|
||||||
public string? SKU { get; set; }
|
|
||||||
public Product Product { get; set; } = null!;
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
|
|
||||||
public decimal GetDiscountedPrice()
|
|
||||||
{
|
|
||||||
if (Discount is > 0)
|
|
||||||
{
|
|
||||||
return Price - Price * Discount.Value / 100m;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Price;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
public interface IProductGroupRepository
|
|
||||||
{
|
|
||||||
Task<ProductGroup?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<ProductGroup>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<ProductGroup> AddAsync(ProductGroup group, CancellationToken cancellationToken = default);
|
|
||||||
Task<ProductGroup> UpdateAsync(ProductGroup group, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
public interface IProductRepository
|
|
||||||
{
|
|
||||||
Task<Product?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<Product>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<Product>> GetByTypeIdAsync(Guid typeId, bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<Product> AddAsync(Product product, CancellationToken cancellationToken = default);
|
|
||||||
Task<Product> UpdateAsync(Product product, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
public interface IProductTypeRepository
|
|
||||||
{
|
|
||||||
Task<ProductType?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<ProductType>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<ProductType>> GetByGroupIdAsync(Guid groupId, CancellationToken cancellationToken = default);
|
|
||||||
Task<ProductType> AddAsync(ProductType type, CancellationToken cancellationToken = default);
|
|
||||||
Task<ProductType> UpdateAsync(ProductType type, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Domain.Repositories;
|
|
||||||
|
|
||||||
public interface IProductVariantRepository
|
|
||||||
{
|
|
||||||
Task<ProductVariant?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<ProductVariant>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<ProductVariant>> GetByProductIdAsync(Guid productId, CancellationToken cancellationToken = default);
|
|
||||||
Task<ProductVariant> AddAsync(ProductVariant variant, CancellationToken cancellationToken = default);
|
|
||||||
Task<ProductVariant> UpdateAsync(ProductVariant variant, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -5,39 +5,4 @@ namespace Printbase.Infrastructure.Database;
|
|||||||
|
|
||||||
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext>? options) : DbContext(options)
|
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext>? options) : DbContext(options)
|
||||||
{
|
{
|
||||||
public DbSet<ProductDbEntity> Products { get; set; } = null!;
|
|
||||||
public DbSet<ProductVariantDbEntity> ProductVariants { get; set; } = null!;
|
|
||||||
public DbSet<ProductTypeDbEntity> ProductTypes { get; set; } = null!;
|
|
||||||
public DbSet<ProductGroupDbEntity> ProductGroups { get; set; } = null!;
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
base.OnModelCreating(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity<ProductDbEntity>()
|
|
||||||
.HasMany(p => p.Variants)
|
|
||||||
.WithOne(v => v.Product)
|
|
||||||
.HasForeignKey(v => v.ProductId)
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
|
|
||||||
modelBuilder.Entity<ProductDbEntity>()
|
|
||||||
.HasOne(p => p.Type)
|
|
||||||
.WithMany(t => t.Products)
|
|
||||||
.HasForeignKey(p => p.TypeId)
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
modelBuilder.Entity<ProductTypeDbEntity>()
|
|
||||||
.HasOne(t => t.Group)
|
|
||||||
.WithMany(g => g.Types)
|
|
||||||
.HasForeignKey(t => t.GroupId)
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
|
|
||||||
modelBuilder.Entity<ProductVariantDbEntity>()
|
|
||||||
.Property(v => v.Price)
|
|
||||||
.HasPrecision(18, 2);
|
|
||||||
|
|
||||||
modelBuilder.Entity<ProductVariantDbEntity>()
|
|
||||||
.Property(v => v.Discount)
|
|
||||||
.HasPrecision(18, 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
[Table("Products")]
|
|
||||||
public class ProductDbEntity
|
|
||||||
{
|
|
||||||
[Key, Required]
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(50), Required]
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(1000)]
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public Guid TypeId { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
|
|
||||||
[ForeignKey(nameof(TypeId)), Required]
|
|
||||||
public required ProductTypeDbEntity Type { get; set; }
|
|
||||||
|
|
||||||
[InverseProperty(nameof(ProductVariantDbEntity.Product)), Required]
|
|
||||||
public required ICollection<ProductVariantDbEntity> Variants { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
[Table("ProductGroups")]
|
|
||||||
[Index(nameof(Name), IsUnique = true)]
|
|
||||||
public class ProductGroupDbEntity
|
|
||||||
{
|
|
||||||
[Key, Required]
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(50), Required]
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(255)]
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
[InverseProperty(nameof(ProductTypeDbEntity.Group)), Required]
|
|
||||||
public required ICollection<ProductTypeDbEntity> Types { get; set; } = [];
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
[Table("ProductTypes")]
|
|
||||||
[Index(nameof(Name), nameof(GroupId), IsUnique = true)]
|
|
||||||
public class ProductTypeDbEntity
|
|
||||||
{
|
|
||||||
[Key, Required]
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(50), Required]
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(255)]
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public Guid GroupId { get; set; }
|
|
||||||
|
|
||||||
[ForeignKey(nameof(GroupId)), Required]
|
|
||||||
public required ProductGroupDbEntity Group { get; set; }
|
|
||||||
|
|
||||||
[InverseProperty(nameof(ProductDbEntity.Type)), Required]
|
|
||||||
public required ICollection<ProductDbEntity> Products { get; set; } = [];
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
[Table("ProductVariants")]
|
|
||||||
[Index(nameof(ProductId), nameof(Color), nameof(Size), IsUnique = true)]
|
|
||||||
public class ProductVariantDbEntity
|
|
||||||
{
|
|
||||||
[Key, Required]
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public Guid ProductId { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(50)]
|
|
||||||
public string? Color { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(20)]
|
|
||||||
public string? Size { get; set; }
|
|
||||||
|
|
||||||
[Column(TypeName = "decimal(18,2)"), Required]
|
|
||||||
[Range(0.01, 9999999.99)]
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
|
|
||||||
[Range(0, 100)]
|
|
||||||
public decimal? Discount { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[Range(0, int.MaxValue)]
|
|
||||||
public int Stock { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(50)]
|
|
||||||
public string? SKU { get; set; }
|
|
||||||
|
|
||||||
[ForeignKey(nameof(ProductId)), Required]
|
|
||||||
public required ProductDbEntity Product { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
using Printbase.Application.Products.Dtos;
|
|
||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
using Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Mappings;
|
|
||||||
|
|
||||||
public class ProductMappingProfile : Profile
|
|
||||||
{
|
|
||||||
public ProductMappingProfile()
|
|
||||||
{
|
|
||||||
// Product DbEntity -> Domain Entity
|
|
||||||
CreateMap<ProductDbEntity, Product>()
|
|
||||||
.ForMember(dest => dest.Type,
|
|
||||||
opt => opt.MapFrom(src => src.Type))
|
|
||||||
.ForMember(dest => dest.Variants,
|
|
||||||
opt => opt.MapFrom(src => src.Variants));
|
|
||||||
|
|
||||||
// ProductGroup DbEntity -> Domain Entity
|
|
||||||
CreateMap<ProductGroupDbEntity, ProductGroup>()
|
|
||||||
.ForMember(dest => dest.Types,
|
|
||||||
opt => opt.MapFrom(src => src.Types));
|
|
||||||
|
|
||||||
// ProductType DbEntity -> Domain Entity
|
|
||||||
CreateMap<ProductTypeDbEntity, ProductType>()
|
|
||||||
.ForMember(dest => dest.Group,
|
|
||||||
opt => opt.MapFrom(src => src.Group))
|
|
||||||
.ForMember(dest => dest.Products,
|
|
||||||
opt => opt.MapFrom(src => src.Products));
|
|
||||||
|
|
||||||
// ProductVariant DbEntity -> Domain Entity
|
|
||||||
CreateMap<ProductVariantDbEntity, ProductVariant>()
|
|
||||||
.ForMember(dest => dest.Product,
|
|
||||||
opt => opt.MapFrom(src => src.Product));
|
|
||||||
|
|
||||||
// Product Domain Entity -> DbEntity
|
|
||||||
CreateMap<Product, ProductDbEntity>()
|
|
||||||
.ForMember(dest => dest.Type,
|
|
||||||
opt => opt.Ignore()) // in repo
|
|
||||||
.ForMember(dest => dest.Variants,
|
|
||||||
opt => opt.Ignore()); // in repo
|
|
||||||
|
|
||||||
// ProductVariant Domain Entity -> DbEntity
|
|
||||||
CreateMap<ProductVariant, ProductVariantDbEntity>()
|
|
||||||
.ForMember(dest => dest.Product,
|
|
||||||
opt => opt.Ignore()); // in repo
|
|
||||||
|
|
||||||
// ProductType Domain Entity -> DbEntity
|
|
||||||
CreateMap<ProductType, ProductTypeDbEntity>()
|
|
||||||
.ForMember(dest => dest.Group,
|
|
||||||
opt => opt.Ignore()) // in repo
|
|
||||||
.ForMember(dest => dest.Products,
|
|
||||||
opt => opt.Ignore()); // in repo
|
|
||||||
|
|
||||||
// ProductGroup Domain Entity -> DbEntity
|
|
||||||
CreateMap<ProductGroup, ProductGroupDbEntity>()
|
|
||||||
.ForMember(dest => dest.Types,
|
|
||||||
opt => opt.Ignore()); // in repo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Printbase.Infrastructure.Database;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
|
||||||
[Migration("20250504210542_InitialCreate")]
|
|
||||||
partial class InitialCreate
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "9.0.4")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<Guid>("TypeId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("TypeId");
|
|
||||||
|
|
||||||
b.ToTable("Products");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductGroupDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(255)
|
|
||||||
.HasColumnType("nvarchar(255)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ProductGroups");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(255)
|
|
||||||
.HasColumnType("nvarchar(255)");
|
|
||||||
|
|
||||||
b.Property<Guid>("GroupId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("GroupId");
|
|
||||||
|
|
||||||
b.HasIndex("Name", "GroupId")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ProductTypes");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductVariantDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<string>("Color")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<decimal?>("Discount")
|
|
||||||
.HasPrecision(18, 2)
|
|
||||||
.HasColumnType("decimal(18,2)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<decimal>("Price")
|
|
||||||
.HasPrecision(18, 2)
|
|
||||||
.HasColumnType("decimal(18,2)");
|
|
||||||
|
|
||||||
b.Property<Guid>("ProductId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<string>("SKU")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("Size")
|
|
||||||
.HasMaxLength(20)
|
|
||||||
.HasColumnType("nvarchar(20)");
|
|
||||||
|
|
||||||
b.Property<int>("Stock")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ProductId", "Color", "Size")
|
|
||||||
.IsUnique()
|
|
||||||
.HasFilter("[Color] IS NOT NULL AND [Size] IS NOT NULL");
|
|
||||||
|
|
||||||
b.ToTable("ProductVariants");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", "Type")
|
|
||||||
.WithMany("Products")
|
|
||||||
.HasForeignKey("TypeId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Type");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Printbase.Infrastructure.DbEntities.Products.ProductGroupDbEntity", "Group")
|
|
||||||
.WithMany("Types")
|
|
||||||
.HasForeignKey("GroupId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Group");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductVariantDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", "Product")
|
|
||||||
.WithMany("Variants")
|
|
||||||
.HasForeignKey("ProductId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Product");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Variants");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductGroupDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Types");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Products");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class InitialCreate : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ProductGroups",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ProductGroups", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ProductTypes",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: true),
|
|
||||||
GroupId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ProductTypes", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_ProductTypes_ProductGroups_GroupId",
|
|
||||||
column: x => x.GroupId,
|
|
||||||
principalTable: "ProductGroups",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Products",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
|
|
||||||
TypeId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Products", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Products_ProductTypes_TypeId",
|
|
||||||
column: x => x.TypeId,
|
|
||||||
principalTable: "ProductTypes",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Restrict);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ProductVariants",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
ProductId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
|
||||||
Color = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
|
||||||
Size = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
|
||||||
Price = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
|
||||||
Discount = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: true),
|
|
||||||
Stock = table.Column<int>(type: "int", nullable: false),
|
|
||||||
SKU = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ProductVariants", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_ProductVariants_Products_ProductId",
|
|
||||||
column: x => x.ProductId,
|
|
||||||
principalTable: "Products",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ProductGroups_Name",
|
|
||||||
table: "ProductGroups",
|
|
||||||
column: "Name",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Products_TypeId",
|
|
||||||
table: "Products",
|
|
||||||
column: "TypeId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ProductTypes_GroupId",
|
|
||||||
table: "ProductTypes",
|
|
||||||
column: "GroupId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ProductTypes_Name_GroupId",
|
|
||||||
table: "ProductTypes",
|
|
||||||
columns: new[] { "Name", "GroupId" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ProductVariants_ProductId_Color_Size",
|
|
||||||
table: "ProductVariants",
|
|
||||||
columns: new[] { "ProductId", "Color", "Size" },
|
|
||||||
unique: true,
|
|
||||||
filter: "[Color] IS NOT NULL AND [Size] IS NOT NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ProductVariants");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Products");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ProductTypes");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ProductGroups");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Printbase.Infrastructure.Database;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
|
||||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "9.0.4")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<Guid>("TypeId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("TypeId");
|
|
||||||
|
|
||||||
b.ToTable("Products");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductGroupDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(255)
|
|
||||||
.HasColumnType("nvarchar(255)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ProductGroups");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(255)
|
|
||||||
.HasColumnType("nvarchar(255)");
|
|
||||||
|
|
||||||
b.Property<Guid>("GroupId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("GroupId");
|
|
||||||
|
|
||||||
b.HasIndex("Name", "GroupId")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ProductTypes");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductVariantDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<string>("Color")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<decimal?>("Discount")
|
|
||||||
.HasPrecision(18, 2)
|
|
||||||
.HasColumnType("decimal(18,2)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<decimal>("Price")
|
|
||||||
.HasPrecision(18, 2)
|
|
||||||
.HasColumnType("decimal(18,2)");
|
|
||||||
|
|
||||||
b.Property<Guid>("ProductId")
|
|
||||||
.HasColumnType("uniqueidentifier");
|
|
||||||
|
|
||||||
b.Property<string>("SKU")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("Size")
|
|
||||||
.HasMaxLength(20)
|
|
||||||
.HasColumnType("nvarchar(20)");
|
|
||||||
|
|
||||||
b.Property<int>("Stock")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ProductId", "Color", "Size")
|
|
||||||
.IsUnique()
|
|
||||||
.HasFilter("[Color] IS NOT NULL AND [Size] IS NOT NULL");
|
|
||||||
|
|
||||||
b.ToTable("ProductVariants");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", "Type")
|
|
||||||
.WithMany("Products")
|
|
||||||
.HasForeignKey("TypeId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Type");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Printbase.Infrastructure.DbEntities.Products.ProductGroupDbEntity", "Group")
|
|
||||||
.WithMany("Types")
|
|
||||||
.HasForeignKey("GroupId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Group");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductVariantDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", "Product")
|
|
||||||
.WithMany("Variants")
|
|
||||||
.HasForeignKey("ProductId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Product");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Variants");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductGroupDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Types");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Printbase.Infrastructure.DbEntities.Products.ProductTypeDbEntity", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Products");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
using Printbase.Infrastructure.Database;
|
|
||||||
using Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Repositories;
|
|
||||||
|
|
||||||
public class ProductGroupRepository(ApplicationDbContext dbContext, IMapper mapper) : IProductGroupRepository
|
|
||||||
{
|
|
||||||
private readonly ApplicationDbContext _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
|
||||||
private readonly IMapper _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
public async Task<ProductGroup?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductGroupDbEntity> query = _dbContext.ProductGroups;
|
|
||||||
|
|
||||||
if (includeRelations) query = query.Include(g => g.Types);
|
|
||||||
|
|
||||||
var dbEntity = await query.FirstOrDefaultAsync(g => g.Id == id, cancellationToken);
|
|
||||||
|
|
||||||
return dbEntity == null ? null : _mapper.Map<ProductGroup>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ProductGroup>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductGroupDbEntity> query = _dbContext.ProductGroups;
|
|
||||||
|
|
||||||
if (includeRelations) query = query.Include(g => g.Types);
|
|
||||||
|
|
||||||
var dbEntities = await query.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<ProductGroup>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ProductGroup> AddAsync(ProductGroup group, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var dbEntity = _mapper.Map<ProductGroupDbEntity>(group);
|
|
||||||
|
|
||||||
_dbContext.ProductGroups.Add(dbEntity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<ProductGroup>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ProductGroup> UpdateAsync(ProductGroup group, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var existingEntity = await _dbContext.ProductGroups
|
|
||||||
.Include(g => g.Types)
|
|
||||||
.FirstOrDefaultAsync(g => g.Id == group.Id, cancellationToken);
|
|
||||||
|
|
||||||
if (existingEntity == null) throw new KeyNotFoundException($"ProductGroup with ID {group.Id} not found");
|
|
||||||
|
|
||||||
_mapper.Map(group, existingEntity);
|
|
||||||
|
|
||||||
existingEntity.UpdatedAt = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<ProductGroup>(existingEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var entity = await _dbContext.ProductGroups.FindAsync([id], cancellationToken);
|
|
||||||
|
|
||||||
if (entity == null) return false;
|
|
||||||
|
|
||||||
_dbContext.ProductGroups.Remove(entity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return await _dbContext.ProductGroups.AnyAsync(g => g.Id == id, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
using Printbase.Infrastructure.Database;
|
|
||||||
using Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Repositories;
|
|
||||||
|
|
||||||
public class ProductRepository(ApplicationDbContext dbContext, IMapper mapper) : IProductRepository
|
|
||||||
{
|
|
||||||
private readonly ApplicationDbContext _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
|
||||||
private readonly IMapper _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
public async Task<Product?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductDbEntity> query = _dbContext.Products;
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Include(p => p.Type)
|
|
||||||
.ThenInclude(t => t.Group)
|
|
||||||
.Include(p => p.Variants);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbEntity = await query.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
|
|
||||||
|
|
||||||
return dbEntity == null ? null : _mapper.Map<Product>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Product>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductDbEntity> query = _dbContext.Products;
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Include(p => p.Type)
|
|
||||||
.ThenInclude(t => t.Group)
|
|
||||||
.Include(p => p.Variants);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbEntities = await query.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<Product>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Product>> GetByTypeIdAsync(Guid typeId, bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var query = _dbContext.Products.Where(p => p.TypeId == typeId);
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Include(p => p.Type)
|
|
||||||
.ThenInclude(t => t.Group)
|
|
||||||
.Include(p => p.Variants);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbEntities = await query.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<Product>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Product> AddAsync(Product product, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var dbEntity = _mapper.Map<ProductDbEntity>(product);
|
|
||||||
|
|
||||||
dbEntity.Type = await _dbContext.ProductTypes.FindAsync([product.TypeId], cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"ProductType with ID {product.TypeId} not found");
|
|
||||||
|
|
||||||
_dbContext.Products.Add(dbEntity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<Product>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Product> UpdateAsync(Product product, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var existingEntity = await _dbContext.Products
|
|
||||||
.Include(p => p.Variants)
|
|
||||||
.FirstOrDefaultAsync(p => p.Id == product.Id, cancellationToken);
|
|
||||||
|
|
||||||
if (existingEntity == null) throw new KeyNotFoundException($"Product with ID {product.Id} not found");
|
|
||||||
|
|
||||||
_mapper.Map(product, existingEntity);
|
|
||||||
|
|
||||||
if (existingEntity.TypeId != product.TypeId)
|
|
||||||
{
|
|
||||||
existingEntity.Type = await _dbContext.ProductTypes.FindAsync([product.TypeId], cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"ProductType with ID {product.TypeId} not found");
|
|
||||||
existingEntity.TypeId = product.TypeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
existingEntity.UpdatedAt = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<Product>(existingEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var entity = await _dbContext.Products.FindAsync([id], cancellationToken);
|
|
||||||
|
|
||||||
if (entity == null) return false;
|
|
||||||
|
|
||||||
_dbContext.Products.Remove(entity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return await _dbContext.Products.AnyAsync(p => p.Id == id, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
using Printbase.Infrastructure.Database;
|
|
||||||
using Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Repositories;
|
|
||||||
|
|
||||||
public class ProductTypeRepository(ApplicationDbContext dbContext, IMapper mapper) : IProductTypeRepository
|
|
||||||
{
|
|
||||||
private readonly ApplicationDbContext _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
|
||||||
private readonly IMapper _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
public async Task<ProductType?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductTypeDbEntity> query = _dbContext.ProductTypes;
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Include(t => t.Group)
|
|
||||||
.Include(t => t.Products);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbEntity = await query.FirstOrDefaultAsync(t => t.Id == id, cancellationToken);
|
|
||||||
|
|
||||||
return dbEntity == null ? null : _mapper.Map<ProductType>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ProductType>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductTypeDbEntity> query = _dbContext.ProductTypes;
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Include(t => t.Group)
|
|
||||||
.Include(t => t.Products);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbEntities = await query.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<ProductType>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ProductType>> GetByGroupIdAsync(Guid groupId, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var dbEntities = await _dbContext.ProductTypes
|
|
||||||
.Where(t => t.GroupId == groupId)
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<ProductType>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ProductType> AddAsync(ProductType type, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var dbEntity = _mapper.Map<ProductTypeDbEntity>(type);
|
|
||||||
|
|
||||||
dbEntity.Group = await _dbContext.ProductGroups.FindAsync([type.GroupId], cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"ProductGroup with ID {type.GroupId} not found");
|
|
||||||
|
|
||||||
_dbContext.ProductTypes.Add(dbEntity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<ProductType>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ProductType> UpdateAsync(ProductType type, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var existingEntity = await _dbContext.ProductTypes
|
|
||||||
.Include(t => t.Products)
|
|
||||||
.FirstOrDefaultAsync(t => t.Id == type.Id, cancellationToken);
|
|
||||||
|
|
||||||
if (existingEntity == null) throw new KeyNotFoundException($"ProductType with ID {type.Id} not found");
|
|
||||||
|
|
||||||
_mapper.Map(type, existingEntity);
|
|
||||||
|
|
||||||
if (existingEntity.GroupId != type.GroupId)
|
|
||||||
{
|
|
||||||
existingEntity.Group = await _dbContext.ProductGroups.FindAsync([type.GroupId], cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"ProductGroup with ID {type.GroupId} not found");
|
|
||||||
existingEntity.GroupId = type.GroupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
existingEntity.UpdatedAt = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<ProductType>(existingEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var entity = await _dbContext.ProductTypes.FindAsync([id], cancellationToken);
|
|
||||||
|
|
||||||
if (entity == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dbContext.ProductTypes.Remove(entity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return await _dbContext.ProductTypes.AnyAsync(t => t.Id == id, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Printbase.Domain.Entities.Products;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
using Printbase.Infrastructure.Database;
|
|
||||||
using Printbase.Infrastructure.DbEntities.Products;
|
|
||||||
|
|
||||||
namespace Printbase.Infrastructure.Repositories;
|
|
||||||
|
|
||||||
public class ProductVariantRepository(ApplicationDbContext dbContext, IMapper mapper) : IProductVariantRepository
|
|
||||||
{
|
|
||||||
private readonly ApplicationDbContext _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
|
||||||
private readonly IMapper _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
public async Task<ProductVariant?> GetByIdAsync(Guid id, bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductVariantDbEntity> query = _dbContext.ProductVariants;
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
query = query.Include(v => v.Product)
|
|
||||||
.ThenInclude(p => p.Type);
|
|
||||||
|
|
||||||
var dbEntity = await query.FirstOrDefaultAsync(v => v.Id == id, cancellationToken);
|
|
||||||
|
|
||||||
return dbEntity == null ? null : _mapper.Map<ProductVariant>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ProductVariant>> GetAllAsync(bool includeRelations = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
IQueryable<ProductVariantDbEntity> query = _dbContext.ProductVariants;
|
|
||||||
|
|
||||||
if (includeRelations)
|
|
||||||
query = query.Include(v => v.Product)
|
|
||||||
.ThenInclude(p => p.Type);
|
|
||||||
|
|
||||||
var dbEntities = await query.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<ProductVariant>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ProductVariant>> GetByProductIdAsync(Guid productId, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var dbEntities = await _dbContext.ProductVariants
|
|
||||||
.Where(v => v.ProductId == productId)
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<ProductVariant>>(dbEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ProductVariant> AddAsync(ProductVariant variant, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var dbEntity = _mapper.Map<ProductVariantDbEntity>(variant);
|
|
||||||
|
|
||||||
dbEntity.Product = await _dbContext.Products.FindAsync([variant.ProductId], cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"Product with ID {variant.ProductId} not found");
|
|
||||||
|
|
||||||
_dbContext.ProductVariants.Add(dbEntity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<ProductVariant>(dbEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ProductVariant> UpdateAsync(ProductVariant variant, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var existingEntity = await _dbContext.ProductVariants
|
|
||||||
.FindAsync([variant.Id], cancellationToken);
|
|
||||||
|
|
||||||
if (existingEntity == null) throw new KeyNotFoundException($"ProductVariant with ID {variant.Id} not found");
|
|
||||||
|
|
||||||
_mapper.Map(variant, existingEntity);
|
|
||||||
|
|
||||||
if (existingEntity.ProductId != variant.ProductId)
|
|
||||||
{
|
|
||||||
existingEntity.Product = await _dbContext.Products.FindAsync([variant.ProductId], cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"Product with ID {variant.ProductId} not found");
|
|
||||||
existingEntity.ProductId = variant.ProductId;
|
|
||||||
}
|
|
||||||
|
|
||||||
existingEntity.UpdatedAt = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<ProductVariant>(existingEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var entity = await _dbContext.ProductVariants.FindAsync([id], cancellationToken);
|
|
||||||
|
|
||||||
if (entity == null) return false;
|
|
||||||
|
|
||||||
_dbContext.ProductVariants.Remove(entity);
|
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return await _dbContext.ProductVariants.AnyAsync(v => v.Id == id, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
using Printbase.Application.Products.Queries.GetAllProducts;
|
|
||||||
using Printbase.Application.Products.Queries.GetProductById;
|
|
||||||
|
|
||||||
namespace Printbase.WebApi.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class ProductsController(IMediator mediator) : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IMediator _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> GetAllProducts()
|
|
||||||
{
|
|
||||||
var query = new GetAllProductsQuery();
|
|
||||||
var result = await _mediator.Send(query);
|
|
||||||
|
|
||||||
if (result == null) return NotFound();
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:guid}")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public async Task<IActionResult> GetProductById(Guid id, [FromQuery] bool includeVariants = true)
|
|
||||||
{
|
|
||||||
var query = new GetProductByIdQuery(id, includeVariants);
|
|
||||||
var result = await _mediator.Send(query);
|
|
||||||
|
|
||||||
if (result == null) return NotFound();
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
||||||
public async Task<IActionResult> CreateProduct([FromBody] CreateProductCommand command)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = await _mediator.Send(command);
|
|
||||||
return CreatedAtAction(nameof(GetProductById), new { id = result.Id }, result);
|
|
||||||
}
|
|
||||||
catch (ArgumentException ex)
|
|
||||||
{
|
|
||||||
return BadRequest(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
using System.Reflection;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
using Printbase.Domain.Repositories;
|
|
||||||
using Printbase.Infrastructure.Database;
|
using Printbase.Infrastructure.Database;
|
||||||
using Printbase.Infrastructure.Mappings;
|
|
||||||
using Printbase.Infrastructure.Repositories;
|
|
||||||
|
|
||||||
namespace Printbase.WebApi;
|
namespace Printbase.WebApi;
|
||||||
|
|
||||||
@@ -13,29 +8,11 @@ public static class Startup
|
|||||||
public static void ConfigureServices(WebApplicationBuilder builder)
|
public static void ConfigureServices(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
var services = builder.Services;
|
var services = builder.Services;
|
||||||
|
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options.UseSqlServer(
|
options.UseSqlServer(
|
||||||
builder.Configuration.GetConnectionString("DefaultConnection"),
|
builder.Configuration.GetConnectionString("DefaultConnection"),
|
||||||
b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
|
b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
|
||||||
|
|
||||||
services.AddMediatR(cfg => {
|
|
||||||
cfg.RegisterServicesFromAssembly(typeof(CreateProductCommand).Assembly);
|
|
||||||
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddScoped<IProductRepository, ProductRepository>();
|
|
||||||
services.AddScoped<IProductVariantRepository, ProductVariantRepository>();
|
|
||||||
services.AddScoped<IProductTypeRepository, ProductTypeRepository>();
|
|
||||||
services.AddScoped<IProductGroupRepository, ProductGroupRepository>();
|
|
||||||
|
|
||||||
services.AddAutoMapper(cfg =>
|
|
||||||
{
|
|
||||||
cfg.AddProfile<ProductMappingProfile>();
|
|
||||||
}, typeof(ProductMappingProfile).Assembly);
|
|
||||||
services.AddSwaggerGen();
|
|
||||||
services.AddControllers();
|
|
||||||
services.AddOpenApi();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
@@ -57,14 +34,12 @@ public static class Startup
|
|||||||
|
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
Console.WriteLine("Development environment variables applied");
|
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("Production environment variables applied");
|
|
||||||
app.UseExceptionHandler("/Error");
|
app.UseExceptionHandler("/Error");
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
using Printbase.Application.Products.Commands.CreateProduct;
|
|
||||||
|
|
||||||
namespace Printbase.Application.Tests.Products.Commands.CreateProduct;
|
|
||||||
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
public class GenerateSkuTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithValidInputs_ReturnsCorrectFormat()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Shirt";
|
|
||||||
const string color = "Blue";
|
|
||||||
const string size = "Medium";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(4, parts.Length);
|
|
||||||
Assert.Equal("SHI", parts[0]);
|
|
||||||
Assert.Equal("BLU", parts[1]);
|
|
||||||
Assert.Equal("MEDIUM", parts[2]);
|
|
||||||
Assert.Matches(@"^\d{3}$", parts[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithShortProductName_UsesEntireProductName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "CU";
|
|
||||||
const string color = "Black";
|
|
||||||
const string size = "Large";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("CU", parts[0]);
|
|
||||||
Assert.Equal("BLA", parts[1]);
|
|
||||||
Assert.Equal("LARGE", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithShortColor_UsesEntireColor()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Mug";
|
|
||||||
const string color = "Red";
|
|
||||||
const string size = "Small";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("MUG", parts[0]);
|
|
||||||
Assert.Equal("RED", parts[1]);
|
|
||||||
Assert.Equal("SMALL", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithNullColor_UsesXXX()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Case";
|
|
||||||
string? color = null;
|
|
||||||
const string size = "Standard";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("CAS", parts[0]);
|
|
||||||
Assert.Equal("XXX", parts[1]);
|
|
||||||
Assert.Equal("STANDARD", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithEmptyColor_UsesXXX()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Notebook";
|
|
||||||
const string color = "";
|
|
||||||
const string size = "Standard";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("NOT", parts[0]);
|
|
||||||
Assert.Equal("XXX", parts[1]);
|
|
||||||
Assert.Equal("STANDARD", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithNullSize_UsesOS()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Watch";
|
|
||||||
const string color = "Silver";
|
|
||||||
string? size = null;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("WAT", parts[0]);
|
|
||||||
Assert.Equal("SIL", parts[1]);
|
|
||||||
Assert.Equal("OS", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_WithEmptySize_UsesOS()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Shirt";
|
|
||||||
const string color = "Black";
|
|
||||||
const string size = "";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("SHI", parts[0]);
|
|
||||||
Assert.Equal("BLA", parts[1]);
|
|
||||||
Assert.Equal("OS", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_CaseInsensitivity_OutputsUppercase()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "hat";
|
|
||||||
const string color = "red";
|
|
||||||
const string size = "small";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("HAT", parts[0]);
|
|
||||||
Assert.Equal("RED", parts[1]);
|
|
||||||
Assert.Equal("SMALL", parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_RandomPartIsInRange()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Book";
|
|
||||||
const string color = "Green";
|
|
||||||
const string size = "Medium";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var parts = sku.Split('-');
|
|
||||||
var randomPart = int.Parse(parts[3]);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.InRange(randomPart, 100, 999);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GenerateSku_GeneratesUniqueSKUs()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string productName = "Mug";
|
|
||||||
const string color = "White";
|
|
||||||
const string size = "Regular";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var sku1 = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
var sku2 = CreateProductCommandHandler.GenerateSku(productName, color, size);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotEqual(sku1, sku2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user