Last active
April 15, 2025 07:09
-
-
Save wizpig64/d0ec4e5bb3735ffe47b7c22ba5d2a91c to your computer and use it in GitHub Desktop.
Import 1099-B into Cash App Taxes (formerly Credit Karma Tax) via CSV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// credit_karma_1099B_csv.js | |
// copyright Phillip Marshall 2019-2025, except lines 28-29, with help from kirill578 | |
// --- README --- | |
// Cash App offers a free tax filing service (formerly under Credit Karma's brand), | |
// but requires you to enter Capital Gains and Losses manually in 'Spreadsheet entry' mode. | |
// This script implements the missing CSV import feature for 1099-B gains and losses. | |
// The csv this was based on came from a wealthfront xls, exported with mm/dd/yyyy dates and NO HEADER. | |
// Here are the first few lines from mine so you can make sure yours lines up: | |
// "2 ISHARES TR NATIONAL MUN E TF",01/30/2017,03/13/2018,216.7,216.72,,0,-0.02,Long-term | |
// "2 ISHARES TR NATIONAL MUN E TF",Various,09/14/2018,216.12,216.27,,0,-0.15,Long-term | |
// "1 ISHARES TR NATIONAL MUN E TF",03/09/2017,09/20/2018,107.7,107.72,,0,-0.02,Long-term | |
// "1 ISHARES INC CORE MSCI EMKT",03/08/2018,04/09/2018,57.53,58.61,,0,-1.08,Short-term | |
// "1 ISHARES INC CORE MSCI EMKT",11/28/2017,04/24/2018,57.07,57.16,,0,-0.09,Short-term | |
// And the original header, since I removed it... | |
// "Description of property","Date acquired","Date sold","Sales price",Cost,Code(s),"Amount of adjustment","Gain (Loss)","Holding period" | |
// To use, paste the whole script into your developer console when on this page: | |
// https://taxes.cash.app/r/capital-gains/spreadsheet | |
// A button will appear at the top of the page to select your CSV(s). | |
// Pick it, and the form will fill itself out. | |
/// --- END README --- | |
// vendored evanplaice/jquery-csv (MIT license): | |
function b(t,n,i=e=>e){let e=Object.create(null);e.options=n||{},e.reviver=i,e.value="",e.entry=[],e.output=[],e.col=1,e.row=1;let l=/"|,|\r\n|\n|\r|[^",\r\n]+/y,a=/^(\r\n|\n|\r)$/,u=[],o="",r=0;for(;(u=l.exec(t))!==null;)switch(o=u[0],r){case 0:switch(!0){case o==='"':r=3;break;case o===",":r=0,s(e);break;case a.test(o):r=0,s(e),c(e);break;default:e.value+=o,r=2;break}break;case 2:switch(!0){case o===",":r=0,s(e);break;case a.test(o):r=0,s(e),c(e);break;default:throw r=4,Error(`CSVError: Illegal state [row:${e.row}, col:${e.col}]`)}break;case 3:switch(!0){case o==='"':r=4;break;default:r=3,e.value+=o;break}break;case 4:switch(!0){case o==='"':r=3,e.value+=o;break;case o===",":r=0,s(e);break;case a.test(o):r=0,s(e),c(e);break;default:throw Error(`CSVError: Illegal state [row:${e.row}, col:${e.col}]`)}break}return e.entry.length!==0&&(s(e),c(e)),e.output}function w(t,n={},i=e=>e){let e=Object.create(null);e.options=n,e.options.eof=e.options.eof!==void 0?e.options.eof:!0,e.row=1,e.col=1,e.output="";let l=/"|,|\r\n|\n|\r/;return t.forEach((a,u)=>{let o="";switch(e.col=1,a.forEach((r,f)=>{typeof r=="string"&&(r=r.replace(/"/g,'""'),r=l.test(r)?`"${r}"`:r),o+=i(r,e.row,e.col),f!==a.length-1&&(o+=","),e.col++}),!0){case e.options.eof:case(!e.options.eof&&u!==t.length-1):e.output+=`${o} | |
`;break;default:e.output+=`${o}`;break}e.row++}),e.output}function s(t){let n=t.options.typed?p(t.value):t.value;t.entry.push(t.reviver(n,t.row,t.col)),t.value="",t.col++}function c(t){t.output.push(t.entry),t.entry=[],t.row++,t.col=1}function p(t){let n=/.\./;switch(!0){case t==="true":case t==="false":return t==="true";case n.test(t):return parseFloat(t);case isFinite(t):return parseInt(t);default:return t}}; | |
form = document.querySelector('form[name="capital-gains.1099B-spreadsheet"]'); | |
lastUiIndex = 0; | |
function do_csv(event) { | |
// add message | |
document.getElementById("runtimemessage").textContent = "Filling out form..."; | |
// read file | |
const csvfile = document.getElementById('csvfile').files[0]; | |
const reader = new FileReader(); | |
reader.readAsText(csvfile, 'UTF-8'); | |
reader.onload = function (e) { | |
// load file content into 2d array: | |
const content = b(e.target.result); | |
// ask for some extra rows. | |
for (let i = 0; i < content.length / 10; i++) { | |
Array.from(form.querySelectorAll("button")).find(button => button.textContent.includes("Add Rows")).click(); | |
} | |
function simulateInput(name, value) { | |
const el = document.getElementsByName(name)[0]; | |
if (!el) return; | |
el.focus(); | |
if (!el.setRangeText) { | |
// for dropdown: | |
el.value = value; | |
} else { | |
// for text fields; | |
el.setRangeText(value); | |
} | |
el.dispatchEvent(new KeyboardEvent("keydown", { key: "1", bubbles: true })); | |
el.dispatchEvent(new KeyboardEvent("keyup", { key: "1", bubbles: true })); | |
el.dispatchEvent(new Event("input", { bubbles: true })); | |
el.dispatchEvent(new Event("change", { bubbles: true })); | |
el.blur(); | |
} | |
// fill in the rows | |
content.forEach((item, index) => { | |
let i = index + lastUiIndex; | |
// belongsTo: see kirill578's comment to this gist for spouses filing jointly | |
// simulateInput(`capitalGains[${i}].belongsTo`, belongsTo); | |
// reportingCategory | |
simulateInput(`capitalGains[${i}].reportingCategory`, content[i][8] === "Short-term" ? 1 : 4); | |
// description | |
simulateInput(`capitalGains[${i}].description`, content[i][0]); | |
// dateAcquired | |
simulateInput(`capitalGains[${i}].dateAcquired`, content[i][1]); | |
// dateSold | |
simulateInput(`capitalGains[${i}].dateSold`, content[i][2]); | |
// salesPrice | |
simulateInput(`capitalGains[${i}].salesPrice`, content[i][3]); | |
// cost | |
simulateInput(`capitalGains[${i}].cost`, content[i][4]); | |
// adjustmentCode | |
simulateInput(`capitalGains[${i}].adjustmentCode`, content[i][5]); | |
// adjustmentAmount | |
simulateInput(`capitalGains[${i}].adjustmentAmount`, content[i][6]); | |
}); | |
lastUiIndex += content.length | |
// done | |
document.querySelector("#runtimemessage").textContent = "Done!"; | |
} | |
} | |
// add file input before form | |
form.insertAdjacentHTML("beforebegin", ` | |
<h2>CSV Import</h2> | |
<p> | |
Pick one CSV file to import. | |
<a href="https://gist.github.com/wizpig64/d0ec4e5bb3735ffe47b7c22ba5d2a91c">readme here</a>. | |
</p> | |
<input type="file" id="csvfile" name="csvfile" onchange="do_csv(event)"/> | |
<p id="runtimemessage"></p> | |
`); | |
console.log("Now go to the top of the page and add your CSV file."); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I ended up adding support for a non-individual tax return, and multi-file support: