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
{
///
/// Service der die Verwaltung von Warenkörben realisiert
///
public class CartService : ServiceBase, ICartService
{
private readonly ITaxRateService _taxRateService;
private readonly IShipmentCostService _shipmentCostService;
private readonly IOptions _shopOptions;
private readonly IRepository _namesRepository;
private readonly IRepository _itemRepository;
private readonly IRepository _itemNamesRepository;
private readonly IRepository _productRepository;
private readonly IRepository _listingRepository;
private readonly IRepository _bannerRepository;
private readonly IRepository _advertisementRepository;
private readonly IRepository _pinRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ITaxRateService
/// Instanz eines IShipmentCostService
/// Instanz von ShopOptions
public CartService(IUnitOfWork unitOfWork, ITaxRateService taxRateService, IShipmentCostService shipmentCostService, IOptions shopOptions) : base(unitOfWork)
{
_taxRateService = taxRateService;
_shipmentCostService = shipmentCostService;
_shopOptions = shopOptions;
_namesRepository = unitOfWork.GetRepository();
_itemRepository = unitOfWork.GetRepository();
_itemNamesRepository = unitOfWork.GetRepository();
_productRepository = unitOfWork.GetRepository();
_listingRepository = unitOfWork.GetRepository();
_bannerRepository = unitOfWork.GetRepository();
_advertisementRepository = unitOfWork.GetRepository();
_pinRepository = unitOfWork.GetRepository();
}
///
/// Gibt eine gefilterte Liste von Warenkörben zurück
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// Gewünschte Prache
/// Fallback Sprache
/// List betroffener Warenkörbe
public IQueryable 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;
}
///
/// Erstellen eines Warenkorbes
///
/// Rechnungsland
/// Lieferland
/// Warenkorb
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;
}
///
/// Erstellen eines Warenkorbes für einen Kunden
///
/// Id des Kunden
/// Rechnungsland
/// Lieferland
/// Warenkorb
public Cart CreateForCustomer(long customerId, string billingCountry, string deliveryCountry)
{
var item = Create(billingCountry, deliveryCountry);
item.OrderSource = OrderSource.Customer;
item.CustomerId = customerId;
return item;
}
///
/// Erstellen eines Warenkorbes für einen App-User
///
/// Id des App-Users
/// Quelle
/// Rechnungsland
/// Lieferland
/// Warenkorb
public Cart CreateForAppUser(string appUserId, OrderSource orderSource, string billingCountry, string deliveryCountry)
{
var item = Create(billingCountry, deliveryCountry);
item.OrderSource = orderSource;
item.AppUserId = appUserId;
return item;
}
///
/// Gibt einen Warenkorb für einen Kunden zurück oder legt einen an
///
/// Id des Kunden
/// Rechnungsland
/// Lieferland
/// Benutzer der den Warenkorb anlegt
/// Warenkorb
public async Task 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;
}
///
/// Gibt einen Warenkorb für einen App-User zurück oder legt einen an
///
/// Id des Hundebesitzers
/// Quelle
/// Rechnungsland
/// Lieferland
/// Benutzer der den Warenkorb anlegt
/// Warenkorb
public async Task 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;
}
///
/// Gibt einen Warenkorb für einen Kunden zurück
///
/// Id des Kunden
/// Warenkorb oder null, wenn nicht gefunden
public async Task GetByCustomerAsync(long customerId)
{
return await Repository.FirstOrDefaultAsync(c => c.CustomerId == customerId).ConfigureAwait(false);
}
///
/// Gibt einen Warenkorb für einen App-Users zurück
///
/// Id des App-Users
/// Warenkorb oder null, wenn nicht gefunden
public async Task GetByAppUserAsync(string appUserId)
{
return await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId).ConfigureAwait(false);
}
///
/// Gibt einen Warenkorb für einen Kunden zurück
///
/// Id des Kunden
/// Gewünschte Prache
/// Fallback Sprache
/// Warenkorb oder null, wenn nicht gefunden
public async Task 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();
}
///
/// Gibt einen Warenkorb für einen App-User zurück
///
/// Id des App-Users
/// Gewünschte Prache
/// Fallback Sprache
/// Warenkorb oder null, wenn nicht gefunden
public async Task 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();
}
///
/// Anlegen eines Artikels für den Warenkorb
///
/// Warenkorb Artikel
public CartItem CreateItem()
{
var item = new CartItem
{
Quantity = 0,
Total = 0,
TotalGross = 0,
Created = DateTimeOffset.UtcNow,
LastUpdate = DateTimeOffset.UtcNow
};
return item;
}
///
/// Hinzufügen eines Artikels zum Warenkorb
///
/// Artikel
public void AddItem(CartItem item)
{
_itemRepository.Add(item);
}
///
/// Gibt einen Artikel im Warenkorb zurück
///
/// Id des Warenkorbs
/// Id des Artikels
/// Artikel im Warenkorb oder null, wenn nicht gefunden
public async Task GetItemAsync(long cartId, int productId)
{
return await _itemRepository.FirstOrDefaultAsync(c => c.CartId == cartId && c.ProductId == productId).ConfigureAwait(false);
}
///
/// Gibt einen Artikel im Warenkorb zurück
///
/// Id des Artikels im Warenkorbs
/// Artikel im Warenkorb oder null, wenn nicht gefunden
public async Task GetItemAsync(long cartItemId)
{
return await _itemRepository.FirstOrDefaultAsync(c => c.Id == cartItemId).ConfigureAwait(false);
}
///
/// Gibt eine Liste von Artikel im Warenkorb zurück
///
/// Id des Warenkorbs
/// Liste der Artikel im Warenkorb
public async Task> GetItemsAsync(long cartId)
{
return (await _itemRepository.FindAsync(c => c.CartId == cartId).ConfigureAwait(false)).ToList();
}
///
/// Gibt eine Liste von Artikel im Warenkorb zurück
///
/// Id des Warenkorbs
/// Gewünschte Prache
/// Fallback Sprache
/// Liste der Artikel im Warenkorb
public async Task> 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);
}
///
/// Aktualisieren oder Anlegen eines Artikels im Warenkorb.
/// Achtung: Es wird keine Berechnung durchgeführt. Dazu den Warenkorb neu berechnen lassen!
///
/// Id des Warenkorbs
/// Id des Artikels
/// Stückzahl
/// Benutzer der den Warenkorb anlegt
/// Warenkorb Artikel oder null, wenn Fehler
public async Task 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;
}
///
/// Berechnen eines Warenkorbes
///
/// Id des Warenkorbes
/// Quelle dser Bestellung - Typ des Benutzers
/// Zielland für Versandkostenberechnung
/// Benutzer der die Kalkulation anstösst
/// Berechneter Warenkorb
public async Task 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();
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;
}
///
/// Leeren des Warenkorbs
///
/// Id des Warenkorbes
/// 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
/// Benutzer der die Aktion anstösst
/// true wenn erfolgreich, false sonst
public async Task 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;
}
///
/// Löschen eines Artikels aus dem Warenkorb nach ArtikelId
///
/// Id des Artikels
/// Task
public async Task DeleteCartItemAsync(long cartItemId)
{
var cartItem = await _itemRepository.FirstOrDefaultAsync(c => c.Id == cartItemId).ConfigureAwait(false);
if (cartItem != null)
{
_itemRepository.Remove(cartItem);
}
}
///
/// Löschen eines Artikels aus dem Warenkorb nach ArtikelId
///
/// Id des Artikels
/// Task
public async Task DeleteCartItemByProductAsync(int productId)
{
var items = await _itemRepository.FindAsync(c => c.ProductId == productId).ConfigureAwait(false);
if (items.Any())
{
_itemRepository.RemoveRange(items);
}
}
///
/// Löschen eines Warenkorbs nach Kunden
///
/// Id des Kunden
/// Task
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);
}
}
///
/// Löschen eines Warenkorbs nach App-User
///
/// Id des Hundebesitzers
/// Task
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);
}
}
}
}