553 lines
23 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}
}