Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

sp-nacha

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sp-nacha - npm Package Compare versions

Comparing version 1.0.2 to 1.0.3

.github/workflows/node.js.yml

2

index.js

@@ -0,1 +1,2 @@

module.exports.EntryAddenda = require('./lib/entry-addenda')
module.exports.Entry = require('./lib/entry');

@@ -6,2 +7,1 @@ module.exports.Batch = require('./lib/batch');

module.exports.Validate = require('./lib/validate');
module.exports.EntryAddenda = require('./lib/entry-addenda')

@@ -40,3 +40,3 @@ module.exports = {

entryDetailSequenceNumber: {
name: 'Entry Detail Sequnce Number',
name: 'Entry Detail Sequence Number',
width: 7,

@@ -43,0 +43,0 @@ position: 5,

@@ -7,3 +7,3 @@ // Entry

let highLevelOverrides = ['addendaTypeCode', 'paymentRelatedInformation', 'addendaSequenceNumber', 'entryDetailSequnceNumber'];
let highLevelOverrides = ['addendaTypeCode', 'paymentRelatedInformation', 'addendaSequenceNumber', 'entryDetailSequenceNumber'];

@@ -27,4 +27,4 @@ function EntryAddenda(options) {

if (options.entryDetailSequnceNumber) {
this.fields.entryDetailSequnceNumber.value = options.entryDetailSequnceNumber.slice(0 - this.fields.entryDetailSequnceNumber.width); // last n digits. pass
if (options.entryDetailSequenceNumber) {
this.fields.entryDetailSequenceNumber.value = options.entryDetailSequenceNumber.slice(0 - this.fields.entryDetailSequenceNumber.width); // last n digits. pass
}

@@ -31,0 +31,0 @@

// Entry
var _ = require('lodash');
var async = require('async');
var utils = require('./../utils');
var validate = require('./../validate');
const _ = require('lodash');
const async = require('async');
const utils = require('./../utils');
const validate = require('./../validate');
var highLevelOverrides = ['transactionCode','receivingDFI','checkDigit','DFIAccount','amount','idNumber','individualName','discretionaryData','addendaId','traceNumber'];
const highLevelOverrides = ['transactionCode','receivingDFI','checkDigit','DFIAccount','amount','idNumber','individualName','discretionaryData','addendaId','traceNumber'];

@@ -69,3 +69,3 @@ function Entry(options) {

Entry.prototype.generateString = function(cb) {
var self = this;
let self = this;
async.map(self._addendas, function(entryAddenda, done) {

@@ -77,3 +77,3 @@ utils.generateString(entryAddenda.fields, function(string) {

utils.generateString(self.fields, function(string) {
cb([string].concat(addendaStrings).join('\n'));
cb([string].concat(addendaStrings).join(utils.newLineChar()));
});

@@ -80,0 +80,0 @@ });

'use strict';
// Create a new object, that prototypally inherits from the Error constructor.
var nACHError = function(errorObj) {
// Create a new object, that prototypal inherits from the Error constructor.
let nACHError = function(errorObj) {
this.name = 'nACHError['+errorObj.name+']' || 'nACHError';

@@ -5,0 +5,0 @@ this.message = errorObj.message || 'Uncaught nACHError';

@@ -1,2 +0,2 @@

var utils = require('../utils');
const utils = require('../utils');

@@ -3,0 +3,0 @@ // The date/time defaults are dynamic, so always create a new object

@@ -7,181 +7,186 @@ // File

let validate = require('../validate');
let highLevelOverrides = ['immediateDestination','immediateOrigin','fileCreationDate','fileCreationTime','fileIdModifier','immediateDestinationName','immediateOriginName','referenceCode'];
let highLevelOverrides = ['immediateDestination', 'immediateOrigin', 'fileCreationDate', 'fileCreationTime', 'fileIdModifier', 'immediateDestinationName', 'immediateOriginName', 'referenceCode'];
function File(options) {
this._batches = [];
this._batches = [];
// Allow the batch header/control defaults to be overriden if provided
this.header = options.header ? _.merge(options.header, require('./header')(), _.defaults) : require('./header')();
this.control = options.control ? _.merge(options.header, require('./control'), _.defaults) : _.cloneDeep(require('./control'));
// Allow the batch header/control defaults to be overriden if provided
this.header = options.header ? _.merge(options.header, require('./header')(), _.defaults) : require('./header')();
this.control = options.control ? _.merge(options.header, require('./control'), _.defaults) : _.cloneDeep(require('./control'));
// Configure high-level overrides (these override the low-level settings if provided)
utils.overrideLowLevel(highLevelOverrides, options, this);
// Configure high-level overrides (these override the low-level settings if provided)
utils.overrideLowLevel(highLevelOverrides, options, this);
// This is done to make sure we have a 9-digit routing number
if(options.immediateDestination) {
if (options.immediateDestination) {
this.header.immediateDestination.value = utils.computeCheckDigit(options.immediateDestination);
}
this._batchSequenceNumber = Number(options.batchSequenceNumber) || 0
this._batchSequenceNumber = Number(options.batchSequenceNumber) || 0
// Validate all values
this._validate();
return this;
// Validate all values
this._validate();
return this;
}
File.prototype.get = function(field) {
// If the header has the field, return the value
if(this.header[field]) {
return this.header[field]['value'];
}
File.prototype.get = function (field) {
// If the control has the field, return the value
if(this.control[field]) {
return this.control[field]['value'];
}
// If the header has the field, return the value
if (this.header[field]) {
return this.header[field]['value'];
}
// If the control has the field, return the value
if (this.control[field]) {
return this.control[field]['value'];
}
}
File.prototype.set = function(field, value) {
File.prototype.set = function (field, value) {
// If the header has the field, set the value
if(this.header[field]) {
this.header[field]['value'] = value;
}
// If the header has the field, set the value
if (this.header[field]) {
this.header[field]['value'] = value;
}
// If the control has the field, set the value
if(this.control[field]) {
this.control[field]['value'] = value;
}
// If the control has the field, set the value
if (this.control[field]) {
this.control[field]['value'] = value;
}
};
File.prototype._validate = function() {
// Validate header field lengths
validate.validateLengths(this.header);
File.prototype._validate = function () {
// Validate header data types
validate.validateDataTypes(this.header);
// Validate header field lengths
validate.validateLengths(this.header);
// Validate control field lengths
validate.validateLengths(this.control);
// Validate header data types
validate.validateDataTypes(this.header);
// Validate header data types
validate.validateDataTypes(this.control);
// Validate control field lengths
validate.validateLengths(this.control);
// Validate header data types
validate.validateDataTypes(this.control);
};
File.prototype.addBatch = function(batch) {
// Set the batch number on the header and control records
batch.header.batchNumber.value = this._batchSequenceNumber
batch.control.batchNumber.value = this._batchSequenceNumber
File.prototype.addBatch = function (batch) {
// Increment the batchSequenceNumber
++this._batchSequenceNumber
// Set the batch number on the header and control records
batch.header.batchNumber.value = this._batchSequenceNumber
batch.control.batchNumber.value = this._batchSequenceNumber
this._batches.push(batch);
// Increment the batchSequenceNumber
++this._batchSequenceNumber
this._batches.push(batch);
};
File.prototype.generatePaddedRows = function(rows, cb) {
let paddedRows = '';
for(let i = 0; i < rows; i++) {
paddedRows += utils.newLineChar() + utils.pad('', 94, '9');
}
// Return control flow back by calling the callback function
cb(paddedRows);
File.prototype.generatePaddedRows = function (rows, cb) {
let paddedRows = '';
for (let i = 0; i < rows; i++) {
paddedRows += utils.newLineChar() + utils.pad('', 94, '9');
}
// Return control flow back by calling the callback function
cb(paddedRows);
}
File.prototype.generateBatches = function(done1) {
File.prototype.generateBatches = function (done1) {
let self = this;
let result = '';
let result = '';
let rows = 2;
let entryHash = 0;
let addendaCount = 0;
let totalDebit = 0;
let totalCredit = 0;
async.each(this._batches, function(batch, done2) {
totalDebit += batch.control.totalDebit.value;
totalCredit += batch.control.totalCredit.value;
async.each(batch._entries, function(entry, done3) {
entry.fields.traceNumber.value = ( entry.fields.traceNumber.value) ? entry.fields.traceNumber.value : self.header.immediateOrigin.value.slice(0,8) + utils.pad(addendaCount, 7, false, '0');
entryHash += Number(entry.fields.receivingDFI.value);
let entryHash = 0;
let addendaCount = 0;
let totalDebit = 0;
let totalCredit = 0;
async.each(this._batches, function (batch, done2) {
totalDebit += batch.control.totalDebit.value;
totalCredit += batch.control.totalCredit.value;
async.each(batch._entries, function (entry, done3) {
entry.fields.traceNumber.value = (entry.fields.traceNumber.value ? entry.fields.traceNumber.value : self.header.immediateOrigin.value.slice(0, 8)) + utils.pad(addendaCount, 7, false, '0');
async.each(entry._addendas, function (addenda, done4) {
addenda.fields.entryDetailSequenceNumber.value = entry.fields.traceNumber.value.slice(0 - addenda.fields.entryDetailSequenceNumber.width);
done4()
})
entryHash += Number(entry.fields.receivingDFI.value);
// Increment the addenda and block count
addendaCount++;
addendaCount++;
rows++;
done3();
}, function(){
done3();
}, function () {
// Only iterate and generate the batch if there is at least one entry in the batch
if(batch._entries.length > 0) {
if (batch._entries.length > 0) {
// Increment the addendaCount of the batch
self.control.batchCount.value++;
// Increment the addendaCount of the batch
self.control.batchCount.value++;
// Bump the number of rows only for batches with at least one entry
rows = rows + 2;
// Generate the batch after we've added the trace numbers
batch.generateString(function(batchString){
result += batchString + utils.newLineChar();
done2();
});
// Generate the batch after we've added the trace numbers
batch.generateString(function (batchString) {
result += batchString + utils.newLineChar();
done2();
});
} else {
done2();
}
});
}, function(){
self.control.totalDebit.value = totalDebit;
self.control.totalCredit.value = totalCredit;
});
}, function () {
self.control.totalDebit.value = totalDebit;
self.control.totalCredit.value = totalCredit;
self.control.addendaCount.value = addendaCount;
self.control.addendaCount.value = addendaCount;
self.control.blockCount.value = utils.getNextMultiple(rows, 10) / 10;
// Slice the 10 rightmost digits.
self.control.entryHash.value = entryHash.toString().slice(-10);
// Slice the 10 rightmost digits.
self.control.entryHash.value = entryHash.toString().slice(-10);
// Pass the result string as well as the number of rows back
done1(result, rows);
});
});
};
File.prototype.generateHeader = function(cb) {
utils.generateString(this.header, function(string) {
cb(string);
});
File.prototype.generateHeader = function (cb) {
utils.generateString(this.header, function (string) {
cb(string);
});
};
File.prototype.generateControl = function(cb) {
utils.generateString(this.control, function(string) {
cb(string);
});
File.prototype.generateControl = function (cb) {
utils.generateString(this.control, function (string) {
cb(string);
});
};
File.prototype.generateFile = function(cb) {
let self = this;
File.prototype.generateFile = function (cb) {
let self = this;
self.generateHeader(function(headerString) {
self.generateBatches(function(batchString, rows) {
self.generateControl(function(controlString) {
self.generateHeader(function (headerString) {
self.generateBatches(function (batchString, rows) {
self.generateControl(function (controlString) {
// These must be within this callback otherwise rows won't be calculated yet
let paddedRows = utils.getNextMultipleDiff(rows, 10);
let paddedRows = utils.getNextMultipleDiff(rows, 10);
self.generatePaddedRows(paddedRows, function(paddedString) {
cb(headerString + utils.newLineChar() + batchString + controlString + paddedString);
});
});
})
});
self.generatePaddedRows(paddedRows, function (paddedString) {
cb(headerString + utils.newLineChar() + batchString + controlString + paddedString);
});
});
})
});
};
module.exports = File;

@@ -1,2 +0,2 @@

var _ = require('lodash')
let _ = require('lodash')
, utils = require('./utils')

@@ -16,3 +16,3 @@ , nACHError = require('./error')

_.forEach(object, function(field) {
// This check ensures a required field's value is not NaN, null, undefined or empty. Zero is valid, but the data type check will make sure any fields with 0 are numeric.
// This check ensures a required field's value is not NaN, null, undefined or empty. Zero is valid, but the data type check will make sure any fields with zero are numeric.
if(field.required === true && (_.isNaN(field.value) || _.isNull(field.value) || _.isUndefined(field.value) || field.value.toString().length === 0)) {

@@ -111,3 +111,3 @@ throw new nACHError({

// Split the routing number into an array of numbers. `array` will look like this: `[2,8,1,0,8,1,4,7,9]`.
// Split the routing number into an array of numbers. `Array` will look like this: `[2,8,1,0,8,1,4,7,9]`.
array = routing.split('').map(Number);

@@ -121,3 +121,3 @@

// Throw an error if the the result of `sum` modulo 10 is not zero. The value of `sum` must be a multiple of 10 to be a valid routing number.
// Throw an error if the result of `sum` modulo 10 is not zero. The value of `sum` must be a multiple of 10 to be a valid routing number.
if(sum % 10 !== 0) {

@@ -124,0 +124,0 @@ throw new nACHError({

{
"name": "sp-nacha",
"description": "NACHA file format is a set of instructions that, when uploaded into a bank portal, successfully initiates a batch of ACH payments. NACHA file format is the protocol for structuring those instructions in a way that successfully initiates the payments. NACHA is a highly customizable Node.js module exposing a high & low-level API for generating ACH files for use within the ACH network.",
"version": "1.0.2",
"version": "1.0.3",
"keywords": [

@@ -28,8 +28,8 @@ "Seerportal Nacha",

"scripts": {
"test": "SET NODE_ENV=test&& mocha --reporter spec --recursive"
"test": "mocha --reporter spec --recursive"
},
"dependencies": {
"async": "^2.0.1",
"lodash": "^4.14.1",
"moment": "^2.14.1"
"async": "^3.2.4",
"lodash": "^4.17.21",
"moment": "^2.29.4"
},

@@ -36,0 +36,0 @@ "devDependencies": {

# SP-NACH
[![Node.js](https://github.com/pradeepvish1213/sp-nacha/actions/workflows/node.js.yml/badge.svg)](https://github.com/pradeepvish1213/sp-nacha/actions/workflows/node.js.yml)

@@ -59,3 +60,3 @@ NACHA file format is a set of instructions that, when uploaded into a bank portal, successfully initiates a batch of ACH

ACH specification requires you to fill in the remainder of the file with rows of 9s until the number of rows is a
multiple of 10. Of course, nACH will handle all of this for you, but it's aways good to know why it's doing it.
multiple of 10. Of course, nACH will handle all of this for you, but it's always good to know why it's doing it.

@@ -153,3 +154,3 @@ ## File Anatomy

Finally to generate the file & write it to a text file
Finally, to generate the file & write it to a text file

@@ -156,0 +157,0 @@ ```js

@@ -64,2 +64,3 @@ let chai = require('chai')

}).catch(error => {
console.log('===========',error)
expect(true).to.be.false;

@@ -66,0 +67,0 @@ expect(error.message).to.equal('Successfully writing file.');

@@ -7,10 +7,10 @@ const Moment = require('moment')

let data = [{
"id": 32,
"id": 1,
"immediateDestination": "325272306",
"immediateOrigin": "041215663",
"immediateDestinationName": "TONGASS FCU",
"immediateDestinationName": "TONGA'S FCU",
"immediateOriginName": "SUTTON BANK",
"referenceCode": " ",
"batchChildren": [{
"id": 32,
"id": 1,
"companyName": "SSA TREAS 310",

@@ -26,3 +26,3 @@ "companyIdentification": "9101036669",

"entryChildren": [{
"id": 32,
"id": 1,
"receivingDFI": "325272306",

@@ -36,3 +36,3 @@ "DFIAccount": "1347569324910",

"transactionType": "Credit",
"paymentRelatedInformation": "N1GDTEYUNNA L SPEARS N1BETEYUNNA L SPEARS 34501116885"
"paymentRelatedInformation": "N1YUNNAN L SPEARS N1YUNNAN L SPEARS 34501116885"
}]

@@ -42,7 +42,60 @@ }]

let dataAddenda = [{
"id": 1,
"immediateDestination": "011002725",
"immediateOrigin": "011001726",
"immediateDestinationName": "BERKSHIRE BANK",
"immediateOriginName": "BROOKLINE BANK",
"referenceCode": " ",
"batchChildren": [{
"id": 1,
"companyName": "FF4 US",
"companyIdentification": "0",
"serviceClassCode": "220",
"standardEntryClassCode": "IAT",
"companyDiscretionaryData": "FF4 US",
"companyEntryDescription": "NA",
"companyDescriptiveDate": Moment(NachaAimPoint.Utils.computeBusinessDay(8)).format('MMM D'),
"effectiveEntryDate": Moment('2023-08-30 05:30:00').toDate(),
"settlementDate": "2023-09-15 05:30:00",
"originatingDFI": "011001726",
"entryChildren": [{
"id": 1,
"receivingDFI": "011002725",
"DFIAccount": "2234532",
"amount": "1000",
"idNumber": "",
"individualName": "Sandeep",
"discretionaryData": " ",
"transactionCode": "22",
"transactionType": "Credit",
"addendaRecords": [{
"payment_related_information": "ANN000000000000100000928383-23939 XYZ Enterprises"
}, {
"payment_related_information": "XYZ Solutions 15 East Place Street"
}, {
"payment_related_information": "SmithTown*PA\\ US*19306\\"
}, {
"payment_related_information": "Citibank 01231380104 US"
}, {
"payment_related_information": "Standard Bank 01121042882 CA"
}, {
"payment_related_information": "9874654932139872122 Front Street"
}, {
"payment_related_information": "BetterTown*AB\\ CA*80015\\"
}, {
"payment_related_information": "Another international payment"
}, {
"payment_related_information": "Bank of Canada 01456456456987988 CA"
}]
}]
}],
"recordCount": 1
}]
async function GenerateAchFile(queuedTransaction = [], fileFullPath = './') {
return new Promise((resolve, reject) => {
let totalRunsFile = [];
queuedTransaction = queuedTransaction.length > 0 ? queuedTransaction : data;
queuedTransaction = queuedTransaction.length > 0 ? queuedTransaction : dataAddenda;
queuedTransaction.forEach(({batchChildren, id, ...restField}) => {

@@ -58,3 +111,3 @@ try {

let batch = new NachaAimPoint.Batch(restField);
entryChildren.forEach(({transactionType, paymentRelatedInformation, id, ...entry}) => {
entryChildren.forEach(({transactionType, addendaRecords, id, ...entry}) => {
if (transactionType === 'Credit') {

@@ -68,7 +121,9 @@ totalCreditAmount = totalCreditAmount + parseInt(entry.amount)

let entryRecord = new NachaAimPoint.Entry(entry);
if (paymentRelatedInformation) {
let addenda = new NachaAimPoint.EntryAddenda({
paymentRelatedInformation
});
entryRecord.addAddenda(addenda);
if (addendaRecords && addendaRecords.length > 0) {
addendaRecords.forEach(addenda => {
let addendaEntry = new NachaAimPoint.EntryAddenda({
paymentRelatedInformation: addenda.payment_related_information
});
entryRecord.addAddenda(addendaEntry);
})
}

@@ -93,4 +148,3 @@ batch.addEntry(entryRecord);

totalRunsFile.push({
message: error.message ? error.message : error.stack,
error: true
message: error.message ? error.message : error.stack, error: true
})

@@ -130,5 +184,3 @@ return reject(totalRunsFile)

return reject({
row_id: id,
message: error.message ? error.message : error.stack,
error: true
row_id: id, message: error.message ? error.message : error.stack, error: true
})

@@ -135,0 +187,0 @@ }

@@ -15,3 +15,3 @@ let chai = require('chai'),

});
describe('GenerateString', function(){

@@ -30,3 +30,3 @@ it("Test to see if object can be passed", function(){

};
expect( function() { utils.generateString(testObj) }).not.to.throw('Not passing object correctly.');

@@ -42,6 +42,6 @@ });

if(dateNum === date) { expect(function() { utils.formatDate }).not.to.throw('Dates match'); }
if(dateNum === date) { expect(function() { utils.formatDate }).not.to.throw('Dates match'); }
// The formatDate() function never throws an error -- this test isn't accurate
//else { expect(function() { utils.formatDate }).to.throw('Dates don\'t match');}
//else { expect(function() { utils.formatDate }).to.throw('Dates don\'t match');}

@@ -62,3 +62,3 @@ });

if(utilsTime === time) { expect(function() { utils.formatTime }).not.to.throw('Times match'); }
// The formatTime() function never throws an error -- this test isn't accurate

@@ -65,0 +65,0 @@ //else { expect(function() { utils.formatTime }).to.throw('Times don\'t match.') }

@@ -44,3 +44,3 @@ let chai = require('chai')

// The expect should not throw an error since the field is not required so an empty string is okay.
// To expect should not throw an error since the field is not required so an empty string is okay.
expect(function () { validate.validateRequiredFields(testObjectOne) }).not.to.throw('fieldOne is a required field but its value is: ');

@@ -162,3 +162,3 @@ });

});
describe('Service Class Codes', function(){

@@ -165,0 +165,0 @@ it('must be valid ACH codes', function(){

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc