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