@hublaw/ofx-parser
Version:
A tool that converts financial data from OFX to JS Object.
426 lines (408 loc) • 18.4 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var Xml2JsParser = require('xml2js');
var fs = require('fs');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
var OfxDateUtil = (function () {
function OfxDateUtil() {
}
OfxDateUtil.DateToOfxDate = function (date) {
var year = date.getUTCFullYear();
var month = this.addLeadingZero(date.getUTCMonth());
var day = this.addLeadingZero(date.getUTCDate());
return "" + year + month + day;
};
OfxDateUtil.OfxDateToDate = function (ofxDate) {
if (!ofxDate) {
return undefined;
}
var year = parseInt(ofxDate.substr(0, 4), 10);
var month = parseInt(ofxDate.substr(4, 2), 10) - 1;
var day = parseInt(ofxDate.substr(6, 2), 10);
if (ofxDate.length === 8) {
return new Date(Date.UTC(year, month, day, 0, 0, 0));
}
var hour = parseInt(ofxDate.substr(8, 2), 10);
var minute = parseInt(ofxDate.substr(10, 2), 10);
var second = parseInt(ofxDate.substr(12, 2), 10);
if (ofxDate.length === 14) {
return new Date(Date.UTC(year, month, day, hour, minute, second));
}
var millisecond;
var indexOfDot = ofxDate.indexOf('.');
if (indexOfDot > -1) {
millisecond = parseInt(ofxDate.substr(indexOfDot + 1, 3), 10);
}
var timezone;
var indexOfBracket = ofxDate.indexOf('[');
var indexOfColon = ofxDate.indexOf(':');
if (indexOfBracket > -1 && indexOfColon > -1) {
timezone = ofxDate.substring(indexOfBracket + 1, indexOfColon);
if (timezone === '0') {
timezone = 'Z';
}
else if (timezone.length === 1) {
timezone = this.addLeadingZero(timezone) + "00";
}
else if (timezone.length === 2) {
timezone = timezone.charAt(0) + "0" + timezone.charAt(1) + "00";
}
}
if (millisecond !== undefined && !timezone) {
return new Date(Date.UTC(year, month, day, hour, minute, second, millisecond));
}
if (millisecond === undefined && timezone) {
millisecond = '000';
}
if (millisecond !== undefined && timezone) {
var dateString = year + "-" + this.addLeadingZero(month + 1) + "-" + this.addLeadingZero(day) +
("T" + this.addLeadingZero(hour) + ":" + this.addLeadingZero(minute) + ":") +
(this.addLeadingZero(second) + "." + this.addLeadingZero(millisecond, 3) + timezone);
var date = new Date(dateString);
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()));
}
throw new Error("bad date format [" + ofxDate + "]");
};
OfxDateUtil.addLeadingZero = function (number, targetLength) {
if (targetLength === void 0) { targetLength = 2; }
if (typeof number === 'number') {
number = number.toString();
}
while (number.length < targetLength) {
number = "0" + number;
}
return number;
};
return OfxDateUtil;
}());
var OfxAccountBalanceAdapter = (function () {
function OfxAccountBalanceAdapter() {
}
OfxAccountBalanceAdapter.convertToAccountBalance = function (balance) {
return {
balanceAmount: parseFloat(balance.BALAMT),
balanceAsOf: OfxDateUtil.OfxDateToDate(balance.DTASOF)
};
};
return OfxAccountBalanceAdapter;
}());
var OfxStatementTransactionAdapter = (function () {
function OfxStatementTransactionAdapter() {
}
OfxStatementTransactionAdapter.convertToTransaction = function (trans) {
var payee;
if (trans.PAYEE) {
payee = {
name: trans.PAYEE.NAME
};
}
return {
transactionType: trans.TRNTYPE,
amount: parseFloat(trans.TRNAMT),
fitId: trans.FITID,
name: trans.NAME,
memo: trans.MEMO,
payee: payee,
extendedName: trans.EXTDNAME,
datePosted: OfxDateUtil.OfxDateToDate(trans.DTPOSTED),
dateAvailable: OfxDateUtil.OfxDateToDate(trans.DTAVAIL),
checkNumber: trans.CHECKNUM,
refNumber: trans.REFNUM,
correctFitId: trans.CORRECTFITID,
correctAction: trans.CORRECTACTION,
serverTransactionId: trans.SRVRTID,
imageData: trans.IMAGEDATA
};
};
OfxStatementTransactionAdapter.convertTransactionList = function (transList) {
if (!transList) {
return [];
}
var transactions = [];
if (Array.isArray(transList)) {
for (var i = 0; i < transList.length; i++) {
transactions.push(OfxStatementTransactionAdapter.convertToTransaction(transList[i]));
}
}
else {
transactions.push(OfxStatementTransactionAdapter.convertToTransaction(transList));
}
return transactions;
};
return OfxStatementTransactionAdapter;
}());
var OfxBankAccountAdapter = (function () {
function OfxBankAccountAdapter() {
}
OfxBankAccountAdapter.convertToAccount = function (accountInfo, accountFrom) {
var accountNumber;
var bankId;
var accountType;
var serviceStatus;
if (accountFrom) {
accountNumber = accountFrom.ACCTID;
bankId = accountFrom.BANKID;
accountType = accountFrom.ACCTTYPE;
}
else {
accountNumber = accountInfo.BANKACCTINFO.BANKACCTFROM.ACCTID;
accountType = accountInfo.BANKACCTINFO.BANKACCTFROM.ACCTTYPE;
bankId = accountInfo.BANKACCTINFO.BANKACCTFROM.BANKID;
serviceStatus = accountInfo.BANKACCTINFO.SVCSTATUS;
}
return {
accountId: accountNumber,
bankId: bankId,
ofxAccountType: accountType,
serviceStatus: serviceStatus
};
};
return OfxBankAccountAdapter;
}());
var OfxInvestmentAccountAdapter = (function () {
function OfxInvestmentAccountAdapter() {
}
OfxInvestmentAccountAdapter.convertToAccount = function (accountInfo) {
return {
accountId: accountInfo.INVACCTINFO.INVACCTFROM.ACCTID,
ofxAccountType: 'INVESTMENT',
serviceStatus: accountInfo.INVACCTINFO.SVCSTATUS,
brokerId: accountInfo.INVACCTINFO.INVACCTFROM.BROKERID
};
};
return OfxInvestmentAccountAdapter;
}());
var OfxCreditCardAccountAdapter = (function () {
function OfxCreditCardAccountAdapter() {
}
OfxCreditCardAccountAdapter.convertToAccount = function (accountInfo) {
return {
accountId: accountInfo.CCACCTINFO.CCACCTFROM.ACCTID,
serviceStatus: accountInfo.CCACCTINFO.SVCSTATUS,
ofxAccountType: accountInfo.CCACCTINFO.CCACCTFROM.ACCTTYPE,
bankId: accountInfo.CCACCTINFO.CCACCTFROM.BANKID
};
};
return OfxCreditCardAccountAdapter;
}());
var OfxAccountInfoAdapter = (function () {
function OfxAccountInfoAdapter() {
}
OfxAccountInfoAdapter.convertToAccount = function (accountInfo) {
if (accountInfo.CCACCTINFO) {
return OfxCreditCardAccountAdapter.convertToAccount(accountInfo);
}
else if (accountInfo.INVACCTINFO) {
return OfxInvestmentAccountAdapter.convertToAccount(accountInfo);
}
else if (accountInfo.BANKACCTINFO) {
return OfxBankAccountAdapter.convertToAccount(accountInfo);
}
else {
throw new Error("Invalid account info [" + accountInfo + "]");
}
};
OfxAccountInfoAdapter.convertToAccountList = function (accountInfo) {
var accountModels = [];
if (Array.isArray(accountInfo)) {
for (var i = 0; i < accountInfo.length; i++) {
accountModels.push(OfxAccountInfoAdapter.convertToAccount(accountInfo[i]));
}
}
else {
accountModels.push(OfxAccountInfoAdapter.convertToAccount(accountInfo));
}
return accountModels;
};
return OfxAccountInfoAdapter;
}());
var OfxStatementDateAdapter = (function () {
function OfxStatementDateAdapter() {
}
OfxStatementDateAdapter.convertStatementDate = function (transactionsList) {
return {
start: OfxDateUtil.OfxDateToDate(transactionsList.DTSTART),
end: OfxDateUtil.OfxDateToDate(transactionsList.DTEND)
};
};
return OfxStatementDateAdapter;
}());
var OfxParser = (function () {
function OfxParser() {
}
OfxParser.prototype.carbonateAccounts = function (xml) {
return __awaiter(this, void 0, void 0, function () {
var body;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.convertFromXML(xml)];
case 1:
body = _a.sent();
return [2, OfxAccountInfoAdapter.convertToAccountList(body.OFX.SIGNUPMSGSRSV1.ACCTINFOTRNRS.ACCTINFORS.ACCTINFO)];
}
});
});
};
OfxParser.prototype.readLocalFile = function (filePath) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2, new Promise(function (resolve, reject) {
var ofxData = '';
var readStream = fs.createReadStream(filePath);
readStream
.on('data', function (data) {
ofxData += data;
})
.on('error', function (e) {
reject(e);
})
.on('end', function () {
resolve(ofxData);
});
})];
});
});
};
OfxParser.prototype.parseStatementFile = function (filePath) {
return __awaiter(this, void 0, void 0, function () {
var ofxData, result, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.readLocalFile(filePath)];
case 1:
ofxData = _a.sent();
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4, this.parseStatement(ofxData)];
case 3:
result = _a.sent();
return [2, result];
case 4:
err_1 = _a.sent();
console.error(err_1);
return [2, err_1];
case 5: return [2];
}
});
});
};
OfxParser.prototype.parseStatement = function (xml) {
return __awaiter(this, void 0, void 0, function () {
var body, ledgerBalance, availableBalance, transactions, positions, statementDate;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.convertFromXML(xml)];
case 1:
body = _a.sent();
statementDate = {};
if (body.OFX.BANKMSGSRSV1) {
ledgerBalance = OfxAccountBalanceAdapter.convertToAccountBalance(body.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.LEDGERBAL);
if (body.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.AVAILBAL) {
availableBalance = OfxAccountBalanceAdapter.convertToAccountBalance(body.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.AVAILBAL);
}
if (body.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.BANKTRANLIST.DTSTART) {
statementDate = OfxStatementDateAdapter.convertStatementDate(body.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.BANKTRANLIST);
}
transactions = OfxStatementTransactionAdapter.convertTransactionList(body.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.BANKTRANLIST.STMTTRN);
}
else {
throw new Error('Extrato Bancário não identificado');
}
return [2, {
ledgerBalance: ledgerBalance.balanceAmount,
availableBalance: availableBalance
? availableBalance.balanceAmount
: undefined,
balanceAsOf: ledgerBalance.balanceAsOf,
startDate: statementDate.start,
endDate: statementDate.end,
transactions: transactions,
positions: positions
}];
}
});
});
};
OfxParser.prototype.convertFromXML = function (ofxString) {
var _this = this;
return new Promise(function (resolve, reject) {
if (!_this.validOfxString(ofxString)) {
reject(new Error('Attempting to convert an invalid string.'));
}
var ofxResult = ofxString.split('<OFX>', 2);
var ofxPart = "<OFX>" + ofxResult[1];
var xml = ofxPart
.replace(/&/g, "&")
.replace(/&/g, "&")
.replace(/>\s+</g, '><')
.replace(/\s+</g, '<')
.replace(/>\s+/g, '>')
.replace(/<([A-Z0-9_]*)+\.+([A-Z0-9_]*)>([^<]+)(<\/\1\.\2>)?/g, '<$1$2>$3')
.replace(/<(\w+?)>([^<]+)/g, '<$1>$2</<added>$1>')
.replace(/<\/<added>(\w+?)>(<\/\1>)?/g, '</$1>');
var json;
var parser = new Xml2JsParser.Parser({ explicitArray: false });
parser.parseString(xml, function (err, result) {
if (err) {
reject(err);
}
json = result;
resolve(json);
});
});
};
OfxParser.prototype.validOfxString = function (ofxString) {
return ofxString.indexOf('<OFX>') > -1;
};
return OfxParser;
}());
exports.OfxParser = OfxParser;
//# sourceMappingURL=main.js.map