From 169c1aa2fd7370b9828c13644ed69a9642cb8a49 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Tue, 12 Aug 2025 14:32:47 +0200 Subject: [PATCH] sonderangebot artikel --- .../Controllers/Public/ProductsController.cs | 10 +++++ .../DTOs/Products/AdminProductDto.cs | 7 +++- .../DTOs/Products/CreateAdminProductDto.cs | 3 ++ .../DTOs/Products/UpdateAdminProductDto.cs | 2 + .../Services/Admin/AdminProductService.cs | 4 ++ .../Public/Interfaces/IProductService.cs | 1 + .../Services/Public/ProductService.cs | 37 +++++++++++++++++++ Webshop.Domain/Entities/Product.cs | 10 ++--- ...20250812123244_featureartikel.Designer.cs} | 10 ++++- ...ut.cs => 20250812123244_featureartikel.cs} | 6 ++- .../ApplicationDbContextModelSnapshot.cs | 6 +++ 11 files changed, 85 insertions(+), 11 deletions(-) rename Webshop.Infrastructure/Migrations/{20250812113218_checkout.Designer.cs => 20250812123244_featureartikel.Designer.cs} (99%) rename Webshop.Infrastructure/Migrations/{20250812113218_checkout.cs => 20250812123244_featureartikel.cs} (99%) diff --git a/Webshop.Api/Controllers/Public/ProductsController.cs b/Webshop.Api/Controllers/Public/ProductsController.cs index bef694a..77316a9 100644 --- a/Webshop.Api/Controllers/Public/ProductsController.cs +++ b/Webshop.Api/Controllers/Public/ProductsController.cs @@ -49,5 +49,15 @@ namespace Webshop.Api.Controllers.Public return Ok(product); } + /// + /// Ruft eine Liste der Sonderangebote für die Startseite ab, sortiert nach Anzeigereihenfolge. + /// + [HttpGet("featured")] + [ProducesResponseType(typeof(IEnumerable), 200)] + public async Task>> GetFeaturedProducts() + { + var products = await _productService.GetFeaturedProductsAsync(); + return Ok(products); + } } } \ No newline at end of file diff --git a/Webshop.Application/DTOs/Products/AdminProductDto.cs b/Webshop.Application/DTOs/Products/AdminProductDto.cs index ee6b150..903db66 100644 --- a/Webshop.Application/DTOs/Products/AdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/AdminProductDto.cs @@ -16,13 +16,16 @@ namespace Webshop.Application.DTOs.Products public bool IsInStock { get; set; } = true; public int StockQuantity { get; set; } public decimal? Weight { get; set; } - // << ENTFERNT: ImageUrl >> public string Slug { get; set; } = string.Empty; public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? LastModifiedDate { get; set; } public Guid? SupplierId { get; set; } public decimal? PurchasePrice { get; set; } public List categorieIds { get; set; } = new List(); - public List Images { get; set; } = new List(); // << NEU >> + public List Images { get; set; } = new List(); + + // << NEU >> + public bool IsFeatured { get; set; } + public int FeaturedDisplayOrder { get; set; } } } \ No newline at end of file diff --git a/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs b/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs index 8949a8e..c2d92ae 100644 --- a/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/CreateAdminProductDto.cs @@ -30,5 +30,8 @@ namespace Webshop.Application.DTOs.Products public decimal? OldPrice { get; set; } public Guid? SupplierId { get; set; } public decimal? PurchasePrice { get; set; } + + public bool IsFeatured { get; set; } = false; + public int FeaturedDisplayOrder { get; set; } = 0; } } \ No newline at end of file diff --git a/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs b/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs index 51c7cba..23c98a4 100644 --- a/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/UpdateAdminProductDto.cs @@ -33,5 +33,7 @@ namespace Webshop.Application.DTOs.Products public decimal? OldPrice { get; set; } public Guid? SupplierId { get; set; } public decimal? PurchasePrice { get; set; } + public bool IsFeatured { get; set; } + public int FeaturedDisplayOrder { get; set; } } } \ No newline at end of file diff --git a/Webshop.Application/Services/Admin/AdminProductService.cs b/Webshop.Application/Services/Admin/AdminProductService.cs index 0764b89..503d6d8 100644 --- a/Webshop.Application/Services/Admin/AdminProductService.cs +++ b/Webshop.Application/Services/Admin/AdminProductService.cs @@ -137,6 +137,8 @@ namespace Webshop.Application.Services.Admin OldPrice = productDto.OldPrice, SupplierId = productDto.SupplierId, PurchasePrice = productDto.PurchasePrice, + IsFeatured = productDto.IsFeatured, // << NEU >> + FeaturedDisplayOrder = productDto.FeaturedDisplayOrder, // << NEU >> Images = images, Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList() }; @@ -196,6 +198,8 @@ namespace Webshop.Application.Services.Admin existingProduct.SupplierId = productDto.SupplierId; existingProduct.PurchasePrice = productDto.PurchasePrice; existingProduct.LastModifiedDate = DateTimeOffset.UtcNow; + existingProduct.IsFeatured = productDto.IsFeatured; // << NEU >> + existingProduct.FeaturedDisplayOrder = productDto.FeaturedDisplayOrder; // << NEU >> // Kategorien synchronisieren existingProduct.Productcategories.Clear(); diff --git a/Webshop.Application/Services/Public/Interfaces/IProductService.cs b/Webshop.Application/Services/Public/Interfaces/IProductService.cs index 4d55b7f..035509b 100644 --- a/Webshop.Application/Services/Public/Interfaces/IProductService.cs +++ b/Webshop.Application/Services/Public/Interfaces/IProductService.cs @@ -10,5 +10,6 @@ namespace Webshop.Application.Services.Public.Interfaces Task> GetAllProductsAsync(); Task GetProductBySlugAsync(string slug); + Task> GetFeaturedProductsAsync(); // << NEU >> } } \ No newline at end of file diff --git a/Webshop.Application/Services/Public/ProductService.cs b/Webshop.Application/Services/Public/ProductService.cs index 2a62d7e..cc0de72 100644 --- a/Webshop.Application/Services/Public/ProductService.cs +++ b/Webshop.Application/Services/Public/ProductService.cs @@ -89,5 +89,42 @@ namespace Webshop.Application.Services.Public }).ToList() }; } + + public async Task> GetFeaturedProductsAsync() + { + var products = await _context.Products + .Where(p => p.IsActive && p.IsFeatured) + .OrderBy(p => p.FeaturedDisplayOrder) + .Include(p => p.Images) + .Include(p => p.Productcategories).ThenInclude(pc => pc.categorie) + .ToListAsync(); + + return products.Select(p => new ProductDto + { + Id = p.Id, + Name = p.Name, + Description = p.ShortDescription, // Für die Startseite ist die Kurzbeschreibung ideal + SKU = p.SKU, + Price = p.Price, + IsActive = p.IsActive, + IsInStock = p.IsInStock, + StockQuantity = p.StockQuantity, + Slug = p.Slug, + categories = p.Productcategories.Select(pc => new CategorieDto + { + Id = pc.categorie.Id, + Name = pc.categorie.Name, + Slug = pc.categorie.Slug + }).ToList(), + Images = p.Images.OrderBy(i => i.DisplayOrder).Select(img => new ProductImageDto + { + Id = img.Id, + Url = img.Url, + IsMainImage = img.IsMainImage, + DisplayOrder = img.DisplayOrder + }).ToList() + }).ToList(); + } + } } \ No newline at end of file diff --git a/Webshop.Domain/Entities/Product.cs b/Webshop.Domain/Entities/Product.cs index 94f871e..82f2896 100644 --- a/Webshop.Domain/Entities/Product.cs +++ b/Webshop.Domain/Entities/Product.cs @@ -33,9 +33,6 @@ namespace Webshop.Domain.Entities public decimal? Height { get; set; } public decimal? Length { get; set; } - // << ENTFERNT: ImageUrl wird durch Images ersetzt >> - // public string? ImageUrl { get; set; } - [Required, MaxLength(255)] public string Slug { get; set; } = string.Empty; [Required] @@ -45,13 +42,16 @@ namespace Webshop.Domain.Entities public Guid? SupplierId { get; set; } public decimal? PurchasePrice { get; set; } + // << NEUE EIGENSCHAFTEN FÜR SONDERANGEBOTE >> + public bool IsFeatured { get; set; } = false; + public int FeaturedDisplayOrder { get; set; } = 0; + // << ENDE NEUE EIGENSCHAFTEN >> + public virtual Supplier? Supplier { get; set; } public virtual ICollection Variants { get; set; } = new List(); public virtual ICollection Reviews { get; set; } = new List(); public virtual ICollection ProductDiscounts { get; set; } = new List(); public virtual ICollection Productcategories { get; set; } = new List(); - - // << NEU: Navigation Property zu Bildern >> public virtual ICollection Images { get; set; } = new List(); } } \ No newline at end of file diff --git a/Webshop.Infrastructure/Migrations/20250812113218_checkout.Designer.cs b/Webshop.Infrastructure/Migrations/20250812123244_featureartikel.Designer.cs similarity index 99% rename from Webshop.Infrastructure/Migrations/20250812113218_checkout.Designer.cs rename to Webshop.Infrastructure/Migrations/20250812123244_featureartikel.Designer.cs index 139313f..f2492b8 100644 --- a/Webshop.Infrastructure/Migrations/20250812113218_checkout.Designer.cs +++ b/Webshop.Infrastructure/Migrations/20250812123244_featureartikel.Designer.cs @@ -12,8 +12,8 @@ using Webshop.Infrastructure.Data; namespace Webshop.Infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20250812113218_checkout")] - partial class checkout + [Migration("20250812123244_featureartikel")] + partial class featureartikel { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -576,6 +576,9 @@ namespace Webshop.Infrastructure.Migrations .HasMaxLength(4000) .HasColumnType("character varying(4000)"); + b.Property("FeaturedDisplayOrder") + .HasColumnType("integer"); + b.Property("Height") .HasPrecision(18, 2) .HasColumnType("numeric(18,2)"); @@ -583,6 +586,9 @@ namespace Webshop.Infrastructure.Migrations b.Property("IsActive") .HasColumnType("boolean"); + b.Property("IsFeatured") + .HasColumnType("boolean"); + b.Property("IsInStock") .HasColumnType("boolean"); diff --git a/Webshop.Infrastructure/Migrations/20250812113218_checkout.cs b/Webshop.Infrastructure/Migrations/20250812123244_featureartikel.cs similarity index 99% rename from Webshop.Infrastructure/Migrations/20250812113218_checkout.cs rename to Webshop.Infrastructure/Migrations/20250812123244_featureartikel.cs index 95a30b0..e0ca3f5 100644 --- a/Webshop.Infrastructure/Migrations/20250812113218_checkout.cs +++ b/Webshop.Infrastructure/Migrations/20250812123244_featureartikel.cs @@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Webshop.Infrastructure.Migrations { /// - public partial class checkout : Migration + public partial class featureartikel : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -460,7 +460,9 @@ namespace Webshop.Infrastructure.Migrations CreatedDate = table.Column(type: "timestamp with time zone", nullable: false), LastModifiedDate = table.Column(type: "timestamp with time zone", nullable: true), SupplierId = table.Column(type: "uuid", nullable: true), - PurchasePrice = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true) + PurchasePrice = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true), + IsFeatured = table.Column(type: "boolean", nullable: false), + FeaturedDisplayOrder = table.Column(type: "integer", nullable: false) }, constraints: table => { diff --git a/Webshop.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/Webshop.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 0392333..42ffa62 100644 --- a/Webshop.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Webshop.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -573,6 +573,9 @@ namespace Webshop.Infrastructure.Migrations .HasMaxLength(4000) .HasColumnType("character varying(4000)"); + b.Property("FeaturedDisplayOrder") + .HasColumnType("integer"); + b.Property("Height") .HasPrecision(18, 2) .HasColumnType("numeric(18,2)"); @@ -580,6 +583,9 @@ namespace Webshop.Infrastructure.Migrations b.Property("IsActive") .HasColumnType("boolean"); + b.Property("IsFeatured") + .HasColumnType("boolean"); + b.Property("IsInStock") .HasColumnType("boolean");