// src/Webshop.Application/Services/Customers/OrderService.cs using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Webshop.Application.DTOs.Customers; using Webshop.Application.DTOs.Orders; using Webshop.Domain.Entities; using Webshop.Domain.Enums; using Webshop.Domain.Interfaces; using Webshop.Infrastructure.Data; namespace Webshop.Application.Services.Customers { public class OrderService : IOrderService { private readonly ApplicationDbContext _context; private readonly IOrderRepository _orderRepository; private readonly IProductRepository _productRepository; private readonly IShippingMethodRepository _shippingMethodRepository; private readonly ICustomerRepository _customerRepository; public OrderService( ApplicationDbContext context, IOrderRepository orderRepository, IProductRepository productRepository, IShippingMethodRepository shippingMethodRepository, ICustomerRepository customerRepository) { _context = context; _orderRepository = orderRepository; _productRepository = productRepository; _shippingMethodRepository = shippingMethodRepository; _customerRepository = customerRepository; } public async Task<(bool Success, OrderDetailDto? CreatedOrder, string? ErrorMessage)> CreateOrderAsync(CreateOrderDto orderDto, string userId) { await using var transaction = await _context.Database.BeginTransactionAsync(); try { var customer = await _customerRepository.GetByUserIdAsync(userId); if (customer == null) { return (false, null, "Kundenprofil nicht gefunden."); } var shippingMethod = await _shippingMethodRepository.GetByIdAsync(orderDto.ShippingMethodId); if (shippingMethod == null || !shippingMethod.IsActive) return (false, null, "Ungültige Versandmethode."); var paymentMethod = await _context.PaymentMethods.FindAsync(orderDto.PaymentMethodId); if (paymentMethod == null || !paymentMethod.IsActive) return (false, null, "Ungültige Zahlungsmethode."); if (!await _context.Addresses.AnyAsync(a => a.Id == orderDto.ShippingAddressId && a.Customer.AspNetUserId == userId) || !await _context.Addresses.AnyAsync(a => a.Id == orderDto.BillingAddressId && a.Customer.AspNetUserId == userId)) { return (false, null, "Ungültige oder nicht zugehörige Liefer- oder Rechnungsadresse."); } var orderItems = new List(); decimal itemsTotal = 0; foreach (var itemDto in orderDto.Items) { var product = await _productRepository.GetProductByIdAsync(itemDto.ProductId); if (product == null || !product.IsActive) { await transaction.RollbackAsync(); return (false, null, $"Produkt mit ID {itemDto.ProductId} ist nicht verfügbar."); } if (product.StockQuantity < itemDto.Quantity) { await transaction.RollbackAsync(); return (false, null, $"Nicht genügend Lagerbestand für {product.Name}. Verfügbar: {product.StockQuantity}."); } var orderItem = new OrderItem { Id = Guid.NewGuid(), ProductId = product.Id, ProductVariantId = itemDto.ProductVariantId, ProductName = product.Name, ProductSKU = product.SKU, Quantity = itemDto.Quantity, UnitPrice = product.Price, TotalPrice = product.Price * itemDto.Quantity }; orderItems.Add(orderItem); itemsTotal += orderItem.TotalPrice; product.StockQuantity -= itemDto.Quantity; _context.Products.Update(product); } decimal shippingCost = shippingMethod.BaseCost; decimal taxAmount = (itemsTotal + shippingCost) * 0.19m; // Annahme: 19% MwSt. decimal orderTotal = itemsTotal + shippingCost + taxAmount; var newOrder = new Order { Id = Guid.NewGuid(), OrderNumber = $"WS-{DateTime.UtcNow:yyyyMMdd}-{new Random().Next(1000, 9999)}", CustomerId = customer.Id, // Verwende die interne Customer-ID OrderDate = DateTimeOffset.UtcNow, OrderStatus = OrderStatus.Pending.ToString(), PaymentStatus = PaymentStatus.Pending.ToString(), OrderTotal = orderTotal, ShippingCost = shippingCost, TaxAmount = taxAmount, DiscountAmount = 0, PaymentMethod = paymentMethod.Name, PaymentMethodId = paymentMethod.Id, ShippingMethodId = shippingMethod.Id, BillingAddressId = orderDto.BillingAddressId, ShippingAddressId = orderDto.ShippingAddressId, OrderItems = orderItems }; await _orderRepository.AddAsync(newOrder); await transaction.CommitAsync(); var createdOrderDto = new OrderDetailDto { Id = newOrder.Id, OrderNumber = newOrder.OrderNumber /* ... weitere Felder mappen ... */ }; return (true, createdOrderDto, null); } catch (Exception ex) { await transaction.RollbackAsync(); return (false, null, $"Ein unerwarteter Fehler ist aufgetreten: {ex.Message}"); } } public async Task> GetMyOrdersAsync(string userId) { var customer = await _customerRepository.GetByUserIdAsync(userId); if (customer == null) { return new List(); } var orders = await _context.Orders .Where(o => o.CustomerId == customer.Id) .OrderByDescending(o => o.OrderDate) .ToListAsync(); return orders.Select(o => new OrderSummaryDto { Id = o.Id, OrderNumber = o.OrderNumber, OrderDate = o.OrderDate, CustomerName = $"{customer.FirstName} {customer.LastName}", TotalAmount = o.OrderTotal, 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 GetMyOrderByIdAsync(Guid orderId, string userId) { var customer = await _customerRepository.GetByUserIdAsync(userId); if (customer == null) { return null; } var order = await _context.Orders .Include(o => o.BillingAddress) // Lade die Address-Entität .Include(o => o.ShippingAddress) // Lade die Address-Entität .Include(o => o.PaymentMethodInfo) .Include(o => o.OrderItems) .ThenInclude(oi => oi.Product) .FirstOrDefaultAsync(o => o.Id == orderId && o.CustomerId == customer.Id); if (order == null) { return null; } // << KORREKTUR: Manuelles Mapping von Address-Entität zu AddressDto >> return new OrderDetailDto { Id = order.Id, OrderNumber = order.OrderNumber, OrderDate = order.OrderDate, CustomerId = order.CustomerId, Status = Enum.TryParse(order.OrderStatus, true, out var orderStatus) ? orderStatus : OrderStatus.Pending, TotalAmount = order.OrderTotal, ShippingAddress = new AddressDto { Id = order.ShippingAddress.Id, Street = order.ShippingAddress.Street, HouseNumber = order.ShippingAddress.HouseNumber, City = order.ShippingAddress.City, PostalCode = order.ShippingAddress.PostalCode, Country = order.ShippingAddress.Country, Type = order.ShippingAddress.Type }, BillingAddress = new AddressDto { Id = order.BillingAddress.Id, Street = order.BillingAddress.Street, HouseNumber = order.BillingAddress.HouseNumber, City = order.BillingAddress.City, PostalCode = order.BillingAddress.PostalCode, Country = order.BillingAddress.Country, Type = order.BillingAddress.Type }, PaymentMethod = order.PaymentMethod, PaymentStatus = Enum.TryParse(order.PaymentStatus, true, out var paymentStatus) ? paymentStatus : PaymentStatus.Pending, ShippingTrackingNumber = order.ShippingTrackingNumber, ShippedDate = order.ShippedDate, DeliveredDate = order.DeliveredDate, 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() }; } } }