sonderangebot artikel

This commit is contained in:
Tizian.Breuch
2025-08-12 14:32:47 +02:00
parent 3e68caf33c
commit 169c1aa2fd
11 changed files with 85 additions and 11 deletions

View File

@@ -49,5 +49,15 @@ namespace Webshop.Api.Controllers.Public
return Ok(product); return Ok(product);
} }
/// <summary>
/// Ruft eine Liste der Sonderangebote f<>r die Startseite ab, sortiert nach Anzeigereihenfolge.
/// </summary>
[HttpGet("featured")]
[ProducesResponseType(typeof(IEnumerable<ProductDto>), 200)]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetFeaturedProducts()
{
var products = await _productService.GetFeaturedProductsAsync();
return Ok(products);
}
} }
} }

View File

@@ -16,13 +16,16 @@ namespace Webshop.Application.DTOs.Products
public bool IsInStock { get; set; } = true; public bool IsInStock { get; set; } = true;
public int StockQuantity { get; set; } public int StockQuantity { get; set; }
public decimal? Weight { get; set; } public decimal? Weight { get; set; }
// << ENTFERNT: ImageUrl >>
public string Slug { get; set; } = string.Empty; public string Slug { get; set; } = string.Empty;
public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset CreatedDate { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? LastModifiedDate { get; set; } public DateTimeOffset? LastModifiedDate { get; set; }
public Guid? SupplierId { get; set; } public Guid? SupplierId { get; set; }
public decimal? PurchasePrice { get; set; } public decimal? PurchasePrice { get; set; }
public List<Guid> categorieIds { get; set; } = new List<Guid>(); public List<Guid> categorieIds { get; set; } = new List<Guid>();
public List<ProductImageDto> Images { get; set; } = new List<ProductImageDto>(); // << NEU >> public List<ProductImageDto> Images { get; set; } = new List<ProductImageDto>();
// << NEU >>
public bool IsFeatured { get; set; }
public int FeaturedDisplayOrder { get; set; }
} }
} }

View File

@@ -30,5 +30,8 @@ namespace Webshop.Application.DTOs.Products
public decimal? OldPrice { get; set; } public decimal? OldPrice { get; set; }
public Guid? SupplierId { get; set; } public Guid? SupplierId { get; set; }
public decimal? PurchasePrice { get; set; } public decimal? PurchasePrice { get; set; }
public bool IsFeatured { get; set; } = false;
public int FeaturedDisplayOrder { get; set; } = 0;
} }
} }

View File

@@ -33,5 +33,7 @@ namespace Webshop.Application.DTOs.Products
public decimal? OldPrice { get; set; } public decimal? OldPrice { get; set; }
public Guid? SupplierId { get; set; } public Guid? SupplierId { get; set; }
public decimal? PurchasePrice { get; set; } public decimal? PurchasePrice { get; set; }
public bool IsFeatured { get; set; }
public int FeaturedDisplayOrder { get; set; }
} }
} }

View File

@@ -137,6 +137,8 @@ namespace Webshop.Application.Services.Admin
OldPrice = productDto.OldPrice, OldPrice = productDto.OldPrice,
SupplierId = productDto.SupplierId, SupplierId = productDto.SupplierId,
PurchasePrice = productDto.PurchasePrice, PurchasePrice = productDto.PurchasePrice,
IsFeatured = productDto.IsFeatured, // << NEU >>
FeaturedDisplayOrder = productDto.FeaturedDisplayOrder, // << NEU >>
Images = images, Images = images,
Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList() Productcategories = productDto.CategorieIds.Select(cId => new Productcategorie { categorieId = cId }).ToList()
}; };
@@ -196,6 +198,8 @@ namespace Webshop.Application.Services.Admin
existingProduct.SupplierId = productDto.SupplierId; existingProduct.SupplierId = productDto.SupplierId;
existingProduct.PurchasePrice = productDto.PurchasePrice; existingProduct.PurchasePrice = productDto.PurchasePrice;
existingProduct.LastModifiedDate = DateTimeOffset.UtcNow; existingProduct.LastModifiedDate = DateTimeOffset.UtcNow;
existingProduct.IsFeatured = productDto.IsFeatured; // << NEU >>
existingProduct.FeaturedDisplayOrder = productDto.FeaturedDisplayOrder; // << NEU >>
// Kategorien synchronisieren // Kategorien synchronisieren
existingProduct.Productcategories.Clear(); existingProduct.Productcategories.Clear();

View File

@@ -10,5 +10,6 @@ namespace Webshop.Application.Services.Public.Interfaces
Task<IEnumerable<ProductDto>> GetAllProductsAsync(); Task<IEnumerable<ProductDto>> GetAllProductsAsync();
Task<ProductDto?> GetProductBySlugAsync(string slug); Task<ProductDto?> GetProductBySlugAsync(string slug);
Task<IEnumerable<ProductDto>> GetFeaturedProductsAsync(); // << NEU >>
} }
} }

View File

@@ -89,5 +89,42 @@ namespace Webshop.Application.Services.Public
}).ToList() }).ToList()
}; };
} }
public async Task<IEnumerable<ProductDto>> 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();
}
} }
} }

View File

@@ -33,9 +33,6 @@ namespace Webshop.Domain.Entities
public decimal? Height { get; set; } public decimal? Height { get; set; }
public decimal? Length { get; set; } public decimal? Length { get; set; }
// << ENTFERNT: ImageUrl wird durch Images ersetzt >>
// public string? ImageUrl { get; set; }
[Required, MaxLength(255)] [Required, MaxLength(255)]
public string Slug { get; set; } = string.Empty; public string Slug { get; set; } = string.Empty;
[Required] [Required]
@@ -45,13 +42,16 @@ namespace Webshop.Domain.Entities
public Guid? SupplierId { get; set; } public Guid? SupplierId { get; set; }
public decimal? PurchasePrice { 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 Supplier? Supplier { get; set; }
public virtual ICollection<ProductVariant> Variants { get; set; } = new List<ProductVariant>(); public virtual ICollection<ProductVariant> Variants { get; set; } = new List<ProductVariant>();
public virtual ICollection<Review> Reviews { get; set; } = new List<Review>(); public virtual ICollection<Review> Reviews { get; set; } = new List<Review>();
public virtual ICollection<ProductDiscount> ProductDiscounts { get; set; } = new List<ProductDiscount>(); public virtual ICollection<ProductDiscount> ProductDiscounts { get; set; } = new List<ProductDiscount>();
public virtual ICollection<Productcategorie> Productcategories { get; set; } = new List<Productcategorie>(); public virtual ICollection<Productcategorie> Productcategories { get; set; } = new List<Productcategorie>();
// << NEU: Navigation Property zu Bildern >>
public virtual ICollection<ProductImage> Images { get; set; } = new List<ProductImage>(); public virtual ICollection<ProductImage> Images { get; set; } = new List<ProductImage>();
} }
} }

View File

@@ -12,8 +12,8 @@ using Webshop.Infrastructure.Data;
namespace Webshop.Infrastructure.Migrations namespace Webshop.Infrastructure.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
[Migration("20250812113218_checkout")] [Migration("20250812123244_featureartikel")]
partial class checkout partial class featureartikel
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -576,6 +576,9 @@ namespace Webshop.Infrastructure.Migrations
.HasMaxLength(4000) .HasMaxLength(4000)
.HasColumnType("character varying(4000)"); .HasColumnType("character varying(4000)");
b.Property<int>("FeaturedDisplayOrder")
.HasColumnType("integer");
b.Property<decimal?>("Height") b.Property<decimal?>("Height")
.HasPrecision(18, 2) .HasPrecision(18, 2)
.HasColumnType("numeric(18,2)"); .HasColumnType("numeric(18,2)");
@@ -583,6 +586,9 @@ namespace Webshop.Infrastructure.Migrations
b.Property<bool>("IsActive") b.Property<bool>("IsActive")
.HasColumnType("boolean"); .HasColumnType("boolean");
b.Property<bool>("IsFeatured")
.HasColumnType("boolean");
b.Property<bool>("IsInStock") b.Property<bool>("IsInStock")
.HasColumnType("boolean"); .HasColumnType("boolean");

View File

@@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Webshop.Infrastructure.Migrations namespace Webshop.Infrastructure.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
public partial class checkout : Migration public partial class featureartikel : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
@@ -460,7 +460,9 @@ namespace Webshop.Infrastructure.Migrations
CreatedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false), CreatedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
LastModifiedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true), LastModifiedDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
SupplierId = table.Column<Guid>(type: "uuid", nullable: true), SupplierId = table.Column<Guid>(type: "uuid", nullable: true),
PurchasePrice = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true) PurchasePrice = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
IsFeatured = table.Column<bool>(type: "boolean", nullable: false),
FeaturedDisplayOrder = table.Column<int>(type: "integer", nullable: false)
}, },
constraints: table => constraints: table =>
{ {

View File

@@ -573,6 +573,9 @@ namespace Webshop.Infrastructure.Migrations
.HasMaxLength(4000) .HasMaxLength(4000)
.HasColumnType("character varying(4000)"); .HasColumnType("character varying(4000)");
b.Property<int>("FeaturedDisplayOrder")
.HasColumnType("integer");
b.Property<decimal?>("Height") b.Property<decimal?>("Height")
.HasPrecision(18, 2) .HasPrecision(18, 2)
.HasColumnType("numeric(18,2)"); .HasColumnType("numeric(18,2)");
@@ -580,6 +583,9 @@ namespace Webshop.Infrastructure.Migrations
b.Property<bool>("IsActive") b.Property<bool>("IsActive")
.HasColumnType("boolean"); .HasColumnType("boolean");
b.Property<bool>("IsFeatured")
.HasColumnType("boolean");
b.Property<bool>("IsInStock") b.Property<bool>("IsInStock")
.HasColumnType("boolean"); .HasColumnType("boolean");