UNPKG

nach2

Version:

nACH is a highly customizable Node.js module exposing a high & low-level API for generating ACH files for use within the ACH network

186 lines (138 loc) 5.47 kB
// File var _ = require('lodash'); var async = require('async'); var utils = require('../utils'); var validate = require('../validate'); var highLevelOverrides = ['immediateDestination','immediateOrigin','fileCreationDate','fileCreationTime','fileIdModifier','immediateDestinationName','immediateOriginName','referenceCode']; function File(options) { this._batches = []; // Allow the batch header/control defaults to be overriden if provided this.header = options.header ? _.merge(options.header, require('./header'), _.defaults) : _.cloneDeep(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); // This is done to make sure we have a 9-digit routing number if(options.immediateDestination) { this.header.immediateDestination.value = utils.computeCheckDigit(options.immediateDestination); } this._batchSequenceNumber = Number(options.batchSequenceNumber) || 0 // 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']; } // If the control has the field, return the value if(this.control[field]) { return this.control[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 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); // Validate header data types validate.validateDataTypes(this.header); // 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 // Increment the batchSequenceNumber ++this._batchSequenceNumber this._batches.push(batch); }; File.prototype.generatePaddedRows = function(rows, cb) { var paddedRows = ''; for(var 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) { var self = this; var result = ''; var rows = 2; var entryHash = 0; var addendaCount = 0; var totalDebit = 0; var 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); // Increment the addenda and block count addendaCount++; rows++; done3(); }, function(err){ // Only iterate and generate the batch if there is at least one entry in the batch if(batch._entries.length > 0) { // 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(); }); } else { done2(); } }); }, function(err){ self.control.totalDebit.value = totalDebit; self.control.totalCredit.value = totalCredit; 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); // 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.generateControl = function(cb) { utils.generateString(this.control, function(string) { cb(string); }); }; File.prototype.generateFile = function(cb) { var self = this; 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 var paddedRows = utils.getNextMultipleDiff(rows, 10); self.generatePaddedRows(paddedRows, function(paddedString) { cb(headerString + utils.newLineChar() + batchString + controlString + paddedString); }); }); }) }); }; module.exports = File;