This commit is contained in:
Tizian.Breuch
2025-11-26 17:02:18 +01:00
parent 2e30755901
commit ee5abfd829
18 changed files with 1968 additions and 208 deletions

View File

@@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
using Webshop.Application.Services.Customers.Interfaces;
using Webshop.Infrastructure.Data;
namespace Webshop.Application.Services.Customers
{
public class CartService : ICartService
{
private readonly ApplicationDbContext _context;
public CartService(ApplicationDbContext context)
{
_context = context;
}
public async Task ClearCartAsync(string userId)
{
// Wir suchen den Warenkorb des Users
// Annahme: Es gibt eine 'Carts' Tabelle und 'CartItems'
var cart = await _context.Carts
.Include(c => c.Items)
.FirstOrDefaultAsync(c => c.UserId == userId);
if (cart != null && cart.Items.Any())
{
// Option A: Nur Items löschen, Warenkorb behalten
_context.CartItems.RemoveRange(cart.Items);
// Option B: Ganzen Warenkorb löschen (dann wird er beim nächsten AddToCart neu erstellt)
// _context.Carts.Remove(cart);
await _context.SaveChangesAsync();
}
}
}
}

View File

@@ -1,15 +1,19 @@
// /src/Webshop.Application/Services/Customers/CheckoutService.cs
// src/Webshop.Application/Services/Customers/CheckoutService.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Webshop.Application;
using Webshop.Application.DTOs.Customers;
using Webshop.Application.DTOs.Orders;
using Webshop.Application.DTOs.Shipping;
using Webshop.Application.Services.Customers.Interfaces;
using Webshop.Application.Services.Public.Interfaces;
using Webshop.Application.Services.Public; // Für DiscountCalculationResult
using Webshop.Domain.Entities;
using Webshop.Domain.Enums;
using Webshop.Domain.Interfaces;
@@ -25,10 +29,20 @@ namespace Webshop.Application.Services.Customers
private readonly IShippingMethodRepository _shippingMethodRepository;
private readonly ISettingService _settingService;
private readonly IDiscountService _discountService;
private readonly ICartService _cartService;
private readonly IEmailService _emailService;
private readonly ILogger<CheckoutService> _logger;
public CheckoutService(
ApplicationDbContext context, IOrderRepository orderRepository, ICustomerRepository customerRepository,
IShippingMethodRepository shippingMethodRepository, ISettingService settingService, IDiscountService discountService)
ApplicationDbContext context,
IOrderRepository orderRepository,
ICustomerRepository customerRepository,
IShippingMethodRepository shippingMethodRepository,
ISettingService settingService,
IDiscountService discountService,
ICartService cartService,
IEmailService emailService,
ILogger<CheckoutService> logger)
{
_context = context;
_orderRepository = orderRepository;
@@ -36,6 +50,63 @@ namespace Webshop.Application.Services.Customers
_shippingMethodRepository = shippingMethodRepository;
_settingService = settingService;
_discountService = discountService;
_cartService = cartService;
_emailService = emailService;
_logger = logger;
}
// =================================================================================
// FIX: Überladung für CartItemDto (Löst den Konvertierungsfehler im Controller)
// =================================================================================
public async Task<ServiceResult<IEnumerable<ShippingMethodDto>>> GetCompatibleShippingMethodsAsync(List<CartItemDto> items)
{
// Mappe CartItemDto zu CreateOrderItemDto, damit wir die Logik wiederverwenden können
var mappedItems = items.Select(x => new CreateOrderItemDto
{
ProductId = x.ProductId,
Quantity = x.Quantity,
ProductVariantId = x.ProductVariantId
});
return await GetCompatibleShippingMethodsAsync(mappedItems);
}
// Die Hauptlogik basierend auf CreateOrderItemDto (für den Checkout)
public async Task<ServiceResult<IEnumerable<ShippingMethodDto>>> GetCompatibleShippingMethodsAsync(IEnumerable<CreateOrderItemDto> items)
{
if (items == null || !items.Any())
return ServiceResult.Ok<IEnumerable<ShippingMethodDto>>(new List<ShippingMethodDto>());
var productIds = items.Select(i => i.ProductId).ToList();
var products = await _context.Products.Where(p => productIds.Contains(p.Id)).ToListAsync();
decimal totalWeight = 0;
foreach (var item in items)
{
var product = products.FirstOrDefault(p => p.Id == item.ProductId);
if (product != null)
{
totalWeight += (decimal)(product.Weight ?? 0) * item.Quantity;
}
}
var allMethods = await _shippingMethodRepository.GetAllAsync();
var compatibleMethods = allMethods
.Where(sm => sm.IsActive && totalWeight >= sm.MinWeight && totalWeight <= sm.MaxWeight)
.Select(sm => new ShippingMethodDto
{
Id = sm.Id,
Name = sm.Name,
Description = sm.Description,
Cost = sm.BaseCost,
IsActive = sm.IsActive,
MinDeliveryDays = sm.MinDeliveryDays,
MaxDeliveryDays = sm.MaxDeliveryDays,
MinWeight = sm.MinWeight,
MaxWeight = sm.MaxWeight
})
.ToList();
return ServiceResult.Ok<IEnumerable<ShippingMethodDto>>(compatibleMethods);
}
public async Task<ServiceResult<OrderDetailDto>> CreateOrderAsync(CreateOrderDto orderDto, string? userId)
@@ -43,102 +114,135 @@ namespace Webshop.Application.Services.Customers
await using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// --- 1. Validierung von Kunde, Adressen und Methoden ---
// 1.1 Kunde Validieren
var customer = await _customerRepository.GetByUserIdAsync(userId!);
if (customer == null)
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Unauthorized, "Kundenprofil nicht gefunden.");
if (!await _context.Addresses.AnyAsync(a => a.Id == orderDto.ShippingAddressId && a.CustomerId == customer.Id) ||
!await _context.Addresses.AnyAsync(a => a.Id == orderDto.BillingAddressId && a.CustomerId == customer.Id))
// =================================================================================
// FIX: E-Mail holen (Passend zu deiner Entity Customer.cs)
// Customer hat keine Email, aber eine AspNetUserId. Wir holen die Mail aus der Users Tabelle.
// =================================================================================
var userEmail = await _context.Users
.Where(u => u.Id == userId) // userId entspricht customer.AspNetUserId
.Select(u => u.Email)
.FirstOrDefaultAsync();
if (string.IsNullOrEmpty(userEmail))
{
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Forbidden, "Ungültige oder nicht zugehörige Liefer- oder Rechnungsadresse.");
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Failure, "Keine E-Mail-Adresse für den Benutzer gefunden.");
}
// 1.2 Adressen Validieren
var addresses = await _context.Addresses
.Where(a => (a.Id == orderDto.ShippingAddressId || a.Id == orderDto.BillingAddressId) && a.CustomerId == customer.Id)
.ToListAsync();
if (!addresses.Any(a => a.Id == orderDto.ShippingAddressId) || !addresses.Any(a => a.Id == orderDto.BillingAddressId))
{
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Forbidden, "Die angegebenen Adressen sind ungültig oder gehören nicht zu diesem Konto.");
}
// 1.3 Methoden Validieren
var shippingMethod = await _shippingMethodRepository.GetByIdAsync(orderDto.ShippingMethodId);
if (shippingMethod == null || !shippingMethod.IsActive)
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, "Ungültige oder inaktive Versandmethode.");
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, "Versandmethode ungültig.");
var paymentMethod = await _context.PaymentMethods.FindAsync(orderDto.PaymentMethodId);
if (paymentMethod == null || !paymentMethod.IsActive)
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, "Ungültige oder inaktive Zahlungsmethode.");
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, "Zahlungsmethode ungültig.");
// --- 2. Artikel verarbeiten und Lagerbestand prüfen ---
var orderItems = new List<OrderItem>();
var productIds = orderDto.Items.Select(i => i.ProductId).ToList();
// 1.4 Produkte Validieren
if (orderDto.Items == null || !orderDto.Items.Any())
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, "Warenkorb ist leer.");
var productIds = orderDto.Items.Select(i => i.ProductId).Distinct().ToList();
var products = await _context.Products.Where(p => productIds.Contains(p.Id)).ToListAsync();
var validationErrors = new StringBuilder();
decimal totalOrderWeight = 0;
decimal itemsTotal = 0;
var preparedOrderItems = new List<OrderItem>();
foreach (var itemDto in orderDto.Items)
{
var product = products.FirstOrDefault(p => p.Id == itemDto.ProductId);
if (product == null || !product.IsActive)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Conflict, "Ein Produkt im Warenkorb ist nicht mehr verfügbar.");
}
if (product.StockQuantity < itemDto.Quantity)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Conflict, $"Nicht genügend Lagerbestand für '{product.Name}'. Verfügbar: {product.StockQuantity}, benötigt: {itemDto.Quantity}.");
}
product.StockQuantity -= itemDto.Quantity;
if (product == null) { validationErrors.AppendLine($"- Produkt {itemDto.ProductId} fehlt."); continue; }
if (!product.IsActive) validationErrors.AppendLine($"- '{product.Name}' ist nicht verfügbar.");
if (product.StockQuantity < itemDto.Quantity) validationErrors.AppendLine($"- '{product.Name}': Zu wenig Bestand.");
var orderItem = new OrderItem
if (validationErrors.Length == 0)
{
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;
totalOrderWeight += (decimal)(product.Weight ?? 0) * itemDto.Quantity;
var orderItem = new OrderItem
{
ProductId = product.Id,
ProductVariantId = itemDto.ProductVariantId,
ProductName = product.Name,
ProductSKU = product.SKU,
Quantity = itemDto.Quantity,
UnitPrice = product.Price,
TotalPrice = product.Price * itemDto.Quantity
};
preparedOrderItems.Add(orderItem);
itemsTotal += orderItem.TotalPrice;
}
}
// --- 3. Preise, Rabatte, Steuern und Gesamtbetrag berechnen ---
if (validationErrors.Length > 0)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Conflict, $"Fehler:\n{validationErrors}");
}
if (totalOrderWeight < shippingMethod.MinWeight || totalOrderWeight > shippingMethod.MaxWeight)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, $"Gesamtgewicht ({totalOrderWeight} kg) passt nicht zur Versandart.");
}
// 2. Preise & Rabatte
decimal discountAmount = 0;
var discountResult = new DiscountCalculationResult();
if (!string.IsNullOrWhiteSpace(orderDto.CouponCode))
{
discountResult = await _discountService.CalculateDiscountAsync(orderItems, orderDto.CouponCode);
discountResult = await _discountService.CalculateDiscountAsync(preparedOrderItems, orderDto.CouponCode);
if (!discountResult.IsValid)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, $"Gutscheinfehler: {discountResult.ErrorMessage ?? "Code ungültig."}");
}
discountAmount = discountResult.TotalDiscountAmount;
if (discountAmount <= 0)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.InvalidInput, "Der angegebene Gutscheincode ist ungültig, abgelaufen oder nicht auf die Produkte im Warenkorb anwendbar.");
}
if (discountResult.AppliedDiscountIds.Count > 1)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Failure, "Es können nicht mehrere Rabatte gleichzeitig angewendet werden.");
}
}
decimal shippingCost = shippingMethod.BaseCost;
decimal subTotalBeforeDiscount = itemsTotal + shippingCost;
if (discountAmount > subTotalBeforeDiscount)
{
discountAmount = subTotalBeforeDiscount;
}
if (discountAmount > subTotalBeforeDiscount) discountAmount = subTotalBeforeDiscount;
decimal subTotalAfterDiscount = subTotalBeforeDiscount - discountAmount;
decimal taxRate = await _settingService.GetSettingValueAsync<decimal>("GlobalTaxRate", 0.19m);
decimal taxAmount = subTotalAfterDiscount * taxRate;
decimal taxAmount = subTotalAfterDiscount * taxRate; // Steuer auf reduzierten Betrag
decimal orderTotal = subTotalAfterDiscount + taxAmount;
// --- 4. Bestellung erstellen ---
// 3. Ausführung (Execution)
foreach (var itemDto in orderDto.Items)
{
var product = products.First(p => p.Id == itemDto.ProductId);
if (product.StockQuantity < itemDto.Quantity)
{
await transaction.RollbackAsync();
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Conflict, "Lagerbestand hat sich während des Vorgangs geändert.");
}
product.StockQuantity -= itemDto.Quantity;
}
var newOrder = new Order
{
CustomerId = customer.Id,
OrderNumber = $"WS-{DateTime.UtcNow:yyyyMMdd}-{new Random().Next(1000, 9999)}",
OrderNumber = GenerateOrderNumber(),
OrderDate = DateTimeOffset.UtcNow,
OrderStatus = OrderStatus.Pending.ToString(),
PaymentStatus = PaymentStatus.Pending.ToString(),
@@ -151,33 +255,58 @@ namespace Webshop.Application.Services.Customers
ShippingMethodId = shippingMethod.Id,
BillingAddressId = orderDto.BillingAddressId,
ShippingAddressId = orderDto.ShippingAddressId,
OrderItems = orderItems
OrderItems = preparedOrderItems
};
await _orderRepository.AddAsync(newOrder);
// --- 5. Rabattnutzung erhöhen ---
if (discountResult.AppliedDiscountIds.Any())
{
var appliedDiscounts = await _context.Discounts.Where(d => discountResult.AppliedDiscountIds.Contains(d.Id)).ToListAsync();
foreach (var discount in appliedDiscounts) { discount.CurrentUsageCount++; }
var appliedDiscounts = await _context.Discounts
.Where(d => discountResult.AppliedDiscountIds.Contains(d.Id))
.ToListAsync();
foreach (var d in appliedDiscounts) d.CurrentUsageCount++;
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
try
{
await _context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch (DbUpdateConcurrencyException ex)
{
await transaction.RollbackAsync();
_logger.LogWarning(ex, "Concurrency Konflikt bei Bestellung.");
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Conflict, "Artikel wurden soeben von einem anderen Kunden gekauft.");
}
// --- 6. Erfolgreiche Antwort erstellen ---
var createdOrder = await _orderRepository.GetByIdAsync(newOrder.Id);
return ServiceResult.Ok(MapToOrderDetailDto(createdOrder!));
// 4. Post-Processing (Warenkorb & Email)
try { await _cartService.ClearCartAsync(userId!); }
catch (Exception ex) { _logger.LogError(ex, "Warenkorb konnte nicht geleert werden."); }
try { await _emailService.SendOrderConfirmationAsync(newOrder.Id, userEmail); }
catch (Exception ex) { _logger.LogError(ex, "Bestätigungs-Email fehlgeschlagen."); }
// Manuelles Setzen der Navigation Properties für das Mapping
newOrder.PaymentMethodInfo = paymentMethod;
newOrder.ShippingAddress = addresses.First(a => a.Id == orderDto.ShippingAddressId);
newOrder.BillingAddress = addresses.First(a => a.Id == orderDto.BillingAddressId);
return ServiceResult.Ok(MapToOrderDetailDto(newOrder));
}
catch (Exception ex)
{
await transaction.RollbackAsync();
// Log the full exception for debugging purposes
// _logger.LogError(ex, "An unexpected error occurred in CreateOrderAsync.");
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Failure, "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.");
_logger.LogError(ex, "Checkout Exception");
return ServiceResult.Fail<OrderDetailDto>(ServiceResultType.Failure, "Ein unerwarteter Fehler ist aufgetreten.");
}
}
private string GenerateOrderNumber()
{
return $"WS-{DateTime.UtcNow:yyyyMMdd}-{Guid.NewGuid().ToString().Substring(0, 6).ToUpper()}";
}
private OrderDetailDto MapToOrderDetailDto(Order order)
{
return new OrderDetailDto
@@ -212,11 +341,8 @@ namespace Webshop.Application.Services.Customers
Country = order.BillingAddress.Country,
Type = order.BillingAddress.Type
},
PaymentMethod = order.PaymentMethodInfo?.Name,
PaymentMethod = order.PaymentMethodInfo?.Name ?? order.PaymentMethod,
PaymentStatus = Enum.TryParse<PaymentStatus>(order.PaymentStatus, true, out var ps) ? ps : PaymentStatus.Pending,
ShippingTrackingNumber = order.ShippingTrackingNumber,
ShippedDate = order.ShippedDate,
DeliveredDate = order.DeliveredDate,
OrderItems = order.OrderItems.Select(oi => new OrderItemDto
{
Id = oi.Id,

View File

@@ -1,13 +1,14 @@
// src/Webshop.Application/Services/Customers/CustomerService.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Resend;
using System; // Für Uri
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Webshop.Application.DTOs;
using Webshop.Application; // Für ServiceResult
using Webshop.Application.DTOs.Auth;
using Webshop.Application.DTOs.Customers;
using Webshop.Application.Services.Customers.Interfaces; // Namespace korrigiert
using Webshop.Application.Services.Public.Interfaces; // WICHTIG: Für IEmailService
using Webshop.Domain.Entities;
using Webshop.Domain.Identity;
using Webshop.Domain.Interfaces;
@@ -18,14 +19,18 @@ namespace Webshop.Application.Services.Customers
private readonly ICustomerRepository _customerRepository;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IConfiguration _configuration;
private readonly IResend _resend;
private readonly IEmailService _emailService; // NEU: Statt IResend
public CustomerService(ICustomerRepository customerRepository, UserManager<ApplicationUser> userManager, IConfiguration configuration, IResend resend)
public CustomerService(
ICustomerRepository customerRepository,
UserManager<ApplicationUser> userManager,
IConfiguration configuration,
IEmailService emailService) // NEU: Injected
{
_customerRepository = customerRepository;
_userManager = userManager;
_configuration = configuration;
_resend = resend;
_emailService = emailService;
}
public async Task<ServiceResult<CustomerDto>> GetMyProfileAsync(string userId)
@@ -60,17 +65,20 @@ namespace Webshop.Application.Services.Customers
var identityUser = await _userManager.FindByIdAsync(userId);
if (identityUser == null) return ServiceResult.Fail(ServiceResultType.NotFound, "Benutzerkonto nicht gefunden.");
// Sicherheitscheck: Passwort prüfen
if (!await _userManager.CheckPasswordAsync(identityUser, profileDto.CurrentPassword))
{
return ServiceResult.Fail(ServiceResultType.InvalidInput, "Das zur Bestätigung eingegebene Passwort ist falsch.");
}
// Customer Daten aktualisieren
customer.FirstName = profileDto.FirstName;
customer.LastName = profileDto.LastName;
customer.DefaultShippingAddressId = profileDto.DefaultShippingAddressId;
customer.DefaultBillingAddressId = profileDto.DefaultBillingAddressId;
await _customerRepository.UpdateAsync(customer);
// User Daten (Telefon) aktualisieren
if (!string.IsNullOrEmpty(profileDto.PhoneNumber) && identityUser.PhoneNumber != profileDto.PhoneNumber)
{
identityUser.PhoneNumber = profileDto.PhoneNumber;
@@ -103,28 +111,29 @@ namespace Webshop.Application.Services.Customers
var user = await _userManager.FindByIdAsync(userId);
if (user == null) return ServiceResult.Fail(ServiceResultType.NotFound, "Benutzer nicht gefunden.");
// Passwort prüfen
if (!await _userManager.CheckPasswordAsync(user, currentPassword))
{
return ServiceResult.Fail(ServiceResultType.InvalidInput, "Das zur Bestätigung eingegebene Passwort ist falsch.");
}
// Prüfen, ob neue Email schon existiert (außer es ist die eigene)
if (user.Email != newEmail && await _userManager.FindByEmailAsync(newEmail) != null)
{
return ServiceResult.Fail(ServiceResultType.Conflict, "Die neue E-Mail-Adresse ist bereits registriert.");
}
// Token generieren
var token = await _userManager.GenerateChangeEmailTokenAsync(user, newEmail);
var clientUrl = _configuration["App:ClientUrl"]!;
var confirmationLink = $"{clientUrl}/confirm-email-change?userId={user.Id}&newEmail={HttpUtility.UrlEncode(newEmail)}&token={HttpUtility.UrlEncode(token)}";
var clientUrl = _configuration["App:ClientUrl"]; // Hier stand vorher ! , besser prüfen oder Null-Check
var message = new EmailMessage();
message.From = _configuration["Resend:FromEmail"]!;
message.To.Add(newEmail);
message.Subject = "Bestätigen Sie Ihre neue E-Mail-Adresse";
message.HtmlBody = $"<h1>E-Mail-Änderung Bestätigung</h1><p>Bitte klicken Sie auf den folgenden Link, um Ihre neue E-Mail-Adresse zu bestätigen:</p><p><a href='{confirmationLink}'>Neue E-Mail-Adresse bestätigen</a></p>";
await _resend.EmailSendAsync(message);
// Link bauen (Uri.EscapeDataString ist sicherer in URLs als HttpUtility)
var confirmationLink = $"{clientUrl}/confirm-email-change?userId={user.Id}&newEmail={Uri.EscapeDataString(newEmail)}&token={Uri.EscapeDataString(token)}";
// Die Erfolgsmeldung wird hier als Teil des "Ok"-Results übermittelt
// --- REFACTORED: Nutzung des EmailService ---
await _emailService.SendEmailChangeConfirmationAsync(newEmail, confirmationLink);
// Wir geben die Erfolgsmeldung im Result zurück (passend zu deinem Controller)
return ServiceResult.Ok("Bestätigungs-E-Mail wurde an die neue Adresse gesendet. Bitte prüfen Sie Ihr Postfach.");
}
}

View File

@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Webshop.Application.Services.Customers.Interfaces
{
public interface ICartService
{
Task ClearCartAsync(string userId);
}
}

View File

@@ -1,13 +1,20 @@
// src/Webshop.Application/Services/Customers/Interfaces/ICheckoutService.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Webshop.Application;
using Webshop.Application.DTOs.Orders;
using Webshop.Application.DTOs.Shipping;
using Webshop.Application; // F<>r ServiceResult
namespace Webshop.Application.Services.Customers.Interfaces
{
public interface ICheckoutService
{
// Methode 1: F<>r den Checkout (interne Verarbeitung)
Task<ServiceResult<IEnumerable<ShippingMethodDto>>> GetCompatibleShippingMethodsAsync(IEnumerable<CreateOrderItemDto> items);
// +++ METHODE 2: DIESE FEHLTE +++
// F<>r den Warenkorb-Check (kommt vom Controller als List<CartItemDto>)
Task<ServiceResult<IEnumerable<ShippingMethodDto>>> GetCompatibleShippingMethodsAsync(List<CartItemDto> items);
Task<ServiceResult<OrderDetailDto>> CreateOrderAsync(CreateOrderDto orderDto, string? userId);
}
}

View File

@@ -1,11 +1,9 @@
// src/Webshop.Application/Services/Customers/ICustomerService.cs
using System.Threading.Tasks;
using Webshop.Application;
using Webshop.Application.DTOs;
using System.Threading.Tasks;
using Webshop.Application; // Für ServiceResult
using Webshop.Application.DTOs.Auth;
using Webshop.Application.DTOs.Customers;
namespace Webshop.Application.Services.Customers
namespace Webshop.Application.Services.Customers.Interfaces // Namespace angepasst auf .Interfaces
{
public interface ICustomerService
{