Skip to content

Instantly share code, notes, and snippets.

@master5o1 master5o1/qif2json.ts
Last active Jul 16, 2019

Embed
What would you like to do?
This is qif2json.ts is a minor refactor to bring Typescript support to the qif2json package available on NPM. It is the basic QIF parsing without any other requirements from Node (fs) or NPM (iconv, jschardet). This means that it is possible to do the QIF parsing in the browser.

This is qif2json.ts is a minor refactor to bring Typescript support to the qif2json package available on NPM. It is the basic QIF parsing without any other requirements from Node (fs) or NPM (iconv, jschardet). This means that it is possible to do the QIF parsing in the browser.

I've written my own QIF parser on StackBlitz which is an improvement in terms of QIF support. Perhaps not as great performance, meh. Can see what it does using this demo.

/* Adapted by Jason Schwarzenberger
* for Typescript from
* qif2json
* https://github.com/spmason/qif2json
*
* Copyright (c) 2012 Steve Mason
* Licensed under the MIT license.
*/
function parseDate(str: string, format: string) {
const [day, month, year]: any[] = str.replace(' ', '').split(/[^0-9]/);
const output: {
year: number;
month: string;
day: string;
} = { year: 0, month: '00', day: '00' };
const yearNow = new Date().getFullYear();
const yearInt = parseInt(year, 10);
const year1900 = 1900 + yearInt;
const year2000 = 2000 + yearInt;
output.day = day.length < 2 ? `0${day}` : day;
output.month = month.length < 2 ? `0${month}` : day;
output.year = year;
if (year.length <= 2) {
output.year = year2000 > yearNow ? year1900 : year2000;
}
if (format === 'us') {
return `${output.year}-${output.day}-${output.month}`;
}
return `${output.year}-${output.month}-${output.day}`;
}
export interface QifOptions {
dateFormat?: string;
ignoreType?: boolean;
}
export interface Qif {
transactions: Transaction[];
type: string;
}
export interface Division {
amount: number;
category: string;
description: string;
subcategory: string;
}
export interface Transaction {
date: string;
amount: number;
number: string;
memo: string;
address: string[];
payee: string;
category: string;
subcategory: string;
clearedStatus: string;
division: Division[];
[key: string]: any;
}
export const parser = (qif: string, options: QifOptions = <QifOptions>{ ignoreType: false }): Qif => {
const lines = qif.split('\n');
let line = lines.shift();
let transaction: Transaction = <Transaction>{};
let division: Division = <Division>{};
const typeArray = /!Type:([^$]*)$/.exec(line!.trim()) || [];
if (!typeArray || !typeArray.length) {
if (!options.ignoreType) {
throw new Error('File does not appear to be a valid qif file: ' + line);
}
typeArray[1] = line!.trim();
}
const type = typeArray[1];
const transactions: Transaction[] = [];
// tslint:disable-next-line: no-conditional-assignment
while ((line = lines.shift())) {
line = line.trim();
if (line === '^') {
transactions.push(transaction);
transaction = <Transaction>{};
continue;
}
switch (line[0]) {
case 'D':
transaction.date = parseDate(line.substring(1), options.dateFormat || '');
break;
case 'T':
transaction.amount = parseFloat(line.substring(1).replace(',', ''));
break;
case 'N':
transaction.number = line.substring(1);
break;
case 'M':
transaction.memo = line.substring(1);
break;
case 'A':
transaction.address = (transaction.address || []).concat(line.substring(1));
break;
case 'P':
transaction.payee = line.substring(1).replace(/&amp;/g, '&');
break;
case 'L':
const lArray = line.substring(1).split(':');
transaction.category = lArray[0];
if (lArray[1] !== undefined) {
transaction.subcategory = lArray[1];
}
break;
case 'C':
transaction.clearedStatus = line.substring(1);
break;
case 'S':
const sArray = line.substring(1).split(':');
division.category = sArray[0];
if (sArray[1] !== undefined) {
division.subcategory = sArray[1];
}
break;
case 'E':
division.description = line.substring(1);
break;
case '$':
division.amount = parseFloat(line.substring(1));
if (!(transaction.division instanceof Array)) {
transaction.division = [];
}
transaction.division.push(division);
division = <Division>{};
break;
default:
throw new Error('Unknown Detail Code: ' + line[0]);
}
}
if (Object.keys(transaction).length) {
transactions.push(transaction);
}
return <Qif>{
transactions,
type
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.