public IEnumerable <Debt> GetDebts(IEnumerable <Action> actions) { if (actions?.Any() != true) { return(Enumerable.Empty <Debt>()); } var debtAggregator = new DebtAggregator(); foreach (var action in actions.OrderBy(a => a.Date)) { var totalAmountPaid = action.Sponsors.Sum(s => s.Amount); var payersDict = action.Goods .SelectMany(good => good.Consumers) .ToLookup(consumer => consumer.Participant) .ToDictionary(p => p.Key, p => p.Sum(consumer => consumer.Amount)); foreach (var sponsor in action.Sponsors) { var sponsorRate = sponsor.Amount / totalAmountPaid; foreach (var pair in payersDict.Where(pair => pair.Key != sponsor.Participant)) { var consumer = pair.Key; var amount = pair.Value; var debtAmount = Math.Round(amount * sponsorRate, 2); var debtTransaction = new DebtTransaction(debtAmount, action.Description, action.Date); debtAggregator.AddTransaction(sponsor.Participant, consumer, debtTransaction); } } } return(debtAggregator.GetDebts().Where(totalDebt => totalDebt.Amount != 0)); }
public void AddTransaction(string creditor, string debtor, DebtTransaction transaction) { if ((transaction?.Amount ?? 0m) < 0m) { throw new ArgumentException($"Cannot add transaction: amount must be > 0 but was {transaction?.Amount}."); } if (creditor.IsNullOrWhiteSpace() || debtor.IsNullOrWhiteSpace()) { throw new ArgumentException($"Cannot add transaction: creditor = [{creditor}], debtor = [{debtor}]."); } if ((transaction?.Amount ?? 0m) == 0m) { return; } var key = new DebtAggregatorKey(creditor, debtor); if (!debts.ContainsKey(key)) { debts.Add(key, new Debt { Creditor = creditor, Debtor = debtor }); } var debt = debts[key]; if (key.Creditor == debt.Creditor) { debt.Transactions.Add(transaction); } else { debt.Transactions.Add(transaction.Reverse()); if (debt.Amount < 0) { debts[key] = debt.Reverse(); } else if (debt.Amount == 0) { debts.Remove(key); } } }