/// <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 TestInitialise() { var accountRepo = new InMemoryAccountTypeRepository(); var bucketRepo = new BucketBucketRepoAlwaysFind(); var subject = new Mapper_LedgerBookDto_LedgerBook(bucketRepo, accountRepo, new LedgerBucketFactory(bucketRepo, accountRepo), new LedgerTransactionFactory(), new Mock<IReconciliationBuilder>().Object); Result = subject.ToModel(TestData); }
internal void RecalculateClosingBalance(LedgerBook ledgerBook) { // Recalc balance based on opening balance and transactions. var previousLine = ledgerBook.Reconciliations.Skip(1).FirstOrDefault(); var openingBalance = LedgerEntryLine.FindPreviousEntryClosingBalance(previousLine, LedgerBucket); RecalculateClosingBalance(openingBalance); }
private static double CalculateChecksum(LedgerBook dataEntity) { // ReSharper disable once EnumerableSumInExplicitUncheckedContext - Used to calculate a checksum and revolving (overflowing) integers are ok here. return(dataEntity.Reconciliations.Sum( l => (double)l.LedgerBalance + l.BankBalanceAdjustments.Sum(b => (double)b.Amount) + l.Entries.Sum(e => (double)e.Balance))); }
public void UnlockMostRecentLineShouldNotThrowIfBookIsEmpty() { var subject = new LedgerBook(new FakeLogger()) { Name = "Foo", Modified = new DateTime(2011, 12, 4), FileName = @"C:\TestLedgerBook.xml", }; LedgerEntryLine result = subject.UnlockMostRecentLine(); Assert.IsNull(result); }
/// <summary> /// Initializes a new instance of the <see cref="ReconciliationConsistencyChecker" /> class. /// </summary> /// <exception cref="System.ArgumentNullException"></exception> public ReconciliationConsistencyChecker([NotNull] LedgerBook book) { if (book == null) { throw new ArgumentNullException(nameof(book)); } this.ledgerBook = book; this.check1 = this.ledgerBook.Reconciliations.Sum(e => e.CalculatedSurplus); }
/// <summary> /// Locates the applicable ledger line. /// </summary> public virtual LedgerEntryLine LocateApplicableLedgerLine(LedgerBook ledgerBook, DateTime beginDate) { CheckCacheForCleanUp(); if (ledgerBook == null) { return(null); } return(LocateLedgerEntryLine(ledgerBook, beginDate, beginDate.AddMonths(1).AddDays(-1))); }
public static Dictionary<BudgetBucket, int> LedgerOrder(LedgerBook book) { var ledgerOrder = new Dictionary<BudgetBucket, int>(); var index = 0; foreach (LedgerBucket ledger in book.Ledgers.OrderBy(l => l.BudgetBucket)) { Debug.Write("{0}", ledger.BudgetBucket.Code.PadRight(20)); ledgerOrder.Add(ledger.BudgetBucket, index++); } return ledgerOrder; }
public void UnlockMostRecentLineShouldNotThrowIfBookIsEmpty() { this.subject = new LedgerBook(new ReconciliationBuilder(new FakeLogger())) { Name = "Foo", Modified = new DateTime(2011, 12, 4), StorageKey = @"C:\TestLedgerBook.xml" }; LedgerEntryLine result = this.subject.UnlockMostRecentLine(); Assert.IsNull(result); }
/// <summary> /// Finds any overspent ledgers for the month and returns the date and value the of the total overspend. This /// resulting /// collection can then be used to subtract from Surplus. /// Overdrawn ledgers are supplemented from Surplus. /// Negative values indicate overdrawn ledgers. /// </summary> public virtual IEnumerable <ReportTransaction> CalculateOverspentLedgers([NotNull] StatementModel statement, [NotNull] LedgerBook ledger, DateTime beginDate) { CheckCacheForCleanUp(); if (statement == null) { throw new ArgumentNullException(nameof(statement)); } if (ledger == null) { throw new ArgumentNullException(nameof(ledger)); } // Given the same ledger, statement and begin date this data won't change. return((IEnumerable <ReportTransaction>)GetOrAddFromCache( BuildCacheKey(statement, ledger, beginDate), () => { var overSpendTransactions = new List <ReportTransaction>(); var ledgerLine = LocateApplicableLedgerLine(ledger, beginDate); if (ledgerLine == null) { return overSpendTransactions; } var endDate = beginDate.AddMonths(1); var currentDate = beginDate; Dictionary <BudgetBucket, decimal> runningBalances = ledgerLine.Entries.ToDictionary(entry => entry.LedgerBucket.BudgetBucket, entry => entry.Balance); Dictionary <BudgetBucket, decimal> previousBalances = ledgerLine.Entries.ToDictionary(entry => entry.LedgerBucket.BudgetBucket, entry => 0M); do { var currentDateCopy = currentDate; foreach (var transaction in statement.Transactions.Where(t => t.Date == currentDateCopy)) { if (runningBalances.ContainsKey(transaction.BudgetBucket)) { runningBalances[transaction.BudgetBucket] += transaction.Amount; } } ProcessOverdrawnLedgers(runningBalances, previousBalances, overSpendTransactions, currentDate); currentDate = currentDate.AddDays(1); } while (currentDate < endDate); return overSpendTransactions; })); }
public void UsingInvalidLedgerBook_Reconcile_ShouldThrow() { var subject = new LedgerBook(new FakeLogger()) { Name = "Foo", Modified = new DateTime(2012, 02, 29), FileName = "", }; subject.Reconcile( new DateTime(2012, 02, 20), new[] { new BankBalance(StatementModelTestData.ChequeAccount, 2050M) }, BudgetModelTestData.CreateTestData1(), StatementModelTestData.TestData1()); }
public void TestIntialise() { this.mockBucketRepo = new Mock<IBudgetBucketRepository>(); this.mockRuleService = new Mock<ITransactionRuleService>(); this.mockReconciliationConsistency = new Mock<IReconciliationConsistency>(); this.subject = new ReconciliationManager(this.mockRuleService.Object, this.mockReconciliationConsistency.Object, new FakeLogger()); this.testDataLedgerBook = LedgerBookTestData.TestData5(() => new LedgerBookTestHarness(new Mock<IReconciliationBuilder>().Object)); this.testDataEntryLine = this.testDataLedgerBook.Reconciliations.First(); this.testDataEntryLine.Unlock(); this.surplusChqLedger = new SurplusLedger { StoredInAccount = StatementModelTestData.ChequeAccount }; this.insHomeSavLedger = this.testDataLedgerBook.Ledgers.Single(l => l.BudgetBucket == StatementModelTestData.InsHomeBucket); this.phNetChqLedger = this.testDataLedgerBook.Ledgers.Single(l => l.BudgetBucket == StatementModelTestData.PhoneBucket); }
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); }
public LedgerBook Build() { var book = new LedgerBook(new ReconciliationBuilder(new FakeLogger())) { Name = Name, Modified = Modified, StorageKey = StorageKey }; book.SetReconciliations(this.reconciliations); if (book.Reconciliations.None()) { this.ledgerBuckets.ForEach(l => book.AddLedger(l)); } LedgerBookTestData.Finalise(book, this.lockWhenFinished); return book; }
/// <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 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 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 async Task SaveAsync(LedgerBook book, string storageKey, bool isEncrypted) { if (book == null) { throw new ArgumentNullException(nameof(book)); } if (storageKey.IsNothing()) { throw new ArgumentNullException(nameof(storageKey)); } var dataEntity = this.mapper.ToDto(book); book.StorageKey = storageKey; dataEntity.StorageKey = storageKey; dataEntity.Checksum = CalculateChecksum(book); await SaveDtoToDiskAsync(dataEntity, isEncrypted); }
public BurnDownChartAnalyserResult CreateNewCustomAggregateChart( StatementModel statementModel, BudgetModel budgetModel, IEnumerable<BudgetBucket> buckets, LedgerBook ledgerBookModel, DateTime beginDate, string chartTitle) { List<BudgetBucket> bucketsList = buckets.ToList(); var result = this.chartAnalyser.Analyse(statementModel, budgetModel, bucketsList, ledgerBookModel, beginDate); result.ChartTitle = chartTitle; var persistChart = new CustomAggregateBurnDownGraph { BucketIds = bucketsList.Select(b => b.Code).ToList(), Name = chartTitle }; this.chartsBuilder.CustomCharts = this.chartsBuilder.CustomCharts.Union(new[] { persistChart }).ToList(); return result; }
public void TestInitialise() { this.bucketRepo = new BucketBucketRepoAlwaysFind(); this.subject = new RemainingSurplusWidget(); this.criteriaTestData = new GlobalFilterCriteria { BeginDate = new DateTime(2015, 10, 20), EndDate = new DateTime(2015, 11, 19) }; StatementModelTestDataForThisTest.AccountTypeRepo = new InMemoryAccountTypeRepository(); StatementModelTestDataForThisTest.BudgetBucketRepo = this.bucketRepo; this.statementTestData = StatementModelTestDataForThisTest.TestDataGenerated(); BudgetModel budgetModel = BudgetModelTestData.CreateTestData1(); this.budgetTestData = new BudgetCurrencyContext(new BudgetCollection(budgetModel), budgetModel); this.ledgerBookTestData = new LedgerBookBuilder { StorageKey = "RemainingSurplusWidgetTest.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(); this.ledgerCalculation = new LedgerCalculation(new FakeLogger()); }
public void Reconcile_ShouldCreateBalanceAdjustmentOf150_GivenSavingsMonthlyBudgetAmountsSumTo150() { // 95 Car Mtc Monthly budget // 55 Hair cut monthly budget // === // 150 Balance Adjustment expected in Savings // Power 175 goes in Chq this.subject = new LedgerBookBuilder() .IncludeLedger(new SavedUpForLedger { BudgetBucket = StatementModelTestData.CarMtcBucket, StoredInAccount = LedgerBookTestData.SavingsAccount }) .IncludeLedger(new SavedUpForLedger { BudgetBucket = StatementModelTestData.HairBucket, StoredInAccount = LedgerBookTestData.SavingsAccount }) .IncludeLedger(LedgerBookTestData.PowerLedger) .Build(); Act(); this.subject.Output(true); var resultRecon = this.subject.Reconciliations.First(); Assert.AreEqual(150M, resultRecon.BankBalanceAdjustments.Single(b => b.BankAccount == LedgerBookTestData.SavingsAccount).Amount); Assert.AreEqual(-150M, resultRecon.BankBalanceAdjustments.Single(b => b.BankAccount == LedgerBookTestData.ChequeAccount).Amount); }
/// <summary> /// Locates the applicable ledger line. /// </summary> /// <exception cref="System.ArgumentNullException"></exception> public virtual LedgerEntryLine LocateApplicableLedgerLine(LedgerBook ledgerBook, [NotNull] GlobalFilterCriteria filter) { CheckCacheForCleanUp(); if (ledgerBook == null) { return(null); } if (filter == null) { throw new ArgumentNullException(nameof(filter)); } if (filter.Cleared) { return(ledgerBook.Reconciliations.FirstOrDefault()); } Debug.Assert(filter.BeginDate != null); Debug.Assert(filter.EndDate != null); return(LocateLedgerEntryLine(ledgerBook, filter.BeginDate.Value, filter.EndDate.Value)); }
private Dictionary <BudgetBucket, decimal> CalculateLedgersBalanceSummary(LedgerBook ledgerBook, DateTime beginDate, StatementModel statement) { var endDate = beginDate.AddMonths(1).AddDays(-1); var currentLegderLine = LocateLedgerEntryLine(ledgerBook, beginDate, endDate); if (currentLegderLine == null) { return(new Dictionary <BudgetBucket, decimal>()); } var ledgersSummary = new Dictionary <BudgetBucket, decimal>(); foreach (var entry in currentLegderLine.Entries) { var closingBalance = CalculateTransactionTotal(beginDate, statement, currentLegderLine, entry.LedgerBucket.BudgetBucket.Code); var balance = entry.Balance + closingBalance; ledgersSummary.Add(entry.LedgerBucket.BudgetBucket, balance); } return(ledgersSummary); }
private static decimal CalculateSavingsToDateWithTrackedLedgers(StatementModel statement, LedgerBook ledger) { if (ledger == null) { return 0; } var trackedSavingsLedgers = ledger.Ledgers .Where(l => l.BudgetBucket is SavingsCommitmentBucket) .Select(l => l.BudgetBucket) .ToList(); if (!trackedSavingsLedgers.Any()) { return SumDebitSavingsTransactions(statement); } decimal savingsToDate = CalculateTrackedSavingLedgersContributions(statement, trackedSavingsLedgers); // Other non-ledger-book-tracked savings will appear as debits in the statement so need to be negated. var otherNontrackedSavings = statement.Transactions.Where(t => t.BudgetBucket is SavingsCommitmentBucket && trackedSavingsLedgers.All(b => b != t.BudgetBucket)); savingsToDate += otherNontrackedSavings.Sum(t => -t.Amount); return savingsToDate; }
/// <summary> /// Calculates the current month ledger balances. /// </summary> /// <exception cref="System.ArgumentNullException"> /// </exception> public virtual IDictionary <BudgetBucket, decimal> CalculateCurrentMonthLedgerBalances( [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)); } var ledgers = new Dictionary <BudgetBucket, decimal>(); if (filter.Cleared || filter.BeginDate == null) { return(ledgers); } Dictionary <BudgetBucket, decimal> ledgersSummary = CalculateLedgersBalanceSummary(ledgerBook, filter.BeginDate.Value, statement); // Check Surplus var surplusBalance = CalculateCurrentMonthSurplusBalance(ledgerBook, filter, statement); ledgersSummary.Add(new SurplusBucket(), surplusBalance); return(ledgersSummary); }
/// <summary> /// Calculates the current month bucket spend. /// </summary> /// <exception cref="System.ArgumentNullException"> /// </exception> public virtual decimal CalculateCurrentMonthBucketSpend( [NotNull] LedgerBook ledgerBook, [NotNull] GlobalFilterCriteria filter, [NotNull] StatementModel statement, [NotNull] string bucketCode) { 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 (bucketCode.IsNothing()) { throw new ArgumentNullException(nameof(bucketCode)); } if (filter.BeginDate == null) { return(0); } var entryLine = LocateApplicableLedgerLine(ledgerBook, filter); var transactionTotal = CalculateTransactionTotal(filter.BeginDate.Value, statement, entryLine, bucketCode); return(transactionTotal); }
private void CreateLedgerBookTestData() { this.ledgerBookTestData = new LedgerBookBuilder { StorageKey = "BudgetBucketMonitorWidgetTest.xml", Modified = new DateTime(2015, 11, 23), Name = "Smith Budget 2015" } .IncludeLedger(LedgerBookTestData.PhoneLedger, 50M) .IncludeLedger(LedgerBookTestData.HouseInsLedgerSavingsAccount, 100M) .AppendReconciliation( new DateTime(2015, 10, 20), new BankBalance(LedgerBookTestData.ChequeAccount, 2000M), new BankBalance(LedgerBookTestData.SavingsAccount, 1000M)) .WithReconciliationEntries( entryBuilder => { entryBuilder.WithLedger(LedgerBookTestData.PhoneLedger) .AppendTransactions(txnBuilder => { txnBuilder.WithCredit(100, "Foo", new DateTime(2015, 10, 20), "automatchref12"); }); entryBuilder.WithLedger(LedgerBookTestData.HouseInsLedgerSavingsAccount) .AppendTransactions(txnBuilder => { txnBuilder.WithCredit(-100, "Foo", new DateTime(2015, 10, 20), "automatchref12"); }); }) .Build(); }
private static LedgerEntryLine LocateLedgerEntryLine(LedgerBook ledgerBook, DateTime begin, DateTime end) { return (ledgerBook.Reconciliations.FirstOrDefault( ledgerEntryLine => ledgerEntryLine.Date >= begin && ledgerEntryLine.Date <= end)); }
private BurnDownChartAnalyserResult AnalyseDataForChart( StatementModel statementModel, BudgetModel budgetModel, LedgerBook ledgerBookModel, IEnumerable<BudgetBucket> buckets, DateTime beginDate) { var analyser = this.chartAnalyserFactory(); var result = analyser.Analyse(statementModel, budgetModel, buckets, ledgerBookModel, beginDate); return result; }
private static LedgerBook Finalise(LedgerBook book) { foreach (LedgerEntryLine line in book.DatedEntries) { PrivateAccessor.SetProperty(line, "IsNew", false); foreach (LedgerEntry entry in line.Entries) { PrivateAccessor.SetField(entry, "isNew", false); } } return book; }
private static IEnumerable<LedgerEntry> CompileLedgersAndBalances(LedgerBook parentLedgerBook) { var ledgersAndBalances = new List<LedgerEntry>(); var previousLine = parentLedgerBook.Reconciliations.FirstOrDefault(); if (previousLine == null) { return parentLedgerBook.Ledgers.Select(ledger => new LedgerEntry { Balance = 0, LedgerBucket = ledger }); } foreach (var ledger in parentLedgerBook.Ledgers) { // Ledger Columns from a previous are not necessarily equal if the StoredInAccount has changed. var previousEntry = previousLine.Entries.FirstOrDefault(e => e.LedgerBucket.BudgetBucket == ledger.BudgetBucket); // Its important to use the ledger column value from the book level map, not from the previous entry. The user // could have moved the ledger to a different account and so, the ledger column value in the book level map will be different. if (previousEntry == null) { // Indicates a new ledger column has been added to the book starting this month. ledgersAndBalances.Add(new LedgerEntry { Balance = 0, LedgerBucket = ledger }); } else { ledgersAndBalances.Add(previousEntry); } } return ledgersAndBalances; }
public IDisposable EnsureConsistency(LedgerBook book) { return new ReconciliationConsistencyChecker(book); }
private static double CalculateChecksum(LedgerBook dataEntity) { // ReSharper disable once EnumerableSumInExplicitUncheckedContext - Used to calculate a checksum and revolving (overflowing) integers are ok here. return dataEntity.Reconciliations.Sum( l => (double) l.LedgerBalance + l.BankBalanceAdjustments.Sum(b => (double) b.Amount) + l.Entries.Sum(e => (double) e.Balance)); }
public BurnDownChartAnalyserResult Analyse(StatementModel statementModel, BudgetModel budgetModel, IEnumerable<BudgetBucket> bucketsSubset, LedgerBook ledgerBook, DateTime beginDate) { if (statementModel == null) { throw new ArgumentNullException(nameof(statementModel)); } if (budgetModel == null) { throw new ArgumentNullException(nameof(budgetModel)); } List<BudgetBucket> bucketsCopy = bucketsSubset.ToList(); this.logger.LogInfo( l => "BurnDownChartAnalyser.Analyse: " + string.Join(" ", bucketsCopy.Select(b => b.Code))); var result = new BurnDownChartAnalyserResult(); List<DateTime> datesOfTheMonth = YieldAllDaysInDateRange(beginDate); var lastDate = datesOfTheMonth.Last(); CreateZeroLine(datesOfTheMonth, result); var openingBalance = GetBudgetedTotal(budgetModel, ledgerBook, bucketsCopy, beginDate); CalculateBudgetLineValues(openingBalance, datesOfTheMonth, result); List<ReportTransactionWithRunningBalance> spendingTransactions = CollateStatementTransactions(statementModel, bucketsCopy, beginDate, lastDate, openingBalance); // Only relevant when calculating surplus burndown - ovespent ledgers are supplemented from surplus so affect its burndown. if (ledgerBook != null && bucketsCopy.OfType<SurplusBucket>().Any()) { List<ReportTransaction> overSpentLedgers = this.ledgerCalculator.CalculateOverspentLedgers(statementModel, ledgerBook, beginDate).ToList(); if (overSpentLedgers.Any()) { spendingTransactions.AddRange( overSpentLedgers.Select(t => new ReportTransactionWithRunningBalance(t))); spendingTransactions = spendingTransactions.OrderBy(t => t.Date).ToList(); UpdateReportTransactionRunningBalances(spendingTransactions); } } // Copy running balance from transaction list into burndown chart data var actualSpending = new SeriesData { SeriesName = BurnDownChartAnalyserResult.BalanceSeriesName, Description = "Running balance over time as transactions spend, the balance decreases." }; result.GraphLines.SeriesList.Add(actualSpending); foreach (var day in datesOfTheMonth) { if (day > DateTime.Today) { break; } var dayClosingBalance = GetDayClosingBalance(spendingTransactions, day); actualSpending.PlotsList.Add(new DatedGraphPlot { Date = day, Amount = dayClosingBalance }); var copyOfDay = day; this.logger.LogInfo(l => l.Format(" {0} Close Bal:{1:N}", copyOfDay, dayClosingBalance)); } result.ReportTransactions = spendingTransactions; return result; }
/// <summary> /// Same as Test Data 2, but with multiple Bank Balances for the latest entry. /// A Test LedgerBook with data populated for June July and August 2013. Also includes some debit transactions. /// August transactions include some balance adjustments. /// </summary> public static LedgerBook TestData4() { var hairLedger = new LedgerColumn { BudgetBucket = new SavedUpForExpenseBucket(TestDataConstants.HairBucketCode, "Hair cuts wheelbarrow.") }; var powerLedger = new LedgerColumn { BudgetBucket = new SpentMonthlyExpenseBucket(TestDataConstants.PowerBucketCode, "Power ") }; var phoneLedger = new LedgerColumn { BudgetBucket = new SpentMonthlyExpenseBucket(TestDataConstants.PhoneBucketCode, "Poo bar") }; var book = new LedgerBook(new FakeLogger()) { Name = "Test Data 4 Book", Modified = new DateTime(2013, 12, 16), FileName = "C:\\Folder\\book1.xml", }; var list = new List<LedgerEntryLine> { CreateLine(new DateTime(2013, 06, 15), new[] { new BankBalance(StatementModelTestData.ChequeAccount, 2500) }, "Lorem ipsum").SetEntries(new List <LedgerEntry> { CreateLedgerEntry(hairLedger).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 55M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 45M, Narrative = "Hair cut" }, }), CreateLedgerEntry(powerLedger).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 140M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 123.56M, Narrative = "Power bill" }, }), CreateLedgerEntry(phoneLedger).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 95M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 86.43M, Narrative = "Pay phones" }, }) }), }; LedgerEntry previousHairEntry = list.Last().Entries.Single(e => e.LedgerColumn.BudgetBucket.Code == TestDataConstants.HairBucketCode); LedgerEntry previousPowerEntry = list.Last().Entries.Single(e => e.LedgerColumn.BudgetBucket.Code == TestDataConstants.PowerBucketCode); LedgerEntry previousPhoneEntry = list.Last().Entries.Single(e => e.LedgerColumn.BudgetBucket.Code == TestDataConstants.PhoneBucketCode); list.Add( CreateLine(new DateTime(2013, 07, 15), new[] { new BankBalance(StatementModelTestData.ChequeAccount, 3700) }, "dolor amet set").SetEntries(new List <LedgerEntry> { CreateLedgerEntry(hairLedger, previousHairEntry.Balance).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 55M, Debit = 0M, Narrative = "Budgeted amount" }, }), CreateLedgerEntry(powerLedger, previousPowerEntry.Balance).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 140M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 145.56M, Narrative = "Power bill" }, }), CreateLedgerEntry(phoneLedger, previousPhoneEntry.Balance).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 95M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 66.43M, Narrative = "Pay phones" }, }) })); previousHairEntry = list.Last().Entries.Single(e => e.LedgerColumn.BudgetBucket.Code == TestDataConstants.HairBucketCode); previousPowerEntry = list.Last().Entries.Single(e => e.LedgerColumn.BudgetBucket.Code == TestDataConstants.PowerBucketCode); previousPhoneEntry = list.Last().Entries.Single(e => e.LedgerColumn.BudgetBucket.Code == TestDataConstants.PhoneBucketCode); LedgerEntryLine line = CreateLine(new DateTime(2013, 08, 15), new[] { new BankBalance(StatementModelTestData.ChequeAccount, 2750), new BankBalance(StatementModelTestData.SavingsAccount, 200) }, "The quick brown fox jumped over the lazy dog").SetEntries(new List<LedgerEntry> { CreateLedgerEntry(hairLedger, previousHairEntry.Balance).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 55M, Debit = 0M, Narrative = "Budgeted amount" }, }), CreateLedgerEntry(powerLedger, previousPowerEntry.Balance).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 140M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 98.56M, Narrative = "Power bill" }, }), CreateLedgerEntry(phoneLedger, previousPhoneEntry.Balance).SetTransactions(new List<LedgerTransaction> { new BudgetCreditLedgerTransaction { Credit = 95M, Debit = 0M, Narrative = "Budgeted amount" }, new DebitLedgerTransaction { Credit = 0M, Debit = 67.43M, Narrative = "Pay phones" }, }) }); line.BalanceAdjustment(-550, "Credit card payment yet to go out."); list.Add(line); book.SetDatedEntries(list); return Finalise(book); }
public void TestInitialise() { this.testDataBudget = BudgetModelTestData.CreateTestData1(); this.testDataStatement = StatementModelTestData.TestData1(); this.subject = LedgerBookTestData.TestData1(); }
private void ActOnTestData5(StatementModel statementModelTestData = null) { this.subject = LedgerBookTestData.TestData5(); this.testDataBudget = BudgetModelTestData.CreateTestData5(); this.testDataStatement = statementModelTestData ?? StatementModelTestData.TestData5(); Console.WriteLine("********************** BEFORE RUNNING RECONCILIATION *******************************"); this.testDataStatement.Output(ReconcileDate.AddMonths(-1)); this.subject.Output(true); var result = Act(bankBalances: new[] { new BankBalance(StatementModelTestData.ChequeAccount, 1850.5M), new BankBalance(StatementModelTestData.SavingsAccount, 1200M) }); this.testDataToDoList = result.Tasks.ToList(); Console.WriteLine(); Console.WriteLine("********************** AFTER RUNNING RECONCILIATION *******************************"); this.subject.Output(true); }
public void TestInitialise() { AutoMapperConfigurationTest.AutoMapperConfiguration(); TestData = LedgerBookTestData.TestData2(); }
public void TestInitialise() { TestData = LedgerBookTestData.TestData2(); }
public void TestInitialise() { Subject = new LedgerCalculation(); TestData = LedgerBookTestData.TestData1(); }
/// <summary> /// When creating a new reconciliation a start date is required to be able to search a statement for transactions /// between the start date and the reconciliation date specified (today or pay day). The start date should start from /// the previous ledger entry line or /// one month prior if no records exist. /// </summary> /// <param name="ledgerBook">The Ledger Book to find the date for</param> /// <param name="reconciliationDate">The chosen reconciliation date from the user</param> internal static DateTime CalculateDateForReconcile(LedgerBook ledgerBook, DateTime reconciliationDate) { if (ledgerBook.Reconciliations.Any()) { return ledgerBook.Reconciliations.First().Date; } var startDateIncl = reconciliationDate.AddMonths(-1); return startDateIncl; }