public static AmazonStatement ReadAmazonCSV(string filepath) { AmazonStatement ret = new AmazonStatement(filepath); string[] lines = File.ReadAllText(filepath).Split('\n'); AmazonTransaction prevEntry = null; foreach (string line in lines) { List <string> parts = FileHelpers.ParseCSVLine(line); if (parts.Count < 30) { continue; } DateTime?date = TryToScanDate(parts[0], null, null); if (!date.HasValue) { continue; } string amountStr = parts[29].Substring(1); double.TryParse(amountStr, out double amount); //Build an entry for this line by itself AmazonTransaction subEntry = new AmazonTransaction { Date = date.Value, Amount = -1 * amount, Description = parts[2], Category = parts[3], OrderNumber = parts[1] }; //Special code here so we can combine multiple transactions with the same order number into one final transaction with sub-transactions AmazonTransaction wrapper = null; if (prevEntry != null) { //If we had an entry in progress and this entry shares the same order number, add this entry as a sub-transaction if (prevEntry.Items[0].OrderNumber.Equals(subEntry.OrderNumber)) { wrapper = prevEntry; wrapper.Amount += subEntry.Amount; wrapper.Category += ";" + subEntry.Category; wrapper.Description += ";" + subEntry.Description; } else { //Commit the previous entry, the new entry starts a new order ret.Transactions.Add(prevEntry); prevEntry = null; } } if (prevEntry == null) { //Create a new wrapper to hold the sub-transactions wrapper = new AmazonTransaction { Date = date.Value, Amount = -1 * amount, Description = parts[2], Category = parts[3], OrderNumber = parts[1], }; } wrapper.Items.Add(subEntry); prevEntry = wrapper; } if (prevEntry != null) { ret.Transactions.Add(prevEntry); } return(ret); }
public static List <string> Load(string directory) { List <string> warnings = new List <string>(); string[] directories = Directory.GetDirectories(directory); Accounts = new List <Account>(); AllStatements = new Dictionary <string, List <Statement> >(); AmazonStatements = new List <AmazonStatement>(); foreach (string subDirectory in directories) { string directoryName = Path.GetFileNameWithoutExtension(subDirectory); bool isAmazon = directoryName.Equals(Constants.AmazonLabel); if (!isAmazon) { AllStatements[directoryName] = new List <Statement>(); } foreach (string file in Directory.GetFiles(subDirectory)) { if (isAmazon) { AmazonStatement statement = ReadAmazonCSV(file); if (statement != null) { AmazonStatements.Add(statement); Console.WriteLine(" Read Amazon statement {0} with {1} transactions", Path.GetFileName(file), statement.Transactions.Count); } } else { Statement statement = LoadStatementFromCache(file) ?? ReadStatementFile(file, directoryName); if (statement == null) { continue; } AllStatements[directoryName].Add(statement); double balance = statement.StartingBalance; foreach (Transaction entry in statement.Transactions) { if (entry.TypeId <= 0) { TransactionType transactionType = TypeManager.Identify(entry.Description); if (transactionType != null) { entry.TypeId = transactionType.Id; } } balance += entry.CheckingAmount + entry.SavingsAmount + entry.CreditAmount; } if (Math.Abs(balance - statement.EndingBalance) > 0.01) { string warning = $"Ending balance mismatch: {balance} vs. stated {statement.EndingBalance}"; warnings.Add(warning); Console.WriteLine(warning); } SaveToCache(statement); Console.WriteLine(" Read statement {0} with {1} transactions (${2} --> ${3}, error={4:0.00})", Path.GetFileName(file), statement.Transactions.Count, statement.StartingBalance, statement.EndingBalance, balance - statement.EndingBalance); } } if (isAmazon) { Console.WriteLine("Finished reading {0} directory", directoryName); } else { Console.WriteLine("Finished reading {0} directory ({1} --> {2})", directoryName, AllStatements[directoryName].First().StartingBalance, AllStatements[directoryName].Last().EndingBalance); } } return(warnings); }