Skip to content

Commit

Permalink
Support TIAA csv import
Browse files Browse the repository at this point in the history
  • Loading branch information
rrelyea committed Jul 4, 2024
1 parent ff1a73b commit e76e9cb
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 2 deletions.
21 changes: 21 additions & 0 deletions Pages/Import.razor
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ else
<a href='/import/other'>other</a> |
<a href='/import/RaymondJames'>Raymond&nbsp;James</a> |
<a href='/import/TRowePrice'>T.&nbsp;Rowe&nbsp;Price</a></span><br/>
<a href='/import/TIAA'>TIAA</a><br/>
</div>

@switch (Custodian)
Expand Down Expand Up @@ -103,6 +104,26 @@ else
</ol>
</p>
break;
case "TIAA":
<h3>Import from @Custodian:</h3>
<p>
<ol>
<li>Login to <a target=_new href=https://tiaa.org>tiaa.org</a>↗️</li>
<li>Click on "Accounts" and then choose the subgroup of accounts whose holdings you wish to download (e.g. "retirement")</li>
<li>Click the "Investments" tab to download holdings for all accounts; otherwise choose the specific account whose holdings you wish to download and only then click the "Investments" tab</li>
<li>At the top right of that tab find the link "Export data" and click on it</li>
<li>Click the choice for ".csv" file as the type of file to export</li>
<li>Click "Choose File" button, find and choose the "investments.csv" file in Downloads folder</li>
<fieldset style=margin-left:20px>
<InputFile multiple accept=".csv" OnChange="@((e) => OnDataFilesImport(e))" /><br/>
<ul>
<li>CAUTION: only share personal info (like these files) to apps worthy of your trust! See our privacy details in our <a href=/help>Help/FAQ page</a></li>
</ul>
</fieldset><br/>
<li>Choose Account Type for each new account</li>
</ol>
</p>
break;
case "TreasuryDirect":
<h3>Import from @Custodian:</h3>
<p>
Expand Down
77 changes: 75 additions & 2 deletions library/Models/Importer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,13 @@ public static async Task<List<Account>> ImportCSV(Stream stream, FamilyData fami
{
return await ImportAmeriprise(RowEnumerator, familyData);
}
else if (headerChunks[0].StartsWith("Data As of "))
{
return await ImportTIAA(RowEnumerator, familyData);
}
else
{
throw new InvalidDataException("Importing this CSV file failed. We currently support importing CSV files from Ameriprise, eTrade, Fidelity, JPMorgan Chase, Merrill Edge, Schwab, or Vanguard");
throw new InvalidDataException("Importing this CSV file failed. We currently support importing CSV files from Ameriprise, eTrade, Fidelity, JPMorgan Chase, Merrill Edge, Schwab, TIAA, or Vanguard");
}
}
catch (Exception e)
Expand All @@ -129,7 +133,7 @@ public static async Task<List<Account>> ImportCSV(Stream stream, FamilyData fami
}
else
{
throw new InvalidDataException("Importing this CSV file failed, even though we think it should have worked (we think it was from Ameriprise, eTrade, Fidelity, JPMorgan Chase, Merrill Edge, Schwab, or Vanguard).", e);
throw new InvalidDataException("Importing this CSV file failed, even though we think it should have worked (we think it was from Ameriprise, eTrade, Fidelity, JPMorgan Chase, Merrill Edge, Schwab, TIAA, or Vanguard).", e);
}
}
}
Expand Down Expand Up @@ -644,6 +648,75 @@ private static async Task<List<Account>> ImportSchwab(IAsyncEnumerator<string[]>
return importedAccounts;
}

private static async Task<List<Account>> ImportTIAA(IAsyncEnumerator<string[]> rowEnumerator, FamilyData familyData)
{
List<Account> importedAccounts = [];
string[] firstLineChunks = rowEnumerator.Current;
bool singleAccountFormat = false;

await rowEnumerator.MoveNextAsync(); // goto headers line
await rowEnumerator.MoveNextAsync(); // go to next line

bool stillProcessing = true;
Account? newAccount = null;

while (stillProcessing)
{
string[] chunks = rowEnumerator.Current;
Console.WriteLine($"{chunks.Length},{chunks[0]}");
if (chunks.Length <3 || newAccount == null)
{
newAccount = new(familyData)
{
Custodian = "TIAA",
Note = chunks[0],
};

importedAccounts.Add(newAccount);

if (chunks.Length < 3)
{
await rowEnumerator.MoveNextAsync(); // go to next line
await rowEnumerator.MoveNextAsync(); // go to next line
continue;
}
}

string? symbol = null;
string? name = null;
var nameAndSymbol = chunks[0];
if (nameAndSymbol == "All Investments" || nameAndSymbol.StartsWith("*Reflects the change"))
{
await rowEnumerator.MoveNextAsync();
break;
}
else
{
var indexOfParen = nameAndSymbol.IndexOf('(');
var indexOfClose = nameAndSymbol.IndexOf(")");
symbol = nameAndSymbol.Substring(indexOfParen + 1, indexOfClose - indexOfParen - 1);
name = nameAndSymbol.Substring(0,indexOfParen).Trim();
}

double shares = FormatUtilities.ParseDouble(chunks[3]);
double value = FormatUtilities.ParseDouble(chunks[1], allowCurrency: true);
AssetTypes? assetType = null;

string? investmentName = name;

Investment newInvestment = new() { Funds = familyData.AppData.Funds, Ticker = symbol, Name = (investmentName ?? null), Value = value, Shares = shares };
if (assetType != null)
{
newInvestment.AssetType = assetType;
}

newAccount.Investments.Add(newInvestment);

stillProcessing = await rowEnumerator.MoveNextAsync();
}

return importedAccounts;
}

private static async Task<List<Account>> ImportTRowePrice(IAsyncEnumerator<string[]> rowEnumerator, FamilyData familyData)
{
Expand Down

0 comments on commit e76e9cb

Please sign in to comment.