Skip to content

Commit

Permalink
Support NOC for IAT Entries
Browse files Browse the repository at this point in the history
Support NOC for IAT Entries
  • Loading branch information
bkmoovio committed Oct 30, 2018
1 parent 2cec3a5 commit 83ead47
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 120 deletions.
217 changes: 117 additions & 100 deletions iatBatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var (
msgBatchIATAddendum = "7 Addendum is the maximum for SEC code IAT"
msgBatchIATAddendumCount = "%v Addenda %v for SEC Code IAT"
msgBatchIATInvalidAddendumer = "invalid Addendumer for SEC Code IAT"
msgBatchIATNOC = "%v invalid for IAT NOC, should be %v"
msgBatchIATInvalidAddenda = "%v is only valid for %v entry"
)

// IATBatch holds the Batch Header and Batch Control and all Entry Records for an IAT batch
Expand Down Expand Up @@ -139,15 +141,16 @@ func (batch *IATBatch) build() error {
batch.Entries[i].SetTraceNumber(batch.Header.ODFIIdentification, seq)
}

// Set TraceNumber for IATEntryDetail Addenda10-16 Record Properties
entry.Addenda10.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda11.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda12.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda13.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda14.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda15.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda16.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])

if entry.Category != CategoryNOC {
// Set TraceNumber for IATEntryDetail Addenda10-16 Record Properties
entry.Addenda10.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda11.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda12.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda13.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda14.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda15.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
entry.Addenda16.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
}
// Set TraceNumber for Addendumer Addenda17 and Addenda18 SequenceNumber and EntryDetailSequenceNumber
seq++
addenda17Seq := 1
Expand Down Expand Up @@ -229,27 +232,30 @@ func (batch *IATBatch) isFieldInclusion() error {
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
}
// Verifies each Addenda* record is valid
if err := entry.Addenda10.Validate(); err != nil {
return err
}
if err := entry.Addenda11.Validate(); err != nil {
return err
}
if err := entry.Addenda12.Validate(); err != nil {
return err
}
if err := entry.Addenda13.Validate(); err != nil {
return err
}
if err := entry.Addenda14.Validate(); err != nil {
return err
}
if err := entry.Addenda15.Validate(); err != nil {
return err
}
if err := entry.Addenda16.Validate(); err != nil {
return err

if entry.Category != CategoryNOC {
// Verifies each Addenda* record is valid
if err := entry.Addenda10.Validate(); err != nil {
return err
}
if err := entry.Addenda11.Validate(); err != nil {
return err
}
if err := entry.Addenda12.Validate(); err != nil {
return err
}
if err := entry.Addenda13.Validate(); err != nil {
return err
}
if err := entry.Addenda14.Validate(); err != nil {
return err
}
if err := entry.Addenda15.Validate(); err != nil {
return err
}
if err := entry.Addenda16.Validate(); err != nil {
return err
}
}
// Verifies addendumer Addenda17 and Addenda18 records are valid
for _, IATAddenda := range entry.Addendum {
Expand Down Expand Up @@ -365,33 +371,35 @@ func (batch *IATBatch) isAddendaSequence() error {
// Verify Addenda* entry detail sequence numbers are valid
entryTN := entry.TraceNumberField()[8:]

if entry.Addenda10.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda10.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda11.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda11.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda12.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda12.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda13.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda13.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda14.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda14.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda15.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda15.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda16.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda16.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
if entry.Category != CategoryNOC {
if entry.Addenda10.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda10.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda11.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda11.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda12.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda12.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda13.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda13.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda14.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda14.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda15.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda15.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
if entry.Addenda16.EntryDetailSequenceNumberField() != entryTN {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda16.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
}

// check if sequence is ascending for addendumer - Addenda17 and Addenda18
Expand Down Expand Up @@ -447,33 +455,36 @@ func (batch *IATBatch) isCategory() error {
}

func (batch *IATBatch) addendaFieldInclusion(entry *IATEntryDetail) error {
if entry.Addenda10 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda10", Msg: msg}
}
if entry.Addenda11 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda11", Msg: msg}
}
if entry.Addenda12 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda12", Msg: msg}
}
if entry.Addenda13 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda13", Msg: msg}
}
if entry.Addenda14 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda14", Msg: msg}
}
if entry.Addenda15 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda15", Msg: msg}
}
if entry.Addenda16 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda16", Msg: msg}

if entry.Category != CategoryNOC {
if entry.Addenda10 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda10", Msg: msg}
}
if entry.Addenda11 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda11", Msg: msg}
}
if entry.Addenda12 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda12", Msg: msg}
}
if entry.Addenda13 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda13", Msg: msg}
}
if entry.Addenda14 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda14", Msg: msg}
}
if entry.Addenda15 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda15", Msg: msg}
}
if entry.Addenda16 == nil {
msg := fmt.Sprint(msgIATBatchAddendaRequired)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda16", Msg: msg}
}
}
return nil
}
Expand All @@ -499,15 +510,17 @@ func (batch *IATBatch) Validate() error {

for _, entry := range batch.Entries {

// Addendum cannot be greater than 7, There can be a maximum of 2 Addenda17 and a maximum of 5 Addenda18
if len(entry.Addendum) > 7 {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddendum}
switch entry.Category {

case CategoryForward:
if len(entry.Addendum) > 7 {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddendum}
}
default:
}

addenda17Count := 0
addenda18Count := 0
addenda98Count := 0
addenda99Count := 0

for _, IATAddenda := range entry.Addendum {

Expand All @@ -525,23 +538,27 @@ func (batch *IATBatch) Validate() error {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg}
}
case "98":
addenda98Count = addenda98Count + 1
if addenda98Count > 1 {
msg := fmt.Sprintf(msgBatchIATAddendumCount, addenda99Count, "98")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg}
if batch.GetHeader().IATIndicator != "IATCOR" {
msg := fmt.Sprintf(msgBatchIATNOC, batch.GetHeader().IATIndicator, "IATCOR")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "IATIndicator", Msg: msg}
}
case "99":
addenda99Count = addenda99Count + 1
if addenda99Count > 1 {
msg := fmt.Sprintf(msgBatchIATAddendumCount, addenda99Count, "99")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg}
if batch.GetHeader().StandardEntryClassCode != "COR" {
msg := fmt.Sprintf(msgBatchIATNOC, batch.GetHeader().StandardEntryClassCode, "COR")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg}
}
switch entry.TransactionCode {
case 22, 27, 32, 37, 42, 47, 52, 55,
23, 28, 33, 38, 43, 48, 53,
24, 29, 34, 39, 44, 49, 54:
msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "IATCOR")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg}
}
case "99":
// Only one Addenda99 can be added in EntryDetail.AddIATAddenda
default:
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATInvalidAddendumer}
}
}
}
// Add type specific validation.
// ...
return nil
}
5 changes: 2 additions & 3 deletions iatBatchHeader.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ type IATBatchHeader struct {
// ForeignExchangeReference Contains either the foreign exchange rate used to execute
// the foreign exchange conversion of a cross-border entry or another reference to the foreign
// exchange transaction.
// ToDo: potentially write a validator
ForeignExchangeReference string `json:"foreignExchangeReference"`

// ISODestinationCountryCode is the two-character code, as approved by the International
Expand Down Expand Up @@ -180,8 +179,8 @@ func (iatBh *IATBatchHeader) Parse(record string) {
iatBh.recordType = "5"
// 2-4 If the entries are credits, always "220". If the entries are debits, always "225"
iatBh.ServiceClassCode = iatBh.parseNumField(record[1:4])
// 05-20 Leave Blank - It is only used for corrected IAT entries
iatBh.IATIndicator = " "
// 05-20 Blank except for corrected IAT entries
iatBh.IATIndicator = iatBh.parseStringField(record[4:20])
// 21-22 A code indicating currency conversion
// “FV” Fixed-to-Variable
// “VF” Variable-to-Fixed
Expand Down
21 changes: 19 additions & 2 deletions iatBatchHeader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ func mockIATReturnBatchHeaderFF() *IATBatchHeader {
return bh
}

// mockIATNOCBatchHeaderFF creates a IAT Return BatchHeader that is Fixed-Fixed
func mockIATNOCBatchHeaderFF() *IATBatchHeader {
bh := NewIATBatchHeader()
bh.ServiceClassCode = 220
bh.IATIndicator = "IATCOR"
bh.ForeignExchangeIndicator = "FF"
bh.ForeignExchangeReferenceIndicator = 3
bh.ISODestinationCountryCode = "US"
bh.OriginatorIdentification = "123456789"
bh.StandardEntryClassCode = "COR"
bh.CompanyEntryDescription = "TRADEPAYMT"
bh.ISOOriginatingCurrencyCode = "CAD"
bh.ISODestinationCurrencyCode = "USD"
bh.ODFIIdentification = "12104288"
return bh
}

// testMockIATBatchHeaderFF creates a IAT BatchHeader Fixed-Fixed
func testMockIATBatchHeaderFF(t testing.TB) {
bh := mockIATBatchHeaderFF()
Expand Down Expand Up @@ -108,8 +125,8 @@ func testParseIATBatchHeader(t testing.TB) {
if record.ServiceClassCode != 220 {
t.Errorf("ServiceClassCode Expected '225' got: %v", record.ServiceClassCode)
}
if record.IATIndicator != " " {
t.Errorf("IATIndicator Expected ' ' got: %v", record.IATIndicator)
if record.IATIndicator != "" {
t.Errorf("IATIndicator Expected '' got: %v", record.IATIndicator)
}
if record.ForeignExchangeIndicator != "FF" {
t.Errorf("ForeignExchangeIndicator Expected ' ' got: %v",
Expand Down
Loading

0 comments on commit 83ead47

Please sign in to comment.