/// <summary> /// Creates a new empty <see cref="BudgetCollection" /> but does not save it. /// </summary> public BudgetCollection CreateNew() { var budget = new BudgetModel(); this.currentBudgetCollection = new BudgetCollection(budget); this.budgetBucketRepository.Initialise(new List<BudgetBucketDto>()); return this.currentBudgetCollection; }
public void ShouldIndicateFutureBudgetWhenOneIsGiven() { var budget3 = new BudgetModel { EffectiveFrom = new DateTime(2020, 01, 30) }; var subject = new BudgetCurrencyContext( new BudgetCollection(new[] { BudgetModelTestData.CreateTestData1(), BudgetModelTestData.CreateTestData2(), budget3 }), budget3 ); Assert.IsFalse(subject.BudgetActive); Assert.IsFalse(subject.BudgetArchived); Assert.IsTrue(subject.BudgetInFuture); }
/// <summary> /// Creates a new LedgerEntryLine for this <see cref="LedgerBook" />. /// </summary> /// <param name="date"> /// The date for the <see cref="LedgerEntryLine" />. Also used to search for transactions in the /// <see cref="statement" />. /// </param> /// <param name="bankBalances"> /// The bank balances as at the <see cref="date" /> to include in this new single line of the /// ledger book. /// </param> /// <param name="budget">The current budget.</param> /// <param name="statement">The currently loaded statement.</param> /// <param name="ignoreWarnings">Ignores validation warnings if true, otherwise <see cref="ValidationWarningException" />.</param> /// <exception cref="InvalidOperationException">Thrown when this <see cref="LedgerBook" /> is in an invalid state.</exception> public LedgerEntryLine Reconcile( DateTime date, IEnumerable<BankBalance> bankBalances, BudgetModel budget, StatementModel statement = null, bool ignoreWarnings = false) { try { PreReconciliationValidation(date, statement); } catch (ValidationWarningException) { if (!ignoreWarnings) { throw; } } decimal consistencyCheck1 = DatedEntries.Sum(e => e.CalculatedSurplus); var newLine = new LedgerEntryLine(date, bankBalances); var previousEntries = new Dictionary<LedgerColumn, LedgerEntry>(); LedgerEntryLine previousLine = this.datedEntries.FirstOrDefault(); foreach (LedgerColumn ledger in Ledgers) { LedgerEntry previousEntry = null; if (previousLine != null) { previousEntry = previousLine.Entries.FirstOrDefault(e => e.LedgerColumn.Equals(ledger)); } previousEntries.Add(ledger, previousEntry); } newLine.AddNew(previousEntries, budget, statement, CalculateStartDateForReconcile(date)); decimal consistencyCheck2 = DatedEntries.Sum(e => e.CalculatedSurplus); if (consistencyCheck1 != consistencyCheck2) { throw new CorruptedLedgerBookException("Code Error: The previous dated entries have changed, this is not allowed. Data is corrupt."); } this.datedEntries.Insert(0, newLine); this.newlyAddedLedgers.Clear(); return newLine; }
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 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; }
/// <summary> /// Creates a new instance of the <see cref="BudgetCurrencyContext" /> class. /// </summary> /// <param name="budgets">The collection of available budgets loaded.</param> /// <param name="budget"> /// The currently selected budget. This isn't necessarily the current one compared with today's date. /// Can be any in the <paramref name="budgets" /> collection. /// </param> public BudgetCurrencyContext([NotNull] BudgetCollection budgets, [NotNull] BudgetModel budget) { if (budgets == null) { throw new ArgumentNullException(nameof(budgets)); } if (budget == null) { throw new ArgumentNullException(nameof(budget)); } BudgetCollection = budgets; Model = budget; if (budgets.IndexOf(budget) < 0) { throw new KeyNotFoundException("The given budget is not found in the given collection."); } }
/// <summary> /// A budget model that is effective from 1/1/2013 /// </summary> public static BudgetModel CreateTestData1() { var budget = new BudgetModel { EffectiveFrom = new DateTime(2013, 01, 01), Name = TestDataConstants.Budget1Name }; var expenses = new List<Expense>( new[] { new Expense { Amount = 95M, Bucket = new SavedUpForExpenseBucket(TestDataConstants.CarMtcBucketCode, "Car maintenance") }, new Expense { Amount = 55M, Bucket = new SpentMonthlyExpenseBucket(TestDataConstants.HairBucketCode, "Hair cuts") }, new Expense { Amount = 175M, Bucket = new SpentMonthlyExpenseBucket(TestDataConstants.PowerBucketCode, "Power") } }); var incomes = new List<Income>( new[] { new Income { Amount = 1500M, Bucket = new IncomeBudgetBucket(TestDataConstants.IncomeBucketCode, "Pay") } }); budget.Update(incomes, expenses); return budget; }
public BucketBurnDownController Load( StatementModel statementModel, BudgetModel budgetModel, [NotNull] BudgetBucket bucket, DateTime beginDate, Engine.Ledger.LedgerBook ledgerBook) { if (bucket == null) { throw new ArgumentNullException("bucket"); } this.Background = ConverterHelper.TileBackgroundBrush; this.Bucket = bucket; this.ActualSpendingLabel = this.Bucket.Code; this.ChartTitle = string.Format(CultureInfo.CurrentCulture, "{0} Spending Chart", bucket.Code); this.burnDownGraphAnalyser.Analyse(statementModel, budgetModel, new[] { bucket }, beginDate, ledgerBook); CopyOutputFromAnalyser(); return this; }
public ReconciliationResult CreateNewMonthlyReconciliation( DateTime reconciliationDateExclusive, BudgetModel budget, StatementModel statement, params BankBalance[] bankBalances) { if (bankBalances == null) { throw new ArgumentNullException(nameof(bankBalances)); } if (budget == null) { throw new ArgumentNullException(nameof(budget)); } if (statement == null) { throw new ArgumentNullException(nameof(statement)); } if (LedgerBook == null) { throw new ArgumentException("The Ledger Book property cannot be null. You must set this prior to calling this method."); } try { this.newReconciliationLine = new LedgerEntryLine(reconciliationDateExclusive, bankBalances); AddNew(budget, statement, CalculateDateForReconcile(LedgerBook, reconciliationDateExclusive)); CreateToDoForAnyOverdrawnSurplusBalance(); return new ReconciliationResult { Reconciliation = this.newReconciliationLine, Tasks = this.toDoList }; } finally { this.newReconciliationLine = null; } }
public BudgetModel CloneBudgetModel(BudgetModel sourceBudget, DateTime newBudgetEffectiveFrom) { if (sourceBudget == null) { throw new ArgumentNullException(nameof(sourceBudget)); } if (newBudgetEffectiveFrom <= sourceBudget.EffectiveFrom) { throw new ArgumentException("The effective date of the new budget must be later than the other budget.", nameof(newBudgetEffectiveFrom)); } if (newBudgetEffectiveFrom <= DateTime.Today) { throw new ArgumentException("The effective date of the new budget must be a future date.", nameof(newBudgetEffectiveFrom)); } var validationMessages = new StringBuilder(); if (!sourceBudget.Validate(validationMessages)) { throw new ValidationWarningException(string.Format(CultureInfo.CurrentCulture, "The source budget is currently in an invalid state, unable to clone it at this time.\n{0}", validationMessages)); } var newBudget = new BudgetModel { EffectiveFrom = newBudgetEffectiveFrom, Name = string.Format(CultureInfo.CurrentCulture, "Copy of {0}", sourceBudget.Name) }; newBudget.Update(CloneBudgetIncomes(sourceBudget), CloneBudgetExpenses(sourceBudget)); if (!newBudget.Validate(validationMessages)) { throw new InvalidOperationException("New cloned budget is invalid but the source budget is ok. Code Error.\n" + validationMessages); } Budgets.Add(newBudget); this.budgetRepository.SaveAsync(); UpdateServiceMonitor(); return newBudget; }
public void Build(GlobalFilterCriteria criteria, StatementModel statementModel, BudgetModel budgetModel, Engine.Ledger.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<BucketBurnDownController>(this.budgetBucketRepository.Buckets.Count()); foreach (BudgetBucket bucket in this.budgetBucketRepository.Buckets .Where(b => b is ExpenseBucket) .OrderBy(b => b.Code)) { BucketBurnDownController chartController = this.bucketSpendingFactory(); chartController.Load(statementModel, budgetModel, bucket, beginDate, ledgerBookModel); listOfCharts.Add(chartController); } listOfCharts = listOfCharts.OrderBy(x => x.Bucket).ToList(); // Put surplus at the top. listOfCharts.Insert( 0, this.bucketSpendingFactory().Load(statementModel, budgetModel, this.budgetBucketRepository.SurplusBucket, beginDate, ledgerBookModel)); // Put any custom charts on top. foreach (CustomAggregateBurnDownGraph customChart in this.CustomCharts) { BucketBurnDownController chartController = this.bucketSpendingFactory(); IEnumerable<BudgetBucket> buckets = this.budgetBucketRepository.Buckets .Join(customChart.BucketIds, bucket => bucket.Code, code => code, (bucket, code) => bucket); chartController.LoadCustomChart(statementModel, budgetModel, buckets, beginDate, ledgerBookModel, customChart.Name); listOfCharts.Insert(0, chartController); } this.Results = new BurnDownChartBuilderResults(beginDate, dateRangeDescription, listOfCharts); }
public async Task<BudgetCollection> CreateNewAndSaveAsync(string storageKey) { if (storageKey.IsNothing()) { throw new ArgumentNullException(nameof(storageKey)); } var newBudget = new BudgetModel { EffectiveFrom = DateTime.Today, Name = Path.GetFileNameWithoutExtension(storageKey).Replace('.', ' ') }; this.currentBudgetCollection = new BudgetCollection(newBudget) { StorageKey = storageKey }; this.budgetBucketRepository.Initialise(new List<BudgetBucketDto>()); await SaveAsync(storageKey, false); return this.currentBudgetCollection; }
public BudgetCollection CreateNew([NotNull] string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentNullException("fileName"); } var newBudget = new BudgetModel { EffectiveFrom = DateTime.Today, Name = "Default Budget", }; var newCollection = new BudgetCollection(new[] { newBudget }) { FileName = fileName }; BudgetBucketRepository.Initialise(new List<BudgetBucketDto>()); Save(newCollection); return newCollection; }
public async Task <BudgetCollection> CreateNewAndSaveAsync(string storageKey) { if (storageKey.IsNothing()) { throw new ArgumentNullException(nameof(storageKey)); } var newBudget = new BudgetModel { EffectiveFrom = DateTime.Today, Name = Path.GetFileNameWithoutExtension(storageKey).Replace('.', ' ') }; this.currentBudgetCollection = new BudgetCollection(newBudget) { StorageKey = storageKey }; this.budgetBucketRepository.Initialise(new List <BudgetBucketDto>()); await SaveAsync(storageKey, false); return(this.currentBudgetCollection); }
public void Load([NotNull] BudgetModel model) { if (model == null) { throw new ArgumentNullException(nameof(model)); } this.surplus = this.budgetPieService.SurplusExpense(model); this.budgetModel = model; ExpensePieChartValues = this.budgetPieService.PrepareExpenseGraphData(model); IncomePieChartValues = this.budgetPieService.PrepareIncomeGraphData(model); Shown = true; RaisePropertyChanged(() => ExpensePieChartValues); RaisePropertyChanged(() => IncomePieChartValues); }
public void ListsAreInitialised() { var subject = new BudgetModel(); Assert.IsNotNull(subject.Incomes); Assert.IsNotNull(subject.Expenses); }
public void Load( [NotNull] StatementModel statementModel, [NotNull] BudgetModel budgetModel, [NotNull] GlobalFilterCriteria criteria, Engine.Ledger.LedgerBook ledgerBookModel) { if (statementModel == null) { throw new ArgumentNullException("statementModel"); } if (budgetModel == null) { throw new ArgumentNullException("budgetModel"); } if (criteria == null) { throw new ArgumentNullException("criteria"); } this.statement = statementModel; this.budget = budgetModel; this.ledgerBook = ledgerBookModel; this.chartBuilder.Build(criteria, statementModel, budgetModel, ledgerBookModel); this.beginDate = this.chartBuilder.Results.BeginDate; DateRangeDescription = this.chartBuilder.Results.DateRangeDescription; ChartControllers = new BindingList<BucketBurnDownController>(this.chartBuilder.Results.Charts.ToList()); RaisePropertyChanged(() => ChartControllers); }
public void Close() { Shown = false; this.budgetModel = null; CurrentExpense = null; CurrentIncome = null; this.surplus = null; ExpensePieChartValues = null; IncomePieChartValues = null; }
private void ShowOtherBudget(BudgetModel budgetToShow) { CurrentBudget = new BudgetCurrencyContext(Budgets, budgetToShow); Shown = true; Dirty = false; // Need to reset this because events fire needlessly (in this case) as a result of setting the CurrentBudget. }
/// <summary> /// Called by <see cref="LedgerBook.Reconcile" />. It builds the contents of the new ledger line based on budget and /// statement input. /// </summary> /// <param name="previousEntries"> /// A collection of previous <see cref="LedgerEntry" />s to construct the running balance for /// the entries this line contains. /// </param> /// <param name="currentBudget">The current applicable budget</param> /// <param name="statement">The current period statement.</param> /// <param name="startDateIncl">The date for this ledger line.</param> internal void AddNew(IEnumerable<KeyValuePair<LedgerColumn, LedgerEntry>> previousEntries, BudgetModel currentBudget, StatementModel statement, DateTime startDateIncl) { if (!IsNew) { throw new InvalidOperationException("Cannot add a new entry to an existing Ledger Line, only new Ledger Lines can have new entries added."); } DateTime finishDateExcl = Date; List<Transaction> filteredStatementTransactions = statement == null ? new List<Transaction>() : statement.AllTransactions.Where(t => t.Date >= startDateIncl && t.Date < finishDateExcl).ToList(); foreach (var previousEntry in previousEntries) { LedgerColumn ledger = previousEntry.Key; decimal balance = previousEntry.Value == null ? 0 : previousEntry.Value.Balance; var newEntry = new LedgerEntry(true) { Balance = balance, LedgerColumn = ledger }; Expense expenseBudget = currentBudget.Expenses.FirstOrDefault(e => e.Bucket.Code == ledger.BudgetBucket.Code); var transactions = new List<LedgerTransaction>(); if (expenseBudget != null) { var budgetedAmount = new BudgetCreditLedgerTransaction { Credit = expenseBudget.Amount, Narrative = "Budgeted Amount" }; transactions.Add(budgetedAmount); } transactions.AddRange(IncludeStatementTransactions(newEntry, filteredStatementTransactions)); newEntry.SetTransactionsForReconciliation(transactions); this.entries.Add(newEntry); } }
/// <summary> /// This is effectively stage 2 of the Reconciliation process. /// Called by <see cref="ReconciliationBuilder.CreateNewMonthlyReconciliation" />. It builds the contents of the new /// ledger line based on budget and /// statement input. /// </summary> /// <param name="budget">The current applicable budget</param> /// <param name="statement">The current period statement.</param> /// <param name="startDateIncl"> /// The date of the previous ledger line. This is used to include transactions from the /// Statement starting from this date and including this date. /// </param> private void AddNew( BudgetModel budget, StatementModel statement, DateTime startDateIncl) { if (!this.newReconciliationLine.IsNew) { throw new InvalidOperationException( "Cannot add a new entry to an existing Ledger Line, only new Ledger Lines can have new entries added."); } var reconciliationDate = this.newReconciliationLine.Date; // Date filter must include the start date, which goes back to and includes the previous ledger date up to the date of this ledger line, but excludes this ledger date. // For example if this is a reconciliation for the 20/Feb then the start date is 20/Jan and the finish date is 20/Feb. So transactions pulled from statement are between // 20/Jan (inclusive) and 19/Feb (inclusive). List<Transaction> filteredStatementTransactions = statement?.AllTransactions.Where( t => t.Date >= startDateIncl && t.Date < reconciliationDate).ToList() ?? new List<Transaction>(); IEnumerable<LedgerEntry> previousLedgerBalances = CompileLedgersAndBalances(LedgerBook); var entries = new List<LedgerEntry>(); foreach (var previousLedgerEntry in previousLedgerBalances) { LedgerBucket ledgerBucket; var openingBalance = previousLedgerEntry.Balance; var currentLedger = LedgerBook.Ledgers.Single(l => l.BudgetBucket == previousLedgerEntry.LedgerBucket.BudgetBucket); if (previousLedgerEntry.LedgerBucket.StoredInAccount != currentLedger.StoredInAccount) { // Check to see if a ledger has been moved into a new default account since last reconciliation. ledgerBucket = currentLedger; } else { ledgerBucket = previousLedgerEntry.LedgerBucket; } var newEntry = new LedgerEntry(true) { Balance = openingBalance, LedgerBucket = ledgerBucket }; List<LedgerTransaction> transactions = IncludeBudgetedAmount(budget, ledgerBucket, reconciliationDate); transactions.AddRange(IncludeStatementTransactions(newEntry, filteredStatementTransactions)); AutoMatchTransactionsAlreadyInPreviousPeriod(filteredStatementTransactions, previousLedgerEntry, transactions); newEntry.SetTransactionsForReconciliation(transactions, reconciliationDate); entries.Add(newEntry); } this.newReconciliationLine.SetNewLedgerEntries(entries); CreateBalanceAdjustmentTasksIfRequired(); if (statement != null) { AddBalanceAdjustmentsForFutureTransactions(statement, reconciliationDate); } CreateTasksToTransferFundsIfPaidFromDifferentAccount(filteredStatementTransactions); }
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); }
private static decimal GetLedgerBalanceForBucket(BudgetModel budgetModel, LedgerEntryLine applicableLine, BudgetBucket bucket) { if (bucket is SurplusBucket) { return applicableLine.CalculatedSurplus; } var ledger = applicableLine.Entries.FirstOrDefault(e => e.LedgerBucket.BudgetBucket == bucket); if (ledger != null) { return ledger.Balance; } // The Ledger line might not actually have a ledger for the given bucket. return GetBudgetModelTotalForBucket(budgetModel, bucket); }
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; }
private static decimal GetBudgetModelTotalForBucket(BudgetModel budgetModel, BudgetBucket bucket) { if (bucket is PayCreditCardBucket) { // Ignore return 0; } if (bucket is IncomeBudgetBucket) { throw new InvalidOperationException( "Code Error: Income bucket detected when Bucket Spending only applies to Expenses."); } // Use budget values instead if (bucket is SurplusBucket) { return budgetModel.Surplus; } var budget = budgetModel.Expenses.FirstOrDefault(e => e.Bucket == bucket); if (budget != null) { return budget.Amount; } return 0; }
private List<LedgerTransaction> IncludeBudgetedAmount(BudgetModel currentBudget, LedgerBucket ledgerBucket, DateTime reconciliationDate) { var budgetedExpense = currentBudget.Expenses.FirstOrDefault(e => e.Bucket.Code == ledgerBucket.BudgetBucket.Code); var transactions = new List<LedgerTransaction>(); if (budgetedExpense != null) { BudgetCreditLedgerTransaction budgetedAmount; if (ledgerBucket.StoredInAccount.IsSalaryAccount) { budgetedAmount = new BudgetCreditLedgerTransaction { Amount = budgetedExpense.Bucket.Active ? budgetedExpense.Amount : 0, Narrative = budgetedExpense.Bucket.Active ? "Budgeted Amount" : "Warning! Bucket has been disabled." }; } else { budgetedAmount = new BudgetCreditLedgerTransaction { Amount = budgetedExpense.Bucket.Active ? budgetedExpense.Amount : 0, Narrative = budgetedExpense.Bucket.Active ? "Budget amount must be transferred into this account with a bank transfer, use the reference number for the transfer." : "Warning! Bucket has been disabled.", AutoMatchingReference = ReferenceNumberGenerator.IssueTransactionReferenceNumber() }; // TODO Maybe the budget should know which account the incomes go into, perhaps mapped against each income? var salaryAccount = this.newReconciliationLine.BankBalances.Single(b => b.Account.IsSalaryAccount).Account; this.toDoList.Add( new TransferTask( string.Format( CultureInfo.CurrentCulture, "Budgeted Amount for {0} transfer {1:C} from Salary Account to {2} with auto-matching reference: {3}", budgetedExpense.Bucket.Code, budgetedAmount.Amount, ledgerBucket.StoredInAccount, budgetedAmount.AutoMatchingReference), true) { Amount = budgetedAmount.Amount, SourceAccount = salaryAccount, DestinationAccount = ledgerBucket.StoredInAccount, BucketCode = budgetedExpense.Bucket.Code, Reference = budgetedAmount.AutoMatchingReference }); } budgetedAmount.Date = reconciliationDate; transactions.Add(budgetedAmount); } return transactions; }
public void TestInitialise() { this.testDataBudget = BudgetModelTestData.CreateTestData1(); this.testDataStatement = StatementModelTestData.TestData1(); this.subject = LedgerBookTestData.TestData1(); }
public void AfterConstructionEffectiveDateIsValidDate() { var subject = new BudgetModel(); Assert.AreNotEqual(DateTime.MinValue, subject.EffectiveFrom); }
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; }
public void AfterConstructionLastModifiedDateIsValidDate() { var subject = new BudgetModel(); Assert.AreNotEqual(DateTime.MinValue, subject.LastModified); }
/// <summary> /// Creates a new LedgerEntryLine for this <see cref="LedgerBook" />. /// </summary> /// <param name="reconciliationDate"> /// The startDate for the <see cref="LedgerEntryLine" />. This is usually the previous Month's "Reconciliation-Date", /// as this month's reconciliation starts with this date and includes transactions /// from that date. This date is different to the "Reconciliation-Date" that appears next to the resulting /// reconciliation which is the end date for the period. /// </param> /// <param name="budget">The current budget.</param> /// <param name="statement">The currently loaded statement.</param> /// <param name="currentBankBalances"> /// The bank balances as at the reconciliation date to include in this new single line of the /// ledger book. /// </param> internal virtual ReconciliationResult Reconcile(DateTime reconciliationDate, BudgetModel budget, StatementModel statement, params BankBalance[] currentBankBalances) { var newRecon = this.reconciliationBuilder.CreateNewMonthlyReconciliation(reconciliationDate, budget, statement, currentBankBalances); this.reconciliations.Insert(0, newRecon.Reconciliation); return newRecon; }