public ExpenseSummary GetExpenseSummary(int accountId, BudgetPlanDetails budgetPlan)
        {
            var account = _bankAccountRepository.GetById(accountId, x => x.Bank, x => x.Currency);

            if (account == null)
            {
                throw new ArgumentException("Account can't be null.");
            }

            var today = DateTime.Now;
            var over12MonthsInterval = new Interval(today, DateTimeUnitEnums.Years, 1);
            var over6MonthsInterval  = new Interval(today, DateTimeUnitEnums.Months, 6);
            var currentMonthInterval = new Interval(today, today);
            var previousInterval     = new Interval(today, DateTimeUnitEnums.Months, 1);

            var categories = _ExpenseTypeRepository.GetList2().GroupBy(x => x.Id).ToDictionary(x => x.Key, y => y.Single());

            var over12MonthsNames = over12MonthsInterval.GetIntervalsByMonth();
            var currentMonthName  = currentMonthInterval.GetSingleMonthName();

            // Retrieve both current month expenses and over 12 months expenses
            var expenses = GetExpenses(new PFM.Services.DTOs.SearchParameters.ExpenseGetListSearchParameters
            {
                AccountId = accountId,
                StartDate = over12MonthsInterval.StartDate,
                EndDate   = currentMonthInterval.EndDate
            });

            // Reset the start date to the first movement (First day of the same month)
            over12MonthsInterval.StartDate = DateTimeFormatHelper.GetFirstDayOfMonth(
                expenses.Any() ?
                expenses.OrderBy(x => x.DateExpense).First().DateExpense :
                today);

            // Count the number of months in the interval
            var nbMonthInterval = over12MonthsInterval.Count(DateTimeUnitEnums.Months);

            if (nbMonthInterval == 0)
            {
                nbMonthInterval = 1; // No expenses -> no division by zero
            }
            // Get current budget plan if it exists
            var budgetPlanByCategory =
                budgetPlan?.ExpenseTypes.GroupBy(x => x.ExpenseType.Id).ToDictionary(x => x.Key, y => y.Single().ExpectedValue)
                ?? categories.ToDictionary(x => x.Key, y => (decimal)0.00);

            var expensesByCategories = expenses.Any()
                ? expenses.GroupBy(x => x.ExpenseTypeId).ToDictionary(x => x.Key, y => y.ToList())
                : categories.ToDictionary(x => x.Key, y => new List <ExpenseList>());

            var expensesByCategory = new List <ExpenseSummaryByCategory>();

            foreach (var exp in expensesByCategories)
            {
                var category              = categories[exp.Key];
                var expensesCurrentMonth  = exp.Value.Where(x => currentMonthInterval.IsBetween(x.DateExpense)).ToList();
                var expensesPreviousMonth = exp.Value.Where(x => previousInterval.IsBetween(x.DateExpense)).ToList();
                var expensesOver12Months  = exp.Value.Where(x => over12MonthsInterval.IsBetween(x.DateExpense)).ToList();

                // ReSharper disable once UseObjectOrCollectionInitializer
                var expenseSummary = new ExpenseSummaryByCategory();
                expenseSummary.CurrencySymbol          = account.Currency.Symbol;
                expenseSummary.CategoryId              = category.Id;
                expenseSummary.CategoryName            = category.Name;
                expenseSummary.CategoryColor           = category.GraphColor;
                expenseSummary.CostCurrentMonth        = expensesCurrentMonth.Sum(x => x.Cost);
                expenseSummary.CostPreviousMonth       = expensesPreviousMonth.Sum(x => x.Cost);
                expenseSummary.CostPlannedMonthly      = budgetPlanByCategory.ContainsKey(exp.Key) ? budgetPlanByCategory[exp.Key] : 0;
                expenseSummary.CostOver12Month         = expensesOver12Months.Sum(x => x.Cost);
                expenseSummary.AverageCostOver12Months = expenseSummary.CostOver12Month / nbMonthInterval;

                // Retrieve the expenses per months (details and summary)
                foreach (var month in over12MonthsNames)
                {
                    var interval   = month.Value;
                    var expByMonth = exp.Value.Where(x => interval.IsBetween(x.DateExpense)).ToList();

                    expenseSummary.Expenses.Add(month.Key, expByMonth);
                    expenseSummary.ExpensesByMonth.Add(month.Key, new ExpenseSummaryByCategoryAndByMonth(expByMonth.Sum(x => x.Cost)));
                }
                expenseSummary.Expenses.Add(currentMonthName, expensesCurrentMonth);
                expenseSummary.ExpensesByMonth.Add(currentMonthName, new ExpenseSummaryByCategoryAndByMonth(expensesCurrentMonth.Sum(x => x.Cost)));

                expensesByCategory.Add(expenseSummary);
            }

            var totalExpensesOver12Months = expenses.Where(x => over12MonthsInterval.IsBetween(x.DateExpense)).Sum(x => x.Cost);

            // Get actual/expected expenses by month for last 12 Months
            var budgetPlanExpenses           = budgetPlanByCategory.Values.Sum(x => x);
            var detailedExpensesOver12Months = new Dictionary <string, ExpenseSummaryByMonth>();

            foreach (var month in over12MonthsNames)
            {
                var interval   = month.Value;
                var expByMonth = expenses.Where(x => interval.IsBetween(x.DateExpense)).Sum(x => x.Cost);
                detailedExpensesOver12Months.Add(month.Key, new ExpenseSummaryByMonth()
                {
                    ExpenseValue         = expByMonth,
                    ExpenseExpectedValue = budgetPlanExpenses
                });
            }
            detailedExpensesOver12Months.Add(currentMonthName, new ExpenseSummaryByMonth()
            {
                ExpenseValue         = expenses.Where(x => currentMonthInterval.IsBetween(x.DateExpense)).Sum(x => x.Cost),
                ExpenseExpectedValue = budgetPlanExpenses
            });

            var incomes = _incomeRepository.GetList2().Where(x => x.AccountId == accountId && x.DateIncome >= over12MonthsInterval.StartDate && x.DateIncome < currentMonthInterval.EndDate).ToList();
            var savings = _savingRepository.GetList2().Where(x => x.AccountId == accountId && x.DateSaving >= over12MonthsInterval.StartDate && x.DateSaving < currentMonthInterval.EndDate).ToList();

            // Get the incomes/expenses/savings by month for last 6 months
            var over6MonthsNames             = over6MonthsInterval.GetIntervalsByMonth();
            var detailedMovementsOver6Months = new Dictionary <string, ExpenseSummaryByMonth>();

            foreach (var month in over6MonthsNames)
            {
                var interval      = month.Value;
                var incomeByMonth = incomes.Where(x => interval.IsBetween(x.DateIncome)).Sum(x => x.Cost);
                var savingByMonth = savings.Where(x => interval.IsBetween(x.DateSaving)).Sum(x => x.Amount);
                detailedMovementsOver6Months.Add(month.Key, new ExpenseSummaryByMonth()
                {
                    ExpenseValue = detailedExpensesOver12Months[month.Key].ExpenseValue,
                    IncomeValue  = incomeByMonth,
                    SavingValue  = savingByMonth
                });
            }
            detailedMovementsOver6Months.Add(currentMonthName, new ExpenseSummaryByMonth()
            {
                ExpenseValue = detailedExpensesOver12Months[currentMonthName].ExpenseValue,
                IncomeValue  = incomes.Where(x => currentMonthInterval.IsBetween(x.DateIncome)).Sum(x => x.Cost),
                SavingValue  = savings.Where(x => currentMonthInterval.IsBetween(x.DateSaving)).Sum(x => x.Amount)
            });

            var ExpenseSummary = new ExpenseSummary()
            {
                Account                      = Mapper.Map <AccountDetails>(account),
                ExpensesByCategory           = expensesByCategory.OrderByDescending(x => x.CostCurrentMonth).ToList(),
                LabelCurrentMonth            = DateTimeFormatHelper.GetMonthNameAndYear(today),
                LabelPreviousMonth           = DateTimeFormatHelper.GetMonthNameAndYear(today.AddMonths(-1)),
                BudgetPlanName               = budgetPlan != null ? budgetPlan.Name : string.Empty,
                AccountName                  = account.Name,
                DisplayDashboard             = true,
                CurrencySymbol               = account.Currency.Symbol,
                HasCurrentBudgetPlan         = budgetPlan != null,
                HasExpenses                  = expenses.Any(),
                HasCategories                = categories.Any(),
                TotalExpensesOver12Months    = totalExpensesOver12Months,
                DetailedExpensesOver12Months = detailedExpensesOver12Months,
                DetailedMovementsOver6Months = detailedMovementsOver6Months,
                CurrentMonthTotalExpense     = expenses.Where(x => currentMonthInterval.IsBetween(x.DateExpense)).Sum(x => x.Cost),
                AverageExpenses              = expenses.Where(x => over12MonthsInterval.IsBetween(x.DateExpense)).Sum(x => x.Cost) / nbMonthInterval,
                AverageIncomes               = incomes.Where(x => over12MonthsInterval.IsBetween(x.DateIncome)).Sum(x => x.Cost) / nbMonthInterval,
                AverageSavings               = savings.Where(x => over12MonthsInterval.IsBetween(x.DateSaving)).Sum(x => x.Amount) / nbMonthInterval
            };

            return(ExpenseSummary);
        }
Example #2
0
        public BudgetPlanDetails BuildBudgetPlan(int accountId, int?budgetPlanId = null)
        {
            var currencySymbol = _bankAccountRepository.GetById(accountId, x => x.Currency).Currency.Symbol;

            var today = DateTime.Now;
            var over12MonthsInterval = new Interval(today, DateTimeUnitEnums.Years, 1);
            var previousInterval     = new Interval(today, DateTimeUnitEnums.Months, 1);
            var firstOfNextMonth     = DateTimeFormatHelper.GetFirstDayOfMonth(today.AddMonths(1));

            // Retrieve the categories
            var categories = _ExpenseTypeRepository.GetList2().GroupBy(x => x.Id).ToDictionary(x => x.Key, y => y.Single());

            // Retrieve the expenses over the last 12 months (excluding current month)
            var expensesOver12Months = _ExpenseRepository.GetByParameters(new ExpenseGetListSearchParameters()
            {
                AccountId = accountId,
                StartDate = over12MonthsInterval.StartDate,
                EndDate   = over12MonthsInterval.EndDate
            });

            // Group by category the expenses over the last 12 months
            var expensesOver12MonthsByCategory = expensesOver12Months.GroupBy(x => x.ExpenseTypeId).ToDictionary(x => x.Key, y => y.ToList());

            over12MonthsInterval.StartDate = DateTimeFormatHelper.GetFirstDayOfMonth(
                expensesOver12Months.Any() ?
                expensesOver12Months.OrderBy(x => x.DateExpense).First().DateExpense :
                today);

            var nbMonthInterval = over12MonthsInterval.Count(DateTimeUnitEnums.Months);

            if (nbMonthInterval == 0)
            {
                nbMonthInterval = 1; // No expenses -> no division by zero
            }
            // Retrieve the expenses last months and group by category
            var lastMonthExpenses           = expensesOver12Months.Where(x => previousInterval.IsBetween(x.DateExpense)).ToList();
            var lastMonthExpensesByCategory = lastMonthExpenses.GroupBy(x => x.ExpenseTypeId).ToDictionary(x => x.Key, y => y.ToList());

            // Get the current Budget Plan for the account. If none, returns a default of cost of 0.00
            var currentBudgetPlan           = GetCurrent(accountId);
            var currentBudgetPlanByCategory = currentBudgetPlan?.ExpenseTypes
                                              .GroupBy(x => x.ExpenseType.Id)
                                              .ToDictionary(x => x.Key, y => y.Single().ExpectedValue);

            // Get the existing Budget Plan for the provided ID. If none, returns a default of cost of 0.00
            var existingBudgetPlan           = budgetPlanId.HasValue ? GetById(budgetPlanId.Value) : null;
            var existingBudgetPlanByCategory = existingBudgetPlan?.ExpenseTypes.GroupBy(x => x.ExpenseType.Id).ToDictionary(x => x.Key, y => y.Single().ExpectedValue);

            BudgetPlanDetails budgetPlan = null;

            if (existingBudgetPlan != null)
            {
                budgetPlan = new BudgetPlanDetails()
                {
                    Id                   = existingBudgetPlan.Id,
                    Name                 = existingBudgetPlan.Name,
                    ExpenseTypes         = new List <BudgetPlanExpenseType>(),
                    CurrencySymbol       = currencySymbol,
                    StartDate            = existingBudgetPlan.StartDate,
                    EndDate              = existingBudgetPlan.EndDate,
                    PlannedStartDate     = firstOfNextMonth,
                    HasCurrentBudgetPlan = currentBudgetPlan != null,
                    BudgetPlanName       = currentBudgetPlan?.Name
                };
            }
            else
            {
                budgetPlan = new BudgetPlanDetails()
                {
                    ExpenseTypes         = new List <BudgetPlanExpenseType>(),
                    CurrencySymbol       = currencySymbol,
                    HasCurrentBudgetPlan = currentBudgetPlan != null,
                    BudgetPlanName       = currentBudgetPlan?.Name
                };
            }

            foreach (var category in categories)
            {
                var expectedValue = 0.00M; var currentBudgetPlanValue = 0.00M;

                if (currentBudgetPlan != null)
                {
                    currentBudgetPlanValue = currentBudgetPlanByCategory.ContainsKey(category.Key)
                                                ? currentBudgetPlanByCategory[category.Key]
                                                : 0.00M;
                }

                if (existingBudgetPlan != null)
                {
                    expectedValue = existingBudgetPlanByCategory.ContainsKey(category.Key)
                                        ? existingBudgetPlanByCategory[category.Key]
                                        : 0.00M;
                }
                else if (currentBudgetPlan != null)
                {
                    expectedValue = currentBudgetPlanValue;
                }

                var previousMonthValue = lastMonthExpensesByCategory.ContainsKey(category.Key)
                                            ? lastMonthExpensesByCategory[category.Key].Sum(x => x.Cost)
                                            : 0.00M;

                var averageMonthValue = expensesOver12MonthsByCategory.ContainsKey(category.Key)
                                            ? expensesOver12MonthsByCategory[category.Key].Sum(x => x.Cost)
                                            : 0.00M;

                var mappedCategory = Mapper.Map <ExpenseTypeList>(category.Value);

                var budgetPlanByCategory = new BudgetPlanExpenseType
                {
                    CurrencySymbol         = budgetPlan.CurrencySymbol,
                    ExpenseType            = mappedCategory,
                    ExpectedValue          = expectedValue,
                    PreviousMonthValue     = previousMonthValue,
                    CurrentBudgetPlanValue = currentBudgetPlanValue,
                    AverageMonthValue      = averageMonthValue / nbMonthInterval
                };

                budgetPlan.ExpenseTypes.Add(budgetPlanByCategory);
            }

            budgetPlan.ExpensePreviousMonthValue     = lastMonthExpenses.Sum(x => x.Cost);
            budgetPlan.ExpenseAverageMonthValue      = expensesOver12Months.Sum(x => x.Cost) / nbMonthInterval;
            budgetPlan.ExpenseCurrentBudgetPlanValue = currentBudgetPlan?.ExpenseTypes.Sum(x => x.ExpectedValue);

            var incomes = _incomeRepository.GetList2().Where(x => x.AccountId == accountId).ToList();

            budgetPlan.IncomeCurrentBudgetPlanValue = currentBudgetPlan?.ExpectedIncomes;
            budgetPlan.IncomePreviousMonthValue     = incomes.Where(x => previousInterval.IsBetween(x.DateIncome)).Sum(x => x.Cost);
            budgetPlan.IncomeAverageMonthValue      = incomes.Where(x => over12MonthsInterval.IsBetween(x.DateIncome)).Sum(x => x.Cost) / nbMonthInterval;

            budgetPlan.ExpectedIncomes = existingBudgetPlan?.ExpectedIncomes ?? budgetPlan.IncomePreviousMonthValue;

            var savings = _savingRepository.GetList2().Where(x => x.AccountId == accountId).ToList();

            budgetPlan.SavingCurrentBudgetPlanValue = currentBudgetPlan?.ExpectedSavings;
            budgetPlan.SavingPreviousMonthValue     = savings.Where(x => previousInterval.IsBetween(x.DateSaving)).Sum(x => x.Amount);
            budgetPlan.SavingAverageMonthValue      = savings.Where(x => over12MonthsInterval.IsBetween(x.DateSaving)).Sum(x => x.Amount) / nbMonthInterval;

            budgetPlan.ExpectedSavings = existingBudgetPlan?.ExpectedSavings ?? budgetPlan.SavingPreviousMonthValue;

            return(budgetPlan);
        }