public async Task <List <TransactionsDTO> > GetTransactionsBySKUinEuro(string SKU) { //I'm understanding that this method will be called after getting rates and transactions. //That's the reason why I'm reading first from DB, to avoid make 2 requests to the URL that provides data. try { //Get rates, first from DB, if there is not data, go to URL var rates = _ratesRepository.FindAll().Select(x => DTOMapper.Rate_To_RateDTO(x)).ToList(); if (!rates.Any()) { rates = await GetRates(); } var euroRates = GetToEuroRates(rates.ToList()); //Get Transactions filtering by SKU, first from DB, if there is not data, go to URL var transactions = _transactionsRepository.FindAll(x => x.SKU == SKU).Select(x => DTOMapper.Transaction_To_TransactionDTO(x)).ToList(); if (!transactions.Any()) { transactions = await GetTransactions(); transactions = transactions.ToList().Where(x => x.SKU == SKU).ToList() ?? new List <TransactionsDTO>(); } if (!transactions.Any()) { //TODO: Add constants or resources file to avoid hardcoded strings LoggerHelper.LogError("There are not transactions for the required SKU"); } else { foreach (var trans in transactions.Where(x => x.Currency != "EUR")) { var rate = euroRates.FirstOrDefault(x => x.From == trans.Currency); if (rate != null) { trans.Amount = RounderHelper.RoundToBankersRounding(trans.Amount * rate.Rate); trans.Currency = "EUR"; } } } return(transactions.ToList()); } catch (Exception ex) { LoggerHelper.LogError(ex.ToString()); return(new List <TransactionsDTO>()); } }
public async Task <JsonResult> Get(string SKU) { var transactionsInEuro = await TransactionsWorker.GetTransactionsBySKUinEuro(SKU); List <object> result = new List <object>(); if (transactionsInEuro.Any()) { result.Add(transactionsInEuro); result.Add(new { SKU = SKU, Total_Amount = RounderHelper.RoundToBankersRounding(transactionsInEuro.Sum(x => x.Amount)) }); } else { result.Add(new { Message = "There are not transactions for the requested SKU!" }); } return(new JsonResult(result)); }
private List <RatesDTO> GetToEuroRates(List <RatesDTO> lRates) { List <RatesDTO> conversedRates = new List <RatesDTO>(); try { List <string> currencies = new List <string>(); //Add to a list the different currencies currencies.AddRange(lRates.Select(x => x.From).Distinct()); currencies.AddRange(lRates.Select(x => x.To).Distinct()); currencies = currencies.Where(x => x != "EUR").Distinct().ToList(); //Find direct conversions that we already have foreach (var currency in currencies) { conversedRates.AddRange(lRates.Where(x => x.From == currency && x.To == "EUR")); } //Add to a new list the pending currencies which still doesn't have a conversion to EUR var pendingCurrencies = currencies.Except(conversedRates.Select(x => x.From)).ToList(); //Simple (and dirty) control to avoid possible infinite loop if can't find conversions var maxRetry = 10; var counter = 0; while (pendingCurrencies.Any() && counter < maxRetry) { counter++; foreach (var currency in pendingCurrencies) { //find a indirect conversion var currencyConversions = lRates.Where(x => x.From == currency).ToList(); var indirectConversion = new RatesDTO(); RatesDTO usedRate = null; foreach (var item in currencyConversions) { if ((indirectConversion = conversedRates.FirstOrDefault(x => x.From == item.To)) != null) { usedRate = item; break; } } //Use the indirect conversion to calculate the EUR conversion if (usedRate != null) { conversedRates.Add(new RatesDTO { From = currency, To = "EUR", Rate = RounderHelper.RoundToBankersRounding(usedRate.Rate * indirectConversion.Rate) }); pendingCurrencies = pendingCurrencies.Except(conversedRates.Select(x => x.From)).ToList(); } } } } catch (Exception ex) { LoggerHelper.LogError(ex.ToString()); } return(conversedRates); }