diff --git a/Webshop.Api/Controllers/Admin/AdminOrdersController.cs b/Webshop.Api/Controllers/Admin/AdminOrdersController.cs index 27d005a..1fb46a1 100644 --- a/Webshop.Api/Controllers/Admin/AdminOrdersController.cs +++ b/Webshop.Api/Controllers/Admin/AdminOrdersController.cs @@ -1,18 +1,57 @@ -// Auto-generiert von CreateWebshopFiles.ps1 -using Microsoft.AspNetCore.Mvc; +// src/Webshop.Api/Controllers/Admin/AdminOrdersController.cs using Microsoft.AspNetCore.Authorization; - +using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Threading.Tasks; +using Webshop.Application.DTOs.Orders; +using Webshop.Application.Services.Admin; +using Webshop.Domain.Enums; namespace Webshop.Api.Controllers.Admin { [ApiController] - [Route("api/v1/admin/[controller]")] + [Route("api/v1/admin/orders")] [Authorize(Roles = "Admin")] public class AdminOrdersController : ControllerBase { + private readonly IAdminOrderService _adminOrderService; + public AdminOrdersController(IAdminOrderService adminOrderService) + { + _adminOrderService = adminOrderService; + } + + [HttpGet] + public async Task>> GetAllOrders() + { + var orders = await _adminOrderService.GetAllOrdersAsync(); + return Ok(orders); + } + + [HttpGet("{id}")] + public async Task> GetOrderById(Guid id) + { + var order = await _adminOrderService.GetOrderByIdAsync(id); + if (order == null) return NotFound(); + return Ok(order); + } + + [HttpPut("{id}/status")] + public async Task UpdateOrderStatus(Guid id, [FromBody] UpdateOrderStatusRequest request) + { + var (success, errorMessage) = await _adminOrderService.UpdateOrderStatusAsync(id, request.NewStatus); + if (!success) + { + return BadRequest(new { Message = errorMessage }); + } + return NoContent(); + } + + // Ein kleines DTO für die Status-Update-Anfrage + public class UpdateOrderStatusRequest + { + public OrderStatus NewStatus { get; set; } + } } -} +} \ No newline at end of file diff --git a/Webshop.Api/Program.cs b/Webshop.Api/Program.cs index 4b6b8ce..aafc681 100644 --- a/Webshop.Api/Program.cs +++ b/Webshop.Api/Program.cs @@ -82,6 +82,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // AUTH Services builder.Services.AddScoped(); @@ -98,6 +99,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // Hinzugefügt für Konsistenz +builder.Services.AddScoped(); //builder.Services.AddScoped(); // Hinzugefügt für Konsistenz //builder.Services.AddScoped(); // Hinzugefügt für Konsistenz //builder.Services.AddScoped(); // Hinzugefügt für Konsistenz diff --git a/Webshop.Application/DTOs/Orders/OrderDetailDto.cs b/Webshop.Application/DTOs/Orders/OrderDetailDto.cs index bf7f15e..4e3bb9d 100644 --- a/Webshop.Application/DTOs/Orders/OrderDetailDto.cs +++ b/Webshop.Application/DTOs/Orders/OrderDetailDto.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Webshop.Domain.Entities; @@ -11,7 +12,7 @@ namespace Webshop.Application.DTOs.Orders { public Guid Id { get; set; } public string OrderNumber { get; set; } = string.Empty; - public Guid CustomerId { get; set; } + public Guid? CustomerId { get; set; } public DateTimeOffset OrderDate { get; set; } public OrderStatus Status { get; set; } public decimal TotalAmount { get; set; } @@ -23,5 +24,8 @@ namespace Webshop.Application.DTOs.Orders public DateTimeOffset? DeliveredDate { get; set; } public PaymentStatus PaymentStatus { get; set; } public List OrderItems { get; set; } = new List(); + public Address ShippingAddress { get; set; } = default!; // << HINZUFÜGEN >> + public Address BillingAddress { get; set; } = default!; // << HINZUFÜGEN >> + public string PaymentMethod { get; set; } = string.Empty; // << HINZUFÜGEN >> } } diff --git a/Webshop.Application/DTOs/Orders/OrderItemDto.cs b/Webshop.Application/DTOs/Orders/OrderItemDto.cs index a83bf57..284bcab 100644 --- a/Webshop.Application/DTOs/Orders/OrderItemDto.cs +++ b/Webshop.Application/DTOs/Orders/OrderItemDto.cs @@ -10,7 +10,7 @@ namespace Webshop.Application.DTOs.Orders { public Guid Id { get; set; } public Guid OrderId { get; set; } // Foreign Key zu Order - public Guid ProductId { get; set; } + public Guid? ProductId { get; set; } public Guid? ProductVariantId { get; set; } public string ProductName { get; set; } = string.Empty; public string ProductSKU { get; set; } = string.Empty; diff --git a/Webshop.Application/DTOs/Orders/OrderSummaryDto.cs b/Webshop.Application/DTOs/Orders/OrderSummaryDto.cs index 7985caa..8816894 100644 --- a/Webshop.Application/DTOs/Orders/OrderSummaryDto.cs +++ b/Webshop.Application/DTOs/Orders/OrderSummaryDto.cs @@ -17,5 +17,6 @@ namespace Webshop.Application.DTOs.Orders public PaymentStatus PaymentStatus { get; set; } public string PaymentMethodName { get; set; } = string.Empty; public string ShippingMethodName { get; set; } = string.Empty; + public string CustomerName { get; set; } = string.Empty; } } diff --git a/Webshop.Application/Services/Admin/AdminOrderService.cs b/Webshop.Application/Services/Admin/AdminOrderService.cs index b047775..7aa6a17 100644 --- a/Webshop.Application/Services/Admin/AdminOrderService.cs +++ b/Webshop.Application/Services/Admin/AdminOrderService.cs @@ -1,18 +1,90 @@ -// Auto-generiert von CreateWebshopFiles.ps1 +// src/Webshop.Application/Services/Admin/AdminOrderService.cs using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; -using Webshop.Application.Services.Admin.Interfaces; - +using Webshop.Application.DTOs.Orders; +using Webshop.Domain.Enums; // Wichtig für Enum-Konvertierung +using Webshop.Domain.Interfaces; namespace Webshop.Application.Services.Admin { public class AdminOrderService : IAdminOrderService { - // Fügen Sie hier Abhängigkeiten per Dependency Injection hinzu (z.B. Repositories) + private readonly IOrderRepository _orderRepository; - // public AdminOrderService(IYourRepository repository) { } + public AdminOrderService(IOrderRepository orderRepository) + { + _orderRepository = orderRepository; + } - // Fügen Sie hier Service-Methoden hinzu + public async Task> GetAllOrdersAsync() + { + var orders = await _orderRepository.GetAllAsync(); + return orders.Select(o => new OrderSummaryDto + { + Id = o.Id, + OrderNumber = o.OrderNumber, + OrderDate = o.OrderDate, + CustomerName = o.Customer != null ? $"{o.Customer.FirstName} {o.Customer.LastName}" : o.GuestEmail ?? "Gastbestellung", + TotalAmount = o.OrderTotal, + // << KORREKTUR: String zu Enum parsen >> + Status = Enum.TryParse(o.OrderStatus, true, out var orderStatus) ? orderStatus : OrderStatus.Pending, + PaymentStatus = Enum.TryParse(o.PaymentStatus, true, out var paymentStatus) ? paymentStatus : PaymentStatus.Pending + }).ToList(); + } + + public async Task GetOrderByIdAsync(Guid id) + { + var order = await _orderRepository.GetByIdAsync(id); + if (order == null) return null; + + return new OrderDetailDto + { + Id = order.Id, + OrderNumber = order.OrderNumber, + OrderDate = order.OrderDate, + CustomerId = order.CustomerId, + // << KORREKTUR: String zu Enum parsen >> + Status = Enum.TryParse(order.OrderStatus, true, out var orderStatus) ? orderStatus : OrderStatus.Pending, + TotalAmount = order.OrderTotal, + ShippingAddress = order.ShippingAddress, // Annahme: Address DTO Mapping + BillingAddress = order.BillingAddress, // Annahme: Address DTO Mapping + PaymentMethod = order.PaymentMethod, + // << KORREKTUR: String zu Enum parsen >> + PaymentStatus = Enum.TryParse(order.PaymentStatus, true, out var paymentStatus) ? paymentStatus : PaymentStatus.Pending, + OrderItems = order.OrderItems.Select(oi => new OrderItemDto + { + Id = oi.Id, + ProductId = oi.ProductId, + ProductVariantId = oi.ProductVariantId, + ProductName = oi.ProductName, + ProductSKU = oi.ProductSKU, + Quantity = oi.Quantity, + UnitPrice = oi.UnitPrice, + TotalPrice = oi.TotalPrice + }).ToList() + }; + } + + public async Task<(bool Success, string? ErrorMessage)> UpdateOrderStatusAsync(Guid id, OrderStatus newStatus) + { + var order = await _orderRepository.GetByIdAsync(id); + if (order == null) + { + return (false, "Bestellung nicht gefunden."); + } + + // << KORREKTUR: Enum zu String konvertieren >> + order.OrderStatus = newStatus.ToString(); + + if (newStatus == OrderStatus.Shipped && string.IsNullOrEmpty(order.ShippingTrackingNumber)) + { + // Hier könnten Sie Logik hinzufügen, um eine Tracking-Nummer zu verlangen + } + + await _orderRepository.UpdateAsync(order); + return (true, null); + } } -} +} \ No newline at end of file diff --git a/Webshop.Application/Services/Admin/Interfaces/IAdminOrderService.cs b/Webshop.Application/Services/Admin/Interfaces/IAdminOrderService.cs index 03f3a4c..9e8cbc7 100644 --- a/Webshop.Application/Services/Admin/Interfaces/IAdminOrderService.cs +++ b/Webshop.Application/Services/Admin/Interfaces/IAdminOrderService.cs @@ -1,16 +1,16 @@ -// Auto-generiert von CreateWebshopFiles.ps1 +// src/Webshop.Application/Services/Admin/IAdminOrderService.cs using System; using System.Collections.Generic; using System.Threading.Tasks; -using Webshop.Application.DTOs; -using Webshop.Application.DTOs.Auth; -using Webshop.Application.DTOs.Users; +using Webshop.Application.DTOs.Orders; +using Webshop.Domain.Enums; - -namespace Webshop.Application.Services.Admin.Interfaces +namespace Webshop.Application.Services.Admin { public interface IAdminOrderService { - // Fügen Sie hier Methodensignaturen hinzu + Task> GetAllOrdersAsync(); + Task GetOrderByIdAsync(Guid id); + Task<(bool Success, string? ErrorMessage)> UpdateOrderStatusAsync(Guid id, OrderStatus newStatus); } -} +} \ No newline at end of file diff --git a/Webshop.Domain/Entities/Order.cs b/Webshop.Domain/Entities/Order.cs index b811b7e..a824a88 100644 --- a/Webshop.Domain/Entities/Order.cs +++ b/Webshop.Domain/Entities/Order.cs @@ -1,90 +1,91 @@ -using System; +// src/Webshop.Domain/Entities/Order.cs +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Webshop.Domain.Entities; - -/// -/// Die Details jeder Kundenbestellung, inklusive Gastbestellungen. -/// -public class Order +namespace Webshop.Domain.Entities { - [Key] - public Guid Id { get; set; } + public class Order + { + [Key] + public Guid Id { get; set; } = Guid.NewGuid(); - // Unique-Constraint wird via Fluent API konfiguriert - [Required] - [MaxLength(50)] - public string OrderNumber { get; set; } + [Required] + [MaxLength(50)] + public string OrderNumber { get; set; } = string.Empty; - [ForeignKey(nameof(Customer))] - public Guid? CustomerId { get; set; } + [ForeignKey(nameof(Customer))] + public Guid? CustomerId { get; set; } - [MaxLength(256)] - [EmailAddress] - public string? GuestEmail { get; set; } + [MaxLength(256)] + [EmailAddress] + public string? GuestEmail { get; set; } - [MaxLength(20)] - [Phone] - public string? GuestPhoneNumber { get; set; } + [MaxLength(20)] + [Phone] + public string? GuestPhoneNumber { get; set; } - [Required] - public DateTimeOffset OrderDate { get; set; } + [Required] + public DateTimeOffset OrderDate { get; set; } = DateTimeOffset.UtcNow; - [Required] - [MaxLength(50)] - public string OrderStatus { get; set; } + [Required] + [MaxLength(50)] + public string OrderStatus { get; set; } = string.Empty; - // Precision wird via Fluent API konfiguriert - [Required] - public decimal OrderTotal { get; set; } + [Required] + public decimal OrderTotal { get; set; } + [Required] + public decimal ShippingCost { get; set; } + [Required] + public decimal TaxAmount { get; set; } + [Required] + public decimal DiscountAmount { get; set; } - [Required] - public decimal ShippingCost { get; set; } + [Required] + [MaxLength(50)] + public string PaymentStatus { get; set; } = string.Empty; - [Required] - public decimal TaxAmount { get; set; } + [Required] + [MaxLength(100)] + public string PaymentMethod { get; set; } = string.Empty; - [Required] - public decimal DiscountAmount { get; set; } + [ForeignKey(nameof(PaymentMethodInfo))] + public Guid? PaymentMethodId { get; set; } - [Required] - [MaxLength(50)] - public string PaymentStatus { get; set; } + [ForeignKey(nameof(ShippingMethodInfo))] + public Guid? ShippingMethodId { get; set; } - [Required] - [MaxLength(100)] - public string PaymentMethod { get; set; } + [MaxLength(255)] + public string? TransactionId { get; set; } - [ForeignKey(nameof(PaymentMethodInfo))] - public Guid? PaymentMethodId { get; set; } + // << NEUE FELDER HINZUFÜGEN >> + [MaxLength(255)] + public string? ShippingTrackingNumber { get; set; } + public DateTimeOffset? ShippedDate { get; set; } + public DateTimeOffset? DeliveredDate { get; set; } + // << ENDE NEUE FELDER >> - [ForeignKey(nameof(ShippingMethodInfo))] - public Guid? ShippingMethodId { get; set; } + [Required] + [ForeignKey(nameof(BillingAddress))] + public Guid BillingAddressId { get; set; } - [MaxLength(255)] - public string? TransactionId { get; set; } + [Required] + [ForeignKey(nameof(ShippingAddress))] + public Guid ShippingAddressId { get; set; } - [Required] - [ForeignKey(nameof(BillingAddress))] - public Guid BillingAddressId { get; set; } + [MaxLength(1000)] + public string? CustomerNotes { get; set; } - [Required] - [ForeignKey(nameof(ShippingAddress))] - public Guid ShippingAddressId { get; set; } + [MaxLength(1000)] + public string? AdminNotes { get; set; } - [MaxLength(1000)] - public string? CustomerNotes { get; set; } - - [MaxLength(1000)] - public string? AdminNotes { get; set; } - - // Navigation Properties - public virtual Customer? Customer { get; set; } - public virtual Address BillingAddress { get; set; } - public virtual Address ShippingAddress { get; set; } - public virtual PaymentMethod? PaymentMethodInfo { get; set; } - public virtual ShippingMethod? ShippingMethodInfo { get; set; } - public virtual ICollection OrderItems { get; set; } = new List(); + // Navigation Properties + public virtual Customer? Customer { get; set; } + public virtual Address BillingAddress { get; set; } = default!; + public virtual Address ShippingAddress { get; set; } = default!; + public virtual PaymentMethod? PaymentMethodInfo { get; set; } + public virtual ShippingMethod? ShippingMethodInfo { get; set; } + public virtual ICollection OrderItems { get; set; } = new List(); + } } \ No newline at end of file diff --git a/Webshop.Domain/Interfaces/IOrderRepository.cs b/Webshop.Domain/Interfaces/IOrderRepository.cs index 64e1101..600c80d 100644 --- a/Webshop.Domain/Interfaces/IOrderRepository.cs +++ b/Webshop.Domain/Interfaces/IOrderRepository.cs @@ -1,14 +1,17 @@ -// Auto-generiert von CreateWebshopFiles.ps1 +// src/Webshop.Domain/Interfaces/IOrderRepository.cs using System; using System.Collections.Generic; using System.Threading.Tasks; using Webshop.Domain.Entities; - namespace Webshop.Domain.Interfaces { public interface IOrderRepository { -// Fügen Sie hier Methodensignaturen hinzu + Task> GetAllAsync(); + Task GetByIdAsync(Guid id); + Task AddAsync(Order order); + Task UpdateAsync(Order order); + Task DeleteAsync(Guid id); } -} +} \ No newline at end of file diff --git a/Webshop.Infrastructure/Repositories/OrderRepository.cs b/Webshop.Infrastructure/Repositories/OrderRepository.cs index 6985d7b..5c9ade6 100644 --- a/Webshop.Infrastructure/Repositories/OrderRepository.cs +++ b/Webshop.Infrastructure/Repositories/OrderRepository.cs @@ -1,11 +1,12 @@ -// Auto-generiert von CreateWebshopFiles.ps1 +// src/Webshop.Infrastructure/Repositories/OrderRepository.cs using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Webshop.Domain.Entities; using Webshop.Domain.Interfaces; using Webshop.Infrastructure.Data; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Webshop.Infrastructure.Repositories { @@ -18,12 +19,49 @@ namespace Webshop.Infrastructure.Repositories _context = context; } - // Fügen Sie hier Repository-Methoden hinzu - // Beispiel: - // public async Task> GetAllAsync() { return await _context.Set().ToListAsync(); } - // public async Task GetByIdAsync(Guid id) { return await _context.Set().FindAsync(id); } - // public async Task AddAsync(T entity) { _context.Set().Add(entity); await _context.SaveChangesAsync(); } - // public async Task UpdateAsync(T entity) { _context.Set().Update(entity); await _context.SaveChangesAsync(); } - // public async Task DeleteAsync(Guid id) { var entity = await _context.Set().FindAsync(id); if (entity != null) { _context.Set().Remove(entity); await _context.SaveChangesAsync(); } } + public async Task> GetAllAsync() + { + // Lade Bestellungen und zugehörige Kunden, um den Kundennamen anzeigen zu können + return await _context.Orders + .Include(o => o.Customer) + .OrderByDescending(o => o.OrderDate) + .ToListAsync(); + } + + public async Task GetByIdAsync(Guid id) + { + // Lade eine einzelne Bestellung mit allen relevanten Details + return await _context.Orders + .Include(o => o.Customer) + .Include(o => o.BillingAddress) + .Include(o => o.ShippingAddress) + .Include(o => o.PaymentMethodInfo) // << KORREKTUR: Name der Navigation Property >> + .Include(o => o.ShippingMethodInfo) // << KORREKTUR: Name der Navigation Property >> + .Include(o => o.OrderItems) + .ThenInclude(oi => oi.Product) + .FirstOrDefaultAsync(o => o.Id == id); + } + + public async Task AddAsync(Order order) + { + _context.Orders.Add(order); + await _context.SaveChangesAsync(); + } + + public async Task UpdateAsync(Order order) + { + _context.Orders.Update(order); + await _context.SaveChangesAsync(); + } + + public async Task DeleteAsync(Guid id) + { + var order = await _context.Orders.FindAsync(id); + if (order != null) + { + _context.Orders.Remove(order); + await _context.SaveChangesAsync(); + } + } } -} +} \ No newline at end of file