553 lines
23 KiB
C#
553 lines
23 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using System.Threading.Tasks;
|
||
using gehGassi.Core.Interfaces;
|
||
using gehGassi.Domain.Advertisements;
|
||
using gehGassi.Domain.Common;
|
||
using gehGassi.Domain.Shop;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Options;
|
||
|
||
namespace gehGassi.Core.Services
|
||
{
|
||
/// <summary>
|
||
/// Service der die Verwaltung von Warenkörben realisiert
|
||
/// </summary>
|
||
public class CartService : ServiceBase<Cart>, ICartService
|
||
{
|
||
private readonly ITaxRateService _taxRateService;
|
||
private readonly IShipmentCostService _shipmentCostService;
|
||
private readonly IOptions<ShopOptions> _shopOptions;
|
||
private readonly IRepository<CartWithNames> _namesRepository;
|
||
private readonly IRepository<CartItem> _itemRepository;
|
||
private readonly IRepository<CartItemWithNames> _itemNamesRepository;
|
||
private readonly IRepository<Product> _productRepository;
|
||
private readonly IRepository<Listing> _listingRepository;
|
||
private readonly IRepository<Banner> _bannerRepository;
|
||
private readonly IRepository<Advertisement> _advertisementRepository;
|
||
private readonly IRepository<Pin> _pinRepository;
|
||
|
||
/// <summary>
|
||
/// Erstellt eine Instanz
|
||
/// </summary>
|
||
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
|
||
/// <param name="taxRateService">Instanz eines ITaxRateService</param>
|
||
/// <param name="shipmentCostService">Instanz eines IShipmentCostService</param>
|
||
/// <param name="shopOptions">Instanz von ShopOptions</param>
|
||
public CartService(IUnitOfWork unitOfWork, ITaxRateService taxRateService, IShipmentCostService shipmentCostService, IOptions<ShopOptions> shopOptions) : base(unitOfWork)
|
||
{
|
||
_taxRateService = taxRateService;
|
||
_shipmentCostService = shipmentCostService;
|
||
_shopOptions = shopOptions;
|
||
_namesRepository = unitOfWork.GetRepository<CartWithNames>();
|
||
_itemRepository = unitOfWork.GetRepository<CartItem>();
|
||
_itemNamesRepository = unitOfWork.GetRepository<CartItemWithNames>();
|
||
_productRepository = unitOfWork.GetRepository<Product>();
|
||
_listingRepository = unitOfWork.GetRepository<Listing>();
|
||
_bannerRepository = unitOfWork.GetRepository<Banner>();
|
||
_advertisementRepository = unitOfWork.GetRepository<Advertisement>();
|
||
_pinRepository = unitOfWork.GetRepository<Pin>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt eine gefilterte Liste von Warenkörben zurück
|
||
/// </summary>
|
||
/// <param name="filter">Filterbegriff der in bestimmten Feldern gesucht wird</param>
|
||
/// <param name="language">Gewünschte Prache</param>
|
||
/// <param name="fallback">Fallback Sprache</param>
|
||
/// <returns>List betroffener Warenkörbe</returns>
|
||
public IQueryable<CartWithNames> Filter(string filter, string language, string fallback)
|
||
{
|
||
language = language.ToUpperInvariant();
|
||
fallback = fallback.ToUpperInvariant();
|
||
|
||
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_CartsWithNames({0},{1})", c => c.CustomerName.Contains(filter) || c.AppUserName.Contains(filter), language, fallback);
|
||
|
||
return baseQuery;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Erstellen eines Warenkorbes
|
||
/// </summary>
|
||
/// <param name="billingCountry">Rechnungsland</param>
|
||
/// <param name="deliveryCountry">Lieferland</param>
|
||
/// <returns>Warenkorb</returns>
|
||
public Cart Create(string billingCountry, string deliveryCountry)
|
||
{
|
||
var item = new Cart
|
||
{
|
||
Total = 0,
|
||
TotalGross = 0,
|
||
Shipment = 0,
|
||
ShipmentGross = 0,
|
||
Created = DateTimeOffset.UtcNow,
|
||
LastUpdate = DateTimeOffset.UtcNow,
|
||
BillingCountryCode = billingCountry,
|
||
DeliveryCountryCode = deliveryCountry
|
||
};
|
||
return item;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Erstellen eines Warenkorbes für einen Kunden
|
||
/// </summary>
|
||
/// <param name="customerId">Id des Kunden</param>
|
||
/// <param name="billingCountry">Rechnungsland</param>
|
||
/// <param name="deliveryCountry">Lieferland</param>
|
||
/// <returns>Warenkorb</returns>
|
||
public Cart CreateForCustomer(long customerId, string billingCountry, string deliveryCountry)
|
||
{
|
||
var item = Create(billingCountry, deliveryCountry);
|
||
item.OrderSource = OrderSource.Customer;
|
||
item.CustomerId = customerId;
|
||
return item;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Erstellen eines Warenkorbes für einen App-User
|
||
/// </summary>
|
||
/// <param name="appUserId">Id des App-Users</param>
|
||
/// <param name="orderSource">Quelle</param>
|
||
/// <param name="billingCountry">Rechnungsland</param>
|
||
/// <param name="deliveryCountry">Lieferland</param>
|
||
/// <returns>Warenkorb</returns>
|
||
public Cart CreateForAppUser(string appUserId, OrderSource orderSource, string billingCountry, string deliveryCountry)
|
||
{
|
||
var item = Create(billingCountry, deliveryCountry);
|
||
item.OrderSource = orderSource;
|
||
item.AppUserId = appUserId;
|
||
return item;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Warenkorb für einen Kunden zurück oder legt einen an
|
||
/// </summary>
|
||
/// <param name="customerId">Id des Kunden</param>
|
||
/// <param name="billingCountry">Rechnungsland</param>
|
||
/// <param name="deliveryCountry">Lieferland</param>
|
||
/// <param name="userName">Benutzer der den Warenkorb anlegt</param>
|
||
/// <returns>Warenkorb</returns>
|
||
public async Task<Cart> GetOrCreateCustomerAsync(long customerId, string billingCountry, string deliveryCountry, string userName)
|
||
{
|
||
var cart = await GetByCustomerAsync(customerId);
|
||
if (cart == null)
|
||
{
|
||
cart = CreateForCustomer(customerId, billingCountry, deliveryCountry);
|
||
Repository.Add(cart);
|
||
await CommitAsync(userName);
|
||
}
|
||
|
||
return cart;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Warenkorb für einen App-User zurück oder legt einen an
|
||
/// </summary>
|
||
/// <param name="appUserId">Id des Hundebesitzers</param>
|
||
/// <param name="orderSource">Quelle</param>
|
||
/// <param name="billingCountry">Rechnungsland</param>
|
||
/// <param name="deliveryCountry">Lieferland</param>
|
||
/// <param name="userName">Benutzer der den Warenkorb anlegt</param>
|
||
/// <returns>Warenkorb</returns>
|
||
public async Task<Cart> GetOrCreateAppUserAsync(string appUserId, OrderSource orderSource, string billingCountry, string deliveryCountry, string userName)
|
||
{
|
||
var cart = await GetByAppUserAsync(appUserId);
|
||
if (cart == null)
|
||
{
|
||
cart = CreateForAppUser(appUserId, orderSource, billingCountry, deliveryCountry);
|
||
Repository.Add(cart);
|
||
await CommitAsync(userName);
|
||
}
|
||
|
||
return cart;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Warenkorb für einen Kunden zurück
|
||
/// </summary>
|
||
/// <param name="customerId">Id des Kunden</param>
|
||
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
|
||
public async Task<Cart> GetByCustomerAsync(long customerId)
|
||
{
|
||
return await Repository.FirstOrDefaultAsync(c => c.CustomerId == customerId).ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Warenkorb für einen App-Users zurück
|
||
/// </summary>
|
||
/// <param name="appUserId">Id des App-Users</param>
|
||
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
|
||
public async Task<Cart> GetByAppUserAsync(string appUserId)
|
||
{
|
||
return await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Warenkorb für einen Kunden zurück
|
||
/// </summary>
|
||
/// <param name="customerId">Id des Kunden</param>
|
||
/// <param name="language">Gewünschte Prache</param>
|
||
/// <param name="fallback">Fallback Sprache</param>
|
||
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
|
||
public async Task<CartWithNames> GetByCustomerWithNamesAsync(long customerId, string language, string fallback)
|
||
{
|
||
language = language.ToUpperInvariant();
|
||
fallback = fallback.ToUpperInvariant();
|
||
|
||
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_CartsWithNames({0},{1})", c => c.CustomerId == customerId, language, fallback);
|
||
|
||
return await baseQuery.FirstOrDefaultAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Warenkorb für einen App-User zurück
|
||
/// </summary>
|
||
/// <param name="appUserId">Id des App-Users</param>
|
||
/// <param name="language">Gewünschte Prache</param>
|
||
/// <param name="fallback">Fallback Sprache</param>
|
||
/// <returns>Warenkorb oder null, wenn nicht gefunden</returns>
|
||
public async Task<CartWithNames> GetByAppUserWithNamesAsync(string appUserId, string language, string fallback)
|
||
{
|
||
language = language.ToUpperInvariant();
|
||
fallback = fallback.ToUpperInvariant();
|
||
|
||
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_CartsWithNames({0},{1})", c => c.AppUserId == appUserId, language, fallback);
|
||
|
||
return await baseQuery.FirstOrDefaultAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Anlegen eines Artikels für den Warenkorb
|
||
/// </summary>
|
||
/// <returns>Warenkorb Artikel</returns>
|
||
public CartItem CreateItem()
|
||
{
|
||
var item = new CartItem
|
||
{
|
||
Quantity = 0,
|
||
Total = 0,
|
||
TotalGross = 0,
|
||
Created = DateTimeOffset.UtcNow,
|
||
LastUpdate = DateTimeOffset.UtcNow
|
||
};
|
||
|
||
return item;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Hinzufügen eines Artikels zum Warenkorb
|
||
/// </summary>
|
||
/// <param name="item">Artikel</param>
|
||
public void AddItem(CartItem item)
|
||
{
|
||
_itemRepository.Add(item);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Artikel im Warenkorb zurück
|
||
/// </summary>
|
||
/// <param name="cartId">Id des Warenkorbs</param>
|
||
/// <param name="productId">Id des Artikels</param>
|
||
/// <returns>Artikel im Warenkorb oder null, wenn nicht gefunden</returns>
|
||
public async Task<CartItem> GetItemAsync(long cartId, int productId)
|
||
{
|
||
return await _itemRepository.FirstOrDefaultAsync(c => c.CartId == cartId && c.ProductId == productId).ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt einen Artikel im Warenkorb zurück
|
||
/// </summary>
|
||
/// <param name="cartItemId">Id des Artikels im Warenkorbs</param>
|
||
/// <returns>Artikel im Warenkorb oder null, wenn nicht gefunden</returns>
|
||
public async Task<CartItem> GetItemAsync(long cartItemId)
|
||
{
|
||
return await _itemRepository.FirstOrDefaultAsync(c => c.Id == cartItemId).ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt eine Liste von Artikel im Warenkorb zurück
|
||
/// </summary>
|
||
/// <param name="cartId">Id des Warenkorbs</param>
|
||
/// <returns>Liste der Artikel im Warenkorb</returns>
|
||
public async Task<List<CartItem>> GetItemsAsync(long cartId)
|
||
{
|
||
return (await _itemRepository.FindAsync(c => c.CartId == cartId).ConfigureAwait(false)).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt eine Liste von Artikel im Warenkorb zurück
|
||
/// </summary>
|
||
/// <param name="cartId">Id des Warenkorbs</param>
|
||
/// <param name="language">Gewünschte Prache</param>
|
||
/// <param name="fallback">Fallback Sprache</param>
|
||
/// <returns>Liste der Artikel im Warenkorb</returns>
|
||
public async Task<List<CartItemWithNames>> GetItemsWithNamesAsync(long cartId, string language, string fallback)
|
||
{
|
||
language = language.ToUpperInvariant();
|
||
fallback = fallback.ToUpperInvariant();
|
||
|
||
var baseQuery = _itemNamesRepository.QueryStoredProcedure("SELECT * FROM tv_CartItemsWithNames({0},{1})", c => c.CartId == cartId, language, fallback);
|
||
|
||
return await baseQuery.ToListAsync().ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Aktualisieren oder Anlegen eines Artikels im Warenkorb.
|
||
/// Achtung: Es wird keine Berechnung durchgeführt. Dazu den Warenkorb neu berechnen lassen!
|
||
/// </summary>
|
||
/// <param name="cartId">Id des Warenkorbs</param>
|
||
/// <param name="productId">Id des Artikels</param>
|
||
/// <param name="quantity">Stückzahl</param>
|
||
/// <param name="userName">Benutzer der den Warenkorb anlegt</param>
|
||
/// <returns>Warenkorb Artikel oder null, wenn Fehler</returns>
|
||
public async Task<CartItem> UpdateOrCreateItemAsync(long cartId, int productId, int quantity, string userName)
|
||
{
|
||
var cartItem = await GetItemAsync(cartId, productId);
|
||
if (cartItem == null)
|
||
{
|
||
cartItem = CreateItem();
|
||
cartItem.CartId = cartId;
|
||
cartItem.ProductId = productId;
|
||
cartItem.Quantity = quantity;
|
||
_itemRepository.Add(cartItem);
|
||
}
|
||
else
|
||
{
|
||
cartItem.Quantity += quantity;
|
||
cartItem.LastUpdate = DateTime.UtcNow;
|
||
}
|
||
|
||
await CommitAsync(userName);
|
||
return cartItem;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Berechnen eines Warenkorbes
|
||
/// </summary>
|
||
/// <param name="cartId">Id des Warenkorbes</param>
|
||
/// <param name="orderSource">Quelle dser Bestellung - Typ des Benutzers</param>
|
||
/// <param name="targetCountry">Zielland für Versandkostenberechnung</param>
|
||
/// <param name="userName">Benutzer der die Kalkulation anstösst</param>
|
||
/// <returns>Berechneter Warenkorb</returns>
|
||
public async Task<Cart> CalculateAsync(long cartId, OrderSource orderSource, string targetCountry, string userName)
|
||
{
|
||
var cart = await Repository.GetAsync(cartId);
|
||
if (cart != null)
|
||
{
|
||
var total = 0M;
|
||
var totalForShipment = 0M; //Hier extra, da Produkte die Versandkostenfrei sind nicht mitgerechnet werden
|
||
var totalGross = 0M;
|
||
var quantity = 0;
|
||
var totalWeight = 0M; //Versandkostenfrei-Produkte werden hier nicht berücksichtigt
|
||
decimal additionalShipmentCosts = 0;
|
||
var taxRates = new Dictionary<decimal, decimal>();
|
||
|
||
var items = await GetItemsAsync(cart.Id);
|
||
foreach (var cartItem in items)
|
||
{
|
||
//Zuerst prüfen ob der Artikel noch interessiert
|
||
var product = await _productRepository.GetAsync(cartItem.ProductId);
|
||
if (product == null || product.Deleted || !product.AvailableFor(orderSource))
|
||
{
|
||
_itemRepository.Remove(cartItem);
|
||
}
|
||
else
|
||
{
|
||
total += cartItem.Total;
|
||
totalGross += cartItem.TotalGross;
|
||
quantity += cartItem.Quantity;
|
||
|
||
if (taxRates.ContainsKey(cartItem.TaxRate))
|
||
{
|
||
taxRates[cartItem.TaxRate] += cartItem.TaxRateValue;
|
||
}
|
||
else
|
||
{
|
||
taxRates.Add(cartItem.TaxRate, cartItem.TaxRateValue);
|
||
}
|
||
|
||
if (!product.IsFreeShipping)
|
||
{
|
||
totalWeight += (cartItem.Quantity * product.Weight);
|
||
totalForShipment += cartItem.Total;
|
||
additionalShipmentCosts += (cartItem.Quantity * product.AdditionalShippingCharge);
|
||
}
|
||
}
|
||
}
|
||
|
||
cart.Total = Math.Round(total, 2, MidpointRounding.AwayFromZero);
|
||
cart.TotalGross = Math.Round(totalGross, 2, MidpointRounding.AwayFromZero);
|
||
|
||
if (quantity > 0)
|
||
{
|
||
var shipmentCost = await _shipmentCostService.CalculateShipmentAsync(targetCountry, totalWeight, totalForShipment, additionalShipmentCosts);
|
||
var taxResult = await _taxRateService.CalculateShipmentGrossAsync(shipmentCost, _shopOptions.Value.SourceCountry, targetCountry);
|
||
cart.Shipment = shipmentCost;
|
||
cart.ShipmentGross = taxResult.Value;
|
||
|
||
if (taxRates.ContainsKey(taxResult.TaxRate))
|
||
{
|
||
taxRates[taxResult.TaxRate] += taxResult.TaxValue;
|
||
}
|
||
else
|
||
{
|
||
taxRates.Add(taxResult.TaxRate, taxResult.TaxValue);
|
||
}
|
||
}
|
||
cart.TaxRates = taxRates;
|
||
|
||
await CommitAsync(userName);
|
||
}
|
||
return cart;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Leeren des Warenkorbs
|
||
/// </summary>
|
||
/// <param name="cartId">Id des Warenkorbes</param>
|
||
/// <param name="removeEntity">Wenn true, werden Entitäten wie Listings, Banner, Werbungen und Pins gelöscht, wenn false werden die Cart- und CartItem Id´s auf null gesetzt</param>
|
||
/// <param name="userName">Benutzer der die Aktion anstösst</param>
|
||
/// <returns>true wenn erfolgreich, false sonst</returns>
|
||
public async Task<bool> ClearAsync(long cartId, bool removeEntity, string userName)
|
||
{
|
||
var cart = await Repository.GetAsync(cartId);
|
||
if (cart != null)
|
||
{
|
||
var items = await GetItemsAsync(cart.Id);
|
||
foreach (var cartItem in items)
|
||
{
|
||
if (cartItem.ProductType == ProductType.Listing)
|
||
{
|
||
var listing = await _listingRepository.GetAsync(cartItem.ItemId);
|
||
if (listing != null)
|
||
{
|
||
if(removeEntity)
|
||
_listingRepository.Remove(listing);
|
||
else
|
||
{
|
||
listing.CartId = null;
|
||
listing.CartItemId = null;
|
||
}
|
||
}
|
||
}
|
||
else if (cartItem.ProductType == ProductType.Banner)
|
||
{
|
||
var banner = await _bannerRepository.GetAsync(cartItem.ItemId);
|
||
if (banner != null)
|
||
{
|
||
if(removeEntity)
|
||
_bannerRepository.Remove(banner);
|
||
else
|
||
{
|
||
banner.CartId = null;
|
||
banner.CartItemId = null;
|
||
}
|
||
}
|
||
}
|
||
else if (cartItem.ProductType == ProductType.Advertisement)
|
||
{
|
||
var advertisement = await _advertisementRepository.GetAsync(cartItem.ItemId);
|
||
if (advertisement != null)
|
||
{
|
||
if(removeEntity)
|
||
_advertisementRepository.Remove(advertisement);
|
||
else
|
||
{
|
||
advertisement.CartId = null;
|
||
advertisement.CartItemId = null;
|
||
}
|
||
}
|
||
}
|
||
else if (cartItem.ProductType == ProductType.Pin)
|
||
{
|
||
var pin = await _pinRepository.GetAsync(cartItem.ItemId);
|
||
if (pin != null)
|
||
{
|
||
if(removeEntity)
|
||
_pinRepository.Remove(pin);
|
||
else
|
||
{
|
||
pin.CartId = null;
|
||
pin.CartItemId = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
_itemRepository.Remove(cartItem);
|
||
}
|
||
|
||
cart.Total = 0;
|
||
cart.TotalGross = 0;
|
||
cart.Shipment = 0;
|
||
cart.ShipmentGross = 0;
|
||
cart.LastUpdate = DateTime.UtcNow;
|
||
|
||
await CommitAsync(userName);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Löschen eines Artikels aus dem Warenkorb nach ArtikelId
|
||
/// </summary>
|
||
/// <param name="cartItemId">Id des Artikels</param>
|
||
/// <returns>Task</returns>
|
||
public async Task DeleteCartItemAsync(long cartItemId)
|
||
{
|
||
var cartItem = await _itemRepository.FirstOrDefaultAsync(c => c.Id == cartItemId).ConfigureAwait(false);
|
||
if (cartItem != null)
|
||
{
|
||
_itemRepository.Remove(cartItem);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Löschen eines Artikels aus dem Warenkorb nach ArtikelId
|
||
/// </summary>
|
||
/// <param name="productId">Id des Artikels</param>
|
||
/// <returns>Task</returns>
|
||
public async Task DeleteCartItemByProductAsync(int productId)
|
||
{
|
||
var items = await _itemRepository.FindAsync(c => c.ProductId == productId).ConfigureAwait(false);
|
||
if (items.Any())
|
||
{
|
||
_itemRepository.RemoveRange(items);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Löschen eines Warenkorbs nach Kunden
|
||
/// </summary>
|
||
/// <param name="customerId">Id des Kunden</param>
|
||
/// <returns>Task</returns>
|
||
public async Task DeleteByCustomerAsync(long customerId)
|
||
{
|
||
var items = await Repository.FindAsync(c => c.CustomerId == customerId).ConfigureAwait(false);
|
||
var enumerable = items.ToList();
|
||
if (enumerable.Any())
|
||
{
|
||
Repository.RemoveRange(enumerable);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Löschen eines Warenkorbs nach App-User
|
||
/// </summary>
|
||
/// <param name="appUserId">Id des Hundebesitzers</param>
|
||
/// <returns>Task</returns>
|
||
public async Task DeleteByAppUserAsync(string appUserId)
|
||
{
|
||
var items = await Repository.FindAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
|
||
var enumerable = items.ToList();
|
||
if (enumerable.Any())
|
||
{
|
||
Repository.RemoveRange(enumerable);
|
||
}
|
||
}
|
||
}
|
||
}
|