/// <summary> /// Import the given file. It is recommended to call <see cref="IBankStatementImporterRepository.CanImportAsync" /> /// first. /// If the file cannot /// be imported by any of this repositories importers a <see cref="NotSupportedException" /> will be thrown. /// </summary> public async Task<StatementModel> ImportAsync(string fullFileName, Account account) { foreach (var importer in this.importers) { if (await importer.TasteTestAsync(fullFileName)) { return await importer.LoadAsync(fullFileName, account); } } throw new NotSupportedException("The requested file name cannot be loaded. It is not of any known format."); }
/// <summary> /// Load the given file into a <see cref="StatementModel" />. /// </summary> /// <param name="fileName">The file to load.</param> /// <param name="account"> /// The account to classify these transactions. This is useful when merging one statement to another. For example, /// merging a cheque account export with visa account export, each can be classified using an account. /// </param> public async Task<StatementModel> LoadAsync(string fileName, Account account) { try { this.importUtilities.AbortIfFileDoesntExist(fileName); } catch (FileNotFoundException ex) { throw new KeyNotFoundException(ex.Message, ex); } var transactions = new List<Transaction>(); var firstTime = true; foreach (var line in await ReadLinesAsync(fileName)) { if (firstTime) { // File contains column headers firstTime = false; continue; } if (string.IsNullOrWhiteSpace(line)) { continue; } string[] split = line.Split(','); var transaction = new Transaction { Account = account, Description = this.importUtilities.FetchString(split, DescriptionIndex), Reference1 = this.importUtilities.FetchString(split, Reference1Index), Reference2 = this.importUtilities.FetchString(split, Reference2Index), Reference3 = this.importUtilities.FetchString(split, Reference3Index), Amount = this.importUtilities.FetchDecimal(split, AmountIndex), Date = this.importUtilities.FetchDate(split, DateIndex) }; transaction.TransactionType = FetchTransactionType(split, transaction.Amount); transactions.Add(transaction); } var statement = new StatementModel(this.logger) { StorageKey = fileName, LastImport = DateTime.Now }.LoadTransactions(transactions); return statement; }
public async Task<StatementModel> ImportBankStatementAsync( string storageKey, Account account) { if (string.IsNullOrWhiteSpace(storageKey)) { throw new ArgumentNullException(nameof(storageKey)); } if (account == null) { throw new ArgumentNullException(nameof(account)); } return await this.importerRepository.ImportAsync(storageKey, account); }
public LedgerBucket Build(string bucketCode, Account account) { var bucket = this.bucketRepo.GetByCode(bucketCode); if (bucket is SavedUpForExpenseBucket) { return new SavedUpForLedger { BudgetBucket = bucket, StoredInAccount = account }; } if (bucket is SpentMonthlyExpenseBucket) { return new SpentMonthlyLedger { BudgetBucket = bucket, StoredInAccount = account }; } if (bucket is SavingsCommitmentBucket) { return new SavedUpForLedger { BudgetBucket = bucket, StoredInAccount = account }; } throw new NotSupportedException( $"Unsupported budget bucket {bucketCode} with type {bucket.GetType().Name}, found in ledger book"); }
public LedgerTransaction CreateBalanceAdjustment(LedgerEntryLine entryLine, decimal amount, string narrative, Account account) { if (entryLine == null) { throw new ArgumentNullException(nameof(entryLine)); } if (narrative == null) { throw new ArgumentNullException(nameof(narrative)); } if (account == null) { throw new ArgumentNullException(nameof(account)); } var adjustmentTransaction = entryLine.BalanceAdjustment(amount, narrative, account); adjustmentTransaction.Date = entryLine.Date; return adjustmentTransaction; }
private void Reset() { this.parentBook = null; Date = DateTime.MinValue; BankBalances = null; BankAccounts = null; SelectedBankAccount = null; }
private void AddNewBankBalance() { BankBalances.Add(new BankBalanceViewModel(null, SelectedBankAccount, BankBalance)); SelectedBankAccount = null; BankBalance = 0; RaisePropertyChanged(() => HasRequiredBalances); }
private decimal TotalBankBalanceAdjustmentForAccount(Account account) { return BankBalanceAdjustments.Where(a => a.BankAccount == account).Sum(a => a.Amount); }
internal BankBalanceAdjustmentTransaction BalanceAdjustment(decimal adjustment, string narrative, Account account) { if (!IsNew) { throw new InvalidOperationException( "Cannot adjust existing ledger lines, only newly added lines can be adjusted."); } if (adjustment == 0) { throw new ArgumentException("The balance adjustment amount cannot be zero.", nameof(adjustment)); } var newAdjustment = new BankBalanceAdjustmentTransaction { Date = Date, Narrative = narrative, Amount = adjustment, BankAccount = account }; this.bankBalanceAdjustments.Add(newAdjustment); return newAdjustment; }
/// <summary> /// Used to allow the UI to set a ledger's account, but only if it is an instance in the <see cref="Ledgers" /> /// collection. /// </summary> /// <param name="ledger"></param> /// <param name="storedInAccount"></param> internal void SetLedgerAccount(LedgerBucket ledger, Account storedInAccount) { if (Ledgers.Any(l => l == ledger)) { ledger.StoredInAccount = storedInAccount; return; } throw new InvalidOperationException( "You cannot change the account in a ledger that is not in the Ledgers collection."); }
private void Reset() { this.ledger = null; MonthlyBudgetAmount = 0; BankAccounts.Clear(); StoredInAccount = null; }
/// <summary> /// Imports a bank's transaction extract and merges it with the currently loaded Budget Analyser Statement. /// This method should not be used without a <see cref="StatementModel" /> loaded. /// It is recommended to follow this up with <see cref="ValidateWithCurrentBudgetsAsync" />. /// </summary> /// <exception cref="System.ArgumentNullException"> /// </exception> /// <exception cref="System.InvalidOperationException"> /// There are no transactions loaded, you must first load an existing /// file or create a new one. /// </exception> /// <exception cref="BudgetAnalyser.Engine.Statement.TransactionsAlreadyImportedException"></exception> public async Task ImportAndMergeBankStatementAsync(string storageKey, Account account) { if (storageKey.IsNothing()) { throw new ArgumentNullException(nameof(storageKey)); } if (account == null) { throw new ArgumentNullException(nameof(account)); } if (StatementModel == null) { throw new InvalidOperationException( "There are no transactions loaded, you must first load an existing file or create a new one."); } var additionalModel = await this.statementRepository.ImportBankStatementAsync(storageKey, account); var combinedModel = StatementModel.Merge(additionalModel); IEnumerable<IGrouping<int, Transaction>> duplicates = combinedModel.ValidateAgainstDuplicates(); if (duplicates.Count() == additionalModel.AllTransactions.Count()) { throw new TransactionsAlreadyImportedException(); } StatementModel.Dispose(); StatementModel = combinedModel; NewDataAvailable(); }
public BudgetBucketChosenEventArgs(Guid correlationId, BudgetBucket bucket, Account storeInThisAccount) : this(correlationId, bucket) { StoreInThisAccount = storeInThisAccount; }
private void ShowDialogCommon(string title) { Canceled = false; List<Account> accountsToShow = this.accountTypeRepository.ListCurrentlyUsedAccountTypes().ToList(); BankAccounts = accountsToShow.OrderBy(a => a.Name); SelectedBankAccount = null; this.dialogCorrelationId = Guid.NewGuid(); var dialogRequest = new ShellDialogRequestMessage(BudgetAnalyserFeature.LedgerBook, this, ShellDialogType.OkCancel) { CorrelationId = this.dialogCorrelationId, Title = title, HelpAvailable = CreateMode }; MessengerInstance.Send(dialogRequest); }
public void ShowDialog([NotNull] Engine.Ledger.LedgerBook parentLedgerBook, [NotNull] LedgerBucket ledgerBucket, [NotNull] BudgetModel budgetModel) { if (parentLedgerBook == null) { throw new ArgumentNullException(nameof(parentLedgerBook)); } if (ledgerBucket == null) { throw new ArgumentNullException(nameof(ledgerBucket)); } if (budgetModel == null) { throw new ArgumentNullException(nameof(budgetModel)); } if (LedgerBucketHistoryAnalysis == null) { LedgerBucketHistoryAnalysis = CreateBucketHistoryAnalyser(); } LedgerBucketHistoryAnalysis.Analyse(ledgerBucket, parentLedgerBook); this.ledger = ledgerBucket; BankAccounts = new ObservableCollection<Account>(this.accountRepo.ListCurrentlyUsedAccountTypes()); BucketBeingTracked = ledgerBucket.BudgetBucket; StoredInAccount = ledgerBucket.StoredInAccount; MonthlyBudgetAmount = budgetModel.Expenses.Single(e => e.Bucket == BucketBeingTracked).Amount; this.correlationId = Guid.NewGuid(); var dialogRequest = new ShellDialogRequestMessage(BudgetAnalyserFeature.LedgerBook, this, ShellDialogType.OkCancel) { CorrelationId = this.correlationId, Title = "Ledger - " + BucketBeingTracked, HelpAvailable = true }; MessengerInstance.Send(dialogRequest); }
/// <summary> /// Initializes a new instance of the <see cref="BankBalance" /> class. /// </summary> public BankBalance(Account account, decimal balance) { Account = account; Balance = balance; }
private void Reset() { if (this.filtered) { BudgetBuckets = this.bucketRepository.Buckets.ToList(); } Selected = null; StoreInThisAccount = null; }