using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using gehGassi.Common; using gehGassi.Core.Interfaces; using gehGassi.Domain.Common; using Microsoft.Extensions.Options; namespace gehGassi.Core.Services { /// /// Service der die Preise eines Produktes berechnet /// public class ShopCalculationService : IShopCalculationService { private readonly IProductService _productService; private readonly ICustomerService _customerService; private readonly IOptions _shopOptions; private readonly IOptions _vatValidationSettings; private readonly ITaxRateService _taxRateService; /// /// Erstellt eine Instanz /// /// Instanz eines IProductService /// Instanz eines ICustomerService /// Instanz von ShopOptions /// Instanz von VatValidationSettings /// Instanz eines ITaxRateService public ShopCalculationService(IProductService productService, ICustomerService customerService, IOptions shopOptions, IOptions vatValidationSettings, ITaxRateService taxRateService) { _productService = productService; _customerService = customerService; _shopOptions = shopOptions; _vatValidationSettings = vatValidationSettings; _taxRateService = taxRateService; } /// /// Berechnet den Preis einer Produktes für einen Kunden. /// Berücksichtigt die Länder- und Kundeneinstellungen /// /// Id des Produktes /// Id des Kunden /// Anzahl - Stück /// Preis-Kalkulation public async Task CalculatePriceAsync(int productId, long customerId, int quantity) { var customer = await _customerService.GetAsync(customerId); var result = await CalculatePriceAsync(productId, customerId, customer.Address.CountryCode, quantity); return result; } /// /// Berechnet den Preis einer Produktes für einen Kunden. /// Berücksichtigt die Länder- und Kundeneinstellungen /// /// Id des Produktes /// Id des Kunden /// Iso2 Land für die Rechnung /// Anzahl - Stück /// Preis-Kalkulation public async Task CalculatePriceAsync(int productId, long customerId, string billingCountry, int quantity) { var result = new PriceCalculation { TaxRate = 0, TaxValue = 0 }; var product = await _productService.GetAsync(productId); var customer = await _customerService.GetAsync(customerId); var productCountry = await _productService.GetCountryAsync(productId, billingCountry); result.Price = product.Price; result.Price2 = product.Price2; result.OldPrice = product.OldPrice; result.OldPrice2 = product.OldPrice2; if (productCountry != null) { result.Price = productCountry.Price; result.Price2 = productCountry.Price2; result.OldPrice = productCountry.OldPrice; result.OldPrice2 = productCountry.OldPrice2; } decimal markup = 0m; switch (product.ProductType) { case ProductType.Physical: break; case ProductType.Download: break; case ProductType.Listing: markup = customer.MarkupListing; break; case ProductType.Advertisement: markup = customer.MarkupAdvertisement; break; case ProductType.Pin: markup = customer.MarkupPin; break; case ProductType.Banner: markup = customer.MarkupBanner; break; default: throw new ArgumentOutOfRangeException(); } if (markup != 0) { result.Price += ((result.Price * markup) / 100); result.OldPrice += ((result.OldPrice * markup) / 100); result.Price2 += ((result.Price2 * markup) / 100); result.OldPrice2 += ((result.OldPrice2 * markup) / 100); } result.Quantity = quantity; result.Total = (result.Price + result.Price2) * quantity; result.TotalOld = (result.OldPrice + result.OldPrice2) * quantity; result.PriceGross = result.Price; result.OldPriceGross = result.OldPrice; result.Price2Gross = result.Price2; result.OldPrice2Gross = result.OldPrice2; result.TotalGross = result.Total; result.TotalOldGross = result.TotalOld; if (!product.IsTaxExempt) { //EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry); var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry); var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry; if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource) { var taxRate = await _taxRateService.GetAsync(product.TaxRateId); result.PriceGross = result.Price + ((result.Price * taxRate.Value) / 100); result.OldPriceGross = result.OldPrice + ((result.OldPrice * taxRate.Value) / 100); result.Price2Gross = result.Price2 + ((result.Price2 * taxRate.Value) / 100); result.OldPrice2Gross = result.OldPrice2 + ((result.OldPrice2 * taxRate.Value) / 100); result.PriceGross = Math.Round(result.PriceGross, 2, MidpointRounding.AwayFromZero); result.OldPriceGross = Math.Round(result.OldPriceGross, 2, MidpointRounding.AwayFromZero); result.Price2Gross = Math.Round(result.Price2Gross, 2, MidpointRounding.AwayFromZero); result.OldPrice2Gross = Math.Round(result.OldPrice2Gross, 2, MidpointRounding.AwayFromZero); result.TotalGross = (result.PriceGross + result.Price2Gross) * quantity; result.TotalOldGross = (result.OldPriceGross + result.OldPrice2Gross) * quantity; result.TaxRate = taxRate.Value; } } result.TaxValue = result.TotalGross - result.Total; return result; } /// /// Berechnet den Preis einer Produktes für einen Kunden. /// Berücksichtigt die Länder- und Kundeneinstellungen /// /// Id des Produktes /// Id des Kunden /// Iso2 Land für die Rechnung /// Anzahl - Stück /// Budget für Banner /// Preis-Kalkulation public async Task CalculatePriceBannerAsync(int productId, long customerId, string billingCountry, int quantity, decimal budget) { var result = new PriceCalculation { TaxRate = 0, TaxValue = 0 }; var product = await _productService.GetAsync(productId); var customer = await _customerService.GetAsync(customerId); var productCountry = await _productService.GetCountryAsync(productId, billingCountry); result.Price = budget; result.Price2 = 0m; result.OldPrice = 0m; result.OldPrice2 = 0m; result.Quantity = quantity; result.Total = (result.Price + result.Price2) * quantity; result.TotalOld = (result.OldPrice + result.OldPrice2) * quantity; result.PriceGross = result.Price; result.OldPriceGross = result.OldPrice; result.Price2Gross = result.Price2; result.OldPrice2Gross = result.OldPrice2; result.TotalGross = result.Total; result.TotalOldGross = result.TotalOld; if (!product.IsTaxExempt) { //EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry); var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry); var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry; if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource) { var taxRate = await _taxRateService.GetAsync(product.TaxRateId); var grossFactor = 1 + (taxRate.Value / 100); result.Price = decimal.Round(result.PriceGross / grossFactor, 4, MidpointRounding.AwayFromZero); result.Total = (result.Price + result.Price2) * quantity; result.TaxRate = taxRate.Value; } } result.TaxValue = result.TotalGross - result.Total; return result; } /// /// Berechnet den Preis einer Produktes für einen Kunden. /// Berücksichtigt die Länder- und Kundeneinstellungen /// /// Id des Produktes /// Id des Kunden /// Preistype /// Typ der aliquoten Berechnung /// Startdatum /// Enddatum /// Anzahl - Stück /// Preis-Kalkulation public async Task CalculatePriceAsync(int productId, long customerId, PriceType priceType, PriceAliquotType priceAliquotType, DateTimeOffset startDate, DateTimeOffset endDate, int quantity) { var customer = await _customerService.GetAsync(customerId); var result = await CalculatePriceAsync(productId, customerId, priceType, priceAliquotType, startDate, endDate, customer.Address.CountryCode, quantity); return result; } /// /// Berechnet den Preis einer Produktes für einen Kunden. /// Berücksichtigt die Länder- und Kundeneinstellungen /// /// Id des Produktes /// Id des Kunden /// Preistype /// Typ der aliquoten Berechnung /// Startdatum /// Enddatum /// Iso2 Land für die Rechnung /// Anzahl - Stück /// Preis-Kalkulation public async Task CalculatePriceAsync(int productId, long customerId, PriceType priceType, PriceAliquotType priceAliquotType, DateTimeOffset startDate, DateTimeOffset endDate, string billingCountry, int quantity) { var result = new PriceCalculation { TaxRate = 0, TaxValue = 0 }; var product = await _productService.GetAsync(productId); var customer = await _customerService.GetAsync(customerId); var productCountry = await _productService.GetCountryAsync(productId, billingCountry); result.Price = product.Price; result.Price2 = product.Price2; result.OldPrice = product.OldPrice; result.OldPrice2 = product.OldPrice2; if (productCountry != null) { result.Price = productCountry.Price; result.Price2 = productCountry.Price2; result.OldPrice = productCountry.OldPrice; result.OldPrice2 = productCountry.OldPrice2; } decimal markup = 0m; switch (product.ProductType) { case ProductType.Physical: break; case ProductType.Download: break; case ProductType.Listing: markup = customer.MarkupListing; break; case ProductType.Advertisement: markup = customer.MarkupAdvertisement; break; case ProductType.Pin: markup = customer.MarkupPin; break; case ProductType.Banner: markup = customer.MarkupBanner; break; default: throw new ArgumentOutOfRangeException(); } if (markup != 0) { result.Price += ((result.Price * markup) / 100); result.OldPrice += ((result.OldPrice * markup) / 100); result.Price2 += ((result.Price2 * markup) / 100); result.OldPrice2 += ((result.OldPrice2 * markup) / 100); } if (priceAliquotType == PriceAliquotType.Month && priceType == PriceType.Year) { result.Price /= 12; result.Price2 /= 12; result.OldPrice /= 12; result.OldPrice2 /= 12; var months = (((endDate.Year - startDate.Year) * 12) + endDate.Month - startDate.Month) + 1; result.Price *= months; result.Price2 *= months; result.OldPrice *= months; result.OldPrice2 *= months; } if (priceAliquotType == PriceAliquotType.Day && priceType == PriceType.Month) { var totalDays = DateTime.DaysInMonth(endDate.Year, endDate.Month); result.Price /= totalDays; result.Price2 /= totalDays; result.OldPrice /= totalDays; result.OldPrice2 /= totalDays; var days = (int)(endDate - startDate).TotalDays + 1; result.Price *= days; result.Price2 *= days; result.OldPrice *= days; result.OldPrice2 *= days; } if (priceType == PriceType.Day) { var days = (int)(endDate - startDate).TotalDays; result.Price *= days; result.Price2 *= days; result.OldPrice *= days; result.OldPrice2 *= days; } result.Quantity = quantity; result.Total = (result.Price + result.Price2) * quantity; result.TotalOld = (result.OldPrice + result.OldPrice2) * quantity; result.PriceGross = result.Price; result.OldPriceGross = result.OldPrice; result.Price2Gross = result.Price2; result.OldPrice2Gross = result.OldPrice2; result.TotalGross = result.Total; result.TotalOldGross = result.TotalOld; if (!product.IsTaxExempt) { //EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry); var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry); var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry; if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource) { var taxRate = await _taxRateService.GetAsync(product.TaxRateId); result.PriceGross = result.Price + ((result.Price * taxRate.Value) / 100); result.OldPriceGross = result.OldPrice + ((result.OldPrice * taxRate.Value) / 100); result.Price2Gross = result.Price2 + ((result.Price2 * taxRate.Value) / 100); result.OldPrice2Gross = result.OldPrice2 + ((result.OldPrice2 * taxRate.Value) / 100); result.PriceGross = Math.Round(result.PriceGross, 2, MidpointRounding.AwayFromZero); result.OldPriceGross = Math.Round(result.OldPriceGross, 2, MidpointRounding.AwayFromZero); result.Price2Gross = Math.Round(result.Price2Gross, 2, MidpointRounding.AwayFromZero); result.OldPrice2Gross = Math.Round(result.OldPrice2Gross, 2, MidpointRounding.AwayFromZero); result.TotalGross = (result.PriceGross + result.Price2Gross) * quantity; result.TotalOldGross = (result.OldPriceGross + result.OldPrice2Gross) * quantity; result.TaxRate = taxRate.Value; } } result.TaxValue = result.TotalGross - result.Total; return result; } /// /// Aufschlagen von X Prozent auf eine Preis-Kalkulation /// /// Preis-Kalkulation /// Aufschlag in Prozent /// Id des Produktes /// Iso2 Land für die Rechnung /// Preis-Kalkulation public async Task AddMarkupAsync(PriceCalculation priceCalculation, decimal markup, int productId, string billingCountry) { var product = await _productService.GetAsync(productId); var productCountry = await _productService.GetCountryAsync(productId, billingCountry); priceCalculation.Price += ((priceCalculation.Price * markup) / 100); priceCalculation.OldPrice += ((priceCalculation.OldPrice * markup) / 100); priceCalculation.Price2 += ((priceCalculation.Price2 * markup) / 100); priceCalculation.OldPrice2 += ((priceCalculation.OldPrice2 * markup) / 100); priceCalculation.Total = (priceCalculation.Price + priceCalculation.Price2) * priceCalculation.Quantity; priceCalculation.TotalOld = (priceCalculation.OldPrice + priceCalculation.OldPrice2) * priceCalculation.Quantity; priceCalculation.PriceGross = priceCalculation.Price; priceCalculation.OldPriceGross = priceCalculation.OldPrice; priceCalculation.Price2Gross = priceCalculation.Price2; priceCalculation.OldPrice2Gross = priceCalculation.OldPrice2; priceCalculation.TotalGross = priceCalculation.Total; priceCalculation.TotalOldGross = priceCalculation.TotalOld; if (!product.IsTaxExempt) { //EU-Ausnahme. Gilt wenn Land in der EU und ungleich Quellland und Kunde ist Firma var isEuCountry = _vatValidationSettings.Value.Countries.Contains(billingCountry); var isNoTaxCountry = _vatValidationSettings.Value.NoTaxCountries.Contains(billingCountry); var isSameAsSource = _shopOptions.Value.SourceCountry == billingCountry; if ((!isEuCountry && !isNoTaxCountry) || isSameAsSource) { var taxRate = await _taxRateService.GetAsync(product.TaxRateId); priceCalculation.PriceGross = priceCalculation.Price + ((priceCalculation.Price * taxRate.Value) / 100); priceCalculation.OldPriceGross = priceCalculation.OldPrice + ((priceCalculation.OldPrice * taxRate.Value) / 100); priceCalculation.Price2Gross = priceCalculation.Price2 + ((priceCalculation.Price2 * taxRate.Value) / 100); priceCalculation.OldPrice2Gross = priceCalculation.OldPrice2 + ((priceCalculation.OldPrice2 * taxRate.Value) / 100); priceCalculation.PriceGross = Math.Round(priceCalculation.PriceGross, 2, MidpointRounding.AwayFromZero); priceCalculation.OldPriceGross = Math.Round(priceCalculation.OldPriceGross, 2, MidpointRounding.AwayFromZero); priceCalculation.Price2Gross = Math.Round(priceCalculation.Price2Gross, 2, MidpointRounding.AwayFromZero); priceCalculation.OldPrice2Gross = Math.Round(priceCalculation.OldPrice2Gross, 2, MidpointRounding.AwayFromZero); priceCalculation.TotalGross = (priceCalculation.PriceGross + priceCalculation.Price2Gross) * priceCalculation.Quantity; priceCalculation.TotalOldGross = (priceCalculation.OldPriceGross + priceCalculation.OldPrice2Gross) * priceCalculation.Quantity; priceCalculation.TaxRate = taxRate.Value; } } priceCalculation.TaxValue = priceCalculation.TotalGross - priceCalculation.Total; return priceCalculation; } } }