/// <summary> /// Find the best category match for given payee and amount from past history of transactions and splits. /// </summary> /// <param name="t">The current transaction</param> /// <param name="payeeOrTransferCaption">The payee we are matching</param> /// <returns></returns> public static object AutoCategoryMatch(Transaction t, string payeeOrTransferCaption) { MyMoney money = t.MyMoney; object found = null; Account a = t.Account; if (a != null) { AutoCategorization ac = new AutoCategorization(); found = ac.FindPreviousTransactionByPayee(a, t, payeeOrTransferCaption); if (found == null) { // try other accounts; foreach (Account other in money.payeeAccountIndex.FindAccountsRelatedToPayee(payeeOrTransferCaption)) { found = ac.FindPreviousTransactionByPayee(other, t, payeeOrTransferCaption); if (found != null) { break; } } } } return(found); }
public void Save(MyMoney money) { StreamWriter writer = null; try { writer = new StreamWriter(this.fileName); WriteTransactionHeader(writer); if (rows != null) { foreach (Transaction t in rows) { if (t != null) { WriteTransaction(writer, t); } } } } finally { if (writer != null) { writer.Close(); writer = null; } } }
/// <summary> /// Import the Transactions & accounts in the given file and return the first account. /// </summary> /// <param name="file"></param> /// <param name="count"></param> /// <returns></returns> public static int ImportCsv(MyMoney myMoney, Account acct, string file) { var uri = new Uri(file); var csvReader = new CsvReader(uri, Encoding.UTF8, null, 4096); csvReader.Delimiter = ','; int total = 0; while (csvReader.Read()) { if (csvReader.FieldCount != 3) { throw new NotSupportedException("Invalid CSV format expecting 3 columns [Date, Payee, Amount]"); } var field1Date = csvReader[0]; var field2Payee = csvReader[1]; var field3Amount = csvReader[2]; if (total == 0) { if (field1Date != "Date" || field2Payee != "Payee" || field3Amount != "Amount") { throw new NotSupportedException("Invalid CSV format The fist row is expected to be the header [Date, Payee, Amount]"); } } else { var dateTokens = field1Date.Split('-'); if (dateTokens.Length != 3) { throw new NotSupportedException("Invalid CSV format The Date needs to be specified in ISO8601 YYYY-MM-DD format : " + field1Date); } if (dateTokens[0].Length != 4) { throw new NotSupportedException("Invalid CSV format The Date Year must be 4 digits : " + field1Date); } Transaction t = myMoney.Transactions.NewTransaction(acct); t.Id = -1; t.Date = DateTime.Parse(field1Date); t.Payee = t.Payee = myMoney.Payees.FindPayee(field2Payee, true); t.Amount = decimal.Parse(field3Amount); myMoney.Transactions.Add(t); } total++; } csvReader.Close(); return(total); }
public static decimal CalculateTheBalances(MyMoney money, Account account, ObservableCollection <LoanPaymentAggregation> loanPayementsView) { decimal runningBalance = 0; foreach (LoanPaymentAggregation l in loanPayementsView) { //------------------------------------------------------------- // Check to see if we need to re-calculate // the Principal & Interest amounts using the Percentage // if (l.Principal == 0 && l.Interest == 0 && l.Percentage != 0) { // // Recalculate the Interest using the Percentage // l.Interest = runningBalance * (l.Percentage / 100) / 12; // and the Principal if we know the total original Payment l.Principal = l.Payment - l.Interest; } if (l.Interest != 0 && runningBalance != 0) { // Reverse calculation of the interest rate LoanPaymentAggregation.CalculatePercentageOfInterest(runningBalance, l); } // Reduce the debt by the amount of the Principal paid // // -1 will reverse the amount if this is a mortgage it will change this to -900 * -1 == 900 // runningBalance += l.Principal * -1; // Snap shot the balance due at each payment l.Balance = runningBalance; } return(runningBalance); }
private object FindPreviousTransactionByPayee(Account a, Transaction t, string payeeOrTransferCaption) { MyMoney money = t.MyMoney; IList <Transaction> list = money.Transactions.GetTransactionsFrom(a); int len = list.Count; if (len == 0) { // Nothing to do here return(null); } // Tally of how close the current transaction is to the amounts for a given category or split. singleNeighbors = new KNearestNeighbor <Category>(); splitNeighbors = new KNearestNeighbor <Category>(); splitCount = normalCount = 0; Transaction closestByDate = null; long ticks = 0; decimal amount = t.Amount; for (int i = 0; i < len; i++) { Transaction u = list[i] as Transaction; if (amount == 0) { // we can't use the probabilities when the amount is zero, so we just return // the closest transaction by date because in the case of something like a paycheck // the most recent paycheck usually has the closest numbers on the splits. long newTicks = Math.Abs((u.Date - t.Date).Ticks); if (closestByDate == null || newTicks < ticks) { closestByDate = u; ticks = newTicks; } } else { AddPossibility(t, u, payeeOrTransferCaption); } } if (closestByDate != null) { return(closestByDate); } IEnumerable <Tuple <object, Category> > result = null; if (splitCount > normalCount) { result = splitNeighbors.GetNearestNeighbors(1, t.Amount); } else { result = singleNeighbors.GetNearestNeighbors(1, t.Amount); } if (result != null && result.Any()) { var first = result.First(); var it = first.Item1 as Transaction; if (it != null && it.IsSplit) { closestByDate = null; ticks = 0; // if this is a "Split" transaction, then we should grab the closest date // so that the copied splits have the best chance of matching. // (e.g. in a split paycheck scenario) foreach (var u in list) { if (u.IsSplit && u.PayeeOrTransferCaption == t.PayeeOrTransferCaption) { long newTicks = Math.Abs((u.Date - t.Date).Ticks); if (closestByDate == null || newTicks < ticks) { closestByDate = u; ticks = newTicks; } } } return(closestByDate); } return(first.Item1); } return(null); }
/// <summary> /// Compute capital gains associated with stock sales and whether they are long term or short term gains. /// </summary> /// <param name="money">The transactions</param> /// <param name="year">The year for the report</param> public CostBasisCalculator(MyMoney money, DateTime toDate) { this.myMoney = money; this.toDate = toDate; Calculate(); }
/// <summary> /// Loans are built from 2 Categories - Principal + Interested /// </summary> /// <param name="toDate"></param> static public ObservableCollection <LoanPaymentAggregation> GetLoanPayementsAggregation(MyMoney money, Account a) { ObservableCollection <LoanPaymentAggregation> view = new ObservableCollection <LoanPaymentAggregation>(); Category categoryPrincipal = a.CategoryForPrincipal; Category categoryInterest = a.CategoryForInterest; //----------------------------------------------------------------- // Get the loan transaction related to the 2 categories set // to this account. Category for Principal & Category for Interest // foreach (Transaction t in money.Transactions.GetAllTransactions()) { if (t.Splits != null && t.Splits.Count > 0) { foreach (Split s in t.Splits) { if (s.Category != null) { AddPaymentIfMatchingCategoriesForPrincipalOrInterest(view, s.Category, categoryPrincipal, categoryInterest, t, s, t.Account, t.Date, s.Amount); } } } else { if (t.Category != null) { AddPaymentIfMatchingCategoriesForPrincipalOrInterest(view, t.Category, categoryPrincipal, categoryInterest, t, null, t.Account, t.Date, t.Amount); } } } //----------------------------------------------------------------- // Additional manual entry made for this Loan // foreach (LoanPayment l in money.LoanPayments) { if (!l.IsDeleted) { if (l.AccountId == a.Id) { LoanPaymentAggregation lp = new LoanPaymentAggregation(); lp.Account = a; lp.Date = l.Date; lp.Principal = l.Principal; lp.Interest = l.Interest; lp.Payment = l.Principal + l.Interest; lp.LoanPayementManualEntry = l; view.Add(lp); } } } //----------------------------------------------------------------- // Sort and recalculate the running balance for all the payment made // to this Loan // var sorted = from item in view orderby item.Date ascending select item; ObservableCollection <LoanPaymentAggregation> list = new ObservableCollection <LoanPaymentAggregation>(sorted); // cannot carry a credit on a loan account, so if the balance is > 0 make it 0. a.Balance = CalculateTheBalances(money, a, list); return(list); }