UNPKG

mt940js

Version:

javascript mt940 bank statement parser

248 lines (224 loc) 6.71 kB
/** * Tags parsing * @module lib/tags */ const helperModels = require('./helperModels'); /** * Builds a proper tag based on id, subid and tag content */ class TagFactory { /** constructor - Builds tag map */ constructor () { this.tagMap = [ TagTransactionReferenceNumber, TagRelatedReference, TagAccountIdentification, TagStatementNumber, TagNonSwift, TagOpeningBalance, TagClosingBalance, TagStatementLine, TagTransactionDetails, TagClosingAvailableBalance, TagForwardAvailableBalance, TagMessageBlock, ].reduce((map, i) => map.set(i.ID, i), new Map()); } /** * Create proper tag instance * @param {string} id - tag id (21, 61 ...) * @param {string} subId - optional tag sub Id (C, F ...) * @param {string} data - tag content (without id part) * @return {Tag} new Tag (specific subclass) instance */ createTag(id, subId, data) { const tagId = isNaN(id) ? id : Number.parseInt(id, 10); const fullTagId = tagId.toString() + (subId ? subId.toString() : ''); const tagClass = this.tagMap.get(fullTagId) || this.tagMap.get(tagId); if (!tagClass) { throw new Error(`Unknown tag ${fullTagId}`) } return new tagClass(data); } } /** * Abstract Tag base class * @abstract */ class Tag { /** Tag ID */ static get ID() { return 0 } /** Tag data regex pattern */ static get PATTERN() { return /^(.*)/ } constructor(data) { if (new.target === Tag) { throw new TypeError('Cannot construct Tag instances directly'); } this.id = this.constructor.ID; this.re = this.constructor.PATTERN; this.data = data; this._parse(); } /** * parses tag data nad saves it as `fields` attribute * @private */ _parse() { const match = this._nextMatch(); if (!match) { throw Error(`Cannot parse tag ${this.id}: ${this.data}`) } this.fields = this._extractFields(match); } _nextMatch() { return this.re.exec(this.data); } _extractFields(match) { return {} } // eslint-disable-line no-unused-vars } class TagTransactionReferenceNumber extends Tag { static get ID() { return 20 } static get PATTERN() { return /^(.{0,16})/ } _extractFields(match) { return { transactionReference: match[1] }; } } class TagRelatedReference extends Tag { static get ID() { return 21 } static get PATTERN() { return /^(.{0,16})/ } _extractFields(match) { return { relatedReference: match[1] }; } } class TagAccountIdentification extends Tag { static get ID() { return 25 } static get PATTERN() { return /^(.{0,35})/ } _extractFields(match) { return { accountIdentification: match[1] }; } } class TagStatementNumber extends Tag { static get ID() { return 28 } static get PATTERN() { return /^(\d{1,5})(\/(\d{1,5}))?(\/(\d{1,5}))?/ } _extractFields(match) { return { statementNumber: match[1], sequenceNumber: match[3] || '', sectionNumber: match[5] || '' }; } } class TagNonSwift extends Tag { static get ID() { return 'NS' } static get PATTERN() { return /^(.*)/ } _extractFields(match) { return { nonSwift: match[1] }; } } class TagBalance extends Tag { static get PATTERN() { const re = '^([DC])' // DC indicator + '(\\d{2})(\\d{2})(\\d{2})' // Date + '([A-Z]{3})' // Currency + '([0-9,]{0,16})'; // Amount return new RegExp(re); } constructor(data) { super(data); if (new.target === TagBalance) { throw new TypeError('Cannot construct TagBalance instances directly'); } } _extractFields(match) { return { date: helperModels.Date.parse(match[2], match[3], match[4]), currency: match[5], amount: helperModels.Amount.parse(match[1], match[6]) }; } } class TagOpeningBalance extends TagBalance { static get ID() { return 60 } } class TagClosingBalance extends TagBalance { static get ID() { return 62 } } class TagClosingAvailableBalance extends TagBalance { static get ID() { return 64 } } class TagForwardAvailableBalance extends TagBalance { static get ID() { return 65 } } class TagStatementLine extends Tag { static get ID() { return 61 } static get PATTERN() { const re = '^(\\d{2})(\\d{2})(\\d{2})' // Date + '((\\d{2})(\\d{2}))?' // Entry date + '(R?[DC])([A-Z])?' // DC indicator + funds code + '([0-9,]{0,16})' // Amount + '([A-Z][A-Z0-9]{3})' // Transaction type + '([^/\\n]{0,16})' // Customer reference + '(//(.{0,16}))?' // Bank reference + '(\\n(.{0,34}))?'; // Extra return new RegExp(re); } _extractFields(match) { return { date: helperModels.Date.parse(match[1], match[2], match[3]), entryDate: match[4] && helperModels.Date.parse(match[1], match[5], match[6]) || '', fundsCode: match[8] || '', amount: helperModels.Amount.parse(match[7], match[9]), isReversal: match[7][0] === 'R', transactionType: match[10], reference: match[11], bankReference: match[13] || '', extraDetails: match[15] || '' }; } } class TagTransactionDetails extends Tag { static get ID() { return 86 } static get PATTERN() { return /^([\s\S]{0,390})/ } _extractFields(match) { return { transactionDetails: match[1] }; } } class TagMessageBlock extends Tag { static get ID() { return 'MB' } static get PATTERN() { const re = '(^-\\})|(' + '\\{(\\d):' + '(.*?)' + '($|\\}(?=(\\{\\d:)|$))' + ')'; return new RegExp(re, 'g'); } _extractFields(match) { const fields = {}; while (match) { if (match[0] === '-}') { fields['EOB'] = ''; } else { fields[match[3]] = match[4]; } match = this._nextMatch(); } this.isStarting = (fields['1'] !== undefined); // has message 1 return fields; } } module.exports = { TagFactory, Tag, TagTransactionReferenceNumber, TagRelatedReference, TagAccountIdentification, TagStatementNumber, TagNonSwift, TagBalance, TagOpeningBalance, TagClosingBalance, TagStatementLine, TagTransactionDetails, TagClosingAvailableBalance, TagForwardAvailableBalance, TagMessageBlock, };