gehgassi_backend/gehGassi.Core/Services/ShopCalculationService.cs

456 lines
22 KiB
C#

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
{
/// <summary>
/// Service der die Preise eines Produktes berechnet
/// </summary>
public class ShopCalculationService : IShopCalculationService
{
private readonly IProductService _productService;
private readonly ICustomerService _customerService;
private readonly IOptions<ShopOptions> _shopOptions;
private readonly IOptions<VatValidationSettings> _vatValidationSettings;
private readonly ITaxRateService _taxRateService;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="productService">Instanz eines IProductService</param>
/// <param name="customerService">Instanz eines ICustomerService</param>
/// <param name="shopOptions">Instanz von ShopOptions</param>
/// <param name="vatValidationSettings">Instanz von VatValidationSettings</param>
/// <param name="taxRateService">Instanz eines ITaxRateService</param>
public ShopCalculationService(IProductService productService, ICustomerService customerService, IOptions<ShopOptions> shopOptions, IOptions<VatValidationSettings> vatValidationSettings, ITaxRateService taxRateService)
{
_productService = productService;
_customerService = customerService;
_shopOptions = shopOptions;
_vatValidationSettings = vatValidationSettings;
_taxRateService = taxRateService;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> 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;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> 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;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <param name="budget">Budget für Banner</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> 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;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="priceType">Preistype</param>
/// <param name="priceAliquotType">Typ der aliquoten Berechnung</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> 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;
}
/// <summary>
/// Berechnet den Preis einer Produktes für einen Kunden.
/// Berücksichtigt die Länder- und Kundeneinstellungen
/// </summary>
/// <param name="productId">Id des Produktes</param>
/// <param name="customerId">Id des Kunden</param>
/// <param name="priceType">Preistype</param>
/// <param name="priceAliquotType">Typ der aliquoten Berechnung</param>
/// <param name="startDate">Startdatum</param>
/// <param name="endDate">Enddatum</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <param name="quantity">Anzahl - Stück</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> 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;
}
/// <summary>
/// Aufschlagen von X Prozent auf eine Preis-Kalkulation
/// </summary>
/// <param name="priceCalculation">Preis-Kalkulation</param>
/// <param name="markup">Aufschlag in Prozent</param>
/// <param name="productId">Id des Produktes</param>
/// <param name="billingCountry">Iso2 Land für die Rechnung</param>
/// <returns>Preis-Kalkulation</returns>
public async Task<PriceCalculation> 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;
}
}
}