Example #1
0
        /// <summary>
        ///     Locates the most recent <see cref="LedgerEntryLine" /> for the given date filter. Note that this will only return
        ///     the most recent line that fits the criteria.
        /// </summary>
        public virtual decimal LocateApplicableLedgerBalance([NotNull] LedgerBook ledgerBook,
                                                             [NotNull] GlobalFilterCriteria filter, string bucketCode)
        {
            CheckCacheForCleanUp();
            if (ledgerBook == null)
            {
                throw new ArgumentNullException(nameof(ledgerBook));
            }

            if (filter == null)
            {
                throw new ArgumentNullException(nameof(filter));
            }

            var line = LocateApplicableLedgerLine(ledgerBook, filter);

            if (line == null)
            {
                return(0);
            }

            return(line.Entries
                   .Where(ledgerEntry => ledgerEntry.LedgerBucket.BudgetBucket.Code == bucketCode)
                   .Select(ledgerEntry => ledgerEntry.Balance)
                   .FirstOrDefault());
        }
Example #2
0
        public void Load(
            [NotNull] StatementModel statementModel,
            [NotNull] BudgetModel budgetModel,
            [NotNull] GlobalFilterCriteria criteria,
            Engine.Ledger.LedgerBook ledgerBookModel)
        {
            if (statementModel == null)
            {
                throw new ArgumentNullException(nameof(statementModel));
            }

            if (budgetModel == null)
            {
                throw new ArgumentNullException(nameof(budgetModel));
            }

            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            this.statement  = statementModel;
            this.budget     = budgetModel;
            this.ledgerBook = ledgerBookModel;

            BurnDownCharts results = this.chartsService.BuildAllCharts(statementModel, this.budget, this.ledgerBook, criteria);

            this.beginDate       = results.BeginDate;
            DateRangeDescription = results.DateRangeDescription;
            ChartControllers     = new BindingList <BucketBurnDownController>(results.Charts.Select(BuildBucketBurnDownController).ToList());

            RaisePropertyChanged(() => ChartControllers);
        }
        private static void AnalysisPreconditions(GlobalFilterCriteria criteria, StatementModel statement,
                                                  BudgetCollection budgets, out DateTime beginDate, out DateTime endDate)
        {
            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            if (!criteria.Cleared && (criteria.BeginDate == null || criteria.EndDate == null))
            {
                throw new ArgumentException("The given criteria does not contain any filtering dates.");
            }

            if (statement == null)
            {
                throw new ArgumentNullException(nameof(statement),
                                                "The statement supplied is null, analysis cannot proceed with no statement.");
            }

            if (budgets == null)
            {
                throw new ArgumentNullException(nameof(budgets));
            }

            if (criteria.Cleared)
            {
                beginDate = statement.AllTransactions.First().Date;
                endDate   = statement.AllTransactions.Last().Date;
            }
            else
            {
                beginDate = criteria.BeginDate ?? DateTime.MinValue;
                endDate   = criteria.EndDate ?? DateTime.MinValue;
            }
        }
        private int SearchForOtherNonLedgerBookOverspentBuckets(
            StatementModel statement,
            GlobalFilterCriteria filter,
            IBudgetCurrencyContext budget,
            IDictionary <BudgetBucket, decimal> overspendingSummary)
        {
            var warnings = 0;
            List <Transaction> transactions = statement.Transactions.Where(t => t.Date < filter.BeginDate?.Date.AddMonths(1)).ToList();

            foreach (var expense in budget.Model.Expenses.Where(e => e.Bucket is BillToPayExpenseBucket))
            {
                if (overspendingSummary.ContainsKey(expense.Bucket))
                {
                    continue;
                }

                var bucketBalance = expense.Amount +
                                    transactions.Where(t => t.BudgetBucket == expense.Bucket).Sum(t => t.Amount);
                overspendingSummary.Add(expense.Bucket, bucketBalance);
                if (bucketBalance < -Tolerance)
                {
                    warnings++;
                }
            }

            return(warnings);
        }
        public void ValidateShouldReturnTrueGivenCleared()
        {
            var subject = new GlobalFilterCriteria();

            Assert.IsTrue(subject.Cleared);
            Assert.IsTrue(subject.Validate(this.validationMessages));
        }
 public BurnDownCharts BuildAllCharts(
     StatementModel statementModel,
     BudgetModel budgetModel,
     LedgerBook ledgerBookModel,
     GlobalFilterCriteria criteria)
 {
     if (criteria == null)
     {
         throw new ArgumentNullException(nameof(criteria));
     }
     if (criteria.Cleared)
     {
         throw new ArgumentException("There is no date range criteria set. This graph is intended for one month of data.");
     }
     if (criteria.EndDate == null || criteria.BeginDate == null)
     {
         throw new ArgumentException("There is no date range set; either the begin or end date is not set. This graph is intended for one month of data.");
     }
     if (criteria.EndDate.Value.Subtract(criteria.EndDate.Value).Days > 31)
     {
         throw new ArgumentException("The date range is too great for this graph. This graph is intended for one month of data.");
     }
     this.chartsBuilder.Build(criteria, statementModel, budgetModel, ledgerBookModel);
     return(this.chartsBuilder.Results);
 }
Example #7
0
        public void Load(StatementModel statementModel, BudgetCollection budgets, GlobalFilterCriteria criteria)
        {
            Analysis           = this.chartService.BuildChart(statementModel, budgets, criteria);
            OverallPerformance = (double)Analysis.OverallPerformance;
            ExpenseFilter      = true;
            IncomeFilter       = false;

            RaisePropertyChanged(() => Analysis);
            ICollectionView view = CollectionViewSource.GetDefaultView(Analysis.Analyses);

            view.Filter = x =>
            {
                var bucketAnalysis = x as BucketPerformanceResult;
                if (bucketAnalysis == null)
                {
                    return(true);
                }

                if (IncomeFilter)
                {
                    return(bucketAnalysis.Bucket is IncomeBudgetBucket);
                }

                bool result = !(bucketAnalysis.Bucket is IncomeBudgetBucket);
                return(result);
            };
        }
Example #8
0
        internal virtual void Filter(GlobalFilterCriteria criteria)
        {
            ThrowIfDisposed();
            if (criteria == null)
            {
                this.changeHash  = Guid.NewGuid();
                Transactions     = AllTransactions.ToList();
                DurationInMonths = this.fullDuration;
                Filtered         = false;
                return;
            }

            if (criteria.BeginDate > criteria.EndDate)
            {
                throw new ArgumentException("End date must be after the begin date.");
            }

            this.currentFilter = criteria;

            this.changeHash = Guid.NewGuid();
            if (criteria.Cleared)
            {
                Transactions     = AllTransactions.ToList();
                DurationInMonths = this.fullDuration;
                Filtered         = false;
                return;
            }

            IEnumerable <Transaction> query = BaseFilterQuery(criteria);

            Transactions     = query.ToList();
            DurationInMonths = CalculateDuration(criteria, Transactions);
            this.duplicates  = null;
            Filtered         = true;
        }
        public void ValidateShouldThrowGivenNullValidationMessages()
        {
            var subject = new GlobalFilterCriteria {
                BeginDate = new DateTime(), EndDate = DateTime.Now
            };

            Assert.IsFalse(subject.Validate(null));
        }
        public void ValidateShouldReturnFalseGivenEndDateIsNull()
        {
            var subject = new GlobalFilterCriteria {
                BeginDate = DateTime.Today.AddDays(-1), EndDate = DateTime.Now
            };

            subject.EndDate = null;
            Assert.IsFalse(subject.Validate(this.validationMessages));
        }
Example #11
0
        public void UsingTestData1_LocateApplicableLedgerBalance_ShouldReturn64()
        {
            var filter = new GlobalFilterCriteria {
                BeginDate = new DateTime(2013, 04, 15), EndDate = new DateTime(2013, 05, 15)
            };
            decimal result = Subject.LocateApplicableLedgerBalance(TestData, filter, StatementModelTestData.PhoneBucket.Code);

            Assert.AreEqual(0M, result);
        }
 private void DateFilterApplied(GlobalFilterCriteria criteria)
 {
     ColourStyleName = this.standardStyleName;
     DetailedText    = string.Format(
         CultureInfo.CurrentCulture,
         "Filtered from {0} to {1}",
         criteria.BeginDate?.Date.ToString("d"),
         criteria.EndDate?.Date.ToString("d"));
 }
        public void ValidateShouldReturnTrueGivenDatesButNoAccount()
        {
            var subject = new GlobalFilterCriteria {
                BeginDate = DateTime.Today.AddDays(-1), EndDate = DateTime.Now
            };

            Assert.IsFalse(subject.Cleared);
            Assert.IsTrue(subject.Validate(this.validationMessages));
        }
 private void DateFilterApplied(GlobalFilterCriteria criteria)
 {
     ColourStyleName = this.standardStyleName;
     DetailedText = string.Format(
         CultureInfo.CurrentCulture,
         "Filtered from {0} to {1}",
         criteria.BeginDate.Value.ToShortDateString(),
         criteria.EndDate.Value.ToShortDateString());
 }
        private static DateTime CalculateEndDate(StatementModel statement, GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared || criteria.EndDate == null)
            {
                DateTime maxDate = statement.AllTransactions.Max(t => t.Date).Date;
                return maxDate.LastDateInMonth();
            }

            return criteria.EndDate.Value;
        }
        /// <summary>
        ///     Filters the transactions using the filter object provided.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        public void FilterTransactions(GlobalFilterCriteria criteria)
        {
            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            this.monitorableDependencies.NotifyOfDependencyChange(criteria);
            StatementModel.Filter(criteria);
        }
Example #17
0
        private static DateTime CalculateEndDate(StatementModel statement, GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared || criteria.EndDate == null)
            {
                var maxDate = statement.AllTransactions.Max(t => t.Date).Date;
                return(maxDate.LastDateInMonth());
            }

            return(criteria.EndDate.Value);
        }
        public void MinDateShouldGetTransalatedToNull()
        {
            var subject = new GlobalFilterCriteria
            {
                BeginDate = DateTime.MinValue,
                EndDate   = DateTime.MinValue
            };

            Assert.IsNull(subject.BeginDate);
            Assert.IsNull(subject.EndDate);
        }
Example #19
0
        private static DateTime CalculateStartDate(StatementModel statement, GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared || criteria.BeginDate == null)
            {
                var minDate = statement.AllTransactions.Min(t => t.Date).Date;
                minDate = minDate.FirstDateInMonth();
                return(minDate);
            }

            return(criteria.BeginDate.Value);
        }
        public void TwoInstancesWithSameValuesShouldHaveSameEqualityHash()
        {
            GlobalFilterCriteria subject1 = CreateSubject_StandardPayMonth();
            GlobalFilterCriteria subject2 = CreateSubject_StandardPayMonth();

            Assert.AreEqual(subject1.SignificantDataChangeHash(), subject2.SignificantDataChangeHash());
            Assert.IsFalse(subject1.SignificantDataChangeHash() != subject2.SignificantDataChangeHash());
            Assert.IsTrue(subject1.SignificantDataChangeHash() == subject2.SignificantDataChangeHash());
            Assert.IsTrue(subject1.SignificantDataChangeHash().Equals(subject2.SignificantDataChangeHash()));
            Assert.AreNotSame(subject1, subject2);
            Assert.AreNotEqual(subject1.GetHashCode(), subject2.GetHashCode());
        }
        public void TwoInstancesWithDifferentValuesAreNotEqual()
        {
            GlobalFilterCriteria subject1 = CreateSubject_StandardPayMonth();
            GlobalFilterCriteria subject2 = CreateSubject_StandardPayMonthAndVisaFilter();

            Assert.AreNotEqual(subject1, subject2);
            Assert.IsTrue(subject1 != subject2);
            Assert.IsFalse(subject1 == subject2);
            Assert.IsFalse(subject1.Equals(subject2));
            Assert.AreNotSame(subject1, subject2);
            Assert.AreNotEqual(subject1.GetHashCode(), subject2.GetHashCode());
        }
        public void EndDateShouldAutoAdjustGivenDateAfterStartDate()
        {
            var invalidEndDate = new DateTime(2014, 3, 1);
            var subject        = new GlobalFilterCriteria
            {
                BeginDate = new DateTime(2014, 5, 1),
                EndDate   = invalidEndDate
            };

            Assert.AreNotEqual(invalidEndDate, subject.EndDate);
            Assert.IsTrue(subject.BeginDate < subject.EndDate);
        }
        public void CheckReferenceInequality()
        {
            GlobalFilterCriteria subject1 = CreateSubject_StandardPayMonth();
            GlobalFilterCriteria subject2 = CreateSubject_NotFiltered();

            Assert.AreNotEqual(subject1, subject2);
            Assert.IsTrue(subject1 != subject2);
            Assert.IsFalse(subject1 == subject2);
            Assert.IsFalse(subject1.Equals(subject2));
            Assert.AreNotSame(subject1, subject2);
            Assert.AreNotEqual(subject1.GetHashCode(), subject2.GetHashCode());
        }
        /// <summary>
        ///     Updates the widget with new input.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        public override void Update([NotNull] params object[] input)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }

            if (!ValidateUpdateInput(input))
            {
                Enabled = false;
                return;
            }

            this.statement        = (StatementModel)input[0];
            this.filter           = (GlobalFilterCriteria)input[1];
            this.ledgerBook       = (LedgerBook)input[2];
            this.ledgerCalculator = (LedgerCalculation)input[3];

            if (this.ledgerBook == null || this.statement == null || this.filter == null || this.filter.Cleared ||
                this.filter.BeginDate == null || this.filter.EndDate == null)
            {
                Enabled = false;
                return;
            }

            if (this.filter.BeginDate.Value.DurationInMonths(this.filter.EndDate.Value) != 1)
            {
                ToolTip = DesignedForOneMonthOnly;
                Enabled = false;
                return;
            }

            Enabled = true;
            var openingBalance   = CalculateOpeningBalance();
            var remainingBalance = this.ledgerCalculator.CalculateCurrentMonthSurplusBalance(this.ledgerBook,
                                                                                             this.filter, this.statement);

            Maximum = Convert.ToDouble(openingBalance);
            Value   = Convert.ToDouble(remainingBalance);
            Minimum = 0;
            if (remainingBalance < 0.2M * openingBalance)
            {
                ColourStyleName = WidgetWarningStyle;
            }
            else
            {
                ColourStyleName = this.standardStyle;
            }

            ToolTip = string.Format(CultureInfo.CurrentCulture, "Remaining Surplus for period is {0:C} of {1:C}",
                                    remainingBalance, openingBalance);
        }
Example #25
0
        public void FilterTransactions_ShouldCallStatementModel_GivenFilterObject()
        {
            this.testData = new StatementModelTestHarness();
            this.testData.LoadTransactions(new List <Transaction>());
            var criteria = new GlobalFilterCriteria {
                BeginDate = new DateTime(2014, 07, 01), EndDate = new DateTime(2014, 08, 01)
            };

            Arrange();

            this.subject.FilterTransactions(criteria);

            Assert.AreEqual(1, ((StatementModelTestHarness)this.testData).FilterByCriteriaWasCalled);
        }
 public void Load(StatementModel statementModel, GlobalFilterCriteria criteria)
 {
     try
     {
         Graph             = this.chartService.BuildChart(statementModel, criteria);
         GraphMaximumValue = Graph.Series.Max(s => s.Plots.Max(p => p.Amount));
         GraphMinimumValue = Graph.Series.Min(s => s.MinimumValue);
     }
     catch (ArgumentException ex)
     {
         Graph = null;
         this.messageService.Show($"Unable to compile data for this report.\n{ex.Message}", "Data Validation");
     }
 }
        public override void Update([NotNull] params object[] input)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            if (!ValidateUpdateInput(input))
            {
                Enabled = false;
                return;
            }

            this.statement = (StatementModel)input[0];
            this.filter = (GlobalFilterCriteria)input[1];
            this.ledgerBook = (LedgerBook)input[2];

            if (this.ledgerBook == null || this.statement == null || this.filter == null || this.filter.Cleared || this.filter.BeginDate == null || this.filter.EndDate == null)
            {
                Enabled = false;
                return;
            }

            if (this.filter.BeginDate.Value.DurationInMonths(this.filter.EndDate.Value) != 1)
            {
                ToolTip = DesignedForOneMonthOnly;
                Enabled = false;
                return;
            }

            Enabled = true;
            decimal openingBalance = CalculateOpeningBalance(this.filter, this.ledgerBook);
            decimal remainingBalance = LedgerCalculation.CalculateCurrentMonthSurplusBalance(this.ledgerBook, this.filter, this.statement);

            Maximum = Convert.ToDouble(openingBalance);
            Value = Convert.ToDouble(remainingBalance);
            Minimum = 0;
            if (remainingBalance < 0.2M * openingBalance)
            {
                ColourStyleName = WidgetWarningStyle;
            }
            else
            {
                ColourStyleName = this.standardStyle;
            }

            ToolTip = string.Format(CultureInfo.CurrentCulture, "Remaining Surplus for period is {0:C} of {1:C}", remainingBalance, openingBalance);
        }
        private void OnApplicationStateLoaded(ApplicationStateLoadedMessage message)
        {
            var filterState = message.ElementOfType <PersistentFiltersApplicationState>();

            if (filterState == null)
            {
                return;
            }

            Criteria = new GlobalFilterCriteria
            {
                BeginDate = filterState.BeginDate,
                EndDate   = filterState.EndDate
            };

            SendFilterAppliedMessage();
        }
        public void Build(
            GlobalFilterCriteria criteria,
            StatementModel statementModel,
            BudgetModel budgetModel,
            LedgerBook ledgerBookModel)
        {
            var beginDate            = CalculateBeginDate(criteria);
            var dateRangeDescription = string.Format(CultureInfo.CurrentCulture,
                                                     "For the month starting {0:D} to {1:D} inclusive.", beginDate, beginDate.AddMonths(1).AddDays(-1));

            var listOfCharts = new List <BurnDownChartAnalyserResult>(this.budgetBucketRepository.Buckets.Count());

            foreach (var bucket in this.budgetBucketRepository.Buckets
                     .Where(b => b is ExpenseBucket && b.Active)
                     .OrderBy(b => b.Code))
            {
                var analysis = AnalyseDataForChart(statementModel, budgetModel, ledgerBookModel, bucket, beginDate);
                analysis.ChartTitle = string.Format(CultureInfo.CurrentCulture, "{0} Spending Chart", bucket.Code);
                listOfCharts.Add(analysis);
            }

            listOfCharts = listOfCharts.ToList();

            // Put surplus at the top.
            var analysisResult = AnalyseDataForChart(statementModel, budgetModel, ledgerBookModel,
                                                     this.budgetBucketRepository.SurplusBucket, beginDate);

            analysisResult.ChartTitle = string.Format(CultureInfo.CurrentCulture, "{0} Spending Chart",
                                                      this.budgetBucketRepository.SurplusBucket);
            listOfCharts.Insert(0, analysisResult);

            // Put any custom charts on top.
            foreach (var customChart in CustomCharts)
            {
                IEnumerable <BudgetBucket> buckets = this.budgetBucketRepository.Buckets
                                                     .Join(customChart.BucketIds, bucket => bucket.Code, code => code, (bucket, code) => bucket);

                var analysis = AnalyseDataForChart(statementModel, budgetModel, ledgerBookModel, buckets, beginDate);
                analysis.ChartTitle             = customChart.Name;
                analysis.IsCustomAggregateChart = true;
                listOfCharts.Insert(0, analysis);
            }

            Results = new BurnDownCharts(beginDate, dateRangeDescription, listOfCharts);
        }
Example #30
0
        /// <summary>
        ///     Analyses the specified statement.
        /// </summary>
        /// <param name="statement">The statement.</param>
        /// <param name="criteria">The criteria.</param>
        /// <exception cref="System.ArgumentNullException">
        /// </exception>
        public void Analyse([NotNull] StatementModel statement, [NotNull] GlobalFilterCriteria criteria)
        {
            if (statement == null)
            {
                throw new ArgumentNullException(nameof(statement));
            }

            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            Reset();
            var minDate = CalculateStartDate(statement, criteria);
            var maxDate = CalculateEndDate(statement, criteria);

            var currentMonth = minDate;
            var nextMonth    = currentMonth.AddMonths(1);
            var subTotals    = new Dictionary <string, decimal>();
            List <SeriesData> allSeriesData = InitialiseSeriesData(minDate, maxDate);

            foreach (var transaction in statement.AllTransactions)
            {
                if (transaction.Date >= nextMonth)
                {
                    StoreSummarisedMonthData(subTotals, allSeriesData, currentMonth);

                    currentMonth = nextMonth;
                    nextMonth    = nextMonth.AddMonths(1);
                    subTotals    = new Dictionary <string, decimal>();
                    if (currentMonth >= maxDate)
                    {
                        break;
                    }
                }

                GetOrAdd(subTotals, transaction.BudgetBucket.Code, transaction.Amount);
            }

            StoreSummarisedMonthData(subTotals, allSeriesData, currentMonth);
            Graph = new GraphData {
                SeriesList = allSeriesData, GraphName = "Long term spending for Budget Buckets"
            };
            RemoveSeriesWithNoData();
        }
Example #31
0
        /// <summary>
        ///     Calculates the current month surplus balance.
        /// </summary>
        /// <exception cref="System.ArgumentNullException">
        /// </exception>
        public virtual decimal CalculateCurrentMonthSurplusBalance([NotNull] LedgerBook ledgerBook,
                                                                   [NotNull] GlobalFilterCriteria filter, [NotNull] StatementModel statement)
        {
            CheckCacheForCleanUp();
            if (ledgerBook == null)
            {
                throw new ArgumentNullException(nameof(ledgerBook));
            }

            if (filter == null)
            {
                throw new ArgumentNullException(nameof(filter));
            }

            if (statement == null)
            {
                throw new ArgumentNullException(nameof(statement));
            }

            if (filter.Cleared || filter.BeginDate == null)
            {
                return(0);
            }

            var entryLine = LocateApplicableLedgerLine(ledgerBook, filter);

            if (entryLine == null)
            {
                return(0);
            }

            var beginningOfMonthBalance = entryLine.CalculatedSurplus;
            var transactionTotal        = CalculateTransactionTotal(filter.BeginDate.Value, statement, entryLine,
                                                                    SurplusBucket.SurplusCode);

            beginningOfMonthBalance += transactionTotal;

            // Find any ledgers that are overpsent and subtract them from the Surplus total.  This is actually what is happening when you overspend a ledger, it spills over and spend Surplus.
            Dictionary <BudgetBucket, decimal> ledgersSummary = CalculateLedgersBalanceSummary(ledgerBook, filter.BeginDate.Value, statement);

            beginningOfMonthBalance += ledgersSummary.Where(kvp => kvp.Value < 0).Sum(kvp => kvp.Value);

            return(beginningOfMonthBalance);
        }
        public GlobalFilterController([NotNull] UiContext uiContext)
        {
            if (uiContext == null)
            {
                throw new ArgumentNullException(nameof(uiContext));
            }

            this.userMessageBox   = uiContext.UserPrompts.MessageBox;
            this.doNotUseCriteria = new GlobalFilterCriteria();

            MessengerInstance = uiContext.Messenger;
            MessengerInstance.Register <ApplicationStateLoadedMessage>(this, OnApplicationStateLoaded);
            MessengerInstance.Register <ApplicationStateLoadFinishedMessage>(this, OnApplicationStateLoadFinished);
            MessengerInstance.Register <ApplicationStateRequestedMessage>(this, OnApplicationStateRequested);
            MessengerInstance.Register <RequestFilterMessage>(this, OnGlobalFilterRequested);
            MessengerInstance.Register <WidgetActivatedMessage>(this, OnWidgetActivatedMessageReceived);
            MessengerInstance.Register <ShellDialogResponseMessage>(this, OnShellDialogResponseReceived);
            MessengerInstance.Register <RequestFilterChangeMessage>(this, OnGlobalFilterChangeRequested);
        }
        private static DateTime CalculateBeginDate(GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared)
            {
                return(DateTime.Today.AddMonths(-1));
            }

            if (criteria.BeginDate != null)
            {
                return(criteria.BeginDate.Value);
            }

            if (criteria.EndDate == null)
            {
                return(DateTime.Today.AddMonths(-1));
            }

            return(criteria.EndDate.Value.AddMonths(-1));
        }
 public BurnDownCharts BuildAllCharts(
     StatementModel statementModel,
     BudgetModel budgetModel,
     LedgerBook ledgerBookModel, 
     GlobalFilterCriteria criteria)
 {
     if (criteria == null) throw new ArgumentNullException(nameof(criteria));
     if (criteria.Cleared) throw new ArgumentException("There is no date range criteria set. This graph is intended for one month of data.");
     if (criteria.EndDate == null || criteria.BeginDate == null)
     {
         throw new ArgumentException("There is no date range set; either the begin or end date is not set. This graph is intended for one month of data.");
     }
     if (criteria.EndDate.Value.Subtract(criteria.EndDate.Value).Days > 31)
     {
         throw new ArgumentException("The date range is too great for this graph. This graph is intended for one month of data.");
     }
     this.chartsBuilder.Build(criteria, statementModel, budgetModel, ledgerBookModel);
     return this.chartsBuilder.Results;
 }
        public void Build(
            GlobalFilterCriteria criteria,
            StatementModel statementModel,
            BudgetModel budgetModel,
            LedgerBook ledgerBookModel)
        {
            var beginDate = CalculateBeginDate(criteria);
            var dateRangeDescription = string.Format(CultureInfo.CurrentCulture,
                "For the month starting {0:D} to {1:D} inclusive.", beginDate, beginDate.AddMonths(1).AddDays(-1));

            var listOfCharts = new List<BurnDownChartAnalyserResult>(this.budgetBucketRepository.Buckets.Count());
            foreach (var bucket in this.budgetBucketRepository.Buckets
                .Where(b => b is ExpenseBucket && b.Active)
                .OrderBy(b => b.Code))
            {
                var analysis = AnalyseDataForChart(statementModel, budgetModel, ledgerBookModel, bucket, beginDate);
                analysis.ChartTitle = string.Format(CultureInfo.CurrentCulture, "{0} Spending Chart", bucket.Code);
                listOfCharts.Add(analysis);
            }

            listOfCharts = listOfCharts.ToList();

            // Put surplus at the top.
            var analysisResult = AnalyseDataForChart(statementModel, budgetModel, ledgerBookModel,
                this.budgetBucketRepository.SurplusBucket, beginDate);
            analysisResult.ChartTitle = string.Format(CultureInfo.CurrentCulture, "{0} Spending Chart",
                this.budgetBucketRepository.SurplusBucket);
            listOfCharts.Insert(0, analysisResult);

            // Put any custom charts on top.
            foreach (var customChart in CustomCharts)
            {
                IEnumerable<BudgetBucket> buckets = this.budgetBucketRepository.Buckets
                    .Join(customChart.BucketIds, bucket => bucket.Code, code => code, (bucket, code) => bucket);

                var analysis = AnalyseDataForChart(statementModel, budgetModel, ledgerBookModel, buckets, beginDate);
                analysis.ChartTitle = customChart.Name;
                analysis.IsCustomAggregateChart = true;
                listOfCharts.Insert(0, analysis);
            }

            Results = new BurnDownCharts(beginDate, dateRangeDescription, listOfCharts);
        }
        public OverallPerformanceBudgetResult BuildChart(StatementModel statementModel, BudgetCollection budgets,
                                                         GlobalFilterCriteria criteria)
        {
            if (statementModel == null)
            {
                throw new ArgumentNullException(nameof(statementModel));
            }

            if (budgets == null)
            {
                throw new ArgumentNullException(nameof(budgets));
            }

            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            return this.analyser.Analyse(statementModel, budgets, criteria);
        }
        public void TestInitialise()
        {
            this.subject = new BudgetBucketMonitorWidget();
            this.subject.BucketCode = StatementModelTestData.PhoneBucket.Code;

            this.bucketRepo = new BucketBucketRepoAlwaysFind();
            this.criteriaTestData = new GlobalFilterCriteria
            {
                BeginDate = new DateTime(2015, 10, 20),
                EndDate = new DateTime(2015, 11, 19)
            };

            CreateStatementTestData();

            BudgetModel budgetModel = BudgetModelTestData.CreateTestData5();
            this.budgetTestData = new BudgetCurrencyContext(new BudgetCollection(budgetModel), budgetModel);

            CreateLedgerBookTestData();

            this.ledgerCalculation = new LedgerCalculation(new FakeLogger());
        }
        public GraphData BuildChart(StatementModel statementModel, GlobalFilterCriteria criteria)
        {
            if (statementModel == null)
            {
                throw new ArgumentNullException(nameof(statementModel));
            }

            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            if (statementModel.Transactions.Any(t => t.BudgetBucket == null))
            {
                throw new ArgumentException("There are uncategorised transactions, finish assigning a bucket to all transactions before this running this graph.");
            }

            this.analyser.Analyse(statementModel, criteria);
            var result = this.analyser.Graph;
            this.analyser.Reset();
            return result;
        }
Example #39
0
        /// <summary>
        ///     Calculates the duration in months from the beginning of the period to the end.
        /// </summary>
        /// <param name="criteria">
        ///     The criteria that is currently applied to the Statement. Pass in null to use first and last
        ///     statement dates.
        /// </param>
        /// <param name="transactions">The list of transactions to use to determine duration.</param>
        public static int CalculateDuration(GlobalFilterCriteria criteria, IEnumerable<Transaction> transactions)
        {
            List<Transaction> list = transactions.ToList();
            DateTime minDate = DateTime.MaxValue, maxDate = DateTime.MinValue;

            if (criteria != null && !criteria.Cleared)
            {
                if (criteria.BeginDate != null)
                {
                    minDate = criteria.BeginDate.Value;
                    Debug.Assert(criteria.EndDate != null);
                    maxDate = criteria.EndDate.Value;
                }
            }
            else
            {
                minDate = list.Min(t => t.Date);
                maxDate = list.Max(t => t.Date);
            }

            return minDate.DurationInMonths(maxDate);
        }
        public void TestInitialise()
        {
            this.subject = new RemainingActualSurplusWidget();
            this.criteriaTestData = new GlobalFilterCriteria
            {
                BeginDate = new DateTime(2015, 10, 20),
                EndDate = new DateTime(2015, 11, 19)
            };
            this.ledgerCalculation = new LedgerCalculation(new FakeLogger());

            StatementModelTestDataForThisTest.AccountTypeRepo = new InMemoryAccountTypeRepository();
            StatementModelTestDataForThisTest.BudgetBucketRepo = new BucketBucketRepoAlwaysFind();
            this.statementTestData = StatementModelTestDataForThisTest.TestDataGenerated();

            this.ledgerBookTestData = new LedgerBookBuilder
            {
                StorageKey = "RemainingActualSurplusWidgetTest.xml",
                Modified = new DateTime(2015, 11, 23),
                Name = "Smith Budget 2015"
            }
                .IncludeLedger(LedgerBookTestData.PhoneLedger, 130M)
                .IncludeLedger(LedgerBookTestData.CarMtcLedger, 90M)
                .IncludeLedger(LedgerBookTestData.PowerLedger)
                .AppendReconciliation(
                    new DateTime(2015, 10, 20),
                    new BankBalance(LedgerBookTestData.ChequeAccount, 4502.75M))
                .WithReconciliationEntries(
                    entryBuilder =>
                    {
                        entryBuilder.WithLedger(LedgerBookTestData.PhoneLedger).HasNoTransactions();
                        entryBuilder.WithLedger(LedgerBookTestData.CarMtcLedger).HasNoTransactions();
                        entryBuilder.WithLedger(LedgerBookTestData.PowerLedger)
                            .AppendTransactions(txnBuilder => { txnBuilder.WithCredit(3000M, "Oct Savings", new DateTime(2015, 10, 20), "automatchref12"); });
                    })
                .Build();
        }
        private static void AnalysisPreconditions(GlobalFilterCriteria criteria, StatementModel statement,
                                                  BudgetCollection budgets, out DateTime beginDate, out DateTime endDate)
        {
            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            if (!criteria.Cleared && (criteria.BeginDate == null || criteria.EndDate == null))
            {
                throw new ArgumentException("The given criteria does not contain any filtering dates.");
            }

            if (statement == null)
            {
                throw new ArgumentNullException(nameof(statement),
                    "The statement supplied is null, analysis cannot proceed with no statement.");
            }

            if (budgets == null)
            {
                throw new ArgumentNullException(nameof(budgets));
            }

            if (criteria.Cleared)
            {
                beginDate = statement.AllTransactions.First().Date;
                endDate = statement.AllTransactions.Last().Date;
            }
            else
            {
                beginDate = criteria.BeginDate ?? DateTime.MinValue;
                endDate = criteria.EndDate ?? DateTime.MinValue;
            }
        }
        private static DateTime CalculateBeginDate(GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared)
            {
                return DateTime.Today.AddMonths(-1);
            }

            if (criteria.BeginDate != null)
            {
                return criteria.BeginDate.Value;
            }

            if (criteria.EndDate == null)
            {
                return DateTime.Today.AddMonths(-1);
            }

            return criteria.EndDate.Value.AddMonths(-1);
        }
Example #43
0
        internal virtual void Filter(GlobalFilterCriteria criteria)
        {
            ThrowIfDisposed();
            if (criteria == null)
            {
                this.changeHash = Guid.NewGuid();
                Transactions = AllTransactions.ToList();
                DurationInMonths = this.fullDuration;
                Filtered = false;
                return;
            }

            if (criteria.BeginDate > criteria.EndDate)
            {
                throw new ArgumentException("End date must be after the begin date.");
            }

            this.currentFilter = criteria;

            this.changeHash = Guid.NewGuid();
            if (criteria.Cleared)
            {
                Transactions = AllTransactions.ToList();
                DurationInMonths = this.fullDuration;
                Filtered = false;
                return;
            }

            IEnumerable<Transaction> query = BaseFilterQuery(criteria);

            Transactions = query.ToList();
            DurationInMonths = CalculateDuration(criteria, Transactions);
            this.duplicates = null;
            Filtered = true;
        }
        public override void Update([NotNull] params object[] input)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            if (!ValidateUpdateInput(input))
            {
                ToolTip = this.disabledToolTip;
                Enabled = false;
                return;
            }

            this.budget = (BudgetCurrencyContext)input[0];
            this.statement = (StatementModel)input[1];
            this.filter = (GlobalFilterCriteria)input[2];
            this.bucketRepository = (IBudgetBucketRepository)input[3];
            this.ledgerBook = (LedgerBook)input[4];

            if (!this.bucketRepository.IsValidCode(BucketCode))
            {
                ToolTip = this.disabledToolTip;
                Enabled = false;
                return;
            }

            if (this.statement == null || this.budget == null || this.filter == null || this.filter.Cleared || this.filter.BeginDate == null || this.filter.EndDate == null)
            {
                ToolTip = this.disabledToolTip;
                Enabled = false;
                return;
            }

            if (this.filter.BeginDate.Value.DurationInMonths(this.filter.EndDate.Value) != 1)
            {
                Enabled = false;
                ToolTip = DesignedForOneMonthOnly;
                return;
            }

            Enabled = true;
            decimal totalBudget = MonthlyBudgetAmount();
            Maximum = Convert.ToDouble(totalBudget);

            // Debit transactions are negative so normally the total spend will be a negative number.
            decimal remainingBudget = totalBudget + this.statement.Transactions.Where(t => t.BudgetBucket != null && t.BudgetBucket.Code == BucketCode).Sum(t => t.Amount);
            if (remainingBudget < 0)
            {
                remainingBudget = 0;
            }

            Value = Convert.ToDouble(remainingBudget);
            ToolTip = string.Format(CultureInfo.CurrentCulture, this.remainingBudgetToolTip, remainingBudget);

            if (remainingBudget < 0.2M * totalBudget)
            {
                ColourStyleName = WidgetWarningStyle;
            }
            else
            {
                ColourStyleName = this.standardStyle;
            }
        }
        private void AnalysisPreconditions(GlobalFilterCriteria criteria, out DateTime beginDate, out DateTime endDate)
        {
            if (criteria == null)
            {
                throw new ArgumentNullException("criteria");
            }

            if (!criteria.Cleared && (criteria.BeginDate == null || criteria.EndDate == null))
            {
                throw new ArgumentException("The given criteria does not contain any filtering dates.");
            }

            if (this.statement == null)
            {
                throw new ArgumentException("The statement supplied is null, analysis cannot proceed with no statement.");
            }

            if (this.budgets == null)
            {
                throw new ArgumentException("budgets");
            }

            if (criteria.Cleared)
            {
                beginDate = this.statement.AllTransactions.First().Date;
                endDate = this.statement.AllTransactions.Last().Date;
            }
            else
            {
                beginDate = criteria.BeginDate.Value;
                endDate = criteria.EndDate.Value;
            }
        }
        private void SearchForOverspentLedgers(StatementModel statement, GlobalFilterCriteria filter, BudgetCurrencyContext budget, IDictionary<BudgetBucket, decimal> overspendingSummary, int warnings)
        {
            List<Transaction> transactions = statement.Transactions.Where(t => t.Date < filter.BeginDate.Value.AddMonths(1)).ToList();
            foreach (Expense expense in budget.Model.Expenses.Where(e => e.Bucket is BillToPayExpenseBucket))
            {
                if (overspendingSummary.ContainsKey(expense.Bucket))
                {
                    continue;
                }

                decimal bucketBalance = expense.Amount + transactions.Where(t => t.BudgetBucket == expense.Bucket).Sum(t => t.Amount);
                overspendingSummary.Add(expense.Bucket, bucketBalance);
                if (bucketBalance < -this.Tolerance)
                {
                    warnings++;
                }
            }
        }
        public void FilterTransactions_ShouldCallStatementModel_GivenFilterObject()
        {
            this.testData = new StatementModelTestHarness();
            this.testData.LoadTransactions(new List<Transaction>());
            var criteria = new GlobalFilterCriteria { BeginDate = new DateTime(2014, 07, 01), EndDate = new DateTime(2014, 08, 01) };

            Arrange();

            this.subject.FilterTransactions(criteria);

            Assert.AreEqual(1, ((StatementModelTestHarness)this.testData).FilterByCriteriaWasCalled);
        }
        public override void Update(params object[] input)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }

            if (!ValidateUpdateInput(input))
            {
                Enabled = false;
                return;
            }

            this.bucketRepository = (IBudgetBucketRepository) input[0];
            this.filter = (GlobalFilterCriteria) input[1];

            if (this.filter == null || this.filter.Cleared || this.filter.BeginDate == null || this.filter.BeginDate == DateTime.MinValue ||
                this.filter.EndDate == null || this.filter.EndDate.Value == DateTime.MinValue || this.bucketRepository == null)
            {
                Enabled = false;
                return;
            }

            if (!this.bucketRepository.IsValidCode(BucketCode))
            {
                Enabled = false;
                LargeNumber = string.Empty;
                ToolTip = string.Empty;
                DetailedText = string.Empty;
                return;
            }

            this.diagLogger.LogInfo(l => l.Format("{0} Calculating Payment Plan for {1}. From {2} to {3}", WidgetType.Name, Id, this.filter.BeginDate, this.filter.EndDate));
            var currentDate = CalculateStartDate(StartPaymentDate, this.filter.BeginDate.Value);
            var content = new StringBuilder();
            // Ignore start date in filter and force it to be one month prior to end date in filter.
            var currentMonthTally = new NextOccurance
            {
                StartDate = this.filter.EndDate.Value.AddDays(1).AddMonths(-1),
                EndDate = this.filter.EndDate.Value
            };
            var alert = false;
            NextOccurance firstOccurance = null;
            do
            {
                if (currentDate.Date <= currentMonthTally.EndDate)
                {
                    currentMonthTally.Tally(currentDate.Date.Day);
                }
                else
                {
                    this.diagLogger.LogInfo(
                        l =>
                            l.Format("    {0} {1}", currentMonthTally.StartDate.ToString("MMMM"),
                                currentMonthTally.ConcatDates()));
                    if (AbnormalNumberOfPayments(currentMonthTally.Dates.Count))
                    {
                        if (firstOccurance == null)
                        {
                            firstOccurance = currentMonthTally;
                        }
                        content.AppendFormat(CultureInfo.CurrentCulture, "{0}, ",
                            currentMonthTally.StartDate.ToString("MMMM"));
                        if (currentMonthTally.EndDate == this.filter.EndDate.Value ||
                            currentMonthTally.EndDate == this.filter.EndDate.Value.AddMonths(1))
                        {
                            // Is current or next month, so signal alert status
                            alert = true;
                            this.diagLogger.LogInfo(l => l.Format("    ***** ALERT *****"));
                        }
                    }
                    currentMonthTally = currentMonthTally.NextMonth(currentDate.Date.Day);
                }

                currentDate = CalculateNextPaymentDate(currentDate);
            } while (currentDate.Date <= this.filter.EndDate.Value.AddYears(1));

            ColourStyleName = alert ? WidgetWarningStyle : WidgetStandardStyle;
            DetailedText = string.Format(CultureInfo.CurrentCulture, "Monitoring {0} {1} bucket. {2}", Frequency,
                BucketCode, content);
            if (firstOccurance == null)
            {
                LargeNumber = string.Empty;
                ToolTip = ToolTipPrefix;
            }
            else
            {
                LargeNumber = firstOccurance.StartDate.ToString("MMMM");
                ToolTip = ToolTipPrefix +
                          string.Format(
                              CultureInfo.InvariantCulture,
                              "{0} to the {1} has payments on {2}",
                              firstOccurance.StartDate.ToString("d-MMM"),
                              firstOccurance.EndDate.ToString("d-MMM"),
                              firstOccurance.ConcatDates());
            }
        }
        /// <summary>
        ///     Filters the transactions using the filter object provided.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        public void FilterTransactions(GlobalFilterCriteria criteria)
        {
            if (criteria == null)
            {
                throw new ArgumentNullException(nameof(criteria));
            }

            this.monitorableDependencies.NotifyOfDependencyChange(criteria);
            StatementModel.Filter(criteria);
        }
Example #50
0
        private int SearchForOtherNonLedgerBookOverspentBuckets(
            StatementModel statement,
            GlobalFilterCriteria filter,
            IBudgetCurrencyContext budget,
            IDictionary<BudgetBucket, decimal> overspendingSummary)
        {
            var warnings = 0;
            List<Transaction> transactions = statement.Transactions.Where(t => t.Date < filter.BeginDate?.Date.AddMonths(1)).ToList();
            foreach (var expense in budget.Model.Expenses.Where(e => e.Bucket is BillToPayExpenseBucket))
            {
                if (overspendingSummary.ContainsKey(expense.Bucket))
                {
                    continue;
                }

                var bucketBalance = expense.Amount +
                                    transactions.Where(t => t.BudgetBucket == expense.Bucket).Sum(t => t.Amount);
                overspendingSummary.Add(expense.Bucket, bucketBalance);
                if (bucketBalance < -Tolerance)
                {
                    warnings++;
                }
            }

            return warnings;
        }
 private static decimal CalculateOpeningBalance(GlobalFilterCriteria filter, LedgerBook ledgerBook)
 {
     LedgerEntryLine line = LedgerCalculation.LocateApplicableLedgerLine(ledgerBook, filter);
     return line == null ? 0 : line.CalculatedSurplus;
 }
        private static DateTime CalculateStartDate(StatementModel statement, GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared || criteria.BeginDate == null)
            {
                var minDate = statement.AllTransactions.Min(t => t.Date).Date;
                minDate = minDate.FirstDateInMonth();
                return minDate;
            }

            return criteria.BeginDate.Value;
        }
Example #53
0
        public void Filter(GlobalFilterCriteria criteria)
        {
            if (criteria == null)
            {
                ChangeHash = Guid.NewGuid();
                Transactions = AllTransactions.ToList();
                DurationInMonths = this.fullDuration;
                Filtered = false;
                return;
            }

            if (criteria.BeginDate > criteria.EndDate)
            {
                throw new ArgumentException("End date must be after the begin date.");
            }

            this.currentFilter = criteria;

            ChangeHash = Guid.NewGuid();
            if (criteria.Cleared)
            {
                Transactions = AllTransactions.ToList();
                DurationInMonths = this.fullDuration;
                Filtered = false;
                return;
            }

            IEnumerable<Transaction> query = AllTransactions;
            if (criteria.BeginDate != null)
            {
                query = AllTransactions.Where(t => t.Date >= criteria.BeginDate.Value);
            }

            if (criteria.EndDate != null)
            {
                query = query.Where(t => t.Date <= criteria.EndDate.Value);
            }

            if (criteria.AccountType != null)
            {
                query = query.Where(t => t.AccountType == criteria.AccountType);
            }

            Transactions = query.ToList();
            DurationInMonths = CalculateDuration(criteria, Transactions);
            this.duplicates = null;
            Filtered = true;
        }
Example #54
0
        private IEnumerable<Transaction> BaseFilterQuery(GlobalFilterCriteria criteria)
        {
            if (criteria.Cleared)
            {
                return AllTransactions.ToList();
            }

            IEnumerable<Transaction> query = AllTransactions;
            if (criteria.BeginDate != null)
            {
                query = AllTransactions.Where(t => t.Date >= criteria.BeginDate.Value);
            }

            if (criteria.EndDate != null)
            {
                query = query.Where(t => t.Date <= criteria.EndDate.Value);
            }

            return query;
        }
        private static decimal GetBudgetedTotal(
            [NotNull] BudgetModel budgetModel,
            [NotNull] IEnumerable<BudgetBucket> buckets,
            int durationInMonths,
            LedgerBook ledgerBook,
            DateTime beginDate,
            DateTime endDate)
        {
            decimal budgetTotal = 0;
            List<BudgetBucket> bucketsCopy = buckets.ToList();
            for (int monthIndex = 0; monthIndex < durationInMonths; monthIndex++)
            {
                var previousMonthCriteria = new GlobalFilterCriteria
                {
                    BeginDate = beginDate.AddMonths(-monthIndex),
                    EndDate = endDate.AddMonths(-monthIndex),
                };

                LedgerEntryLine applicableLine = LedgerCalculation.LocateApplicableLedgerLine(ledgerBook, previousMonthCriteria);
                if (applicableLine == null)
                {
                    // Use budget values from budget model instead, there is no ledger book line for this month.
                    budgetTotal += bucketsCopy.Sum(bucket => GetBudgetModelTotalForBucket(budgetModel, bucket));
                }
                else
                {
                    budgetTotal += bucketsCopy.Sum(bucket =>
                    {
                        decimal ledgerBal = GetLedgerBalance(applicableLine, bucket);
                        if (ledgerBal < 0)
                        {
                            // The Ledger line might not actually have a ledger for the given bucket.
                            return GetBudgetModelTotalForBucket(budgetModel, bucket);
                        }

                        return ledgerBal;
                    });
                }
            }

            return budgetTotal;
        }