/// <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()); }
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); }
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); }; }
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)); }
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); }
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); }
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); }
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); }
/// <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(); }
/// <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; }
/// <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 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); }
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()); } }
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; }
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; }
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; }