From 7fd2af2d5ac6ac1ae18292923d15dbdf4d2a940e Mon Sep 17 00:00:00 2001 From: lumijiez <59575049+lumijiez@users.noreply.github.com> Date: Sun, 4 May 2025 00:53:16 +0300 Subject: [PATCH] Add base product DbEntities and domain Entities --- src/Printbase.Application/Class1.cs | 5 - .../Printbase.Application.csproj | 4 + src/Printbase.Domain/Class1.cs | 5 - .../Entities/Products/Product.cs | 80 +++++++++++++++ .../Entities/Products/ProductGroup.cs | 49 ++++++++++ .../Entities/Products/ProductType.cs | 38 ++++++++ .../Entities/Products/ProductVariant.cs | 97 +++++++++++++++++++ src/Printbase.Domain/Printbase.Domain.csproj | 4 + src/Printbase.Infrastructure/Class1.cs | 5 - .../DbEntities/Products/ProductDbEntity.cs | 29 ++++++ .../Products/ProductGroupDbEntity.cs | 20 ++++ .../Products/ProductTypeDbEntity.cs | 26 +++++ .../Products/ProductVariantDbEntity.cs | 32 ++++++ 13 files changed, 379 insertions(+), 15 deletions(-) delete mode 100644 src/Printbase.Application/Class1.cs delete mode 100644 src/Printbase.Domain/Class1.cs create mode 100644 src/Printbase.Domain/Entities/Products/Product.cs create mode 100644 src/Printbase.Domain/Entities/Products/ProductGroup.cs create mode 100644 src/Printbase.Domain/Entities/Products/ProductType.cs create mode 100644 src/Printbase.Domain/Entities/Products/ProductVariant.cs delete mode 100644 src/Printbase.Infrastructure/Class1.cs create mode 100644 src/Printbase.Infrastructure/DbEntities/Products/ProductDbEntity.cs create mode 100644 src/Printbase.Infrastructure/DbEntities/Products/ProductGroupDbEntity.cs create mode 100644 src/Printbase.Infrastructure/DbEntities/Products/ProductTypeDbEntity.cs create mode 100644 src/Printbase.Infrastructure/DbEntities/Products/ProductVariantDbEntity.cs diff --git a/src/Printbase.Application/Class1.cs b/src/Printbase.Application/Class1.cs deleted file mode 100644 index fff6417..0000000 --- a/src/Printbase.Application/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Printbase.Application; - -public class Class1 -{ -} \ No newline at end of file diff --git a/src/Printbase.Application/Printbase.Application.csproj b/src/Printbase.Application/Printbase.Application.csproj index 17b910f..d096b39 100644 --- a/src/Printbase.Application/Printbase.Application.csproj +++ b/src/Printbase.Application/Printbase.Application.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/src/Printbase.Domain/Class1.cs b/src/Printbase.Domain/Class1.cs deleted file mode 100644 index faafe41..0000000 --- a/src/Printbase.Domain/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Printbase.Domain; - -public class Class1 -{ -} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Products/Product.cs b/src/Printbase.Domain/Entities/Products/Product.cs new file mode 100644 index 0000000..bd7dbdf --- /dev/null +++ b/src/Printbase.Domain/Entities/Products/Product.cs @@ -0,0 +1,80 @@ +namespace Printbase.Domain.Entities.Products; + +public class Product +{ + public Guid Id { get; private set; } + public string Name { get; private set; } + public string? Description { get; private set; } + public decimal? Discount { get; private set; } + public Guid TypeId { get; private set; } + public IReadOnlyCollection Variants => _variants.AsReadOnly(); + + private readonly List _variants = []; + + private Product() { } + + public Product(Guid id, string name, Guid typeId, string? description = null, decimal? discount = null) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Product name cannot be empty", nameof(name)); + + if (typeId == Guid.Empty) + throw new ArgumentException("Type ID cannot be empty", nameof(typeId)); + + Id = id; + Name = name; + TypeId = typeId; + Description = description; + Discount = discount; + } + + public void AddVariant(ProductVariant variant) + { + ArgumentNullException.ThrowIfNull(variant); + + _variants.Add(variant); + } + + public void RemoveVariant(Guid variantId) + { + var variant = _variants.Find(v => v.Id == variantId); + if (variant != null) + { + _variants.Remove(variant); + } + } + + public void UpdateName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Product name cannot be empty", nameof(name)); + + Name = name; + } + + public void UpdateDescription(string? description) + { + Description = description; + } + + public void UpdateDiscount(decimal? discount) + { + if (discount.HasValue && (discount.Value < 0 || discount.Value > 100)) + throw new ArgumentException("Discount must be between 0 and 100", nameof(discount)); + + Discount = discount; + } + + public void UpdateType(Guid typeId) + { + if (typeId == Guid.Empty) + throw new ArgumentException("Type ID cannot be empty", nameof(typeId)); + + TypeId = typeId; + } + + public decimal? GetEffectiveDiscount() + { + return Discount; + } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Products/ProductGroup.cs b/src/Printbase.Domain/Entities/Products/ProductGroup.cs new file mode 100644 index 0000000..8241e7a --- /dev/null +++ b/src/Printbase.Domain/Entities/Products/ProductGroup.cs @@ -0,0 +1,49 @@ +namespace Printbase.Domain.Entities.Products; + +public class ProductGroup +{ + public Guid Id { get; private set; } + public string Name { get; private set; } + public string? Description { get; private set; } + public IReadOnlyCollection Types => _types.AsReadOnly(); + + private readonly List _types = new(); + + private ProductGroup() { } + + public ProductGroup(Guid id, string name, string? description = null) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Group name cannot be empty", nameof(name)); + + Id = id; + Name = name; + Description = description; + } + + public void AddType(ProductType type) + { + ArgumentNullException.ThrowIfNull(type); + + _types.Add(type); + } + + public void RemoveType(Guid typeId) + { + var type = _types.Find(t => t.Id == typeId); + if (type != null) _types.Remove(type); + } + + public void UpdateName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Group name cannot be empty", nameof(name)); + + Name = name; + } + + public void UpdateDescription(string? description) + { + Description = description; + } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Products/ProductType.cs b/src/Printbase.Domain/Entities/Products/ProductType.cs new file mode 100644 index 0000000..1658608 --- /dev/null +++ b/src/Printbase.Domain/Entities/Products/ProductType.cs @@ -0,0 +1,38 @@ +namespace Printbase.Domain.Entities.Products; + +public class ProductType +{ + public Guid Id { get; private set; } + public string Name { get; private set; } + public string? Description { get; private set; } + public Guid GroupId { get; private set; } + + private ProductType() { } + + public ProductType(Guid id, string name, Guid groupId, string? description = null) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Type name cannot be empty", nameof(name)); + + if (groupId == Guid.Empty) + throw new ArgumentException("Group ID cannot be empty", nameof(groupId)); + + Id = id; + Name = name; + GroupId = groupId; + Description = description; + } + + public void UpdateName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Type name cannot be empty", nameof(name)); + + Name = name; + } + + public void UpdateDescription(string? description) + { + Description = description; + } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Products/ProductVariant.cs b/src/Printbase.Domain/Entities/Products/ProductVariant.cs new file mode 100644 index 0000000..078f16e --- /dev/null +++ b/src/Printbase.Domain/Entities/Products/ProductVariant.cs @@ -0,0 +1,97 @@ +namespace Printbase.Domain.Entities.Products; + +public class ProductVariant +{ + public Guid Id { get; private set; } + public Guid ProductId { get; private set; } + public string? Color { get; private set; } + public string? Size { get; private set; } + public decimal Price { get; private set; } + public decimal? Discount { get; private set; } + public int Stock { get; private set; } + + private ProductVariant() { } + + public ProductVariant(Guid id, Guid productId, decimal price, string? color = null, string? size = null, + decimal? discount = null, int stock = 0) + { + if (price < 0) + throw new ArgumentException("Price cannot be negative", nameof(price)); + + Id = id; + ProductId = productId; + Color = color; + Size = size; + Price = price; + Discount = discount; + Stock = stock; + } + + public void UpdateColor(string? color) + { + Color = color; + } + + public void UpdateSize(string? size) + { + Size = size; + } + + public void UpdatePrice(decimal price) + { + if (price < 0) throw new ArgumentException("Price cannot be negative", nameof(price)); + + Price = price; + } + + public void UpdateDiscount(decimal? discount) + { + if (discount is < 0 or > 100) + throw new ArgumentException("Discount must be between 0 and 100", nameof(discount)); + + Discount = discount; + } + + public void UpdateStock(int quantity) + { + if (quantity < 0) + throw new ArgumentException("Stock quantity cannot be negative", nameof(quantity)); + + Stock = quantity; + } + + public void AddStock(int quantity) + { + if (quantity <= 0) + throw new ArgumentException("Quantity to add must be positive", nameof(quantity)); + + Stock += quantity; + } + + public bool RemoveStock(int quantity) + { + if (quantity <= 0) + throw new ArgumentException("Quantity to remove must be positive", nameof(quantity)); + + if (Stock < quantity) return false; + + Stock -= quantity; + return true; + } + + public decimal GetEffectivePrice(decimal? productDiscount = null) + { + var effectivePrice = Price; + + var discountToApply = Discount ?? productDiscount; + + if (discountToApply is > 0) effectivePrice -= (effectivePrice * discountToApply.Value / 100); + + return Math.Round(effectivePrice, 2); + } + + public decimal? GetEffectiveDiscount(decimal? productDiscount = null) + { + return Discount ?? productDiscount; + } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Printbase.Domain.csproj b/src/Printbase.Domain/Printbase.Domain.csproj index 17b910f..93e8640 100644 --- a/src/Printbase.Domain/Printbase.Domain.csproj +++ b/src/Printbase.Domain/Printbase.Domain.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/src/Printbase.Infrastructure/Class1.cs b/src/Printbase.Infrastructure/Class1.cs deleted file mode 100644 index 73d80a3..0000000 --- a/src/Printbase.Infrastructure/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Printbase.Infrastructure; - -public class Class1 -{ -} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/DbEntities/Products/ProductDbEntity.cs b/src/Printbase.Infrastructure/DbEntities/Products/ProductDbEntity.cs new file mode 100644 index 0000000..a0d870b --- /dev/null +++ b/src/Printbase.Infrastructure/DbEntities/Products/ProductDbEntity.cs @@ -0,0 +1,29 @@ +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; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? Discount { get; set; } + + [Required] + public Guid TypeId { get; set; } + + [ForeignKey(nameof(TypeId)), Required] + public required ProductTypeDbEntity Type { get; set; } + + [InverseProperty(nameof(ProductVariantDbEntity.Product)), Required] + public required ICollection Variants { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/DbEntities/Products/ProductGroupDbEntity.cs b/src/Printbase.Infrastructure/DbEntities/Products/ProductGroupDbEntity.cs new file mode 100644 index 0000000..08bacd0 --- /dev/null +++ b/src/Printbase.Infrastructure/DbEntities/Products/ProductGroupDbEntity.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Printbase.Infrastructure.DbEntities.Products; + +[Table("ProductGroups")] +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 Types { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/DbEntities/Products/ProductTypeDbEntity.cs b/src/Printbase.Infrastructure/DbEntities/Products/ProductTypeDbEntity.cs new file mode 100644 index 0000000..127b334 --- /dev/null +++ b/src/Printbase.Infrastructure/DbEntities/Products/ProductTypeDbEntity.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Printbase.Infrastructure.DbEntities.Products; + +[Table("ProductTypes")] +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))] + public ICollection? Products { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/DbEntities/Products/ProductVariantDbEntity.cs b/src/Printbase.Infrastructure/DbEntities/Products/ProductVariantDbEntity.cs new file mode 100644 index 0000000..e158e3a --- /dev/null +++ b/src/Printbase.Infrastructure/DbEntities/Products/ProductVariantDbEntity.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Printbase.Infrastructure.DbEntities.Products; + +[Table("ProductVariants")] +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] + public decimal Price { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? Discount { get; set; } + + [Required] + public int Stock { get; set; } + + [ForeignKey(nameof(ProductId)), Required] + public required ProductDbEntity Product { get; set; } +} \ No newline at end of file